mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-08-20 09:57:10 +00:00
refactor: faster chunk & schematic loading
This commit is contained in:
parent
342a195ba8
commit
4dfba6ccd8
|
|
@ -269,39 +269,103 @@ void LevelGenerationOptions::addAttribute(const std::wstring& attributeName,
|
||||||
GameRuleDefinition::addAttribute(attributeName, attributeValue);
|
GameRuleDefinition::addAttribute(attributeName, attributeValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 4jcraft: better schematic caching
|
||||||
void LevelGenerationOptions::processSchematics(LevelChunk* chunk) {
|
void LevelGenerationOptions::processSchematics(LevelChunk* chunk) {
|
||||||
|
|
||||||
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
||||||
Level::maxBuildHeight, chunk->z * 16 + 16);
|
Level::maxBuildHeight, chunk->z * 16 + 16);
|
||||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
|
||||||
++it) {
|
ChunkRuleCacheKey key;
|
||||||
ApplySchematicRuleDefinition* rule = *it;
|
key.chunkX = chunk->x;
|
||||||
rule->processSchematic(&chunkBox, chunk);
|
key.chunkZ = chunk->z;
|
||||||
|
key.dimension = chunk->level->dimension->id;
|
||||||
|
|
||||||
|
auto cacheIt = m_chunkRuleCache.find(key);
|
||||||
|
if (cacheIt == m_chunkRuleCache.end()) {
|
||||||
|
// if no cache hit, show em the goods
|
||||||
|
ChunkRuleCacheEntry entry;
|
||||||
|
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||||
|
++it) {
|
||||||
|
ApplySchematicRuleDefinition* rule = *it;
|
||||||
|
if (rule->checkIntersects(chunkBox.x0, chunkBox.y0, chunkBox.z0,
|
||||||
|
chunkBox.x1, chunkBox.y1, chunkBox.z1)) {
|
||||||
|
entry.schematicRules.push_back(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int cx = (chunk->x << 4);
|
||||||
|
int cz = (chunk->z << 4);
|
||||||
|
for (auto it = m_structureRules.begin(); it != m_structureRules.end();
|
||||||
|
++it) {
|
||||||
|
ConsoleGenerateStructure* structureStart = *it;
|
||||||
|
if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15,
|
||||||
|
cz + 15)) {
|
||||||
|
entry.structureRules.push_back(structureStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheIt = m_chunkRuleCache.insert(
|
||||||
|
std::pair<ChunkRuleCacheKey, ChunkRuleCacheEntry>(key, entry)).first;
|
||||||
|
} else if (cacheIt->second.structureRules.empty() && !m_structureRules.empty()) {
|
||||||
|
int cx = (chunk->x << 4);
|
||||||
|
int cz = (chunk->z << 4);
|
||||||
|
for (auto it = m_structureRules.begin(); it != m_structureRules.end();
|
||||||
|
++it) {
|
||||||
|
ConsoleGenerateStructure* structureStart = *it;
|
||||||
|
if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15,
|
||||||
|
cz + 15)) {
|
||||||
|
cacheIt->second.structureRules.push_back(structureStart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = cacheIt->second.schematicRules.begin();
|
||||||
|
it != cacheIt->second.schematicRules.end(); ++it) {
|
||||||
|
(*it)->processSchematic(&chunkBox, chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cx = (chunk->x << 4);
|
int cx = (chunk->x << 4);
|
||||||
int cz = (chunk->z << 4);
|
int cz = (chunk->z << 4);
|
||||||
|
|
||||||
for (auto it = m_structureRules.begin(); it != m_structureRules.end();
|
for (auto it = cacheIt->second.structureRules.begin();
|
||||||
it++) {
|
it != cacheIt->second.structureRules.end(); ++it) {
|
||||||
ConsoleGenerateStructure* structureStart = *it;
|
ConsoleGenerateStructure* structureStart = *it;
|
||||||
|
BoundingBox* bb = new BoundingBox(cx, cz, cx + 15, cz + 15);
|
||||||
if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15,
|
structureStart->postProcess(chunk->level, nullptr, bb);
|
||||||
cz + 15)) {
|
delete bb;
|
||||||
BoundingBox* bb = new BoundingBox(cx, cz, cx + 15, cz + 15);
|
|
||||||
structureStart->postProcess(chunk->level, nullptr, bb);
|
|
||||||
delete bb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelGenerationOptions::processSchematicsLighting(LevelChunk* chunk) {
|
void LevelGenerationOptions::processSchematicsLighting(LevelChunk* chunk) {
|
||||||
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
||||||
Level::maxBuildHeight, chunk->z * 16 + 16);
|
Level::maxBuildHeight, chunk->z * 16 + 16);
|
||||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
|
||||||
++it) {
|
ChunkRuleCacheKey key;
|
||||||
ApplySchematicRuleDefinition* rule = *it;
|
key.chunkX = chunk->x;
|
||||||
rule->processSchematicLighting(&chunkBox, chunk);
|
key.chunkZ = chunk->z;
|
||||||
|
key.dimension = chunk->level->dimension->id;
|
||||||
|
|
||||||
|
auto cacheIt = m_chunkRuleCache.find(key);
|
||||||
|
if (cacheIt == m_chunkRuleCache.end()) {
|
||||||
|
// lighting shouldn't affect structure rules...
|
||||||
|
ChunkRuleCacheEntry entry;
|
||||||
|
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||||
|
++it) {
|
||||||
|
ApplySchematicRuleDefinition* rule = *it;
|
||||||
|
if (rule->checkIntersects(chunkBox.x0, chunkBox.y0, chunkBox.z0,
|
||||||
|
chunkBox.x1, chunkBox.y1, chunkBox.z1)) {
|
||||||
|
entry.schematicRules.push_back(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// structureRules is initially empty because it will be populated by processSchematics later onn
|
||||||
|
|
||||||
|
cacheIt = m_chunkRuleCache.insert(
|
||||||
|
std::pair<ChunkRuleCacheKey, ChunkRuleCacheEntry>(key, entry)).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = cacheIt->second.schematicRules.begin();
|
||||||
|
it != cacheIt->second.schematicRules.end(); ++it) {
|
||||||
|
(*it)->processSchematicLighting(&chunkBox, chunk);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,6 +423,11 @@ void LevelGenerationOptions::clearSchematics() {
|
||||||
delete it->second;
|
delete it->second;
|
||||||
}
|
}
|
||||||
m_schematics.clear();
|
m_schematics.clear();
|
||||||
|
clearChunkRuleCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LevelGenerationOptions::clearChunkRuleCache() {
|
||||||
|
m_chunkRuleCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile(
|
ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile(
|
||||||
|
|
@ -376,8 +445,9 @@ ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleSchematicFile* schematic = nullptr;
|
ConsoleSchematicFile* schematic = nullptr;
|
||||||
|
// 4jcraft: we use a constructor to reduce copies.
|
||||||
std::vector<uint8_t> data(pbData, pbData + dataLength);
|
std::vector<uint8_t> data(pbData, pbData + dataLength);
|
||||||
ByteArrayInputStream bais(data);
|
ByteArrayInputStream bais(std::move(data));
|
||||||
DataInputStream dis(&bais);
|
DataInputStream dis(&bais);
|
||||||
schematic = new ConsoleSchematicFile();
|
schematic = new ConsoleSchematicFile();
|
||||||
schematic->load(&dis);
|
schematic->load(&dis);
|
||||||
|
|
@ -572,13 +642,15 @@ int LevelGenerationOptions::packMounted(void* pParam, int iPad, uint32_t dwErr,
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelGenerationOptions::reset_start() {
|
void LevelGenerationOptions::reset_start() {
|
||||||
|
clearChunkRuleCache();
|
||||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||||
it++) {
|
++it) { // what in the flip in the fuck
|
||||||
(*it)->reset();
|
(*it)->reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelGenerationOptions::reset_finish() {
|
void LevelGenerationOptions::reset_finish() {
|
||||||
|
clearChunkRuleCache();
|
||||||
// if (m_spawnPos) { delete m_spawnPos; m_spawnPos
|
// if (m_spawnPos) { delete m_spawnPos; m_spawnPos
|
||||||
// = nullptr; } if (m_stringTable) { delete m_stringTable;
|
// = nullptr; } if (m_stringTable) { delete m_stringTable;
|
||||||
// m_stringTable = nullptr; }
|
// m_stringTable = nullptr; }
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,31 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ChunkRuleCacheKey {
|
||||||
|
int chunkX;
|
||||||
|
int chunkZ;
|
||||||
|
int dimension;
|
||||||
|
|
||||||
|
bool operator==(const ChunkRuleCacheKey& other) const {
|
||||||
|
return chunkX == other.chunkX && chunkZ == other.chunkZ &&
|
||||||
|
dimension == other.dimension;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkRuleCacheKeyHash {
|
||||||
|
std::size_t operator()(const ChunkRuleCacheKey& key) const {
|
||||||
|
std::size_t h1 = std::hash<int>()(key.chunkX);
|
||||||
|
std::size_t h2 = std::hash<int>()(key.chunkZ);
|
||||||
|
std::size_t h3 = std::hash<int>()(key.dimension);
|
||||||
|
return h1 ^ (h2 << 1) ^ (h3 << 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkRuleCacheEntry {
|
||||||
|
std::vector<ApplySchematicRuleDefinition*> schematicRules;
|
||||||
|
std::vector<ConsoleGenerateStructure*> structureRules;
|
||||||
|
};
|
||||||
|
|
||||||
eSrc m_src;
|
eSrc m_src;
|
||||||
|
|
||||||
GrSource* m_pSrc;
|
GrSource* m_pSrc;
|
||||||
|
|
@ -164,6 +189,8 @@ private:
|
||||||
bool m_bHaveMinY;
|
bool m_bHaveMinY;
|
||||||
int m_minY;
|
int m_minY;
|
||||||
std::unordered_map<std::wstring, ConsoleSchematicFile*> m_schematics;
|
std::unordered_map<std::wstring, ConsoleSchematicFile*> m_schematics;
|
||||||
|
std::unordered_map<ChunkRuleCacheKey, ChunkRuleCacheEntry, ChunkRuleCacheKeyHash>
|
||||||
|
m_chunkRuleCache;
|
||||||
std::vector<BiomeOverride*> m_biomeOverrides;
|
std::vector<BiomeOverride*> m_biomeOverrides;
|
||||||
std::vector<StartFeature*> m_features;
|
std::vector<StartFeature*> m_features;
|
||||||
|
|
||||||
|
|
@ -196,6 +223,7 @@ public:
|
||||||
|
|
||||||
void processSchematics(LevelChunk* chunk);
|
void processSchematics(LevelChunk* chunk);
|
||||||
void processSchematicsLighting(LevelChunk* chunk);
|
void processSchematicsLighting(LevelChunk* chunk);
|
||||||
|
void clearChunkRuleCache();
|
||||||
|
|
||||||
bool checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1);
|
bool checkIntersects(int x0, int y0, int z0, int x1, int y1, int z1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ public:
|
||||||
ByteArrayInputStream(std::vector<uint8_t>& buf, unsigned int offset,
|
ByteArrayInputStream(std::vector<uint8_t>& buf, unsigned int offset,
|
||||||
unsigned int length);
|
unsigned int length);
|
||||||
ByteArrayInputStream(std::vector<uint8_t>& buf);
|
ByteArrayInputStream(std::vector<uint8_t>& buf);
|
||||||
|
// takes ownership of the vector
|
||||||
|
ByteArrayInputStream(std::vector<uint8_t>&& buf);
|
||||||
virtual ~ByteArrayInputStream();
|
virtual ~ByteArrayInputStream();
|
||||||
virtual int read();
|
virtual int read();
|
||||||
virtual int read(std::vector<uint8_t>& b);
|
virtual int read(std::vector<uint8_t>& b);
|
||||||
|
|
@ -36,4 +38,4 @@ public:
|
||||||
mark = 0;
|
mark = 0;
|
||||||
pos = 0;
|
pos = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,11 @@ ByteArrayInputStream::ByteArrayInputStream(std::vector<uint8_t>& buf)
|
||||||
this->buf = buf;
|
this->buf = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4jcraft: helper function to create a ByteArrayInputStream from a vector of bytes to avoid one copy
|
||||||
|
ByteArrayInputStream::ByteArrayInputStream(std::vector<uint8_t>&& buf)
|
||||||
|
: buf(std::move(buf)), pos(0), count(this->buf.size()), mark(0) {
|
||||||
|
}
|
||||||
|
|
||||||
// Reads the next byte of data from this input stream. The value byte is
|
// Reads the next byte of data from this input stream. The value byte is
|
||||||
// returned as an int in the range 0 to 255. If no byte is available because the
|
// returned as an int in the range 0 to 255. If no byte is available because the
|
||||||
// end of the stream has been reached, the value -1 is returned. This read
|
// end of the stream has been reached, the value -1 is returned. This read
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue