Browse Source

improved world generation speed to about 100 chunks per second

Kolja Strohm 3 months ago
parent
commit
ef81ccb5f2

+ 2 - 3
FactoryCraft/BasicTool.cpp

@@ -1849,7 +1849,7 @@ JSONObjectValidationBuilder* DamagingItemSkillFactory::addToValidator(
             ->whichIsGreaterOrEqual(0.0)
             ->withDefault(0.8)
             ->finishNumber()
-            ->withRequiredNumber("invalidAdditionalStaminaCostDeviderPerLevel")
+            ->withRequiredNumber("invalidStaminaCostDeviderPerLevel")
             ->whichIsGreaterOrEqual(0.0)
             ->withDefault(0.2)
             ->finishNumber()
@@ -1865,8 +1865,7 @@ JSONObjectValidationBuilder* DamagingItemSkillFactory::addToValidator(
             ->whichIsGreaterOrEqual(0.0)
             ->withDefault(0.98)
             ->finishNumber()
-            ->withRequiredNumber(
-                "invalidAdditionalDurabilityCostDeviderPerLevel")
+            ->withRequiredNumber("invalidDurabilityCostDeviderPerLevel")
             ->whichIsGreaterOrEqual(0.0)
             ->withDefault(0.02)
             ->finishNumber()

+ 23 - 22
FactoryCraft/BiomGenerator.cpp

@@ -30,7 +30,10 @@ void BiomGenerator::initialize(JExpressionMemory* zMemory)
     {
         collection->initialize(zMemory);
     }
-    condition->compile(zMemory);
+    if (condition)
+    {
+        condition->compile(zMemory);
+    }
 }
 
 Framework::Either<Block*, int> BiomGenerator::generateBlock(
@@ -53,7 +56,7 @@ Framework::Either<Block*, int> BiomGenerator::generateBlock(
 
 bool BiomGenerator::isApplicable()
 {
-    return condition->getValue();
+    return !condition || condition->getValue();
 }
 
 void BiomGenerator::generateStructures(int x,
@@ -64,20 +67,10 @@ void BiomGenerator::generateStructures(int x,
     Framework::Vec3<int>& maxPos,
     Framework::RCArray<GeneratedStructure>* zResult)
 {
-    int minSearchX = minPos.x - maxStructureOffset.x;
-    int minSearchY = minPos.y - maxStructureOffset.y;
-    int minSearchZ = MAX(minPos.z - maxStructureOffset.z, 0);
-    int maxSearchX = maxPos.x - minStructureOffset.x;
-    int maxSearchY = maxPos.y - minStructureOffset.y;
-    int maxSearchZ = MIN(maxPos.z - minStructureOffset.z, WORLD_HEIGHT - 1);
-    if (x >= minSearchX && x <= maxSearchX && y >= minSearchY && y <= maxSearchY
-        && z >= minSearchZ && z <= maxSearchZ)
+    for (StructureTemplateCollection* collection : templates)
     {
-        for (StructureTemplateCollection* collection : templates)
-        {
-            collection->generateStructures(
-                x, y, z, dimensionId, minPos, maxPos, zResult);
-        }
+        collection->generateStructures(
+            x, y, z, dimensionId, minPos, maxPos, zResult);
     }
 }
 
@@ -198,9 +191,12 @@ BiomGenerator* BiomGeneratorFactory::fromJson(
 {
     BiomGenerator* result = new BiomGenerator();
     result->setName(zJson->zValue("name")->asString()->getString());
-    result->setCondition(
-        Game::INSTANCE->zTypeRegistry()->fromJson<JBoolExpression>(
-            zJson->zValue("condition")));
+    if (zJson->hasValue("condition"))
+    {
+        result->setCondition(
+            Game::INSTANCE->zTypeRegistry()->fromJson<JBoolExpression>(
+                zJson->zValue("condition")));
+    }
     bool first = 1;
     for (Framework::JSON::JSONValue* value :
         *zJson->zValue("structurCollections")->asArray())
@@ -231,9 +227,12 @@ Framework::JSON::JSONObject* BiomGeneratorFactory::toJsonObject(
     Framework::JSON::JSONObject* result = new Framework::JSON::JSONObject();
     result->addValue(
         "name", new Framework::JSON::JSONString(zObject->getName()));
-    result->addValue("condition",
-        Game::INSTANCE->zTypeRegistry()->toJson<JBoolExpression>(
-            zObject->getCondition()));
+    if (zObject->getCondition())
+    {
+        result->addValue("condition",
+            Game::INSTANCE->zTypeRegistry()->toJson<JBoolExpression>(
+                zObject->getCondition()));
+    }
     Framework::JSON::JSONArray* collections = new Framework::JSON::JSONArray();
     for (StructureTemplateCollection* collection : zObject->getTemplates())
     {
@@ -264,7 +263,9 @@ JSONObjectValidationBuilder* BiomGeneratorFactory::addToValidator(
     return builder->withRequiredString("name")
         ->finishString()
         ->withRequiredAttribute("condition",
-            Game::INSTANCE->zTypeRegistry()->getValidator<JBoolExpression>())
+            Game::INSTANCE->zTypeRegistry()->getValidator<JBoolExpression>(),
+            false,
+            true)
         ->withRequiredArray("structurCollections")
         ->addAcceptedTypeInArray(Game::INSTANCE->zTypeRegistry()
                 ->getValidator<StructureTemplateCollection>())

+ 2 - 41
FactoryCraft/Block.cpp

@@ -30,7 +30,6 @@ Block::Block(
     currentTickTimeout = 0;
     interactable = 0;
     deadAndRemoved = 0;
-    memset(zNeighbours, 0, sizeof(Block*) * 6);
     memset(lightEmisionColor, 0, 3);
     mapColor = 0;
 }
@@ -45,7 +44,8 @@ void Block::onDestroy(Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill)
         {
             Framework::Vec3<int> pos
                 = getPos() + getDirection(getDirectionFromIndex(i));
-            if (neighbourTypes[i] == BlockTypeEnum::NO_BLOCK)
+            int type = Game::INSTANCE->getBlockType(pos, getDimensionId());
+            if (type == BlockTypeEnum::NO_BLOCK)
             {
                 Game::INSTANCE->zDimension(dimensionId)
                     ->placeBlock(pos,
@@ -209,29 +209,6 @@ void Block::postTick()
     }
 }
 
-void Block::setNeighbour(
-    Direction dir, Framework::Either<Block*, int> neighbour)
-{
-    if (neighbour.isA())
-        setNeighbourBlock(dir, neighbour);
-    else
-    {
-        setNeighbourBlock(dir, 0);
-        setNeighbourType(dir, neighbour);
-    }
-}
-
-void Block::setNeighbourBlock(Direction dir, Block* zN)
-{
-    if (zN) setNeighbourType(dir, zN->zBlockType()->getId());
-    zNeighbours[getDirectionIndex(dir)] = zN;
-}
-
-void Block::setNeighbourType(Direction dir, int type)
-{
-    neighbourTypes[getDirectionIndex(dir)] = type;
-}
-
 void Block::addToStructure(MultiblockStructure* structure)
 {
     if (structure->isBlockMember(this))
@@ -356,17 +333,6 @@ const Framework::Vec3<int> Block::getPos() const
     return (Framework::Vec3<int>)location;
 }
 
-bool Block::isVisible() const
-{
-    if (passable || transparent) return 1;
-    for (int i = 0; i < 6; i++)
-    {
-        const Block* neighbour = CONST_BLOCK(zNeighbours[i], neighbourTypes[i]);
-        if (neighbour->isPassable() || neighbour->isTransparent()) return 1;
-    }
-    return 0;
-}
-
 void Block::setHP(
     Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill, float hp)
 {
@@ -406,11 +372,6 @@ void Block::filterPassingLight(unsigned char rgb[3]) const
         memset(rgb, 0, 3);
 }
 
-Block* Block::zNeighbor(Direction dir) const
-{
-    return zNeighbours[getDirectionIndex(dir)];
-}
-
 void Block::updateModel(ModelInfo* zInfo) const
 {
     Dimension* dim = Game::INSTANCE->zDimension(getDimensionId());

+ 0 - 8
FactoryCraft/Block.h

@@ -41,8 +41,6 @@ protected:
     float hardness;
     int typeId;
     float speedModifier;
-    Block* zNeighbours[6];
-    int neighbourTypes[6];
 
     int minTickTimeout;
     int maxTickTimeout;
@@ -93,10 +91,6 @@ public:
     void addToStructure(MultiblockStructure* structure);
     virtual void onLoaded();
     virtual void onUnloaded();
-    virtual void setNeighbour(
-        Direction dir, Framework::Either<Block*, int> neighbor);
-    virtual void setNeighbourBlock(Direction dir, Block* zN);
-    virtual void setNeighbourType(Direction dir, int type);
     virtual Framework::XML::Element* getTargetUIML() const;
     virtual void sendModelInfo(NetworkMessage* zMessage);
     virtual bool interact(Item* zItem, Entity* zActor, bool& itemChanged);
@@ -114,13 +108,11 @@ public:
     float getHardness() const;
     float getSpeedModifier() const;
     const Framework::Vec3<int> getPos() const;
-    bool isVisible() const;
     void setHP(
         Entity* zActor, Item* zUsedItem, ItemSkill* zUsedSkill, float hp);
     bool isDeadAndRemoved() const;
     virtual void getLightEmisionColor(unsigned char* result) const;
     virtual void filterPassingLight(unsigned char rgb[3]) const;
-    Block* zNeighbor(Direction dir) const;
     void updateModel(ModelInfo* zInfo) const;
     int getMapColor() const;
 

+ 249 - 241
FactoryCraft/Chunk.cpp

@@ -104,62 +104,175 @@ void Chunk::removeLightSource(int index)
     }
 }
 
-void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
+void Chunk::sendToClient(Framework::StreamWriter* zWriter, bool* instanceMap)
 {
-    for (int z = 0; z < WORLD_HEIGHT; z++)
+    for (int x = 0; x < CHUNK_SIZE; x++)
     {
-        for (int x = -1; x <= CHUNK_SIZE; x++)
+        for (int y = 0; y < CHUNK_SIZE; y++)
         {
-            for (int y = -1; y <= CHUNK_SIZE; y++)
+            for (int z = 0; z < WORLD_HEIGHT; z++)
             {
-                if ((x < 0 || x == CHUNK_SIZE) && (y < 0 || y > CHUNK_SIZE))
+                int index = Chunk::index({x, y, z});
+                const BlockType* type
+                    = Game::INSTANCE->zBlockType(blockIds[index]);
+                if (isVisible(index) && type->doesNeedClientInstance())
                 {
-                    continue;
+                    if (z > 0 && z < WORLD_HEIGHT - 1)
+                    {
+                        instanceMap[index + (CHUNK_SIZE + 1) * WORLD_HEIGHT + 1]
+                            = 1;
+                        instanceMap[index + (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);
+                    char state = 0;
+                    if (type->isFluid())
+                    {
+                        state |= 1;
+                    }
+                    if ((blocks[index] && blocks[index]->isPassable())
+                        || (type->zDefault()->isPassable()))
+                    {
+                        state |= 2;
+                    }
+                    zWriter->schreibe((char*)&state, 1);
+                    if ((state | 1) == state)
+                    {
+                        FluidBlock* fluidBlock
+                            = dynamic_cast<FluidBlock*>(blocks[index]);
+                        char data
+                            = fluidBlock ? fluidBlock->getFlowOptions() : 0;
+                        zWriter->schreibe(&data, 1);
+                        data = fluidBlock ? fluidBlock->getDistanceToSource()
+                                          : 0;
+                        zWriter->schreibe(&data, 1);
+                    }
+                    if ((state | 2) == state)
+                    {
+                        float speedModifier
+                            = blocks[index]
+                                ? blocks[index]->getSpeedModifier()
+                                : type->zDefault()->getSpeedModifier();
+                        zWriter->schreibe((char*)&speedModifier, 4);
+                    }
                 }
-                bool needSend = 0;
-                for (int i = 0; i < 6; i++)
+            }
+        }
+    }
+    unsigned short end = 0;
+    zWriter->schreibe((char*)&end, 2);
+}
+
+void Chunk::sendLightToClient(
+    Framework::StreamWriter* zWriter, bool* instanceMap)
+{
+    cs.lock();
+    Chunk* zNeighbours[4];
+    for (int i = 0; i < 4; i++)
+    {
+        zNeighbours[i]
+            = this->zNeighbours[i]
+                ? dynamic_cast<Chunk*>(this->zNeighbours[i]->getThis())
+                : 0;
+        if (zNeighbours[i])
+        {
+            Direction dir = getDirectionFromIndex(i);
+            switch (dir)
+            {
+            case WEST:
+                for (int z = 0; z < WORLD_HEIGHT; z++)
                 {
-                    Framework::Vec3<int> pos
-                        = Framework::Vec3<int>(x, y, z)
-                        + getDirection(getDirectionFromIndex(i));
-                    if (pos.z >= 0 && pos.z < WORLD_HEIGHT)
+                    for (int j = 0; j < CHUNK_SIZE; j++)
                     {
-                        if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0
-                            && pos.y < CHUNK_SIZE)
+                        int type = zNeighbours[i]->getBlockTypeAt(
+                            {CHUNK_SIZE - 1, j, z});
+                        if (type
+                            && Game::INSTANCE->zBlockType(type)
+                                ->doesNeedClientInstance())
                         {
-                            int bi = (pos.x * CHUNK_SIZE + pos.y) * WORLD_HEIGHT
-                                   + pos.z;
-                            int type = blockIds[bi];
-                            needSend |= type != BlockTypeEnum::NO_BLOCK
-                                     && Game::INSTANCE->zBlockType(type)
-                                            ->doesNeedClientInstance();
+                            instanceMap[(CHUNK_SIZE + j + 1) * WORLD_HEIGHT + z]
+                                = 1;
                         }
-                        else
+                    }
+                }
+                break;
+            case NORTH:
+                for (int z = 0; z < WORLD_HEIGHT; z++)
+                {
+                    for (int j = 0; j < CHUNK_SIZE; j++)
+                    {
+                        int type = zNeighbours[i]->getBlockTypeAt(
+                            {j, CHUNK_SIZE - 1, z});
+                        if (type
+                            && Game::INSTANCE->zBlockType(type)
+                                ->doesNeedClientInstance())
                         {
-                            if (x >= 0 && x < CHUNK_SIZE && y >= 0
-                                && y < CHUNK_SIZE)
-                            {
-                                cs.lock();
-                                if (i < 4 && zNeighbours[i])
-                                {
-                                    Framework::Vec3<int> offset
-                                        = getDirection(getDirectionFromIndex(i))
-                                        * 16;
-                                    int bi = ((pos.x - offset.x) * CHUNK_SIZE
-                                                 + (pos.y - offset.y))
-                                               * WORLD_HEIGHT
-                                           + (pos.z - offset.z);
-                                    int type = zNeighbours[i]->blockIds[bi];
-                                    needSend |= Game::INSTANCE->zBlockType(type)
-                                                    ->doesNeedClientInstance();
-                                }
-                                cs.unlock();
-                            }
+                            instanceMap[((j + 1) * CHUNK_SIZE + 1)
+                                            * WORLD_HEIGHT
+                                        + z]
+                                = 1;
                         }
-                        if (needSend) break;
                     }
                 }
-                if (needSend)
+                break;
+            case EAST:
+                for (int z = 0; z < WORLD_HEIGHT; z++)
+                {
+                    for (int j = 0; j < CHUNK_SIZE; j++)
+                    {
+                        int type = zNeighbours[i]->getBlockTypeAt({0, j, z});
+                        if (type
+                            && Game::INSTANCE->zBlockType(type)
+                                ->doesNeedClientInstance())
+                        {
+                            instanceMap[((CHUNK_SIZE)*CHUNK_SIZE + j + 1)
+                                            * WORLD_HEIGHT
+                                        + z]
+                                = 1;
+                        }
+                    }
+                }
+                break;
+            case SOUTH:
+                for (int z = 0; z < WORLD_HEIGHT; z++)
+                {
+                    for (int j = 0; j < CHUNK_SIZE; j++)
+                    {
+                        int type = zNeighbours[i]->getBlockTypeAt({j, 0, z});
+                        if (type
+                            && Game::INSTANCE->zBlockType(type)
+                                ->doesNeedClientInstance())
+                        {
+                            instanceMap[((j + 1) * CHUNK_SIZE + CHUNK_SIZE)
+                                            * WORLD_HEIGHT
+                                        + z]
+                                = 1;
+                        }
+                    }
+                }
+                break;
+            }
+        }
+    }
+    cs.unlock();
+    for (int z = 0; z < WORLD_HEIGHT; z++)
+    {
+        for (int x = -1; x <= CHUNK_SIZE; x++)
+        {
+            for (int y = -1; y <= CHUNK_SIZE; y++)
+            {
+                if ((x < 0 || x == CHUNK_SIZE) && (y < 0 || y > CHUNK_SIZE))
+                {
+                    continue;
+                }
+                if (instanceMap[((x + 1) * CHUNK_SIZE + y + 1) * WORLD_HEIGHT
+                                + z])
                 {
                     if (x >= 0 && x < CHUNK_SIZE && y >= 0 && y < CHUNK_SIZE)
                     {
@@ -196,7 +309,6 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
                             dir = getDirectionIndex(SOUTH);
                             index = (x * CHUNK_SIZE) * WORLD_HEIGHT + z;
                         }
-                        cs.lock();
                         if (zNeighbours[dir])
                         {
                             int i = -1;
@@ -209,7 +321,6 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
                                         + index * 6),
                                 6);
                         }
-                        cs.unlock();
                     }
                 }
             }
@@ -217,68 +328,68 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
     }
     int end = -2;
     zWriter->schreibe((char*)&end, 4);
+    for (int i = 0; i < 4; i++)
+    {
+        if (zNeighbours[i])
+        {
+            zNeighbours[i]->release();
+        }
+    }
 }
 
 bool Chunk::isVisible(int index) const
 {
-    if (!blocks[index])
+    unsigned short blockType
+        = blocks[index] ? (unsigned short)blocks[index]->zBlockType()->getId()
+                        : blockIds[index];
+    if (blockType)
     {
-        unsigned short blockType
-            = blocks[index]
-                ? (unsigned short)blocks[index]->zBlockType()->getId()
-                : blockIds[index];
-        if (blockType)
+        if (CONST_BLOCK(0, blockIds[index])->isTransparent()
+            || CONST_BLOCK(0, blockIds[index])->isPassable())
+            return 1;
+        else
         {
-            if (CONST_BLOCK(0, blockIds[index])->isTransparent()
-                || CONST_BLOCK(0, blockIds[index])->isPassable())
-                return 1;
-            else
+            Framework::Vec3<int> indexPos
+                = {(index / WORLD_HEIGHT) / CHUNK_SIZE,
+                    (index / WORLD_HEIGHT) % CHUNK_SIZE,
+                    index % WORLD_HEIGHT};
+            for (int d = 0; d < 6; d++)
             {
-                Framework::Vec3<int> indexPos
-                    = {(index / WORLD_HEIGHT) / CHUNK_SIZE,
-                        (index / WORLD_HEIGHT) % CHUNK_SIZE,
-                        index % WORLD_HEIGHT};
-                for (int d = 0; d < 6; d++)
+                Framework::Either<Block*, int> n = BlockTypeEnum::NO_BLOCK;
+                Framework::Vec3<int> pos
+                    = getDirection((Directions)getDirectionFromIndex(d))
+                    + indexPos;
+                if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0
+                    && pos.y < CHUNK_SIZE && pos.z >= 0 && pos.z < WORLD_HEIGHT)
                 {
-                    Framework::Either<Block*, int> n = BlockTypeEnum::NO_BLOCK;
-                    Framework::Vec3<int> pos
-                        = getDirection((Directions)getDirectionFromIndex(d))
-                        + indexPos;
-                    if (pos.x >= 0 && pos.x < CHUNK_SIZE && pos.y >= 0
-                        && pos.y < CHUNK_SIZE && pos.z >= 0
-                        && pos.z < WORLD_HEIGHT)
-                    {
-                        n = zBlockAt(pos);
-                    }
-                    else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4
-                             && zNeighbours[d])
-                    {
-                        if (pos.x < 0) pos.x += CHUNK_SIZE;
-                        if (pos.x >= CHUNK_SIZE) pos.x -= CHUNK_SIZE;
-                        if (pos.y < 0) pos.y += CHUNK_SIZE;
-                        if (pos.y >= CHUNK_SIZE) pos.y -= CHUNK_SIZE;
-                        n = zNeighbours[d]->zBlockAt(pos);
-                    }
-                    else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4
-                             && !zNeighbours[d])
-                    {
-                        return 1;
-                    }
-                    if (n.isA()
-                        && (((Block*)n)->isPassable()
-                            || ((Block*)n)->isTransparent()))
-                        return 1;
-                    if (n.isB()
-                        && (CONST_BLOCK(0, n)->isTransparent()
-                            || CONST_BLOCK(0, n)->isPassable()))
-                        return 1;
+                    n = zBlockAt(pos);
+                }
+                else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4
+                         && zNeighbours[d])
+                {
+                    if (pos.x < 0) pos.x += CHUNK_SIZE;
+                    if (pos.x >= CHUNK_SIZE) pos.x -= CHUNK_SIZE;
+                    if (pos.y < 0) pos.y += CHUNK_SIZE;
+                    if (pos.y >= CHUNK_SIZE) pos.y -= CHUNK_SIZE;
+                    n = zNeighbours[d]->zBlockAt(pos);
                 }
+                else if (pos.z >= 0 && pos.z < WORLD_HEIGHT && d < 4
+                         && !zNeighbours[d])
+                {
+                    return 1;
+                }
+                if (n.isA()
+                    && (((Block*)n)->isPassable()
+                        || ((Block*)n)->isTransparent()))
+                    return 1;
+                if (n.isB()
+                    && (CONST_BLOCK(0, n)->isTransparent()
+                        || CONST_BLOCK(0, n)->isPassable()))
+                    return 1;
             }
         }
-        return 0;
     }
-    else
-        return blocks[index]->isVisible();
+    return 0;
 }
 
 void Chunk::broadcastLightData(int index, bool foreground)
@@ -358,13 +469,17 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
         buffer.schreibe("\4", 1);
         buffer.schreibe((char*)&location.x, 4);
         buffer.schreibe((char*)&location.y, 4);
-        sendToClient(&buffer);
-        sendLightToClient(&buffer);
+        bool instanceMap[(CHUNK_SIZE + 2) * (CHUNK_SIZE + 2) * WORLD_HEIGHT];
+        memset(instanceMap, 0, sizeof(instanceMap));
+        sendToClient(&buffer, instanceMap);
+        sendLightToClient(&buffer, instanceMap);
         NetworkMessage* msg = new NetworkMessage();
         msg->addressDimension(Game::INSTANCE->zDimension(dimensionId));
+#ifdef _DEBUG
         Framework::Logging::debug()
             << "chunk size: " << location.x << ", " << location.y << ": "
             << buffer.getSize() << "b";
+#endif
         char* message = new char[buffer.getSize()];
         buffer.lese(message, (int)buffer.getSize());
         msg->setMessage(message, (int)buffer.getSize());
@@ -567,7 +682,9 @@ void Chunk::initializeLightning()
             }
         }
     }
