ソースを参照

fix issue with block and light transmission at chunk borders

Kolja Strohm 1 年間 前
コミット
d418f08601

+ 1 - 1
FactoryCraft/BlockInfoCommand.cpp

@@ -53,7 +53,7 @@ void BlockInfoCommand::execute(
         {
             Block* zBlock = block.getA();
             result.append()
-                << "Block instance found.\n  "
+                << "Block instance found.\n"
                 << "  Block Type: " << zBlock->zBlockType()->getName() << " ("
                 << zBlock->zBlockType()->getId() << ")\n"
                 << "  HP: " << zBlock->getHP() << " / " << zBlock->getMaxHP()

+ 134 - 77
FactoryCraft/Chunk.cpp

@@ -112,7 +112,9 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
                                    + pos.z;
                             int type = blockIds[bi];
                             needSend |= type != BlockTypeEnum::NO_BLOCK
-                                     && type != BlockTypeEnum::AIR;
+                                     && StaticRegistry<BlockType>::INSTANCE
+                                            .zElement(type)
+                                            ->doesNeedClientInstance();
                         }
                         else
                         {
@@ -129,8 +131,10 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
                                                * WORLD_HEIGHT
                                            + (pos.z - offset.z);
                                     int type = zNeighbours[i]->blockIds[bi];
-                                    needSend |= type != BlockTypeEnum::NO_BLOCK
-                                             && type != BlockTypeEnum::AIR;
+                                    needSend
+                                        |= StaticRegistry<BlockType>::INSTANCE
+                                               .zElement(type)
+                                               ->doesNeedClientInstance();
                                 }
                             }
                         }
@@ -195,6 +199,62 @@ void Chunk::sendLightToClient(Framework::StreamWriter* zWriter)
     zWriter->schreibe((char*)&end, 4);
 }
 
+bool Chunk::isVisible(int index) const
+{
+    if (!blocks[index])
+    {
+        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
+            {
+                Vec3<int> indexPos = {(index / WORLD_HEIGHT) / CHUNK_SIZE,
+                    (index / WORLD_HEIGHT) % CHUNK_SIZE,
+                    index % WORLD_HEIGHT};
+                for (int d = 0; d < 6; d++)
+                {
+                    Either<Block*, int> n = BlockTypeEnum::NO_BLOCK;
+                    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);
+                    }
+                    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();
+}
+
 void Chunk::broadcastLightData(int index, bool foreground)
 {
     int x = (index / WORLD_HEIGHT) / CHUNK_SIZE;
@@ -261,9 +321,8 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
         if (observer->getEntityId() == zEntity->getId()) return;
     }
     int id = zEntity->getId();
-    InformationObserver* observer = new InformationObserver(id);
-    observers.add(observer);
-    laterHandler.addTodo([this, id, observer]() {
+    observers.add(new InformationObserver(id));
+    laterHandler.addTodo([this, id]() {
         InMemoryBuffer buffer;
         buffer.schreibe("\4", 1);
         buffer.schreibe((char*)&location.x, 4);
@@ -280,17 +339,6 @@ void Chunk::addObserver(Entity* zEntity, DoLaterHandler& laterHandler)
         Entity* e = Game::INSTANCE->zEntity(id);
         if (e)
         {
-            Punkt p = location;
-            int dimId = dimensionId;
-            msg->setOnAfterSend([this, p, dimId, observer]() {
-                // check if chunk is still loaded
-                if (Game::INSTANCE->zDimension(dimId)
-                    && Game::INSTANCE->zDimension(dimId)->zChunk(p) == this)
-                {
-                    // send all waiting messages to the observer
-                    observer->setReady();
-                }
-            });
             Game::INSTANCE->sendMessage(msg, e);
         }
         else
@@ -328,6 +376,15 @@ void Chunk::api(Framework::StreamReader* zRequest,
         // unsubscribe
         removeObserver(zSource);
         break;
+    case 2:
+        // observer ready
+        for (InformationObserver* observer : observers)
+        {
+            if (observer->getEntityId() == zSource->getId())
+            {
+                observer->setReady();
+            }
+        }
     }
 }
 
@@ -423,6 +480,15 @@ const Block* Chunk::zBlockConst(Framework::Vec3<int> location) const
     return StaticRegistry<BlockType>::INSTANCE.zElement(b.getB())->zDefault();
 }
 
+const Block* Chunk::zBlockConst(int index) const
+{
+    if (blocks[index])
+        return blocks[index];
+    else
+        return StaticRegistry<BlockType>::INSTANCE.zElement(blockIds[index])
+            ->zDefault();
+}
+
 void Chunk::instantiateBlock(Framework::Vec3<int> location)
 {
     auto b = zBlockAt(location);
@@ -630,7 +696,9 @@ void Chunk::sendBlockInfo(Framework::Vec3<int> location)
 
 void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
 {
-    zNeighbours[getDirectionIndex(dir)] = zChunk;
+    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++)
@@ -657,8 +725,10 @@ void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
                 index = i * WORLD_HEIGHT + z;
                 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
@@ -667,12 +737,26 @@ void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
                     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))
+                {
+                    needsTransmission = 1;
+                }
             }
             if (zChunk)
             {
                 if (!blocks[index])
                 {
-                    if (zChunk->blockIds[j] == BlockTypeEnum::AIR
+                    if (zChunk->zBlockConst(j)->isTransparent()
                         && !blockIds[index])
                     {
                         generateBlock(
@@ -681,11 +765,12 @@ void Chunk::setNeighbor(Direction dir, Chunk* zChunk)
                                 index % WORLD_HEIGHT));
                     }
                 }
-                if (blockIds[index] != BlockTypeEnum::AIR
-                    && blockIds[index] != BlockTypeEnum::NO_BLOCK)
-                {
-                    zChunk->broadcastLightData(j, true);
-                }
+            }
+            if (needsTransmission && added)
+            {
+                sendBlockInfo(Vec3<int>((index / WORLD_HEIGHT) / CHUNK_SIZE,
+                    (index / WORLD_HEIGHT) % CHUNK_SIZE,
+                    index % WORLD_HEIGHT));
             }
         }
     }
