mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-05-06 19:27:14 +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);
|
||||
}
|
||||
}
|
||||
|
||||
// 4jcraft: better schematic caching
|
||||
void LevelGenerationOptions::processSchematics(LevelChunk* chunk) {
|
||||
|
||||
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
||||
Level::maxBuildHeight, chunk->z * 16 + 16);
|
||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||
++it) {
|
||||
ApplySchematicRuleDefinition* rule = *it;
|
||||
rule->processSchematic(&chunkBox, chunk);
|
||||
|
||||
ChunkRuleCacheKey key;
|
||||
key.chunkX = chunk->x;
|
||||
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 cz = (chunk->z << 4);
|
||||
|
||||
for (auto it = m_structureRules.begin(); it != m_structureRules.end();
|
||||
it++) {
|
||||
for (auto it = cacheIt->second.structureRules.begin();
|
||||
it != cacheIt->second.structureRules.end(); ++it) {
|
||||
ConsoleGenerateStructure* structureStart = *it;
|
||||
|
||||
if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15,
|
||||
cz + 15)) {
|
||||
BoundingBox* bb = new BoundingBox(cx, cz, cx + 15, cz + 15);
|
||||
structureStart->postProcess(chunk->level, nullptr, bb);
|
||||
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) {
|
||||
AABB chunkBox(chunk->x * 16, 0, chunk->z * 16, chunk->x * 16 + 16,
|
||||
Level::maxBuildHeight, chunk->z * 16 + 16);
|
||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||
++it) {
|
||||
ApplySchematicRuleDefinition* rule = *it;
|
||||
rule->processSchematicLighting(&chunkBox, chunk);
|
||||
|
||||
ChunkRuleCacheKey key;
|
||||
key.chunkX = chunk->x;
|
||||
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;
|
||||
}
|
||||
m_schematics.clear();
|
||||
clearChunkRuleCache();
|
||||
}
|
||||
|
||||
void LevelGenerationOptions::clearChunkRuleCache() {
|
||||
m_chunkRuleCache.clear();
|
||||
}
|
||||
|
||||
ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile(
|
||||
|
|
@ -376,8 +445,9 @@ ConsoleSchematicFile* LevelGenerationOptions::loadSchematicFile(
|
|||
}
|
||||
|
||||
ConsoleSchematicFile* schematic = nullptr;
|
||||
// 4jcraft: we use a constructor to reduce copies.
|
||||
std::vector<uint8_t> data(pbData, pbData + dataLength);
|
||||
ByteArrayInputStream bais(data);
|
||||
ByteArrayInputStream bais(std::move(data));
|
||||
DataInputStream dis(&bais);
|
||||
schematic = new ConsoleSchematicFile();
|
||||
schematic->load(&dis);
|
||||
|
|
@ -572,13 +642,15 @@ int LevelGenerationOptions::packMounted(void* pParam, int iPad, uint32_t dwErr,
|
|||
}
|
||||
|
||||
void LevelGenerationOptions::reset_start() {
|
||||
clearChunkRuleCache();
|
||||
for (auto it = m_schematicRules.begin(); it != m_schematicRules.end();
|
||||
it++) {
|
||||
++it) { // what in the flip in the fuck
|
||||
(*it)->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void LevelGenerationOptions::reset_finish() {
|
||||
clearChunkRuleCache();
|
||||
// if (m_spawnPos) { delete m_spawnPos; m_spawnPos
|
||||
// = nullptr; } if (m_stringTable) { delete m_stringTable;
|
||||
// m_stringTable = nullptr; }
|
||||
|
|
|
|||
|
|
@ -106,6 +106,31 @@ public:
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
GrSource* m_pSrc;
|
||||
|
|
@ -164,6 +189,8 @@ private:
|
|||
bool m_bHaveMinY;
|
||||
int m_minY;
|
||||
std::unordered_map<std::wstring, ConsoleSchematicFile*> m_schematics;
|
||||
std::unordered_map<ChunkRuleCacheKey, ChunkRuleCacheEntry, ChunkRuleCacheKeyHash>
|
||||
m_chunkRuleCache;
|
||||
std::vector<BiomeOverride*> m_biomeOverrides;
|
||||
std::vector<StartFeature*> m_features;
|
||||
|
||||
|
|
@ -196,6 +223,7 @@ public:
|
|||
|
||||
void processSchematics(LevelChunk* chunk);
|
||||
void processSchematicsLighting(LevelChunk* chunk);
|
||||
void clearChunkRuleCache();
|
||||
|
||||
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,
|
||||
unsigned int length);
|
||||
ByteArrayInputStream(std::vector<uint8_t>& buf);
|
||||
// takes ownership of the vector
|
||||
ByteArrayInputStream(std::vector<uint8_t>&& buf);
|
||||
virtual ~ByteArrayInputStream();
|
||||
virtual int read();
|
||||
virtual int read(std::vector<uint8_t>& b);
|
||||
|
|
@ -36,4 +38,4 @@ public:
|
|||
mark = 0;
|
||||
pos = 0;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ ByteArrayInputStream::ByteArrayInputStream(std::vector<uint8_t>& 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
|
||||
// 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
|
||||
|
|
|
|||
Loading…
Reference in a new issue