+#ifdef _DEBUG
     Framework::Logging::debug() << "goUps: " << goUps << " minZ: " << minZ;
+#endif
 }
 
 void Chunk::updateLightSources()
@@ -701,31 +818,6 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
         change = old != 0;
     }
     blocks[index] = block;
-    for (int i = 0; i < 6; i++)
-    {
-        Direction d = getDirectionFromIndex(i);
-        Chunk* zNeighborChunk = 0;
-        Framework::Either<Block*, int> neighbor
-            = zBlockNeighbor(location + getDirection(d), &zNeighborChunk);
-        if (neighbor.isA())
-        {
-            if (block)
-            {
-                ((Block*)neighbor)
-                    ->setNeighbour(getOppositeDirection(d), block);
-            }
-            else
-            {
-                ((Block*)neighbor)
-                    ->setNeighbour(getOppositeDirection(d), blockIds[index]);
-            }
-        }
-        if (block) block->setNeighbour(d, neighbor);
-        if (zNeighborChunk && zNeighborChunk != this)
-        {
-            zNeighborChunk->worldUpdated = 1;
-        }
-    }
     if (old) old->release();
     if (block && block->isTickSource() != TickSourceType::NONE)
     { // add to tick sources
@@ -780,20 +872,6 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
     if (blockIds[index] != (unsigned short)type)
     {
         blockIds[index] = (unsigned short)type;
-        for (int i = 0; i < 6; i++)
-        {
-            Direction d = getDirectionFromIndex(i);
-            Chunk* zNeighborChunk = 0;
-            Framework::Either<Block*, int> neighbor
-                = zBlockNeighbor(location + getDirection(d), &zNeighborChunk);
-            if (neighbor.isA())
-                ((Block*)neighbor)
-                    ->setNeighbourType(getOppositeDirection(d), type);
-            if (zNeighborChunk && zNeighborChunk != this)
-            {
-                zNeighborChunk->worldUpdated = 1;
-            }
-        }
         if (isLightSource != wasLightSource)
         {
             if (isLightSource)
@@ -927,31 +1005,12 @@ void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
                 j = ((CHUNK_SIZE - 1) * CHUNK_SIZE + i) * WORLD_HEIGHT + z;
             }
             bool needsTransmission = 0;
-            if (blocks[index])
-            {
-                bool visible = blocks[index]->isVisible();
-                if (zChunk && zChunk->blocks[j])
-                    blocks[index]->setNeighbour(dir, zChunk->blocks[j]);
-                else
-                {
-                    blocks[index]->setNeighbour(dir, 0);
-                    blocks[index]->setNeighbourType(
-                        dir, zChunk ? zChunk->blockIds[j] : 0);
-                }
-                if (!visible && blocks[index]->isVisible())
-                {
-                    needsTransmission = 1;
-                }
-            }
-            else
+            zNeighbours[dirIndex] = old;
+            bool visible = isVisible(index);
+            zNeighbours[dirIndex] = zChunk;
+            if (!visible && isVisible(index))
             {
-                zNeighbours[dirIndex] = old;
-                bool visible = isVisible(index);
-                zNeighbours[dirIndex] = zChunk;
-                if (!visible && isVisible(index))
-                {
-                    needsTransmission = 1;
-                }
+                needsTransmission = 1;
             }
             if (zChunk)
             {
@@ -1040,97 +1099,45 @@ void Chunk::save(Framework::StreamWriter* zWriter)
     }
 }
 
-void Chunk::sendToClient(Framework::StreamWriter* zWriter)
+void Chunk::removeUnusedBlocks()
 {
-    for (int x = 0; x < CHUNK_SIZE; x++)
+/* for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
+{
+    if (!blocks[i] && blockIds[i])
     {
-        for (int y = 0; y < CHUNK_SIZE; y++)
+        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 z = 0; z < WORLD_HEIGHT; z++)
+            for (int d = 0; d < 6 && !visible; d++)
             {
-                int index = Chunk::index({x, y, z});
-                const BlockType* type
-                    = Game::INSTANCE->zBlockType(blockIds[index]);
-                if (isVisible(index) && type->doesNeedClientInstance())
-                {
-                    zWriter->schreibe((char*)&blockIds[index], 2);
-                    zWriter->schreibe((char*)&index, 4);
-                    char state = 0;
-                    if (type->isFluid())
-                    {
-                        state |= 1;
-                    }
-                    if ((blocks[index] && blocks[index]->isPassable())
-                        || (type->zDefault()->isPassable()))
-                    {
-                        state |= 2;
-                    }
-                    zWriter->schreibe((char*)&state, 1);
-                    if ((state | 1) == state)
-                    {
-                        FluidBlock* fluidBlock
-                            = dynamic_cast<FluidBlock*>(blocks[index]);
-                        char data
-                            = fluidBlock ? fluidBlock->getFlowOptions() : 0;
-                        zWriter->schreibe(&data, 1);
-                        data = fluidBlock ? fluidBlock->getDistanceToSource()
-                                          : 0;
-                        zWriter->schreibe(&data, 1);
-                    }
-                    if ((state | 2) == state)
-                    {
-                        float speedModifier
-                            = blocks[index]
-                                ? blocks[index]->getSpeedModifier()
-                                : type->zDefault()->getSpeedModifier();
-                        zWriter->schreibe((char*)&speedModifier, 4);
-                    }
-                }
+                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;
             }
         }
-    }
-    unsigned short end = 0;
-    zWriter->schreibe((char*)&end, 2);
-}
-
-void Chunk::removeUnusedBlocks()
-{
-    for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
-    {
-        if (!blocks[i] && blockIds[i])
+        if (!visible)
         {
-            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);
-            }
+            putBlockAt({x, y, z}, 0);
+            putBlockTypeAt({x, y, z}, 0);
         }
     }
+}*/
+#ifdef _DEBUG
     int count = 0;
     for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
     {
@@ -1140,6 +1147,7 @@ void Chunk::removeUnusedBlocks()
     Framework::Logging::debug()
         << "chunk " << location.x << ", " << location.y
         << " was generated with " << count << " blocks.";
+#endif
 }
 
 int Chunk::getDimensionId() const

+ 2 - 2
FactoryCraft/Chunk.h

@@ -37,7 +37,8 @@ private:
 
     void addLightSource(int index);
     void removeLightSource(int index);
-    void sendLightToClient(Framework::StreamWriter* zWriter);
+    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;
@@ -75,7 +76,6 @@ public:
     void setNeighbor(Direction dir, Chunk* zChunk);
     void load(Framework::StreamReader* zReader);
     void save(Framework::StreamWriter* zWriter);
-    void sendToClient(Framework::StreamWriter* zWriter);
     void removeUnusedBlocks();
     int getDimensionId() const;
     void onLoaded();

+ 61 - 30
FactoryCraft/Dimension.cpp

@@ -513,7 +513,7 @@ void Dimension::setChunk(Chunk* chunk, Punkt center)
     laterHandler.execute();
     if (chunk)
     {
-        updateLightAtChunkBorders(center);
+        updateLightAtChunkBorders(chunk);
         chunk->updateLightSources();
     }
 }
@@ -726,56 +726,87 @@ void Dimension::updateLightningWithoutWait(Framework::Vec3<int> location)
     prioLightCs.unlock();
 }
 