@@ -761,56 +846,18 @@ void Chunk::sendToClient(Framework::StreamWriter* zWriter)
             for (int z = 0; z < WORLD_HEIGHT; z++)
             {
                 int index = Chunk::index({x, y, z});
-                unsigned short blockType
-                    = blocks[index]
-                        ? (unsigned short)blocks[index]->zBlockType()->getId()
-                        : blockIds[index];
-                if (blockType)
+                if (isVisible(index)
+                    && StaticRegistry<BlockType>::INSTANCE
+                           .zElement(blockIds[index])
+                           ->doesNeedClientInstance())
                 {
-                    bool visible = 0;
-                    if (!visible)
-                    {
-                        if (!blocks[index])
-                        {
-                            if (CONST_BLOCK(0, blockIds[index])->isTransparent()
-                                || CONST_BLOCK(0, blockIds[index])
-                                       ->isPassable())
-                                visible = 1;
-                            else
-                            {
-                                for (int d = 0; d < 6 && !visible; d++)
-                                {
-                                    Vec3<int> pos
-                                        = getDirection(
-                                              (Directions)getDirectionFromIndex(
-                                                  d))
-                                        + Framework::Vec3<int>(x, y, z);
-                                    auto n = zBlockNeighbor(pos);
-                                    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 (pos.x < 0 || pos.y < 0 || pos.z < 0
-                                        || pos.x >= CHUNK_SIZE
-                                        || pos.y >= CHUNK_SIZE
-                                        || pos.z >= WORLD_HEIGHT)
-                                        visible = 1;
-                                }
-                            }
-                        }
-                        else
-                            visible = blocks[index]->isVisible();
-                    }
-                    if (visible
-                        && (blocks[index] || blockType != BlockTypeEnum::AIR))
-                    {
-                        zWriter->schreibe((char*)&blockType, 2);
-                        zWriter->schreibe((char*)&index, 4);
-                    }
+                    unsigned short blockType = blocks[index]
+                                                 ? (unsigned short)blocks[index]
+                                                       ->zBlockType()
+                                                       ->getId()
+                                                 : blockIds[index];
+                    zWriter->schreibe((char*)&blockType, 2);
+                    zWriter->schreibe((char*)&index, 4);
                 }
             }
         }
@@ -859,7 +906,9 @@ void Chunk::removeUnusedBlocks()
     int count = 0;
     for (int i = 0; i < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT; i++)
     {
-        if (blockIds[i] && blockIds[i] != BlockTypeEnum::AIR) count++;
+        if (StaticRegistry<BlockType>::INSTANCE.zElement(blockIds[i])
+                ->doesNeedClientInstance())
+            count++;
     }
     std::cout << "chunk " << location.x << ", " << location.y
               << " was generated with " << count << " blocks.\n";
