Procházet zdrojové kódy

reduce the amount of memory needed per chunk by about 50% and bring chunk generation speed up to 115 chunks per second

Kolja Strohm před 3 měsíci
rodič
revize
684046bf87

+ 2 - 2
FactoryCraft/Block.h

@@ -14,8 +14,8 @@
 #include "TickSourceType.h"
 
 #define CONST_BLOCK(maybeBlock, type) \
-    (maybeBlock ? maybeBlock          \
-                : Game::INSTANCE->zBlockType((int)type)->zDefault())
+    ((maybeBlock) ? (maybeBlock)      \
+                  : Game::INSTANCE->zBlockType((int)type)->zDefault())
 
 class ItemType;
 class Chunk;

+ 3 - 2
FactoryCraft/BlockType.h

@@ -33,7 +33,6 @@ private:
     Framework::Text name;
     bool needModelSubscription;
     int initialMapColor;
-    Block* defaultBlock;
     Framework::RCArray<Framework::Text> groupNames;
     float hardness;
     Framework::RCArray<DropConfig> dropConfigs;
@@ -41,6 +40,8 @@ private:
     Framework::RCArray<InteractionConfig> interactionConfigs;
 
 protected:
+    Block* defaultBlock;
+
     BlockType();
     virtual ~BlockType();
 
@@ -60,7 +61,7 @@ public:
     virtual BlockType* initializeDefault();
     void addDropConfig(DropConfig* config);
     const Framework::RCArray<DropConfig>& getDropConfigs() const;
-    virtual const Block* zDefault() const;
+    const Block* zDefault() const;
 
     virtual ItemType* createItemType() const = 0;
 

+ 6 - 3
FactoryCraft/Chest.cpp

@@ -21,9 +21,10 @@ void Chest::onDialogClosed(Framework::Text dialogId)
     open = 0;
     userEntityId = 0;
     NetworkMessage* msg = new NetworkMessage();