-void Dimension::updateLightAtChunkBorders(Punkt chunkCenter)
+void Dimension::updateLightAtChunkBorders(Chunk* zChunk)
 {
     if (lightUpdateQueue.getEintragAnzahl() > 300000)
     {
         Logging::warning()
             << "light calculation queue is over 300000 blocks long";
     }
-    bool xn = zChunk(chunkCenter - Punkt(CHUNK_SIZE, 0)) != 0;
-    bool xp = zChunk(chunkCenter + Punkt(CHUNK_SIZE, 0)) != 0;
-    bool yn = zChunk(chunkCenter - Punkt(0, CHUNK_SIZE)) != 0;
-    bool yp = zChunk(chunkCenter + Punkt(0, CHUNK_SIZE)) != 0;
+    Punkt center = zChunk->getCenter();
+    Chunk* xn = this->zChunk(center - Punkt(CHUNK_SIZE, 0));
+    Chunk* xp = this->zChunk(center + Punkt(CHUNK_SIZE, 0));
+    Chunk* yn = this->zChunk(center - Punkt(0, CHUNK_SIZE));
+    Chunk* yp = this->zChunk(center + Punkt(0, CHUNK_SIZE));
     for (int i = WORLD_HEIGHT - 1; i >= 0; i--)
     {
         for (int j = 0; j < CHUNK_SIZE; j++)
         {
             if (xn)
             {
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 - 1,
-                    chunkCenter.y - CHUNK_SIZE / 2 + j,
-                    i));
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2,
-                    chunkCenter.y - CHUNK_SIZE / 2 + j,
-                    i));
+                unsigned char* light
+                    = xn->getLightData(Vec3<int>(CHUNK_SIZE - 1, j, i));
+                unsigned char* light2
+                    = zChunk->getLightData(Vec3<int>(0, j, i));
+                if (*(int*)light != *(int*)light2
+                    || *(int*)(light + 2) != *(int*)(light2 + 2))
+                {
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2 - 1,
+                        center.y - CHUNK_SIZE / 2 + j,
+                        i));
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2,
+                        center.y - CHUNK_SIZE / 2 + j,
+                        i));
+                }
             }
             if (xp)
             {
-                updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2 - 1,
-                    chunkCenter.y - CHUNK_SIZE / 2 + j,
-                    i));
-                updateLightning(Vec3<int>(chunkCenter.x + CHUNK_SIZE / 2,
-                    chunkCenter.y - CHUNK_SIZE / 2 + j,
-                    i));
+                unsigned char* light = xp->getLightData(Vec3<int>(0, j, i));
+                unsigned char* light2
+                    = zChunk->getLightData(Vec3<int>(CHUNK_SIZE - 1, j, i));
+                if (*(int*)light != *(int*)light2
+                    || *(int*)(light + 2) != *(int*)(light2 + 2))
+                {
+                    updateLightning(Vec3<int>(center.x + CHUNK_SIZE / 2 - 1,
+                        center.y - CHUNK_SIZE / 2 + j,
+                        i));
+                    updateLightning(Vec3<int>(center.x + CHUNK_SIZE / 2,
+                        center.y - CHUNK_SIZE / 2 + j,
+                        i));
+                }
             }
             if (yn)
             {
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                    chunkCenter.y - CHUNK_SIZE / 2 - 1,
-                    i));
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                    chunkCenter.y - CHUNK_SIZE / 2,
-                    i));
+                unsigned char* light
+                    = yn->getLightData(Vec3<int>(j, CHUNK_SIZE - 1, i));
+                unsigned char* light2
+                    = zChunk->getLightData(Vec3<int>(j, 0, i));
+                if (*(int*)light != *(int*)light2
+                    || *(int*)(light + 2) != *(int*)(light2 + 2))
+                {
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2 + j,
+                        center.y - CHUNK_SIZE / 2 - 1,
+                        i));
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2 + j,
+                        center.y - CHUNK_SIZE / 2,
+                        i));
+                }
             }
             if (yp)
             {
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                    chunkCenter.y + CHUNK_SIZE / 2 - 1,
-                    i));
-                updateLightning(Vec3<int>(chunkCenter.x - CHUNK_SIZE / 2 + j,
-                    chunkCenter.y + CHUNK_SIZE / 2,
-                    i));
+                unsigned char* light = yp->getLightData(Vec3<int>(j, 0, i));
+                unsigned char* light2
+                    = zChunk->getLightData(Vec3<int>(j, CHUNK_SIZE - 1, i));
+                if (*(int*)light != *(int*)light2
+                    || *(int*)(light + 2) != *(int*)(light2 + 2))
+                {
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2 + j,
+                        center.y + CHUNK_SIZE / 2 - 1,
+                        i));
+                    updateLightning(Vec3<int>(center.x - CHUNK_SIZE / 2 + j,
+                        center.y + CHUNK_SIZE / 2,
+                        i));
+                }
             }
         }
     }