@@ -951,13 +1000,21 @@ void Chunk::setLightData(
             {
                 int bi = (pos.x * CHUNK_SIZE + pos.y) * WORLD_HEIGHT + pos.z;
                 int type = blockIds[bi];
-                needSend |= type != BlockTypeEnum::NO_BLOCK
-                         && type != BlockTypeEnum::AIR;
+                needSend |= StaticRegistry<BlockType>::INSTANCE.zElement(type)
+                                ->doesNeedClientInstance();
                 if (needSend) break;
             }
             else
             {
-                needSend = 1; // TODO: check if the block is visible
+                int type = Game::INSTANCE->getBlockType(
+                    pos
+                        + Vec3<int>(this->location.x - CHUNK_SIZE / 2,
+                            this->location.y - CHUNK_SIZE / 2,
+                            0),
+                    dimensionId);
+                needSend |= StaticRegistry<BlockType>::INSTANCE.zElement(type)
+                                ->doesNeedClientInstance();
+                if (needSend) break;
             }
         }
     }

+ 2 - 0
FactoryCraft/Chunk.h

@@ -37,6 +37,8 @@ private:
     void removeLightSource(int index);
     void sendLightToClient(Framework::StreamWriter* zWriter);
     void broadcastLightData(int index, bool foreground);
+    const Block* zBlockConst(int index) const;
+    bool isVisible(int index) const;
 
 public:
     Chunk(Framework::Punkt location, int dimensionId);

+ 10 - 0
FactoryCraft/Dimension.cpp

@@ -351,6 +351,16 @@ const Block* Dimension::zBlockOrDefault(Framework::Vec3<int> location)
     return &NoBlock::INSTANCE;
 }
 
+int Dimension::getBlockType(Framework::Vec3<int> location) {
+    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
+    if (c)
+    {
+        location = chunkCoordinates(location);
+        return c->getBlockTypeAt(location);
+    }
+    return BlockTypeEnum::NO_BLOCK;
+}
+
 void Dimension::placeBlock(
     Framework::Vec3<int> location, Framework::Either<Block*, int> block)
 {

+ 1 - 0
FactoryCraft/Dimension.h

@@ -57,6 +57,7 @@ public:
     Framework::Either<Block*, int> zBlock(Framework::Vec3<int> location);
     Block* zRealBlockInstance(Framework::Vec3<int> location);
     const Block* zBlockOrDefault(Framework::Vec3<int> location);
+    int getBlockType(Framework::Vec3<int> location);
     void placeBlock(
         Framework::Vec3<int> location, Framework::Either<Block*, int> block);
     void sendBlockInfo(Framework::Vec3<int> location);

+ 7 - 0
FactoryCraft/Game.cpp

@@ -819,6 +819,13 @@ Block* Game::zRealBlockInstance(Framework::Vec3<int> location, int dimension)
     return 0;
 }
 
+int Game::getBlockType(Framework::Vec3<int> location, int dimension)
+{
+    Dimension* dim = zDimension(dimension);
+    if (dim) return dim->getBlockType(location);
+    return 0;
+}
+
 Dimension* Game::zDimension(int id) const
 {
     for (auto dim : *dimensions)

+ 1 - 0
FactoryCraft/Game.h

@@ -126,6 +126,7 @@ public:
     Framework::Either<Block*, int> zBlockAt(
         Framework::Vec3<int> location, int dimension) const;
     Block* zRealBlockInstance(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);
     Area getChunckArea(Punkt center) const;

+ 8 - 0
FactoryCraft/Grass.cpp

@@ -11,6 +11,7 @@ GrassBlock::GrassBlock(int typeId,
     : Block(typeId, zTool, pos, dimensionId, 0)
 {
     tickSource = 1;
+    transparent = 1;
 }
 
 bool GrassBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
@@ -66,6 +67,13 @@ void GrassBlock::onDestroy()
     }
 }
 
+void GrassBlock::filterPassingLight(unsigned char rgb[3]) const
+{
+    rgb[0] = (unsigned char)(rgb[0] * 0.7);
+    rgb[1] = (unsigned char)(rgb[1] * 0.9);
+    rgb[2] = (unsigned char)(rgb[2] * 0.7);
+}
+
 GrassBlockType::GrassBlockType(
     int typeId, int itemTypeId, ModelInfo model, const char* name, int mapColor)
     : BlockType(typeId, 0, model, 1, 10, 0, name, false, mapColor),

+ 1 - 0
FactoryCraft/Grass.h

@@ -15,6 +15,7 @@ public:
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
     virtual void onDestroy() override;
+    void filterPassingLight(unsigned char rgb[3]) const override;
 
     friend GrassBlockType;
 };