+    auto pos = Dimension::chunkCoordinates(getPos());
     msg->animateBlockBone(getDimensionId(),
         Game::getChunkCenter(getPos().x, getPos().y),
-        Chunk::index(Dimension::chunkCoordinates(getPos())),
+        Chunk::index(pos.x, pos.y) * WORLD_HEIGHT + pos.z,
         1,
         1.0,
         Framework::Vec3<float>(0.5f, 0.f, 0.45f),
@@ -60,9 +61,10 @@ bool Chest::interact(Item* zItem, Entity* zActor, bool& itemChanged)
             userEntityId = zActor->getId();
             open = 1;
             NetworkMessage* msg = new NetworkMessage();
+            auto pos = Dimension::chunkCoordinates(getPos());
             msg->animateBlockBone(getDimensionId(),
                 Game::getChunkCenter(getPos().x, getPos().y),
-                Chunk::index(Dimension::chunkCoordinates(getPos())),
+                Chunk::index(pos.x, pos.y) * WORLD_HEIGHT + pos.z,
                 1,
                 1.0,
                 Framework::Vec3<float>(0.5f, 0.f, 0.45f),
@@ -80,9 +82,10 @@ void Chest::sendModelInfo(NetworkMessage* zMessage)
 {
     if (open)
     {
+        auto pos = Dimension::chunkCoordinates(getPos());
         zMessage->animateBlockBone(getDimensionId(),
             Game::getChunkCenter(getPos().x, getPos().y),
-            Chunk::index(Dimension::chunkCoordinates(getPos())),
+            Chunk::index(pos.x, pos.y) * WORLD_HEIGHT + pos.z,
             1,
             0.0,
             Framework::Vec3<float>(0.5f, 0.f, 0.45f),

+ 193 - 227
FactoryCraft/Chunk.cpp

@@ -20,13 +20,11 @@ Chunk::Chunk(Framework::Punkt location, int dimensionId)
       worldUpdated(1),
       currentlyLoading(1)
 {
-    blocks = new Block*[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT];
-    blockIds = new unsigned short[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT];
+    blocks = new Block**[WORLD_HEIGHT];
+    blockIds = new unsigned short*[WORLD_HEIGHT];
     lightData = new unsigned char[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6];
-    memset(blocks, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(Block*));
-    memset(blockIds,
-        0,
-        CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * sizeof(unsigned short));
+    memset(blocks, 0, WORLD_HEIGHT * sizeof(Block**));
+    memset(blockIds, 0, WORLD_HEIGHT * sizeof(unsigned short*));
     memset(lightData, 0, CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6);
     zNeighbours[0] = 0;
     zNeighbours[1] = 0;
@@ -44,9 +42,16 @@ Chunk::Chunk(Framework::Punkt location,
 
 Chunk::~Chunk()
 {
-    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
+    for (int h = 0; h < WORLD_HEIGHT; h++)
     {
-        if (blocks[i]) blocks[i]->release();
+        for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++)
+        {
+            if (blocks[h] && blocks[h][i]) blocks[h][i]->release();
+        }
+        delete[] blocks[h];
+        blocks[h] = 0;
+        delete[] blockIds[h];
+        blockIds[h] = 0;
     }
     delete[] blocks;
     delete[] blockIds;
@@ -83,20 +88,20 @@ void Chunk::tick(TickQueue* zQueue)
 
 void Chunk::postTick() {}
 
-void Chunk::addLightSource(int index)
+void Chunk::addLightSource(int z, int index)
 {
     for (int i : lightSources)
     {
-        if (i == index) return;
+        if (i == index * WORLD_HEIGHT + z) return;
     }
-    lightSources.add(index);
+    lightSources.add(index * WORLD_HEIGHT + z);
 }
 
-void Chunk::removeLightSource(int index)
+void Chunk::removeLightSource(int z, int index)
 {
     for (auto i = lightSources.begin(); i; i++)
     {
-        if (i.val() == index)
+        if (i.val() == index * WORLD_HEIGHT + z)
         {
             i.remove();
             return;
@@ -112,31 +117,36 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
         {
             for (int z = 0; z < WORLD_HEIGHT; z++)
             {
-                int index = Chunk::index({x, y, z});
-                const BlockType* type
-                    = Game::INSTANCE->zBlockType(blockIds[index]);
-                if (isVisible(index) && type->doesNeedClientInstance())
+                int index = Chunk::index(x, y);
+                const BlockType* type = Game::INSTANCE->zBlockType(
+                    blockIds[z] ? blockIds[z][index] : 0);
+                if (isVisible(z, index) && type->doesNeedClientInstance())
                 {
-                    if (z > 0 && z < WORLD_HEIGHT - 1)
+                    int mI = index * WORLD_HEIGHT + z;
+                    if (z < WORLD_HEIGHT - 1)
                     {
-                        instanceMap[index + (CHUNK_SIZE + 1) * WORLD_HEIGHT + 1]
+                        instanceMap[mI + (CHUNK_SIZE + 1) * WORLD_HEIGHT + 1]
                             = 1;
-                        instanceMap[index + (CHUNK_SIZE + 1) * WORLD_HEIGHT - 1]
+                    }
+                    if (z > 0)
+                    {
+                        instanceMap[mI + (CHUNK_SIZE + 1) * WORLD_HEIGHT - 1]
                             = 1;
                     }
-                    instanceMap[index + WORLD_HEIGHT] = 1;
-                    instanceMap[index + (2 * CHUNK_SIZE + 1) * WORLD_HEIGHT]
-                        = 1;
-                    instanceMap[index + CHUNK_SIZE * WORLD_HEIGHT] = 1;
-                    instanceMap[index + (CHUNK_SIZE + 2) * WORLD_HEIGHT] = 1;
-                    zWriter->schreibe((char*)&blockIds[index], 2);
-                    zWriter->schreibe((char*)&index, 4);
+                    instanceMap[mI + WORLD_HEIGHT] = 1;
+                    instanceMap[mI + (2 * CHUNK_SIZE + 1) * WORLD_HEIGHT] = 1;
+                    instanceMap[mI + CHUNK_SIZE * WORLD_HEIGHT] = 1;
+                    instanceMap[mI + (CHUNK_SIZE + 2) * WORLD_HEIGHT] = 1;
+                    assert(blockIds[z]);
+                    zWriter->schreibe((char*)&blockIds[z][index], 2);
+                    zWriter->schreibe((char*)&mI, 4);
                     char state = 0;
                     if (type->isFluid())
                     {
                         state |= 1;
                     }
-                    if ((blocks[index] && blocks[index]->isPassable())
+                    if ((blocks[z] && blocks[z][index]
+                            && blocks[z][index]->isPassable())
                         || (type->zDefault()->isPassable()))
                     {
                         state |= 2;
@@ -145,7 +155,9 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
                     if ((state | 1) == state)
                     {
                         FluidBlock* fluidBlock
-                            = dynamic_cast<FluidBlock*>(blocks[index]);
+                            = blocks[z]
+                                ? dynamic_cast<FluidBlock*>(blocks[z][index])
+                                : 0;
                         char data
                             = fluidBlock ? fluidBlock->getFlowOptions() : 0;
                         zWriter->schreibe(&data, 1);
@@ -156,8 +168,8 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
                     if ((state | 2) == state)
                     {
                         float speedModifier
-                            = blocks[index]
-                                ? blocks[index]->getSpeedModifier()
+                            = blocks[z] && blocks[z][index]
+                                ? blocks[z][index]->getSpeedModifier()
                                 : type->zDefault()->getSpeedModifier();
                         zWriter->schreibe((char*)&speedModifier, 4);
                     }
@@ -276,7 +288,7 @@ void Chunk::sendLightToClient(
                 {
                     if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE)
                     {
-                        int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
+                        int index = Chunk::index(x, y) * WORLD_HEIGHT + z;
                         zWriter->schreibe((char*)&index, 4);
                         zWriter->schreibe((char*)(lightData + index * 6), 6);
                     }
@@ -337,22 +349,18 @@ void Chunk::sendLightToClient(
     }
 }
 
-bool Chunk::isVisible(int index) const
+bool Chunk::isVisible(int z, int index) const
 {
-    unsigned short blockType
-        = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId()
-                        : blockIds[index];
+    unsigned short blockType = blockIds[z] ? blockIds[z][index] : 0;
     if (blockType)
     {
-        if (CONST_BLOCK(0, blockIds[index])->isTransparent()
-            || CONST_BLOCK(0, blockIds[index])->isPassable())
+        if (CONST_BLOCK(0, blockType)->isTransparent()
+            || CONST_BLOCK(0, blockType)->isPassable())
             return 1;
         else
         {
             Framework::Vec3<int> indexPos
-                = {(index / WORLD_HEIGHT) / CHUNK_SIZE,
-                    (index / WORLD_HEIGHT) % CHUNK_SIZE,
-                    index % WORLD_HEIGHT};
+                = {(index) / CHUNK_SIZE, (index) % CHUNK_SIZE, z};
             for (int d = 0; d < 6; d++)
             {
                 Framework::Either<Block*, int> n = BlockTypeEnum::NO_BLOCK;
@@ -392,11 +400,10 @@ bool Chunk::isVisible(int index) const
     return 0;
 }
 
-void Chunk::broadcastLightData(int index, bool foreground)
+void Chunk::broadcastLightData(int z, int index, bool foreground)
 {
-    int x = (index / WORLD_HEIGHT) / CHUNK_SIZE;
-    int y = (index / WORLD_HEIGHT) % CHUNK_SIZE;
-    int z = index % WORLD_HEIGHT;
+    int x = index / CHUNK_SIZE;
+    int y = index % CHUNK_SIZE;
     NetworkMessage* msg = new NetworkMessage();
     msg->addressDimension(Game::INSTANCE->zDimension(dimensionId));
     char* message = new char[19];
@@ -404,7 +411,7 @@ void Chunk::broadcastLightData(int index, bool foreground)
     *(int*)(message + 1) = x + this->location.x - CHUNK_SIZE / 2;
     *(int*)(message + 5) = y + this->location.y - CHUNK_SIZE / 2;
     *(int*)(message + 9) = z;
-    memcpy(message + 13, lightData + index * 6, 6);
+    memcpy(message + 13, lightData + (index * WORLD_HEIGHT + z) * 6, 6);
     msg->setMessage(message, 19);
     if (!foreground) msg->setUseBackground();
     notifyObservers(msg);
@@ -417,16 +424,15 @@ Framework::Either<Block*, int> Chunk::zBlockNeighbor(
         && location.y < CHUNK_SIZE && location.z >= 0
         && location.z < WORLD_HEIGHT)
     {
-        int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT
-                  + location.z;
+        int index = Chunk::index(location.x, location.y);
         if (zNeighborChunk)
         {
             *zNeighborChunk = this;
         }
-        if (blocks[index])
-            return blocks[index];
+        if (blocks[location.z] && blocks[location.z][index])
+            return blocks[location.z][index];
         else
-            return (int)blockIds[index];
+            return blockIds[location.z] ? (int)blockIds[location.z][index] : 0;
     }
     if (location.z >= 0 && location.z < WORLD_HEIGHT)
         return Game::INSTANCE->zBlockAt(
@@ -563,11 +569,13 @@ void Chunk::initializeLightning()
                     visited[y][z] |= 1 << x;
                     unsigned char* light
                         = getLightData(Framework::Vec3<int>(x, y, z));
-                    int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
+                    int index = Chunk::index(x, y);
                     const Block* current
-                        = blocks[index]
-                            ? blocks[index]
-                            : Game::INSTANCE->zBlockType(blockIds[index])
+                        = (blocks[z] && blocks[z][index])
+                            ? blocks[z][index]
+                            : Game::INSTANCE
+                                  ->zBlockType(
+                                      blockIds[z] ? blockIds[z][index] : 0)
                                   ->zDefault();
                     light[0] = lightAbove[0];
                     light[1] = lightAbove[1];
@@ -598,14 +606,16 @@ void Chunk::initializeLightning()
                     {
                         for (int y = 0; y < CHUNK_SIZE; y++)
                         {
-                            int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT + z;
+                            int index = Chunk::index(x, y);
                             unsigned char* light
                                 = getLightData(Framework::Vec3<int>(x, y, z));
                             const Block* current
-                                = blocks[index]
-                                    ? blocks[index]
+                                = (blocks[z] && blocks[z][index])
+                                    ? blocks[z][index]
                                     : Game::INSTANCE
-                                          ->zBlockType(blockIds[index])
+                                          ->zBlockType(blockIds[z]
+                                                           ? blockIds[z][index]
+                                                           : 0)
                                           ->zDefault();
                             unsigned char newLight[3] = {0, 0, 0};
                             for (int i = 0; i < 4; i++)
@@ -649,11 +659,14 @@ void Chunk::initializeLightning()
                                     newLight[2]
                                         = (unsigned char)(light[2] * 0.8f);
                                     const Block* above
-                                        = blocks[index - 1]
-                                            ? blocks[index - 1]
+                                        = blocks[z + 1] && blocks[z + 1][index]
+                                            ? blocks[z + 1][index]
                                             : Game::INSTANCE
                                                   ->zBlockType(
-                                                      blockIds[index - 1])
+                                                      blockIds[z + 1]
+                                                          ? blockIds[z + 1]
+                                                                    [index]
+                                                          : 0)
                                                   ->zDefault();
                                     above->filterPassingLight(newLight);
                                     if (newLight[0] > lightAbove[0]
@@ -707,12 +720,12 @@ Framework::Either<Block*, int> Chunk::zBlockAt(
     Framework::Vec3<int> location) const
 {
     if (location.z < 0 || location.z >= WORLD_HEIGHT) return 0;
-    int index = Chunk::index(location);
+    int index = Chunk::index(location.x, location.y);
     assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT);
-    if (blocks[index])
-        return blocks[index];
+    if (blocks[location.z] && blocks[location.z][index])
+        return blocks[location.z][index];
     else
-        return (int)blockIds[index];
+        return blockIds[location.z] ? (int)blockIds[location.z][index] : 0;
 }
 
 const Block* Chunk::zBlockConst(Framework::Vec3<int> location) const
@@ -722,12 +735,13 @@ const Block* Chunk::zBlockConst(Framework::Vec3<int> location) const
     return Game::INSTANCE->zBlockType(b.getB())->zDefault();
 }
 
-const Block* Chunk::zBlockConst(int index) const
+const Block* Chunk::zBlockConst(int z, int index) const
 {
-    if (blocks[index])
-        return blocks[index];
+    if (blocks[z] && blocks[z][index])
+        return blocks[z][index];
     else
-        return Game::INSTANCE->zBlockType(blockIds[index])->zDefault();
+        return Game::INSTANCE->zBlockType(blockIds[z] ? blockIds[z][index] : 0)
+            ->zDefault();
 }
 
 void Chunk::instantiateBlock(Framework::Vec3<int> location)
@@ -748,8 +762,8 @@ void Chunk::instantiateBlock(Framework::Vec3<int> location)
 
 void Chunk::generateBlock(Framework::Vec3<int> location)
 {
-    int index = Chunk::index(location);
-    if (blockIds[index]) return;
+    int index = Chunk::index(location.x, location.y);
+    if (blockIds[location.z] && blockIds[location.z][index]) return;
     auto generated = Game::INSTANCE->zGenerator()->generateSingleBlock(
         {location.x + this->location.x - CHUNK_SIZE / 2,
             location.y + this->location.y - CHUNK_SIZE / 2,
@@ -763,9 +777,9 @@ void Chunk::generateBlock(Framework::Vec3<int> location)
 
 void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
 {
-    int index = Chunk::index(location);
-    assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT && index >= 0);
-    Block* old = blocks[index];
+    int index = Chunk::index(location.x, location.y);
+    assert(index < CHUNK_SIZE * CHUNK_SIZE && index >= 0);
+    Block* old = blocks[location.z] ? blocks[location.z][index] : 0;
     if (old && old->isTickSource() != TickSourceType::NONE)
     { // remove from tick sorces
         if (old->isTickSource() == TickSourceType::EACH_TICK)
@@ -797,27 +811,42 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
             }
         }
     }
+    if (!blockIds[location.z])
+    {
+        blockIds[location.z] = new unsigned short[CHUNK_SIZE * CHUNK_SIZE];
+        memset(blockIds[location.z],
+            0,
+            CHUNK_SIZE * CHUNK_SIZE * sizeof(unsigned short));
+    }
+    if (!blocks[location.z])
+    {
+        blocks[location.z] = new Block*[CHUNK_SIZE * CHUNK_SIZE];
+        memset(blocks[location.z], 0, CHUNK_SIZE * CHUNK_SIZE * sizeof(Block*));
+    }
     bool change = 0;
     bool wasLightSource
         = old ? old->zBlockType()->isLightSource()
-              : Game::INSTANCE->zBlockType(blockIds[index])->isLightSource();
+              : Game::INSTANCE->zBlockType(blockIds[location.z][index])
+                    ->isLightSource();
     bool isLightSource = 0;
     if (block)
     {
-        change
-            = blockIds[index] != (unsigned short)block->zBlockType()->getId();
-        blockIds[index] = (unsigned short)block->zBlockType()->getId();
+        change = blockIds[location.z][index]
+              != (unsigned short)block->zBlockType()->getId();
+
+        blockIds[location.z][index]
+            = (unsigned short)block->zBlockType()->getId();
         isLightSource = block->zBlockType()->isLightSource();
     }
     else
     {
         if (old != 0)
         {
-            blockIds[index] = BlockTypeEnum::NO_BLOCK;
+            blockIds[location.z][index] = BlockTypeEnum::NO_BLOCK;
         }
         change = old != 0;
     }
-    blocks[index] = block;
+    blocks[location.z][index] = block;
     if (old) old->release();
     if (block && block->isTickSource() != TickSourceType::NONE)
     { // add to tick sources
@@ -836,9 +865,9 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
         if (isLightSource != wasLightSource)
         {
             if (isLightSource)
-                addLightSource(index);
+                addLightSource(location.z, index);
             else
-                removeLightSource(index);
+                removeLightSource(location.z, index);
         }
         if (added)
         {
@@ -864,20 +893,27 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
 
 void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
 {
-    int index = Chunk::index(location);
-    assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT);
-    bool wasLightSource
-        = Game::INSTANCE->zBlockType(blockIds[index])->isLightSource();
+    int index = Chunk::index(location.x, location.y);
+    assert(index < CHUNK_SIZE * CHUNK_SIZE);
+    int oldType = blockIds[location.z] ? blockIds[location.z][index] : 0;
+    bool wasLightSource = Game::INSTANCE->zBlockType(oldType)->isLightSource();
     bool isLightSource = Game::INSTANCE->zBlockType(type)->isLightSource();
-    if (blockIds[index] != (unsigned short)type)
+    if (oldType != (unsigned short)type)
     {
-        blockIds[index] = (unsigned short)type;
+        if (!blockIds[location.z])
+        {
+            blockIds[location.z] = new unsigned short[CHUNK_SIZE * CHUNK_SIZE];
+            memset(blockIds[location.z],
+                0,
+                CHUNK_SIZE * CHUNK_SIZE * sizeof(unsigned short));
+        }
+        blockIds[location.z][index] = (unsigned short)type;
         if (isLightSource != wasLightSource)
         {
             if (isLightSource)
-                addLightSource(index);
+                addLightSource(location.z, index);
             else
-                removeLightSource(index);
+                removeLightSource(location.z, index);
         }
         if (added)
         {
@@ -898,18 +934,20 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
 
 void Chunk::sendBlockInfo(Framework::Vec3<int> location)
 {
-    int index = Chunk::index(location);
+    int index = Chunk::index(location.x, location.y);
     char* msg = new char[14];
     msg[0] = 0; // set block
-    *(unsigned short*)(msg + 1) = blockIds[index];
+    int typeId = blockIds[location.z] ? blockIds[location.z][index] : 0;
+    *(unsigned short*)(msg + 1) = typeId;
     *(int*)(msg + 3) = index;
     char state = 0;
-    const BlockType* type = Game::INSTANCE->zBlockType(blockIds[index]);
+    const BlockType* type = Game::INSTANCE->zBlockType(typeId);
     if (type->isFluid())
     {
         state |= 1;
     }
-    if ((blocks[index] && blocks[index]->isPassable())
+    if ((blocks[location.z] && blocks[location.z][index]
+            && blocks[location.z][index]->isPassable())
         || (type->zDefault()->isPassable()))
     {
         state |= 2;
@@ -917,24 +955,25 @@ void Chunk::sendBlockInfo(Framework::Vec3<int> location)
     msg[7] = state;
     if ((state | 1) == state)
     {
-        FluidBlock* fluidBlock = dynamic_cast<FluidBlock*>(blocks[index]);
+        FluidBlock* fluidBlock = dynamic_cast<FluidBlock*>(
+            blocks[location.z] ? blocks[location.z][index] : 0);
         msg[8] = fluidBlock ? fluidBlock->getFlowOptions() : 0;
         msg[9] = fluidBlock ? fluidBlock->getDistanceToSource() : 0;
     }
     if ((state | 2) == state)
     {
-        *(float*)(msg + 10) = blocks[index]
-                                ? blocks[index]->getSpeedModifier()
+        *(float*)(msg + 10) = blocks[location.z] && blocks[location.z][index]
+                                ? blocks[location.z][index]->getSpeedModifier()
                                 : type->zDefault()->getSpeedModifier();
     }
     NetworkMessage* message = new NetworkMessage();
     message->addressChunck(this);
     message->setMessage(msg, 14);
     notifyObservers(message);
-    if (blocks[index])
+    if (blocks[location.z] && blocks[location.z][index])
     {
         NetworkMessage* message = new NetworkMessage();
-        blocks[index]->sendModelInfo(message);
+        blocks[location.z][index]->sendModelInfo(message);
         if (message->isEmpty())
         {
             message->release();
@@ -952,7 +991,7 @@ void Chunk::sendBlockInfo(Framework::Vec3<int> location)
         if (loc.x >= 0 && loc.x < CHUNK_SIZE && loc.y >= 0 && loc.y < CHUNK_SIZE
             && loc.z >= 0 && loc.z < WORLD_HEIGHT)
         {
-            broadcastLightData(Chunk::index(loc), true);
+            broadcastLightData(loc.z, Chunk::index(loc.x, loc.y), true);
         }
         else if (loc.z >= 0 && loc.z < WORLD_HEIGHT && i < 4 && zNeighbours[i])
         {
@@ -976,68 +1015,15 @@ void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
 {
     cs.lock();
     int dirIndex = getDirectionIndex(dir);
-    Chunk* old = zNeighbours[dirIndex];
     zNeighbours[dirIndex] = zChunk;
-    for (int i = 0; i < CHUNK_SIZE; i++)
-    {
-        for (int z = 0; z < WORLD_HEIGHT; z++)
-        {
-            int index = 0;
-            int j = 0;
-            if (dir == NORTH)
-            {
-                index = i * CHUNK_SIZE * WORLD_HEIGHT + z;
-                j = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z;
-            }
-            else if (dir == EAST)
-            {
-                index = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z;
-                j = i * WORLD_HEIGHT + z;
-            }
-            else if (dir == SOUTH)
-            {
-                index = (i * CHUNK_SIZE + CHUNK_SIZE - 1) * WORLD_HEIGHT + z;
-                j = i * CHUNK_SIZE * WORLD_HEIGHT + z;
-            }
-            else if (dir == WEST)
-            {
-                index = i * WORLD_HEIGHT + z;
-                j = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z;
-            }
-            bool needsTransmission = 0;
-            zNeighbours[dirIndex] = old;
-            bool visible = isVisible(index);
-            zNeighbours[dirIndex] = zChunk;
-            if (!visible && isVisible(index))
-            {
-                needsTransmission = 1;
-            }
-            if (zChunk)
-            {
-                if (!blocks[index])
-                {
-                    if (zChunk->zBlockConst(j)->isTransparent()
-                        && !blockIds[index])
-                    {
-                        generateBlock(Framework::Vec3<int>(
-                            (index / WORLD_HEIGHT) / CHUNK_SIZE,
-                            (index / WORLD_HEIGHT) % CHUNK_SIZE,
-                            index % WORLD_HEIGHT));
-                    }
-                }
-            }
-            if (needsTransmission && added)
-            {
-                sendBlockInfo(
-                    Framework::Vec3<int>((index / WORLD_HEIGHT) / CHUNK_SIZE,
-                        (index / WORLD_HEIGHT) % CHUNK_SIZE,
-                        index % WORLD_HEIGHT));
-            }
-        }
-    }
     cs.unlock();
 }
 
+Chunk* Chunk::zNeighbor(Direction dir) const
+{
+    return zNeighbours[getDirectionIndex(dir)];
+}
+
 void Chunk::load(Framework::StreamReader* zReader)
 {
     for (int index = 0; index < WORLD_HEIGHT * CHUNK_SIZE * CHUNK_SIZE; index++)
@@ -1074,26 +1060,26 @@ void Chunk::load(Framework::StreamReader* zReader)
 
 void Chunk::save(Framework::StreamWriter* zWriter)
 {
-    for (int index = 0; index < WORLD_HEIGHT * CHUNK_SIZE * CHUNK_SIZE; index++)
+    for (int index = 0; index < CHUNK_SIZE * CHUNK_SIZE; index++)
     {
-        unsigned short blockType
-            = blocks[index]
-                ? (unsigned short)blocks[index]->zBlockType()->getId()
-                : blockIds[index];
-        zWriter->schreibe((char*)&blockType, 2);
-        if (blockType)
+        for (int z = 0; z < WORLD_HEIGHT; z++)
         {
-            if (blocks[index])
-            {
-                bool d = 1;
-                zWriter->schreibe((char*)&d, 1);
-                Game::INSTANCE->zBlockType(blockType)->saveBlock(
-                    blocks[index], zWriter);
-            }
-            else
+            unsigned short blockType = blockIds[z] ? blockIds[z][index] : 0;
+            zWriter->schreibe((char*)&blockType, 2);
+            if (blockType)
             {
-                bool d = 0;
-                zWriter->schreibe((char*)&d, 1);
+                if (blocks[z] && blocks[z][index])
+                {
+                    bool d = 1;
+                    zWriter->schreibe((char*)&d, 1);
+                    Game::INSTANCE->zBlockType(blockType)->saveBlock(
+                        blocks[z][index], zWriter);
+                }
+                else
+                {
+                    bool d = 0;
+                    zWriter->schreibe((char*)&d, 1);
+                }
             }
         }
     }
@@ -1101,48 +1087,17 @@ void Chunk::save(Framework::StreamWriter* zWriter)
 
 void Chunk::removeUnusedBlocks()
 {
-/* for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
-{
-    if (!blocks[i] && blockIds[i])
-    {
-        int x = (i / WORLD_HEIGHT) / CHUNK_SIZE;
-        int y = (i / WORLD_HEIGHT) % CHUNK_SIZE;
-        int z = i % WORLD_HEIGHT;
-        bool visible = 0;
-        if (CONST_BLOCK(0, blockIds[i])->isTransparent()
-            || CONST_BLOCK(0, blockIds[i])->isPassable())
-            visible = 1;
-        else
-        {
-            for (int d = 0; d < 6 && !visible; d++)
-            {
-                auto n = zBlockNeighbor(
-                    getDirection((Directions)getDirectionFromIndex(d))
-                        + Framework::Vec3<int>(x, y, z),
-                    0);
-                if (n.isA()
-                    && (((Block*)n)->isPassable()
-                        || ((Block*)n)->isTransparent()))
-                    visible = 1;
-                if (n.isB()
-                    && (CONST_BLOCK(0, n)->isTransparent()
-                        || CONST_BLOCK(0, n)->isPassable()))
-                    visible = 1;
-            }
-        }
-        if (!visible)
-        {
-            putBlockAt({x, y, z}, 0);
-            putBlockTypeAt({x, y, z}, 0);
-        }
-    }
-}*/
+    // no longer needed becaus only used blocks are generated in the first place
 #ifdef _DEBUG
     int count = 0;
-    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
+    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++)
     {
-        if (Game::INSTANCE->zBlockType(blockIds[i])->doesNeedClientInstance())
-            count++;
+        for (int z = 0; z < WORLD_HEIGHT; z++)
+        {
+            if (Game::INSTANCE->zBlockType(blockIds[z] ? blockIds[z][i] : 0)
+                    ->doesNeedClientInstance())
+                count++;
+        }
     }
     Framework::Logging::debug()
         << "chunk " << location.x << ", " << location.y
@@ -1157,18 +1112,24 @@ int Chunk::getDimensionId() const
 
 void Chunk::onLoaded()
 {
-    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
+    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++)
     {
-        if (blocks[i]) blocks[i]->onLoaded();
+        for (int z = 0; z < WORLD_HEIGHT; z++)
+        {
+            if (blocks[z] && blocks[z][i]) blocks[z][i]->onLoaded();
+        }
     }
     currentlyLoading = 0;
 }
 
 void Chunk::onUnloaded()
 {
-    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
+    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE; i++)
     {
-        if (blocks[i]) blocks[i]->onUnloaded();
+        for (int z = 0; z < WORLD_HEIGHT; z++)
+        {
+            if (blocks[z] && blocks[z][i]) blocks[z][i]->onUnloaded();
+        }
     }
 }
 
@@ -1216,7 +1177,9 @@ bool Chunk::hasObservers() const
 
 unsigned char* Chunk::getLightData(Framework::Vec3<int> location) const
 {
-    int index = Chunk::index(location) * 6;
+    int index
+        = (Chunk::index(location.x, location.y) * WORLD_HEIGHT + location.z)
+        * 6;
     assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT * 6);
     return lightData + index;
 }
@@ -1224,8 +1187,8 @@ unsigned char* Chunk::getLightData(Framework::Vec3<int> location) const
 void Chunk::setLightData(
     Framework::Vec3<int> location, unsigned char* data, bool foreground)
 {
-    int index = Chunk::index(location);
-    memcpy(lightData + index * 6, data, 6);
+    int index = Chunk::index(location.x, location.y);
+    memcpy(lightData + (index * WORLD_HEIGHT + location.z) * 6, data, 6);
     // check if neighbor is a visible block and send update to clients
     bool needSend = 0;
     for (int i = 0; i < 6; i++)
@@ -1237,8 +1200,8 @@ void Chunk::setLightData(
             if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0
                 && pos.y < CHUNK_SIZE)
             {
-                int bi = (pos.x * CHUNK_SIZE + pos.y) * WORLD_HEIGHT + pos.z;
-                int type = blockIds[bi];
+                int bi = (pos.x * CHUNK_SIZE + pos.y);
+                int type = blockIds[pos.z] ? blockIds[pos.z][bi] : 0;
                 needSend |= Game::INSTANCE->zBlockType(type)
                                 ->doesNeedClientInstance();
                 if (needSend) break;
@@ -1260,18 +1223,21 @@ void Chunk::setLightData(
     }
     if (needSend)
     {
-        broadcastLightData(index, foreground);
+        broadcastLightData(location.z, index, foreground);
     }
 }
 
 int Chunk::getBlockTypeAt(Framework::Vec3<int> location) const
 {
-    return blockIds[index(location)];
+    return blockIds[location.z]
+             ? blockIds[location.z][index(location.x, location.y)]
+             : 0;
 }
 
 int Chunk::getBlockTypeAtWC(int x, int y, int z) const
 {
-    return blockIds[index(Dimension::chunkCoordinates({x, y, z}))];
+    auto pos = Dimension::chunkCoordinates({x, y, z});
+    return blockIds[pos.z] ? blockIds[pos.z][index(pos.x, pos.y)] : 0;
 }
 
 void Chunk::onEntityEnters(Entity* zEntity, Chunk* lastChunk)

+ 10 - 10
FactoryCraft/Chunk.h

@@ -20,9 +20,9 @@ class Chunk : public virtual Framework::ReferenceCounter,
 private:
     int dimensionId;
     Framework::Punkt location;
-    Block** blocks;
+    Block*** blocks;
     Chunk* zNeighbours[4];
-    unsigned short* blockIds;
+    unsigned short** blockIds;
     Framework::Either<Block*, int> zBlockNeighbor(
         Framework::Vec3<int> location, OUT Chunk** zNeighborChunk);
     bool added;
@@ -35,13 +35,13 @@ private:
     unsigned char* lightData;
     bool currentlyLoading;
 
-    void addLightSource(int index);
-    void removeLightSource(int index);
+    void addLightSource(int z, int index);
+    void removeLightSource(int z, int index);
     void sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap);
     void sendLightToClient(Framework::StreamWriter* zWriter, bool* instanceMap);
-    void broadcastLightData(int index, bool foreground);
-    const Block* zBlockConst(int index) const;
-    bool isVisible(int index) const;
+    void broadcastLightData(int z, int index, bool foreground);
+    const Block* zBlockConst(int z, int index) const;
+    bool isVisible(int z, int index) const;
 
 public:
     Chunk(Framework::Punkt location, int dimensionId);
@@ -74,6 +74,7 @@ public:
     void putBlockTypeAt(Framework::Vec3<int> location, int type);
     void sendBlockInfo(Framework::Vec3<int> location);
     void setNeighbor(Direction dir, Chunk* zChunk);
+    Chunk* zNeighbor(Direction dir) const;
     void load(Framework::StreamReader* zReader);
     void save(Framework::StreamWriter* zWriter);
     void removeUnusedBlocks();
@@ -95,10 +96,9 @@ public:
     void onEntityLeaves(Entity* zEntity, Chunk* zNextChunk);
     bool hasObserver(int entityId) const;
 
-    inline static int index(Framework::Vec3<int> localLocation)
+    inline static int index(int x, int y)
     {
-        return (localLocation.x * CHUNK_SIZE + localLocation.y) * WORLD_HEIGHT
-             + localLocation.z;
+        return x * CHUNK_SIZE + y;
     }
 
     friend ChunkMap;

+ 13 - 5
FactoryCraft/ChunkMap.cpp

@@ -27,13 +27,21 @@ ChunkMap::ChunkMap(Chunk* zChunk)
             bool visible = 1;
             for (int height = WORLD_HEIGHT / 2 - 1; height >= 0; height--)
             {
-                int index = (x * CHUNK_SIZE + y) * WORLD_HEIGHT;
+                int index = x * CHUNK_SIZE + y;
                 const Block* block1
-                    = CONST_BLOCK(zChunk->blocks[index + height * 2],
-                        zChunk->blockIds[index + height * 2]);
+                    = CONST_BLOCK(zChunk->blocks[height * 2]
+                                      ? zChunk->blocks[height * 2][index]
+                                      : 0,
+                        zChunk->blockIds[height * 2]
+                            ? zChunk->blockIds[height * 2][index]
+                            : 0);
                 const Block* block2
-                    = CONST_BLOCK(zChunk->blocks[index + height * 2 + 1],
-                        zChunk->blockIds[index + height * 2 + 1]);
+                    = CONST_BLOCK(zChunk->blocks[height * 2 + 1]
+                                      ? zChunk->blocks[height * 2 + 1][index]
+                                      : 0,
+                        zChunk->blockIds[height * 2 + 1]
+                            ? zChunk->blockIds[height * 2 + 1][index]
+                            : 0);
                 int color1 = 0;
                 int color2 = 0;
                 if (visible) color2 = block2->getMapColor();

+ 19 - 4
FactoryCraft/Dimension.cpp

@@ -12,6 +12,7 @@
 #include "NoBlock.h"
 #include "Player.h"
 #include "TickOrganizer.h"
+#include "WorldGenerator.h"
 
 using namespace Framework;
 
@@ -437,7 +438,6 @@ void Dimension::setChunk(Chunk* chunk, Punkt center)
     if (chunk)
     {
         chunkList.add(chunk);
-        chunk->setAdded();
     }
     getAddrOfWorld(center + Punkt(CHUNK_SIZE, 0), addr);
     Chunk* zChunk = chunks->z(addr, 8);
@@ -447,6 +447,7 @@ void Dimension::setChunk(Chunk* chunk, Punkt center)
         if (chunk)
         {
             chunk->setNeighbor(EAST, zChunk);
+            Game::INSTANCE->zGenerator()->postprocessChunk(zChunk);
         }
     }
     getAddrOfWorld(center + Punkt(-CHUNK_SIZE, 0), addr);
@@ -454,25 +455,39 @@ void Dimension::setChunk(Chunk* chunk, Punkt center)
     if (zChunk)
     {
         zChunk->setNeighbor(EAST, chunk);
-        if (chunk) chunk->setNeighbor(WEST, zChunk);
+        if (chunk)
+        {
+            chunk->setNeighbor(WEST, zChunk);
+            Game::INSTANCE->zGenerator()->postprocessChunk(zChunk);
+        }
     }
     getAddrOfWorld(center + Punkt(0, CHUNK_SIZE), addr);
     zChunk = chunks->z(addr, 8);
     if (zChunk)
     {
         zChunk->setNeighbor(NORTH, chunk);
-        if (chunk) chunk->setNeighbor(SOUTH, zChunk);
+        if (chunk)
+        {
+            chunk->setNeighbor(SOUTH, zChunk);
+            Game::INSTANCE->zGenerator()->postprocessChunk(zChunk);
+        }
     }
     getAddrOfWorld(center + Punkt(0, -CHUNK_SIZE), addr);
     zChunk = chunks->z(addr, 8);
     if (zChunk)
     {
         zChunk->setNeighbor(SOUTH, chunk);
-        if (chunk) chunk->setNeighbor(NORTH, zChunk);
+        if (chunk)
+        {
+            chunk->setNeighbor(NORTH, zChunk);
+            Game::INSTANCE->zGenerator()->postprocessChunk(zChunk);
+        }
     }
     DoLaterHandler laterHandler;
     if (chunk)
     {
+        Game::INSTANCE->zGenerator()->postprocessChunk(chunk);
+        chunk->setAdded();
         cs.lock();
         int index = 0;
         for (ArrayIterator<RequestQueue> iterator = waitingRequests.begin();

+ 125 - 0
FactoryCraft/DimensionGenerator.cpp

@@ -470,6 +470,131 @@ Chunk* BiomedCavedDimensionGenerator::generateChunk(int centerX, int centerY)
     return chunk;
 }
 
+void BiomedCavedDimensionGenerator::postprocessChunk(Chunk* zChunk)
+{
+    zMemory()->lock();
+    int borderOffset = 1;
+    bool generatedMore = true;
+    while (generatedMore && borderOffset <= CHUNK_SIZE / 2)
+    {
+        int centerX = zChunk->getCenter().x;
+        int centerY = zChunk->getCenter().y;
+        generatedMore = false;
+        for (int d = 0; d < 4; d++)
+        {
+            Direction dir = getDirectionFromIndex(d);
+            Chunk* neighbor = zChunk->zNeighbor(dir);
+            if (!neighbor) continue;
+            int xOffset = 0;
+            int yOffset = 0;
+            int xj = 0;
+            int yj = 0;
+            switch (dir)
+            {
+            case WEST:
+                yj = 1;
+                xOffset = 1;
+                break;
+            case NORTH:
+                xj = 1;
+                yOffset = 1;
+                break;
+            case EAST:
+                yj = 1;
+                xOffset = -1;
+                break;
+            case SOUTH:
+                xj = 1;
+                yOffset = -1;
+                break;
+            }
+            for (int o = 0; o < borderOffset; o++)
+            {
+                for (int j = 0; j < CHUNK_SIZE; j++)
+                {
+                    int x = xOffset * o + xj * j;
+                    if (xOffset < 0)
+                    {
+                        x += CHUNK_SIZE - 1;
+                    }
+                    int y = yOffset * o + yj * j;
+                    if (yOffset < 0)
+                    {
+                        y += CHUNK_SIZE - 1;
+                    }
+                    *xPos = (float)x + (float)centerX - CHUNK_SIZE / 2;
+                    *yPos = (float)y + (float)centerY - CHUNK_SIZE / 2;
+                    // calculate height layers
+                    calculateHeightLayers();
+                    // calculate biom
+                    BiomGenerator* biom = zBiomGenerator();
+                    // generate blocks
+                    for (int z = (int)round(*terrainHeightP) - 1; z >= 0; z--)
+                    {
+                        *zPos = (float)z;
+                        int type = zChunk->getBlockTypeAt(
+                            Framework::Vec3<int>(x, y, z));
+                        if (!type)
+                        {
+                            bool needed = false;
+                            for (int i = 0; i < 6; i++)
+                            {
+                                Framework::Vec3<int> pos
+                                    = getDirection(
+                                          (Directions)getDirectionFromIndex(i))
+                                    + Framework::Vec3<int>(x, y, z);
+                                const Block* neighborBlock = 0;
+                                if (pos.x >= 0 && pos.x < CHUNK_SIZE
+                                    && pos.y >= 0 && pos.y < CHUNK_SIZE
+                                    && pos.z >= 0 && pos.z < WORLD_HEIGHT)
+                                {
+                                    neighborBlock = zChunk->zBlockConst(pos);
+                                }
+                                else if (pos.z >= 0 && pos.z < WORLD_HEIGHT
+                                         && i == d)
+                                {
+                                    neighborBlock = neighbor->zBlockConst(
+                                        {pos.x + centerX
+                                                - neighbor->getCenter().x,
+                                            pos.y + centerY
+                                                - neighbor->getCenter().y,
+                                            pos.z});
+                                }
+                                if (neighborBlock
+                                    && neighborBlock->isTransparent())
+                                {
+                                    needed = true;
+                                    break;
+                                }
+                            }
+                            if (needed)
+                            {
+                                auto generated = biom->generateBlock(
+                                    x + centerX - CHUNK_SIZE / 2,
+                                    y + centerY - CHUNK_SIZE / 2,
+                                    z,
+                                    getDimensionId(),
+                                    zChunk);
+                                if (generated.isA())
+                                    zChunk->putBlockAt(
+                                        Framework::Vec3<int>(x, y, z),
+                                        generated);
+                                else
+                                    zChunk->putBlockTypeAt(
+                                        Framework::Vec3<int>(x, y, z),
+                                        generated);
+                                generatedMore = true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        borderOffset++;
+    }
+    zMemory()->unlock();
+}
+
 void BiomedCavedDimensionGenerator::generateEntities(Chunk* zChunk)
 {
     zMemory()->lock();

+ 7 - 4
FactoryCraft/DimensionGenerator.h

@@ -74,6 +74,7 @@ public:
     Dimension* createDimension();
     virtual void initialize(int worldSeed);
     virtual Chunk* generateChunk(int centerX, int centerY) = 0;
+    virtual void postprocessChunk(Chunk* zChunk) = 0;
     virtual Framework::Either<Block*, int> generateBlock(
         Framework::Vec3<int> location)
         = 0;
@@ -193,11 +194,13 @@ public:
 
     virtual void initialize(int worldSeed) override;
 
-    Chunk* generateChunk(int centerX, int centerY);
-    void generateEntities(Chunk* zChunk);
-    Framework::Either<Block*, int> generateBlock(Framework::Vec3<int> location);
+    Chunk* generateChunk(int centerX, int centerY) override;
+    void postprocessChunk(Chunk* zChunk) override;
+    void generateEntities(Chunk* zChunk) override;
+    Framework::Either<Block*, int> generateBlock(
+        Framework::Vec3<int> location) override;
     bool spawnStructure(Framework::Vec3<int> location,
-        std::function<bool(GeneratorTemplate* tmpl)> filter);
+        std::function<bool(GeneratorTemplate* tmpl)> filter) override;
 
     void addBiomGenerator(BiomGenerator* biomGenerator);
     const Framework::RCArray<BiomGenerator>& getBiomGenerators() const;

+ 5 - 3
FactoryCraft/Game.cpp

@@ -153,9 +153,11 @@ void Game::initialize()
     Framework::Logging::info() << "Loaded " << blockTypeArray.getEintragAnzahl()
                                << " block types from data/blocks";
     blockTypes = new BlockType*[2 + blockTypeArray.getEintragAnzahl()];
-    blockTypes[0]
-        = new NoBlockBlockType(&NoBlock::INSTANCE, "__not_yet_generated");
-    blockTypes[1] = new NoBlockBlockType(&AirBlock::INSTANCE, "Air");
+    blockTypes[0] = new NoBlockBlockType(
+        dynamic_cast<Block*>(NoBlock::INSTANCE.getThis()),
+        "__not_yet_generated");
+    blockTypes[1] = new NoBlockBlockType(
+        dynamic_cast<Block*>(AirBlock::INSTANCE.getThis()), "Air");
     blockTypeCount = 2;
     for (BlockType* blockType : blockTypeArray)
     {

+ 5 - 10
FactoryCraft/NoBlock.cpp

@@ -1,8 +1,7 @@
 #include "NoBlock.h"
 
-NoBlockBlockType::NoBlockBlockType(const Block* defaultB, Framework::Text name)
-    : BlockType(),
-      defaultB(defaultB)
+NoBlockBlockType::NoBlockBlockType(Block* defaultB, Framework::Text name)
+    : BlockType()
 {
     setModel(new ModelInfo("", Framework::RCArray<Framework::Text>(), 0, 1.f));
     setNeedModelSubscription(false);
@@ -12,6 +11,7 @@ NoBlockBlockType::NoBlockBlockType(const Block* defaultB, Framework::Text name)
     setNeedsClientInstance(false);
     setMapColor(0);
     setHardness(0.f);
+    defaultBlock = defaultB;
 }
 
 ItemType* NoBlockBlockType::createItemType() const
@@ -52,11 +52,6 @@ Block* NoBlockBlockType::createBlockAt(
     return 0;
 }
 
-const Block* NoBlockBlockType::zDefault() const
-{
-    return defaultB;
-}
-
 NoBlock::NoBlock()
     : Block(BlockTypeEnum::NO_BLOCK, {0, 0, 0}, 0, false)
 {
@@ -74,7 +69,7 @@ bool NoBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 
 void NoBlock::onPostTick() {}
 
-const NoBlock NoBlock::INSTANCE;
+NoBlock NoBlock::INSTANCE;
 
 AirBlock::AirBlock()
     : Block(BlockTypeEnum::AIR, {0, 0, 0}, 0, false)
@@ -93,4 +88,4 @@ bool AirBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 
 void AirBlock::onPostTick() {}
 
-const AirBlock AirBlock::INSTANCE;
+AirBlock AirBlock::INSTANCE;

+ 3 - 7
FactoryCraft/NoBlock.h

@@ -5,9 +5,6 @@
 
 class NoBlockBlockType : public BlockType
 {
-private:
-    const Block* defaultB;
-
 protected:
     virtual Block* createBlock(
         Framework::Vec3<int> position, int dimensionId) const override;
@@ -21,17 +18,16 @@ protected:
     virtual Block* createBlockAt(Framework::Vec3<int> position,
         int dimensionId,
         Item* zUsedItem) const override;
-    virtual const Block* zDefault() const override;
 
 public:
-    NoBlockBlockType(const Block* defaultB, Framework::Text name);
+    NoBlockBlockType(Block* defaultB, Framework::Text name);
     virtual ItemType* createItemType() const override;
 };
 
 class NoBlock : public Block
 {
 public:
-    static const NoBlock INSTANCE;
+    static NoBlock INSTANCE;
 
 protected:
     NoBlock();
@@ -44,7 +40,7 @@ protected:
 class AirBlock : public Block
 {
 public:
-    static const AirBlock INSTANCE;
+    static AirBlock INSTANCE;
 
 protected:
     AirBlock();

+ 5 - 0
FactoryCraft/WorldGenerator.cpp

@@ -201,6 +201,11 @@ Framework::Either<Block*, int> WorldGenerator::generateSingleBlock(
     return zGenerator(dimensionId)->generateBlock(location);
 }
 
+void WorldGenerator::postprocessChunk(Chunk* zChunk)
+{
+    zGenerator(zChunk->getDimensionId())->postprocessChunk(zChunk);
+}
+
 bool WorldGenerator::spawnStructure(Framework::Vec3<int> location,
     int dimensionId,
     std::function<bool(GeneratorTemplate* tmpl)> filter)

+ 3 - 0
FactoryCraft/WorldGenerator.h

@@ -6,6 +6,8 @@
 #include "Area.h"
 #include "DimensionGenerator.h"
 
+class Chunk;
+
 class WorldGenerator : public Framework::Thread
 {
 private:
@@ -27,6 +29,7 @@ public:
     void exitAndWait();
     Framework::Either<Block*, int> generateSingleBlock(
         Framework::Vec3<int> location, int dimensionId);
+    void postprocessChunk(Chunk* zChunk);
     bool spawnStructure(Framework::Vec3<int> location,
         int dimensionId,
         std::function<bool(GeneratorTemplate* tmpl)> filter);