+ 1 - 1
FactoryCraft/Dimension.h

@@ -99,7 +99,7 @@ public:
     void removeSubscriptions(Entity* zEntity);
     void updateLightning(Framework::Vec3<int> location);
     void updateLightningWithoutWait(Framework::Vec3<int> location);
-    void updateLightAtChunkBorders(Framework::Punkt chunkCenter);
+    void updateLightAtChunkBorders(Chunk* zChunk);
     __int64 getNextStructureId();
     void addStructure(MultiblockStructure* structure);
     MultiblockStructure* zStructureByPosition(

+ 152 - 14
FactoryCraft/DimensionGenerator.cpp

@@ -228,6 +228,8 @@ void BiomedCavedDimensionGenerator::initialize(int worldSeed)
         gen->initialize(zMemory());
     }
     caveGenerator = new WormCaveGenerator(75, 150, 1, 6, 0.1f, worldSeed - 1);
+    terrainHeightP = zMemory()->getFloatVariableP(terrainHeightLayerName);
+    seaFluidBlockTypeId = Game::INSTANCE->getBlockTypeId(seaFluidBlockType);
 }
 
 BiomGenerator* BiomedCavedDimensionGenerator::zBiomGenerator()
@@ -315,6 +317,7 @@ Chunk* BiomedCavedDimensionGenerator::generateChunk(int centerX, int centerY)
             calculateHeightLayers();
             // calculate biom
             BiomGenerator* biom = zBiomGenerator();
