Browse Source

improve performance of fluid block ticking

Kolja Strohm 2 months ago
parent
commit
0d55d8f2cd

+ 7 - 3
FactoryCraft/Block.cpp

@@ -27,7 +27,6 @@ Block::Block(
     onTickCalled = 0;
     minTickTimeout = -1;
     maxTickTimeout = -1;
-    tickSource = 0;
     currentTickTimeout = 0;
     interactable = 0;
     deadAndRemoved = 0;
@@ -261,9 +260,14 @@ void Block::api(Framework::StreamReader* zRequest, NetworkMessage* zResponse)
     }
 }
 
-bool Block::isTickSource() const
+TickSourceType Block::isTickSource() const
 {
-    return tickSource;
+    return NONE;
+}
+
+bool Block::needsTick() const
+{
+    return 1;
 }
 
 const BlockType* Block::zBlockType() const

+ 3 - 3
FactoryCraft/Block.h

@@ -11,6 +11,7 @@
 #include "NetworkMessage.h"
 #include "ReferenceCounter.h"
 #include "Tickable.h"
+#include "TickSourceType.h"
 
 #define CONST_BLOCK(maybeBlock, type) \
     (maybeBlock ? maybeBlock          \
@@ -45,7 +46,6 @@ protected:
 
     int minTickTimeout;
     int maxTickTimeout;
-    bool tickSource;
     bool interactable;
     bool transmissionRequested;
     bool deadAndRemoved;
@@ -96,8 +96,8 @@ public:
     virtual void sendModelInfo(NetworkMessage* zMessage);
     virtual bool interact(Item* zItem, Entity* zActor);
     void api(Framework::StreamReader* zRequest, NetworkMessage* zResponse);
-
-    bool isTickSource() const;
+    virtual TickSourceType isTickSource() const;
+    virtual bool needsTick() const;
     const BlockType* zBlockType() const;
     bool isTransparent() const;
     bool isPassable() const;

+ 1 - 1
FactoryCraft/BlockInfoCommand.cpp

@@ -36,7 +36,7 @@ bool BlockInfoCommand::execute(
     bool ok = 1;
     if (Game::INSTANCE->zDimension(dimension))
     {
-        auto block = Game::INSTANCE->zBlockAt(location, dimension);
+        auto block = Game::INSTANCE->zBlockAt(location, dimension, 0);
 
         if (block.isB())
         {

+ 71 - 14
FactoryCraft/Chunk.cpp

@@ -17,6 +17,7 @@ Chunk::Chunk(Framework::Punkt location, int dimensionId)
       dimensionId(dimensionId),
       location(location),
       added(0),
+      worldUpdated(1),
       currentlyLoading(1)
 {
     blocks = new Block*[CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT];
@@ -64,8 +65,20 @@ void Chunk::unlock()
 
 void Chunk::tick(TickQueue* zQueue)
 {
-    for (Block* source : tickSources)
+    for (Block* source : tickSourcesEachTick)
         zQueue->addToQueue(source);
+    if (worldUpdated)
+    {
+        worldUpdated = 0;
+        for (Block* source : tickSourcesAfterUpdate)
+        {
+            if (source->needsTick())
+            {
+                zQueue->addToQueue(source);
+                worldUpdated = 1;
+            }
+        }
+    }
 }
 
 void Chunk::postTick() {}
@@ -287,7 +300,7 @@ void Chunk::broadcastLightData(int index, bool foreground)
 }
 
 Framework::Either<Block*, int> Chunk::zBlockNeighbor(
-    Framework::Vec3<int> location)
+    Framework::Vec3<int> location, OUT Chunk** zNeighborChunk)
 {
     if (location.x >= 0 && location.x < CHUNK_SIZE && location.y >= 0
         && location.y < CHUNK_SIZE && location.z >= 0
@@ -295,6 +308,10 @@ Framework::Either<Block*, int> Chunk::zBlockNeighbor(
     {
         int index = (location.x * CHUNK_SIZE + location.y) * WORLD_HEIGHT
                   + location.z;
+        if (zNeighborChunk)
+        {
+            *zNeighborChunk = this;
+        }
         if (blocks[index])
             return blocks[index];
         else
@@ -305,7 +322,8 @@ Framework::Either<Block*, int> Chunk::zBlockNeighbor(
             {location.x + this->location.x - CHUNK_SIZE / 2,
                 location.y + this->location.y - CHUNK_SIZE / 2,
                 location.z},
-            dimensionId);
+            dimensionId,
+            zNeighborChunk);
     return 0;
 }
 
@@ -541,15 +559,34 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
     int index = Chunk::index(location);
     assert(index < CHUNK_SIZE * CHUNK_SIZE * WORLD_HEIGHT && index >= 0);
     Block* old = blocks[index];
-    if (old && old->isTickSource())
+    if (old && old->isTickSource() != TickSourceType::NONE)
     { // remove from tick sorces
-        for (Framework::ArrayIterator<Block*> obj = tickSources.begin(); obj;
-             obj++)
+        if (old->isTickSource() == TickSourceType::EACH_TICK)
+        {
+            for (Framework::ArrayIterator<Block*> obj
+                 = tickSourcesEachTick.begin();
+                 obj;
+                 obj++)
+            {
+                if (obj.val() == old)
+                {
+                    obj.remove();
+                    break;
+                }
+            }
+        }
+        else if (old->isTickSource() == TickSourceType::AFTER_WORLD_UPDATE)
         {
-            if (obj.val() == old)
+            for (Framework::ArrayIterator<Block*> obj
+                 = tickSourcesAfterUpdate.begin();
+                 obj;
+                 obj++)
             {
-                obj.remove();
-                break;
+                if (obj.val() == old)
+                {
+                    obj.remove();
+                    break;
+                }
             }
         }
     }
@@ -577,8 +614,9 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
     for (int i = 0; i < 6; i++)
     {
         Direction d = getDirectionFromIndex(i);
+        Chunk* zNeighborChunk = 0;
         Framework::Either<Block*, int> neighbor
-            = zBlockNeighbor(location + getDirection(d));
+            = zBlockNeighbor(location + getDirection(d), &zNeighborChunk);
         if (neighbor.isA())
         {
             if (block)
@@ -593,12 +631,24 @@ void Chunk::putBlockAt(Framework::Vec3<int> location, Block* block)
             }
         }
         if (block) block->setNeighbour(d, neighbor);
+        if (zNeighborChunk && zNeighborChunk != this)
+        {
+            zNeighborChunk->worldUpdated = 1;
+        }
     }
     if (old) old->release();
-    if (block && block->isTickSource())
+    if (block && block->isTickSource() != TickSourceType::NONE)
     { // add to tick sources
-        tickSources.add(block);
+        if (block->isTickSource() == TickSourceType::EACH_TICK)
+        {
+            tickSourcesEachTick.add(block);
+        }
+        else if (block->isTickSource() == TickSourceType::AFTER_WORLD_UPDATE)
+        {
+            tickSourcesAfterUpdate.add(block);
+        }
     }
+    worldUpdated = 1;
     if (change)
     {
         if (isLightSource != wasLightSource)
@@ -643,11 +693,16 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
         for (int i = 0; i < 6; i++)
         {
             Direction d = getDirectionFromIndex(i);
+            Chunk* zNeighborChunk = 0;
             Framework::Either<Block*, int> neighbor
-                = zBlockNeighbor(location + getDirection(d));
+                = zBlockNeighbor(location + getDirection(d), &zNeighborChunk);
             if (neighbor.isA())
                 ((Block*)neighbor)
                     ->setNeighbourType(getOppositeDirection(d), type);
+            if (zNeighborChunk && zNeighborChunk != this)
+            {
+                zNeighborChunk->worldUpdated = 1;
+            }
         }
         if (isLightSource != wasLightSource)
         {
@@ -669,6 +724,7 @@ void Chunk::putBlockTypeAt(Framework::Vec3<int> location, int type)
                     location.y + this->location.y - CHUNK_SIZE / 2,
                     location.z);
         }
+        worldUpdated = 1;
     }
 }
 
@@ -966,7 +1022,8 @@ void Chunk::removeUnusedBlocks()
                 {
                     auto n = zBlockNeighbor(
                         getDirection((Directions)getDirectionFromIndex(d))
-                        + Framework::Vec3<int>(x, y, z));
+                            + Framework::Vec3<int>(x, y, z),
+                        0);
                     if (n.isA()
                         && (((Block*)n)->isPassable()
                             || ((Block*)n)->isTransparent()))

+ 6 - 4
FactoryCraft/Chunk.h

@@ -7,10 +7,10 @@
 #include <Vec3.h>
 
 #include "Block.h"
-#include "DoLaterHandler.h"
-#include "Tickable.h"
 #include "Constants.h"
+#include "DoLaterHandler.h"
 #include "InformationObserver.h"
+#include "Tickable.h"
 
 class ChunkMap;
 
@@ -24,12 +24,14 @@ private:
     Chunk* zNeighbours[4];
     unsigned short* blockIds;
     Framework::Either<Block*, int> zBlockNeighbor(
-        Framework::Vec3<int> location);
+        Framework::Vec3<int> location, OUT Chunk** zNeighborChunk);
     bool added;
     Framework::Critical cs;
     Framework::RCArray<InformationObserver> observers;
     Framework::Array<int> lightSources;
-    Framework::Array<Block*> tickSources;
+    Framework::Array<Block*> tickSourcesEachTick;
+    Framework::Array<Block*> tickSourcesAfterUpdate;
+    bool worldUpdated;
     unsigned char* lightData;
     bool currentlyLoading;
 

+ 6 - 2
FactoryCraft/Constants.h

@@ -12,8 +12,12 @@
 #ifdef _DEBUG
 #    define DEFAULT_VIEW_DISTANCE 2
 #else
-#    define DEFAULT_VIEW_DISTANCE 64
+#    define DEFAULT_VIEW_DISTANCE 32
 #endif
 #define MAX_SURFACE_HEIGHT    50
 #define VARIABLE_SURFACE_PART 0.5f
-#define MAX_TICKS_PER_SECOND  20
+#define MAX_TICKS_PER_SECOND  20
+
+#ifndef OUT
+#    define OUT
+#endif

+ 9 - 3
FactoryCraft/Dimension.cpp

@@ -99,7 +99,7 @@ void Dimension::api(Framework::InMemoryBuffer* zRequest,
             zRequest->lese((char*)&location.x, 4);
             zRequest->lese((char*)&location.y, 4);
             zRequest->lese((char*)&location.z, 4);
-            Framework::Either<Block*, int> block = zBlock(location);
+            Framework::Either<Block*, int> block = zBlock(location, 0);
             if (block.isA())
             {
                 block.getA()->api(zRequest, zResponse);
@@ -341,9 +341,15 @@ Chunk* Dimension::zChunk(Punkt wPos) const
     return chunks->z(addr, 8);
 }
 
-Framework::Either<Block*, int> Dimension::zBlock(Vec3<int> location)
+Framework::Either<Block*, int> Dimension::zBlock(
+    Vec3<int> location, OUT Chunk** zChunk)
 {
-    Chunk* c = zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
+    Chunk* c
+        = this->zChunk(Game::INSTANCE->getChunkCenter(location.x, location.y));
+    if (zChunk)
+    {
+        *zChunk = c;
+    }
     if (c)
     {
         location = chunkCoordinates(location);

+ 5 - 4
FactoryCraft/Dimension.h

@@ -1,15 +1,15 @@
 #pragma once
 
+#include <Critical.h>
+#include <Either.h>
 #include <InMemoryBuffer.h>
 #include <Punkt.h>
 #include <Thread.h>
 #include <Trie.h>
-#include <Either.h>
-#include <Critical.h>
 
+#include "Chunk.h"
 #include "Constants.h"
 #include "MultiblockStructure.h"
-#include "Chunk.h"
 
 class DimensionMap;
 class NetworkMessage;
@@ -73,7 +73,8 @@ public:
 
     void thread() override;
 
-    Framework::Either<Block*, int> zBlock(Framework::Vec3<int> location);
+    Framework::Either<Block*, int> zBlock(
+        Framework::Vec3<int> location, OUT Chunk** zChunk);
     Block* zRealBlockInstance(Framework::Vec3<int> location);
     const Block* zBlockOrDefault(Framework::Vec3<int> location);
     int getBlockType(Framework::Vec3<int> location);

+ 4 - 3
FactoryCraft/Entity.cpp

@@ -132,7 +132,7 @@ void ActionTarget::toMessage(
         {
             Framework::Text targetUIML = "";
             auto block
-                = Game::INSTANCE->zBlockAt(zTarget->blockPos, dimensionId);
+                = Game::INSTANCE->zBlockAt(zTarget->blockPos, dimensionId, 0);
             if (block.isA())
             {
                 targetUIML = block.getA()->getTargetUIML();
@@ -419,8 +419,9 @@ void Entity::calculateTarget(Framework::Vec3<float> basePos,
     Direction dir = BOTTOM;
     while (true)
     {
-        if (getDefaultBlock(Game::INSTANCE->zBlockAt(
-                                Framework::Vec3<int>{px, py, pz}, dimensionId))
+        if (getDefaultBlock(
+                Game::INSTANCE->zBlockAt(
+                    Framework::Vec3<int>{px, py, pz}, dimensionId, 0))
                 ->isInteractable(zItem))
         {
             if (!target || !target->isBlock({px, py, pz}, dir))

+ 1 - 0
FactoryCraft/FactoryCraft.vcxproj

@@ -187,6 +187,7 @@
     <ClInclude Include="Tickable.h" />
     <ClInclude Include="TickOrganizer.h" />
     <ClInclude Include="TickQueue.h" />
+    <ClInclude Include="TickSourceType.h" />
     <ClInclude Include="TickWorker.h" />
     <ClInclude Include="TreeSeblingBlock.h" />
     <ClInclude Include="TreeTemplate.h" />

+ 3 - 0
FactoryCraft/FactoryCraft.vcxproj.filters

@@ -414,6 +414,9 @@
     <ClInclude Include="AnimalyAI.h">
       <Filter>entities\animals</Filter>
     </ClInclude>
+    <ClInclude Include="TickSourceType.h">
+      <Filter>world\ticking</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Server.cpp">

+ 10 - 1
FactoryCraft/FluidBlock.cpp

@@ -22,7 +22,6 @@ FluidBlock::FluidBlock(int typeId,
     maxHP = 1;
     hardness = -1.f;
     speedModifier = 0.5f;
-    tickSource = 1;
     interactable = 0;
     flowOptions = 0;
     distanceToSource = 0;
@@ -203,6 +202,16 @@ void FluidBlock::filterPassingLight(unsigned char rgb[3]) const
     rgb[2] = (unsigned char)(rgb[2] * lightWeights.z);
 }
 
+TickSourceType FluidBlock::isTickSource() const
+{
+    return TickSourceType::AFTER_WORLD_UPDATE;
+}
+
+bool FluidBlock::needsTick() const
+{
+    return neighborChanged;
+}
+
 char FluidBlock::getDistanceToSource() const
 {
     return distanceToSource;

+ 2 - 0
FactoryCraft/FluidBlock.h

@@ -34,6 +34,8 @@ public:
 
     virtual bool isInteractable(const Item* zItem) const override;
     virtual void filterPassingLight(unsigned char rgb[3]) const override;
+    virtual TickSourceType isTickSource() const override;
+    virtual bool needsTick() const override;
 
     char getDistanceToSource() const;
     char getFlowOptions() const;

+ 5 - 4
FactoryCraft/Game.cpp

@@ -895,7 +895,7 @@ void Game::api(Framework::InMemoryBuffer* zRequest, GameClient* zOrigin)
                 zRequest->lese((char*)&pos.x, 4);
                 zRequest->lese((char*)&pos.y, 4);
                 zRequest->lese((char*)&pos.z, 4);
-                target = zBlockAt(pos, dim);
+                target = zBlockAt(pos, dim, 0);
             }
             if (target)
                 target->inventoryApi(zRequest, response, zOrigin->zEntity());
@@ -1091,7 +1091,8 @@ GameClient* Game::addPlayer(FCKlient* client, Framework::Text name)
             b = zBlockAt({(int)player->getPosition().x,
                              (int)player->getPosition().y,
                              --h},
-                player->getDimensionId());
+                player->getDimensionId(),
+                0);
         player->setPosition(
             {player->getPosition().x, player->getPosition().y, (float)h + 2.f});
     }
@@ -1161,10 +1162,10 @@ void Game::spawnItem(
 }
 
 Framework::Either<Block*, int> Game::zBlockAt(
-    Framework::Vec3<int> location, int dimension) const
+    Framework::Vec3<int> location, int dimension, OUT Chunk** zChunk) const
 {
     Dimension* dim = zDimension(dimension);
-    if (dim) return dim->zBlock(location);
+    if (dim) return dim->zBlock(location, zChunk);
     return 0;
 }
 

+ 1 - 1
FactoryCraft/Game.h

@@ -148,7 +148,7 @@ public:
         Framework::Vec3<float> location, int dimensionId, ItemStack* stack);
     bool isChunkLoaded(int x, int y, int dimension) const;
     Framework::Either<Block*, int> zBlockAt(
-        Framework::Vec3<int> location, int dimension) const;
+        Framework::Vec3<int> location, int dimension, OUT Chunk** zChunk) const;
     Block* zRealBlockInstance(Framework::Vec3<int> location, int dimension);
     int getBlockType(Framework::Vec3<int> location, int dimension);
     Dimension* zDimension(int id) const;

+ 5 - 1
FactoryCraft/Grass.cpp

@@ -7,7 +7,6 @@
 GrassBlock::GrassBlock(int typeId, Framework::Vec3<int> pos, int dimensionId)
     : AdditionalItemSpawningBlock(typeId, pos, dimensionId)
 {
-    tickSource = 1;
     transparent = 1;
 }
 
@@ -26,6 +25,11 @@ void GrassBlock::filterPassingLight(unsigned char rgb[3]) const
     rgb[2] = (unsigned char)(rgb[2] * 0.7);
 }
 
+TickSourceType GrassBlock::isTickSource() const
+{
+    return TickSourceType::EACH_TICK;
+}
+
 GrassBlockType::GrassBlockType()
     : AdditionalItemSpawningBlockType()
 {}

+ 1 - 0
FactoryCraft/Grass.h

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

+ 6 - 3
FactoryCraft/GrowingPlant.cpp

@@ -38,9 +38,7 @@ GrowingPlantBlock::GrowingPlantBlock(int typeId,
       blockTypeAfterGrowth(blockTypeAfterGrowth),
       plantSpawned(0),
       lastSendState(-1)
-{
-    tickSource = 1;
-}
+{}
 
 bool GrowingPlantBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 {
@@ -107,6 +105,11 @@ void GrowingPlantBlock::sendModelInfo(NetworkMessage* zMessage)
     }
 }
 
+TickSourceType GrowingPlantBlock::isTickSource() const
+{
+    return TickSourceType::EACH_TICK;
+}
+
 Framework::Text GrowingPlantBlock::getTargetUIML()
 {
     return Framework::Text("<targetInfo><text width=\"auto\" height=\"auto\">")

+ 1 - 0
FactoryCraft/GrowingPlant.h

@@ -40,6 +40,7 @@ public:
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
     virtual void sendModelInfo(NetworkMessage* zMessage) override;
+    virtual TickSourceType isTickSource() const override;
     virtual Framework::Text getTargetUIML();
     GrowingPlantBlock* addGrowthState(GrowthState* state);
 

+ 3 - 4
FactoryCraft/Item.cpp

@@ -134,7 +134,7 @@ bool Item::canBeStackedWith(const Item* zItem) const
 
 bool Item::canBePlacedAt(int dimensionId, Framework::Vec3<int> worldPos) const
 {
-    auto b = Game::INSTANCE->zBlockAt(worldPos, dimensionId);
+    auto b = Game::INSTANCE->zBlockAt(worldPos, dimensionId, 0);
     return (b.isA()
                && (b.getA()->zBlockType()->getId() == BlockTypeEnum::AIR
                    || b.getA()->zBlockType()->isFluid()))
@@ -204,7 +204,7 @@ void ItemJsonType::toJson(
 }
 
 JSONObjectValidationBuilder* ItemJsonType::addToValidator(
-    JSONObjectValidationBuilder *builder) const
+    JSONObjectValidationBuilder* builder) const
 {
     Framework::RCArray<Framework::Text> itemTypes;
     for (int index = 0; index < Game::INSTANCE->getItemTypeCount(); index++)
@@ -215,8 +215,7 @@ JSONObjectValidationBuilder* ItemJsonType::addToValidator(
                 Game::INSTANCE->zItemType(index)->getName()));
         }
     }
-    return builder
-        ->withRequiredString("type")
+    return builder->withRequiredString("type")
         ->whichIsOneOf(itemTypes)
         ->finishString()
         ->allowAdditionalAttriutes();

+ 2 - 2
FactoryCraft/Player.cpp

@@ -275,7 +275,7 @@ void Player::playerApi(
                 zRequest->lese((char*)&pos.x, 4);
                 zRequest->lese((char*)&pos.y, 4);
                 zRequest->lese((char*)&pos.z, 4);
-                source = Game::INSTANCE->zBlockAt(pos, dim);
+                source = Game::INSTANCE->zBlockAt(pos, dim, 0);
             }
             int sourceSlotId;
             zRequest->lese((char*)&sourceSlotId, 4);
@@ -295,7 +295,7 @@ void Player::playerApi(
                 zRequest->lese((char*)&pos.x, 4);
                 zRequest->lese((char*)&pos.y, 4);
                 zRequest->lese((char*)&pos.z, 4);
-                target = Game::INSTANCE->zBlockAt(pos, dim);
+                target = Game::INSTANCE->zBlockAt(pos, dim, 0);
             }
             if (source && target)
             {

+ 8 - 0
FactoryCraft/TickSourceType.h

@@ -0,0 +1,8 @@
+#pragma once
+
+enum TickSourceType
+{
+    NONE,
+    AFTER_WORLD_UPDATE,
+    EACH_TICK
+};

+ 6 - 3
FactoryCraft/TreeSeblingBlock.cpp

@@ -18,9 +18,7 @@ TreeSeblingBlock::TreeSeblingBlock(int typeId,
       seblingTicksMax(10000),
       wood(wood),
       leaves(leaves)
-{
-    tickSource = 1;
-}
+{}
 
 bool TreeSeblingBlock::onTick(TickQueue* zQueue, int numTicks, bool& blocked)
 {
@@ -58,6 +56,11 @@ void TreeSeblingBlock::onPostTick()
     }
 }
 
+TickSourceType TreeSeblingBlock::isTickSource() const
+{
+    return TickSourceType::EACH_TICK;
+}
+
 Framework::Text TreeSeblingBlock::getTargetUIML()
 {
     return Framework::Text("<targetInfo><text width=\"auto\" height=\"auto\">")

+ 1 - 0
FactoryCraft/TreeSeblingBlock.h

@@ -21,6 +21,7 @@ public:
     virtual bool onTick(
         TickQueue* zQueue, int numTicks, bool& blocked) override;
     virtual void onPostTick() override;
+    virtual TickSourceType isTickSource() const override;
     virtual Framework::Text getTargetUIML();
 
     friend TreeSeblingBlockType;

+ 1 - 0
Windows Version/Windows Version.vcxproj

@@ -348,6 +348,7 @@ copy ..\..\..\..\..\Allgemein\Framework\x64\release\Framework.dll Framework.dll<
     <ClInclude Include="..\FactoryCraft\Tickable.h" />
     <ClInclude Include="..\FactoryCraft\TickOrganizer.h" />
     <ClInclude Include="..\FactoryCraft\TickQueue.h" />
+    <ClInclude Include="..\FactoryCraft\TickSourceType.h" />
     <ClInclude Include="..\FactoryCraft\TickWorker.h" />
     <ClInclude Include="..\FactoryCraft\TreeSeblingBlock.h" />
     <ClInclude Include="..\FactoryCraft\TreeTemplate.h" />

+ 3 - 0
Windows Version/Windows Version.vcxproj.filters

@@ -710,5 +710,8 @@
     <ClInclude Include="..\FactoryCraft\AnimalAI.h">
       <Filter>entities\animals</Filter>
     </ClInclude>
+    <ClInclude Include="..\FactoryCraft\TickSourceType.h">
+      <Filter>world\ticking</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

BIN
Windows Version/error_core_memory_dump.dmp