+            int terrainHeight = (int)round(*terrainHeightP);
             // generate blocks
             for (int z = 0; z < WORLD_HEIGHT; z++)
             {
@@ -334,12 +337,12 @@ Chunk* BiomedCavedDimensionGenerator::generateChunk(int centerX, int centerY)
                         break;
                     }
                 }
+                bool inCave = false;
                 if (!structureAffected)
                 {
                     // check if block is a cave block
-                    bool inCave
-                        = caveGen->isInCave(x + centerX, y + centerY, z);
-                    if (!inCave)
+                    inCave = caveGen->isInCave(x + centerX, y + centerY, z);
+                    if (!inCave && z == terrainHeight - 1)
                     {
                         // generate biom block
 #ifdef CHUNK_GENERATION_DEBUG_LOG
@@ -356,16 +359,101 @@ Chunk* BiomedCavedDimensionGenerator::generateChunk(int centerX, int centerY)
 #endif
                     }
                 }
-                if (generated.isA())
-                    chunk->putBlockAt(
-                        Framework::Vec3<int>(
-                            x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, z),
-                        generated);
-                else
-                    chunk->putBlockTypeAt(
-                        Framework::Vec3<int>(
-                            x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, z),
-                        generated);
+                if (inCave || structureAffected || z >= terrainHeight - 1)
+                {
+                    if (!inCave && !structureAffected && z > terrainHeight - 1
+                        && z < globalSeaLevel)
+                    {
+                        generated = seaFluidBlockTypeId;
+                    }
+                    if (generated.isA())
+                        chunk->putBlockAt(
+                            Framework::Vec3<int>(
+                                x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, z),
+                            generated);
+                    else
+                        chunk->putBlockTypeAt(
+                            Framework::Vec3<int>(
+                                x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, z),
+                            generated);
+                }
+            }
+        }
+    }
+    bool generatedMore = true;
+    while (generatedMore)
+    {
+        generatedMore = false;
+        for (int x = -CHUNK_SIZE / 2; x < CHUNK_SIZE / 2; x++)
+        {
+            for (int y = -CHUNK_SIZE / 2; y < CHUNK_SIZE / 2; y++)
+            {
+                *xPos = (float)x + (float)centerX;
+                *yPos = (float)y + (float)centerY;
+                // 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 = chunk->getBlockTypeAt(Framework::Vec3<int>(
+                        x + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, 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 + CHUNK_SIZE / 2, y + CHUNK_SIZE / 2, 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 = chunk->zBlockConst(pos);
+                            }
+                            else if (pos.z >= 0 && pos.z < WORLD_HEIGHT)
+                            {
+                                neighborBlock = Game::INSTANCE->zConstBlock(
+                                    Framework::Vec3<int>(pos.x + centerX,
+                                        pos.y + centerY,
+                                        pos.z),
+                                    getDimensionId());
+                            }
+                            if (neighborBlock && neighborBlock->isTransparent())
+                            {
+                                needed = true;
+                                break;
+                            }
+                        }
+                        if (needed)
+                        {
+                            auto generated = biom->generateBlock(x + centerX,
+                                y + centerY,
+                                z,
+                                getDimensionId(),
+                                chunk);
+                            if (generated.isA())
+                                chunk->putBlockAt(
+                                    Framework::Vec3<int>(x + CHUNK_SIZE / 2,
+                                        y + CHUNK_SIZE / 2,
+                                        z),
+                                    generated);
+                            else
+                                chunk->putBlockTypeAt(
+                                    Framework::Vec3<int>(x + CHUNK_SIZE / 2,
+                                        y + CHUNK_SIZE / 2,
+                                        z),
+                                    generated);
+                            generatedMore = true;
+                        }
+                    }
+                }
             }
         }
     }
@@ -572,6 +660,37 @@ BiomedCavedDimensionGenerator::zBiomNoiseConfig() const
     return noiseConfig;
 }
 
+void BiomedCavedDimensionGenerator::setGlobalSeaLevel(int seaLevel)
+{
+    globalSeaLevel = seaLevel;
+}
+
+int BiomedCavedDimensionGenerator::getGlobalSeaLevel() const
+{
+    return globalSeaLevel;
+}
+
+void BiomedCavedDimensionGenerator::setTerrainHeightLayerName(
+    Framework::Text name)
+{
+    terrainHeightLayerName = name;
+}
+
+Framework::Text BiomedCavedDimensionGenerator::getTerrainHeightLayerName() const
+{
+    return terrainHeightLayerName;
+}
+
+void BiomedCavedDimensionGenerator::setSeaFluidBlockType(Framework::Text type)
+{
+    seaFluidBlockType = type;
+}
+
+Framework::Text BiomedCavedDimensionGenerator::getSeaFluidBlockType() const
+{
+    return seaFluidBlockType;
+}
+
 BiomedCavedDimensionGeneratorFactory::BiomedCavedDimensionGeneratorFactory() {}
 
 BiomedCavedDimensionGenerator*
@@ -592,6 +711,12 @@ BiomedCavedDimensionGenerator* BiomedCavedDimensionGeneratorFactory::fromJson(
         result->addBiomGenerator(
             Game::INSTANCE->zTypeRegistry()->fromJson<BiomGenerator>(value));
     }
+    result->setGlobalSeaLevel(
+        (int)zJson->zValue("globalSeaLevel")->asNumber()->getNumber());
+    result->setTerrainHeightLayerName(
+        zJson->zValue("terrainHeightLeyerName")->asString()->getString());
+    result->setSeaFluidBlockType(
+        zJson->zValue("seaFluidBlockType")->asString()->getString());
     return result;
 }
 
@@ -610,6 +735,12 @@ Framework::JSON::JSONObject* BiomedCavedDimensionGeneratorFactory::toJsonObject(
     result->addValue("biomNoise",
         dynamic_cast<Framework::JSON::JSONValue*>(
             zObject->zBiomNoiseConfig()->getThis()));
+    result->addValue("globalSeaLevel",
+        new Framework::JSON::JSONNumber(zObject->getGlobalSeaLevel()));
+    result->addValue("terrainHeightLeyerName",
+        new Framework::JSON::JSONString(zObject->getTerrainHeightLayerName()));
+    result->addValue("seaFluidBlockType",
+        new Framework::JSON::JSONString(zObject->getSeaFluidBlockType()));
     return result;
 }
 
@@ -622,7 +753,14 @@ BiomedCavedDimensionGeneratorFactory::addToValidator(
             ->addAcceptedTypeInArray(
                 Game::INSTANCE->zTypeRegistry()->getValidator<BiomGenerator>())
             ->finishArray()
-            ->withRequiredAttribute("biomNoise", JNoise::getValidator(false)));
+            ->withRequiredAttribute("biomNoise", JNoise::getValidator(false))
+            ->withRequiredNumber("globalSeaLevel")
+            ->finishNumber()
+            ->withRequiredString("terrainHeightLeyerName")
+            ->finishString()
+            ->withRequiredAttribute("seaFluidBlockType",
+                Game::INSTANCE->zTypeRegistry()->getValidator<Framework::Text>(
+                    BlockTypeNameFactory::TYPE_ID)));
 }
 
 const char* BiomedCavedDimensionGeneratorFactory::getTypeToken() const

+ 11 - 0
FactoryCraft/DimensionGenerator.h

@@ -175,6 +175,11 @@ private:
     Noise* biomNoise;
     Framework::Vec3<int> minStructureOffset;
     Framework::Vec3<int> maxStructureOffset;
+    Framework::Text terrainHeightLayerName;
+    int globalSeaLevel;
+    float* terrainHeightP;
+    Framework::Text seaFluidBlockType;
+    int seaFluidBlockTypeId;
 
     BiomGenerator* zBiomGenerator();
 
@@ -198,6 +203,12 @@ public:
     const Framework::RCArray<BiomGenerator>& getBiomGenerators() const;
     void setBiomNoiseConfig(Framework::JSON::JSONObject* biomNoiseConfig);
     Framework::JSON::JSONObject* zBiomNoiseConfig() const;
+    void setGlobalSeaLevel(int seaLevel);
+    int getGlobalSeaLevel() const;
+    void setTerrainHeightLayerName(Framework::Text name);
+    Framework::Text getTerrainHeightLayerName() const;
+    void setSeaFluidBlockType(Framework::Text type);
+    Framework::Text getSeaFluidBlockType() const;
 };
 
 class BiomedCavedDimensionGeneratorFactory

+ 29 - 52
FactoryCraft/FluidBlock.cpp

@@ -12,7 +12,6 @@ FluidBlock::FluidBlock(int typeId,
     Framework::Vec3<float> lightWeights)
     : Block(typeId, pos, dimensionId, 0),
       lightWeights(lightWeights),
-      neighborChanged(1),
       nextFlow(0),
       maxFlowDistance(8)
 {
@@ -31,37 +30,26 @@ FluidBlock::~FluidBlock() {}
 
 bool FluidBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 {
-    if (neighborChanged)
+    nextFlow -= numTicks;
+    if (nextFlow <= 0)
     {
-        nextFlow -= numTicks;
-        if (nextFlow <= 0)
+        const FluidBlockType* zType
+            = dynamic_cast<const FluidBlockType*>(zBlockType());
+        if (zType)
         {
-            const FluidBlockType* zType
-                = dynamic_cast<const FluidBlockType*>(zBlockType());
-            if (zType)
-            {
-                nextFlow = zType->getTicktsToFlow();
-            }
-            else
-            {
-                nextFlow = 0;
-            }
-            neighborChanged = 0;
-            doFlow();
+            nextFlow = zType->getTicktsToFlow();
+        }
+        else
+        {
+            nextFlow = 0;
         }
-        return true;
+        doFlow();
     }
-    return false;
+    return true;
 }
 
 void FluidBlock::onPostTick() {}
 
-void FluidBlock::setNeighbourType(Direction dir, int type)
-{
-    Block::setNeighbourType(dir, type);
-    neighborChanged = 1;
-}
-
 void FluidBlock::sendModelInfo(NetworkMessage* zMessage)
 {
     zMessage->addressBlock(this);
@@ -75,12 +63,10 @@ void FluidBlock::sendModelInfo(NetworkMessage* zMessage)
 void FluidBlock::doFlow()
 {
     bool doesFlowSidewards = 1;
-    if (((zNeighbours[getDirectionIndex(Direction::BOTTOM)]
-                 && zNeighbours[getDirectionIndex(Direction::BOTTOM)]
-                            ->zBlockType()
-                        == zBlockType()
-             || neighbourTypes[getDirectionIndex(Direction::BOTTOM)]
-                    == BlockTypeEnum::AIR)
+    Framework::Either<Block*, int> below = Game::INSTANCE->zBlockAt(
+        getPos() + getDirection(Direction::BOTTOM), getDimensionId(), 0);
+    if (((below.isA() && below.getA()->zBlockType() == zBlockType()
+             || below.getB() == BlockTypeEnum::AIR)
             && distanceToSource)
         || distanceToSource >= maxFlowDistance)
     {
@@ -92,23 +78,28 @@ void FluidBlock::doFlow()
     for (int i = 0; i < 6; i++)
     {
         Direction dir = getDirectionFromIndex(i);
+        Framework::Either<Block*, int> neighbor = Game::INSTANCE->zBlockAt(
+            getPos() + getDirection(dir), getDimensionId(), 0);
         if (dir & Direction::TOP)
         {
-            if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType())
+            if (neighbor.isA() && neighbor.getA()->zBlockType() == zBlockType())
             {
                 FluidBlock* neighbour
-                    = dynamic_cast<FluidBlock*>(zNeighbours[i]);
+                    = dynamic_cast<FluidBlock*>(neighbor.getA());
                 minNeighborDistance = 0;
                 nextFlowOptions = (char)getOppositeDirection(dir);
             }
             continue;
         }
-        if (neighbourTypes[i] == BlockTypeEnum::AIR)
+        if (neighbor.isB() && neighbor.getB() == BlockTypeEnum::AIR)
         {
             if (dir & Direction::BOTTOM || doesFlowSidewards)
             {
                 Game::INSTANCE->doLater([this, dir, i]() {
-                    if (neighbourTypes[i] == BlockTypeEnum::AIR)
+                    Framework::Either<Block*, int> neighbor
+                        = Game::INSTANCE->zBlockAt(
+                            getPos() + getDirection(dir), getDimensionId(), 0);
+                    if (neighbor.isB() && neighbor.getB() == BlockTypeEnum::AIR)
                     {
                         Block* belowBlock = zBlockType()->createBlockAt(
                             getPos() + getDirection(dir), getDimensionId(), 0);
@@ -136,10 +127,11 @@ void FluidBlock::doFlow()
                 });
             }
         }
-        else if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType())
+        else if (neighbor.isA()
+                 && neighbor.getA()->zBlockType() == zBlockType())
         {
             if (dir & Direction::BOTTOM) continue;
-            FluidBlock* neighbour = dynamic_cast<FluidBlock*>(zNeighbours[i]);
+            FluidBlock* neighbour = dynamic_cast<FluidBlock*>(neighbor.getA());
             if (neighbour)
             {
                 if (neighbour->distanceToSource < minNeighborDistance)
@@ -171,21 +163,6 @@ void FluidBlock::doFlow()
     if (changed)
     {
         broadcastModelInfoChange();
-        neighborChanged = 1;
-        for (int i = 0; i < 6; i++)
-        {
-            Direction dir = getDirectionFromIndex(i);
-            if (dir & (Direction::TOP | Direction::BOTTOM)) continue;
-            if (zNeighbours[i] && zNeighbours[i]->zBlockType() == zBlockType())
-            {
-                FluidBlock* neighbour
-                    = dynamic_cast<FluidBlock*>(zNeighbours[i]);
-                if (neighbour)
-                {
-                    neighbour->neighborChanged = 1;
-                }
-            }
-        }
     }
 }
 
@@ -209,7 +186,7 @@ TickSourceType FluidBlock::isTickSource() const
 
 bool FluidBlock::needsTick() const
 {
-    return neighborChanged;
+    return true;
 }
 
 char FluidBlock::getDistanceToSource() const

+ 0 - 2
FactoryCraft/FluidBlock.h

@@ -13,7 +13,6 @@ private:
     char distanceToSource;
     int nextFlow;
     Framework::Vec3<float> lightWeights;
-    bool neighborChanged;
     unsigned char maxFlowDistance;
 
 protected:
@@ -29,7 +28,6 @@ public:
         Framework::Vec3<float> lightWeights);
     virtual ~FluidBlock();
 
-    virtual void setNeighbourType(Direction dir, int type) override;
     virtual void sendModelInfo(NetworkMessage* zMessage) override;
 
     virtual bool isInteractable(const Item* zItem) const override;

+ 8 - 0
FactoryCraft/Game.cpp

@@ -484,6 +484,7 @@ void Game::thread()
         }
         if (nextTimeSync <= 0)
         {
+            consoleHandler->print();
             nextTimeSync = MAX_TICKS_PER_SECOND;
         }
         for (auto i : removed)
@@ -918,6 +919,13 @@ Block* Game::zRealBlockInstance(Framework::Vec3<int> location, int dimension)
     return 0;
 }
 
+const Block* Game::zConstBlock(Framework::Vec3<int> location, int dimension)
+{
+    Dimension* dim = zDimension(dimension);
+    if (dim) return dim->zBlockOrDefault(location);
+    return 0;
+}
+
 int Game::getBlockType(Framework::Vec3<int> location, int dimension)
 {
     Dimension* dim = zDimension(dimension);

+ 1 - 0
FactoryCraft/Game.h

@@ -102,6 +102,7 @@ public:
     Framework::Either<Block*, int> zBlockAt(
         Framework::Vec3<int> location, int dimension, OUT Chunk** zChunk) const;
     Block* zRealBlockInstance(Framework::Vec3<int> location, int dimension);
+    const Block* zConstBlock(Framework::Vec3<int> location, int dimension);
     int getBlockType(Framework::Vec3<int> location, int dimension);
     Dimension* zDimension(int id) const;
     static Framework::Punkt getChunkCenter(int x, int y);

+ 5 - 0
FactoryCraft/MultiblockStructure.cpp

@@ -193,4 +193,9 @@ void MultiblockStructureType::saveStructure(
 int MultiblockStructureType::getId() const
 {
     return id;
+}
+
+int MultiblockStructure::getDimensionId() const
+{
+    return dimensionId;
 }

+ 1 - 0
FactoryCraft/MultiblockStructure.h

@@ -56,6 +56,7 @@ public:
     __int64 getStructureId() const;
     Framework::Vec3<int> getUniquePosition() const;
     int getStructureTypeId() const;
+    int getDimensionId() const;
 
     friend MultiblockStructureType;
 };

+ 18 - 4
FactoryCraft/MultiblockTree.cpp

@@ -24,13 +24,22 @@ void MultiblockTree::onBlockRemoved(
             bool foundStablizer = 0;
             Array<Block*> checked;
             Array<Block*> queue;
-            Block* current = zBlock->zNeighbor(getDirectionFromIndex(d));
+            Either<Block*, int> tmp = Game::INSTANCE->zBlockAt(
+                zBlock->getPos() + getDirection(getDirectionFromIndex(d)),
+                getDimensionId(),
+                0);
+            Block* current = tmp.isA() ? tmp.getA() : 0;
             if (current && isBlockMember(current)) queue.add(current);
             while (queue.getEintragAnzahl() > 0)
             {
                 current = queue.get(0);
                 queue.remove(0);
-                Block* bottom = current->zNeighbor(BOTTOM);
+                tmp = Game::INSTANCE->zBlockAt(
+                    zBlock->getPos()
+                        + getDirection(getDirectionFromIndex(d) | BOTTOM),
+                    getDimensionId(),
+                    0);
+                Block* bottom = tmp.isA() ? tmp.getA() : 0;
                 if (bottom && isBlockMember(bottom))
                 {
                     foundStablizer = 1;
@@ -39,8 +48,13 @@ void MultiblockTree::onBlockRemoved(
                 checked.add(current);
                 for (int i = 0; i < 4; i++)
                 {
-                    Block* neighbor
-                        = current->zNeighbor(getDirectionFromIndex(i));
+                    tmp = Game::INSTANCE->zBlockAt(
+                        zBlock->getPos()
+                            + getDirection(getDirectionFromIndex(d)
+                                           | getDirectionFromIndex(i)),
+                        getDimensionId(),
+                        0);
+                    Block* neighbor = tmp.isA() ? tmp.getA() : 0;
                     if (neighbor && isBlockMember(neighbor))
                     {
                         bool found = 0;

+ 11 - 10
FactoryCraft/StructureCollection.cpp

@@ -39,18 +39,19 @@ void StructureTemplateCollection::generateStructures(int x,
     Framework::Vec3<int>& maxPos,
     Framework::RCArray<GeneratedStructure>* zResult)
 {
-    int minSearchX = minPos.x - maxAffected.x;
-    int minSearchY = minPos.y - maxAffected.y;
-    int minSearchZ = MAX(minPos.z - maxAffected.z, 0);
-    int maxSearchX = maxPos.x - minAffected.x;
-    int maxSearchY = maxPos.y - minAffected.y;
-    int maxSearchZ = MIN(maxPos.z - minAffected.z, WORLD_HEIGHT - 1);
-    if (x >= minSearchX && x <= maxSearchX && y >= minSearchY && y <= maxSearchY
-        && z >= minSearchZ && z <= maxSearchZ)
+    if (condition->getValue())
     {
-        if (activeNoise->getNoise((double)x, (double)y, (double)z) < threshold)
+        int minSearchX = minPos.x - maxAffected.x;
+        int minSearchY = minPos.y - maxAffected.y;
+        int minSearchZ = minPos.z - maxAffected.z;
+        int maxSearchX = maxPos.x - minAffected.x;
+        int maxSearchY = maxPos.y - minAffected.y;
+        int maxSearchZ = maxPos.z - minAffected.z;
+        if (x >= minSearchX && x <= maxSearchX && y >= minSearchY
+            && y <= maxSearchY && z >= minSearchZ && z <= maxSearchZ)
         {
-            if (condition->getValue())
+            if (activeNoise->getNoise((double)x, (double)y, (double)z)
+                < threshold)
             {
                 double rValue
                     = structureNoise->getNoise((double)x, (double)y, (double)z);

+ 99 - 40
FactoryCraft/WormCaveGenerator.cpp

@@ -20,21 +20,23 @@ NoiseWorm3D::NoiseWorm3D(Noise* pitch,
       startChunk(Game::INSTANCE->getChunkCenter(startPos.x, startPos.y))
 {
     Framework::Vec3<float> lastPos = (Framework::Vec3<float>)startPos;
-    keyPoints.add(lastPos);
-    this->size.add((float)minRad);
+    Framework::Array<Framework::Vec3<float>> tmpPointList;
+    Framework::Array<float> tmpSizeList;
+    tmpPointList.add(lastPos);
+    tmpSizeList.add((float)minRad);
     minAffected.x = (int)lastPos.x - minRad;
     minAffected.y = (int)lastPos.y - minRad;
     minAffected.z = (int)lastPos.z - minRad;
     maxAffected.x = (int)lastPos.x + minRad;
     maxAffected.y = (int)lastPos.y + minRad;
     maxAffected.z = (int)lastPos.z + minRad;
-    while (keyPoints.getEintragAnzahl() < distant * 20)
+    while (tmpSizeList.getEintragAnzahl() < distant * 20)
     {
         Framework::Vec3<float> defaultDir(1.f, 0.f, 0.f);
-        if (keyPoints.getEintragAnzahl() > 1)
+        if (tmpPointList.getEintragAnzahl() > 1)
         {
-            defaultDir = keyPoints.get(keyPoints.getEintragAnzahl() - 1)
-                       - keyPoints.get(keyPoints.getEintragAnzahl() - 2);
+            defaultDir = tmpPointList.get(tmpPointList.getEintragAnzahl() - 1)
+                       - tmpPointList.get(tmpPointList.getEintragAnzahl() - 2);
         }
         float n = (float)yaw->getNoise(lastPos.x, lastPos.y, lastPos.z);
         defaultDir.rotateZ((n - 0.5f) / 2.f);
@@ -48,11 +50,11 @@ NoiseWorm3D::NoiseWorm3D(Noise* pitch,
                 .getLengthSq()
             >= (float)(distant * distant))
             break;
-        keyPoints.add(lastPos);
+        tmpPointList.add(lastPos);
         float rad = (float)size->getNoise(lastPos.x, lastPos.y, lastPos.z)
                       * (float)(maxRad - minRad)
                   + (float)minRad;
-        this->size.add(rad);
+        tmpSizeList.add(rad);
         minAffected.x = MIN(minAffected.x, (int)(lastPos.x - rad));
         minAffected.y = MIN(minAffected.y, (int)(lastPos.y - rad));
         minAffected.z = MIN(minAffected.z, (int)(lastPos.z - rad));
@@ -60,9 +62,30 @@ NoiseWorm3D::NoiseWorm3D(Noise* pitch,
         maxAffected.y = MAX(maxAffected.y, (int)(lastPos.y + rad));
         maxAffected.z = MAX(maxAffected.z, (int)(lastPos.z + rad));
     }
+    keyPoints = new float[3 * tmpPointList.getEintragAnzahl()];
+    keyPointSize = new float[tmpPointList.getEintragAnzahl()];
+    float* keyPointsP = keyPoints;
+    float* keyPointSizeP = keyPointSize;
+    auto pi = tmpPointList.begin();
+    auto si = tmpSizeList.begin();
+    while (pi)
+    {
+        *(keyPointsP++) = pi.val().x;
+        *(keyPointsP++) = pi.val().y;
+        *(keyPointsP++) = pi.val().z;
+        *(keyPointSizeP++) = si.val();
+        ++pi;
+        ++si;
+    }
+}
+
+NoiseWorm3D::~NoiseWorm3D()
+{
+    delete[] keyPoints;
+    delete[] keyPointSize;
 }
 
-Framework::Punkt NoiseWorm3D::getStartChunkCenter()
+const Framework::Punkt& NoiseWorm3D::getStartChunkCenter()
 {
     return startChunk;
 }
@@ -71,50 +94,52 @@ void NoiseWorm3D::getPartAffectedByChunk(
     int x, int y, Framework::RCArray<NoiseWorm3D>* zResult)
 {
     NoiseWorm3D* result = 0;
+    Framework::Array<Framework::Vec3<float>> tmpPointList;
+    Framework::Array<float> tmpSizeList;
     if (x - CHUNK_SIZE / 2 <= maxAffected.x
         && x + CHUNK_SIZE / 2 >= minAffected.x
         && y - CHUNK_SIZE / 2 <= maxAffected.y
         && y + CHUNK_SIZE / 2 >= minAffected.y)
     {
-        auto pi = keyPoints.begin();
-        auto si = size.begin();
+        float* pi = keyPoints;
+        float* si = keyPointSize;
         int newWormThreshold = 5;
         int outsideCounter = 0;
-        while (pi && si)
+        for (int i = 0; i < keyPointCount; i++)
         {
             if ((Framework::Vec2<float>((float)x, (float)y)
-                    - Framework::Vec2<float>(pi.val().x, pi.val().y))
+                    - Framework::Vec2<float>(*pi, *(pi + 1)))
                     .getLengthSq()
-                < (si.val() + CHUNK_SIZE / 2) * (si.val() + CHUNK_SIZE / 2))
+                < (*si + CHUNK_SIZE / 2) * (*si + CHUNK_SIZE / 2))
             {
                 outsideCounter = 0;
                 if (result == 0)
                 {
                     result = new NoiseWorm3D();
-                    result->minAffected.x = (int)(pi.val().x - si.val());
-                    result->minAffected.y = (int)(pi.val().y - si.val());
-                    result->minAffected.z = (int)(pi.val().z - si.val());
-                    result->maxAffected.x = (int)(pi.val().x + si.val());
-                    result->maxAffected.y = (int)(pi.val().y + si.val());
-                    result->maxAffected.z = (int)(pi.val().z + si.val());
+                    result->minAffected.x = (int)(*pi - *si);
+                    result->minAffected.y = (int)(*(pi + 1) - *si);
+                    result->minAffected.z = (int)(*(pi + 2) - *si);
+                    result->maxAffected.x = (int)(*pi + *si);
+                    result->maxAffected.y = (int)(*(pi + 1) + *si);
+                    result->maxAffected.z = (int)(*(pi + 2) + *si);
                 }
                 else
                 {
-                    result->minAffected.x = MIN(
-                        result->minAffected.x, (int)(pi.val().x - si.val()));
-                    result->minAffected.y = MIN(
-                        result->minAffected.y, (int)(pi.val().y - si.val()));
-                    result->minAffected.z = MIN(
-                        result->minAffected.z, (int)(pi.val().z - si.val()));
-                    result->maxAffected.x = MAX(
-                        result->maxAffected.x, (int)(pi.val().x + si.val()));
-                    result->maxAffected.y = MAX(
-                        result->maxAffected.y, (int)(pi.val().y + si.val()));
-                    result->maxAffected.z = MAX(
-                        result->maxAffected.z, (int)(pi.val().z + si.val()));
+                    result->minAffected.x
+                        = MIN(result->minAffected.x, (int)(*pi - *si));
+                    result->minAffected.y
+                        = MIN(result->minAffected.y, (int)(*(pi + 1) - *si));
+                    result->minAffected.z
+                        = MIN(result->minAffected.z, (int)(*(pi + 2) - *si));
+                    result->maxAffected.x
+                        = MAX(result->maxAffected.x, (int)(*pi + *si));
+                    result->maxAffected.y
+                        = MAX(result->maxAffected.y, (int)(*(pi + 1) + *si));
+                    result->maxAffected.z
+                        = MAX(result->maxAffected.z, (int)(*(pi + 2) + *si));
                 }
-                result->keyPoints.add(pi.val());
-                result->size.add(si.val());
+                tmpPointList.add({*pi, *(pi + 1), *(pi + 2)});
+                tmpSizeList.add(*si);
             }
             else
             {
@@ -123,18 +148,52 @@ void NoiseWorm3D::getPartAffectedByChunk(
                 {
                     if (result != 0)
                     {
+                        result->keyPoints
+                            = new float[tmpPointList.getEintragAnzahl() * 3];
+                        result->keyPointSize
+                            = new float[tmpPointList.getEintragAnzahl()];
+                        float* rkeyPointsP = result->keyPoints;
+                        float* rkeyPointSizeP = result->keyPointSize;
+                        auto rpi = tmpPointList.begin();
+                        auto rsi = tmpSizeList.begin();
+                        while (rpi)
+                        {
+                            *(rkeyPointsP++) = rpi.val().x;
+                            *(rkeyPointsP++) = rpi.val().y;
+                            *(rkeyPointsP++) = rpi.val().z;
+                            *(rkeyPointSizeP++) = rsi.val();
+                            ++rpi;
+                            ++rsi;
+                        }
+                        tmpPointList.leeren();
+                        tmpSizeList.leeren();
                         zResult->add(result);
                         result = 0;
                     }
                     outsideCounter = 0;
                 }
             }
-            ++pi;
+            pi += 3;
             ++si;
         }
     }
     if (result)
     {
+        result->keyPoints = new float[tmpPointList.getEintragAnzahl() * 3];
+        result->keyPointSize = new float[tmpPointList.getEintragAnzahl()];
+        float* rkeyPointsP = result->keyPoints;
+        float* rkeyPointSizeP = result->keyPointSize;
+        auto rpi = tmpPointList.begin();
+        auto rsi = tmpSizeList.begin();
+        while (rpi)
+        {
+            *(rkeyPointsP++) = rpi.val().x;
+            *(rkeyPointsP++) = rpi.val().y;
+            *(rkeyPointsP++) = rpi.val().z;
+            *(rkeyPointSizeP++) = rsi.val();
+            ++rpi;
+            ++rsi;
+        }
         zResult->add(result);
     }
 }
@@ -144,15 +203,15 @@ bool NoiseWorm3D::isInside(int x, int y, int z)
     if (x >= minAffected.x && x <= maxAffected.x && y >= minAffected.y
         && y <= maxAffected.y && z >= minAffected.z && z <= maxAffected.z)
     {
-        auto pi = keyPoints.begin();
-        auto si = size.begin();
+        float* pi = keyPoints;
+        float* si = keyPointSize;
         while (pi && si)
         {
             if (Framework::Vec3<float>((float)x, (float)y, (float)z)
-                    .abstandSq(pi.val())
-                < si.val() * si.val())
+                    .abstandSq({*pi, *(pi + 1), *(pi + 2)})
+                < *si * *si)
                 return 1;
-            ++pi;
+            pi += 3;
             ++si;
         }
     }

+ 5 - 3
FactoryCraft/WormCaveGenerator.h

@@ -14,8 +14,9 @@ private:
     Framework::Vec3<int> minAffected;
     Framework::Vec3<int> maxAffected;
 
-    Framework::Array<Framework::Vec3<float>> keyPoints;
-    Framework::Array<float> size;
+    float* keyPoints;
+    float* keyPointSize;
+    int keyPointCount;
     NoiseWorm3D();
 
 public:
@@ -26,7 +27,8 @@ public:
         int distant,
         int minRad,
         int maxRad);
-    Framework::Punkt getStartChunkCenter();
+    ~NoiseWorm3D();
+    const Framework::Punkt& getStartChunkCenter();
     void getPartAffectedByChunk(
         int x, int y, Framework::RCArray<NoiseWorm3D>* zResult);
     bool isInside(int x, int y, int z);

+ 8 - 185
Windows Version/data/generator/overworld.json

@@ -3,6 +3,9 @@
         "type": "cavedBioms",
         "name": "Overworld",
         "id": 0,
+        "seaFluidBlockType": "Water",
+        "globalSeaLevel": 200,
+        "terrainHeightLeyerName": "h",
         "dimensionSeed": {
             "type": "operator",
             "operator": "+",
@@ -364,118 +367,6 @@
                     }
                 ],
                 "blocks": [
-                    {
-                        "type": "blockInstance",
-                        "blockType": "Water",
-                        "bottomLayer": "h",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": ">=i",
-                            "values": [
-                                {
-                                    "type": "constant",
-                                    "value": 199
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
-                    },
-                    {
-                        "type": "blockType",
-                        "blockType": "Air",
-                        "bottomLayer": "h",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": ">i",
-                            "values": [
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
-                    },
-                    {
-                        "type": "blockType",
-                        "blockType": "Grass",
-                        "noise": {
-                            "type": "random",
-                            "seed": {
-                                "type": "operator",
-                                "operator": "+",
-                                "values": [
-                                    {
-                                        "type": "variable",
-                                        "name": "dimensionSeed"
-                                    },
-                                    {
-                                        "type": "constant",
-                                        "value": 3
-                                    }
-                                ]
-                            }
-                        },
-                        "threshold": 0.25,
-                        "topLayer": "h",
-                        "bottomLayer": "h",
-                        "condition": {
-                            "type": "operator",
-                            "operator": "&&",
-                            "values": [
-                                {
-                                    "type": "comparsion",
-                                    "operator": "==i",
-                                    "values": [
-                                        {
-                                            "type": "variable",
-                                            "name": "z"
-                                        },
-                                        {
-                                            "type": "variable",
-                                            "name": "h"
-                                        }
-                                    ]
-                                },
-                                {
-                                    "type": "blockType",
-                                    "x": {
-                                        "type": "variable",
-                                        "name": "x"
-                                    },
-                                    "y": {
-                                        "type": "variable",
-                                        "name": "y"
-                                    },
-                                    "z": {
-                                        "type": "operator",
-                                        "operator": "-",
-                                        "values": [
-                                            {
-                                                "type": "variable",
-                                                "name": "z"
-                                            },
-                                            {
-                                                "type": "constant",
-                                                "value": 1
-                                            }
-                                        ]
-                                    },
-                                    "blockType": "Dirt"
-                                }
-                            ]
-                        }
-                    },
                     {
                         "type": "blockType",
                         "blockType": "Gravel",
@@ -498,21 +389,7 @@
                             }
                         },
                         "threshold": 0.35,
-                        "topLayer": "h",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": "<i",
-                            "values": [
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
+                        "topLayer": "h"
                     },
                     {
                         "type": "blockType",
@@ -536,21 +413,7 @@
                             }
                         },
                         "threshold": 0.35,
-                        "topLayer": "h",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": "<i",
-                            "values": [
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
+                        "topLayer": "h"
                     },
                     {
                         "type": "blockType",
@@ -575,60 +438,20 @@
                         },
                         "threshold": 0.35,
                         "topLayer": "h",
-                        "bottomLayer": "u",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": "<i",
-                            "values": [
-                                {
-                                    "type": "variable",
-                                    "name": "u"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
+                        "bottomLayer": "u"
                     },
                     {
                         "type": "blockType",
                         "blockType": "Dirt",
                         "topLayer": "h",
-                        "bottomLayer": "u",
-                        "condition": {
-                            "type": "comparsion",
-                            "operator": "<i",
-                            "values": [
-                                {
-                                    "type": "variable",
-                                    "name": "u"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "z"
-                                },
-                                {
-                                    "type": "variable",
-                                    "name": "h"
-                                }
-                            ]
-                        }
+                        "bottomLayer": "u"
                     },
                     {
                         "type": "blockType",
                         "blockType": "Stone",
                         "topLayer": "u"
                     }
-                ],
-                "condition": {
-                    "type": "constant",
-                    "value": true
-                }
+                ]
             }
         ]
     }