summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--VC2008/MCServer.vcproj128
-rw-r--r--VC2010/MCServer.vcxproj24
-rw-r--r--VC2010/MCServer.vcxproj.filters92
-rw-r--r--WebServer/Socket.cpp1
-rw-r--r--WebServer/WebServer.cpp74
-rw-r--r--WebServer/WebServer.h10
-rw-r--r--source/Bindings.cpp362
-rw-r--r--source/Bindings.h2
-rw-r--r--source/BioGen.cpp89
-rw-r--r--source/BioGen.h75
-rw-r--r--source/BlockID.cpp186
-rw-r--r--source/BlockID.h83
-rw-r--r--source/ChunkDef.h180
-rw-r--r--source/ChunkSender.cpp57
-rw-r--r--source/ChunkSender.h32
-rw-r--r--source/CompoGen.cpp174
-rw-r--r--source/CompoGen.h101
-rw-r--r--source/Defines.h4
-rw-r--r--source/FastNBT.h2
-rw-r--r--source/FinishGen.cpp154
-rw-r--r--source/FinishGen.h82
-rw-r--r--source/Globals.h10
-rw-r--r--source/HeiGen.cpp91
-rw-r--r--source/HeiGen.h64
-rw-r--r--source/LightingThread.cpp467
-rw-r--r--source/LightingThread.h170
-rw-r--r--source/NBT.cpp899
-rw-r--r--source/NBT.h232
-rw-r--r--source/StringUtils.cpp20
-rw-r--r--source/StructGen.cpp463
-rw-r--r--source/StructGen.h122
-rw-r--r--source/Trees.cpp546
-rw-r--r--source/Trees.h85
-rw-r--r--source/WGFlat.cpp101
-rw-r--r--source/WGFlat.h35
-rw-r--r--source/WSSAnvil.cpp205
-rw-r--r--source/WSSAnvil.h7
-rw-r--r--source/WSSCompact.cpp115
-rw-r--r--source/WSSCompact.h30
-rw-r--r--source/WorldStorage.cpp65
-rw-r--r--source/WorldStorage.h32
-rw-r--r--source/cAuthenticator.cpp2
-rw-r--r--source/cChatColor.cpp41
-rw-r--r--source/cChatColor.h6
-rw-r--r--source/cChunk.cpp244
-rw-r--r--source/cChunk.h59
-rw-r--r--source/cChunk.inl.h2
-rw-r--r--source/cChunkGenerator.cpp289
-rw-r--r--source/cChunkGenerator.h173
-rw-r--r--source/cChunkMap.cpp218
-rw-r--r--source/cChunkMap.h53
-rw-r--r--source/cClientHandle.cpp37
-rw-r--r--source/cFireSimulator.cpp2
-rw-r--r--source/cFireSimulator.h18
-rw-r--r--source/cFurnaceRecipe.h16
-rw-r--r--source/cGenSettings.cpp13
-rw-r--r--source/cGenSettings.h9
-rw-r--r--source/cGroup.h7
-rw-r--r--source/cHeartBeat.h11
-rw-r--r--source/cLuaCommandBinder.h16
-rw-r--r--source/cMonster.h11
-rw-r--r--source/cNoise.h5
-rw-r--r--source/cNoise.inc79
-rw-r--r--source/cPlugin.h5
-rw-r--r--source/cPlugin_Lua.h4
-rw-r--r--source/cPlugin_NewLua.h11
-rw-r--r--source/cReferenceManager.h16
-rw-r--r--source/cRoot.cpp32
-rw-r--r--source/cRoot.h1
-rw-r--r--source/cSandSimulator.h18
-rw-r--r--source/cServer.cpp143
-rw-r--r--source/cSocket.cpp2
-rw-r--r--source/cWebAdmin.cpp23
-rw-r--r--source/cWorld.cpp492
-rw-r--r--source/cWorld.h75
-rw-r--r--source/cWorldGenerator.cpp438
-rw-r--r--source/cWorldGenerator.h53
-rw-r--r--source/cWorldGenerator_Test.cpp36
-rw-r--r--source/cWorldGenerator_Test.h25
-rw-r--r--source/packets/cPacket.cpp9
-rw-r--r--source/packets/cPacket_MapChunk.cpp2
-rw-r--r--source/packets/cPacket_MapChunk.h2
82 files changed, 5415 insertions, 2954 deletions
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 41727364d..84a4ebad2 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -265,6 +265,10 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
+ RelativePath="..\source\BlockID.cpp"
+ >
+ </File>
+ <File
RelativePath="..\source\BlockID.h"
>
</File>
@@ -337,14 +341,6 @@
>
</File>
<File
- RelativePath="..\source\cGenSettings.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cGenSettings.h"
- >
- </File>
- <File
RelativePath="..\source\cGroup.cpp"
>
</File>
@@ -463,22 +459,6 @@
>
</File>
<File
- RelativePath="..\source\cPlugin.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cPlugin.h"
- >
- </File>
- <File
- RelativePath="..\source\cPluginManager.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cPluginManager.h"
- >
- </File>
- <File
RelativePath="..\source\cRecipeChecker.cpp"
>
</File>
@@ -1660,6 +1640,14 @@
>
</File>
<File
+ RelativePath="..\source\cPlugin.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\cPlugin.h"
+ >
+ </File>
+ <File
RelativePath="..\source\cPlugin_Lua.cpp"
>
</File>
@@ -1676,6 +1664,14 @@
>
</File>
<File
+ RelativePath="..\source\cPluginManager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\cPluginManager.h"
+ >
+ </File>
+ <File
RelativePath="..\source\cStringMap.cpp"
>
</File>
@@ -1748,14 +1744,6 @@
>
</File>
<File
- RelativePath="..\source\NBT.cpp"
- >
- </File>
- <File
- RelativePath="..\source\NBT.h"
- >
- </File>
- <File
RelativePath="..\source\WorldStorage.cpp"
>
</File>
@@ -1784,6 +1772,14 @@
Name="Generating"
>
<File
+ RelativePath="..\source\BioGen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\BioGen.h"
+ >
+ </File>
+ <File
RelativePath="..\source\cChunkGenerator.cpp"
>
</File>
@@ -1792,31 +1788,87 @@
>
</File>
<File
- RelativePath="..\source\cWorldGenerator.cpp"
+ RelativePath="..\source\CompoGen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\CompoGen.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\FinishGen.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\FinishGen.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\HeiGen.cpp"
>
</File>
<File
- RelativePath="..\source\cWorldGenerator.h"
+ RelativePath="..\source\HeiGen.h"
>
</File>
<File
- RelativePath="..\source\cWorldGenerator_Test.cpp"
+ RelativePath="..\source\StructGen.cpp"
>
</File>
<File
- RelativePath="..\source\cWorldGenerator_Test.h"
+ RelativePath="..\source\StructGen.h"
>
</File>
<File
- RelativePath="..\source\WGFlat.cpp"
+ RelativePath="..\source\Trees.cpp"
>
</File>
<File
- RelativePath="..\source\WGFlat.h"
+ RelativePath="..\source\Trees.h"
>
</File>
</Filter>
</Filter>
+ <Filter
+ Name="Config files"
+ >
+ <File
+ RelativePath="..\banned.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\furnace.txt"
+ >
+ </File>
+ <File
+ RelativePath="..\groups.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\items.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\monsters.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\settings.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\users.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\webadmin.ini"
+ >
+ </File>
+ <File
+ RelativePath="..\world\world.ini"
+ >
+ </File>
+ </Filter>
<File
RelativePath="..\makefile"
>
diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj
index ca7323f31..279bf347c 100644
--- a/VC2010/MCServer.vcxproj
+++ b/VC2010/MCServer.vcxproj
@@ -302,6 +302,8 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
+ <ClCompile Include="..\source\BioGen.cpp" />
+ <ClCompile Include="..\source\BlockID.cpp" />
<ClCompile Include="..\source\cAggressiveMonster.cpp" />
<ClCompile Include="..\Source\cAuthenticator.cpp" />
<ClCompile Include="..\source\cBlockingTCPLink.cpp" />
@@ -324,7 +326,6 @@
<ClCompile Include="..\source\cFurnaceEntity.cpp" />
<ClCompile Include="..\Source\cFurnaceRecipe.cpp" />
<ClCompile Include="..\source\cFurnaceWindow.cpp" />
- <ClCompile Include="..\source\cGenSettings.cpp" />
<ClCompile Include="..\source\cGhast.cpp" />
<ClCompile Include="..\Source\cGroup.cpp" />
<ClCompile Include="..\Source\cGroupManager.cpp" />
@@ -347,6 +348,7 @@
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Default</BasicRuntimeChecks>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="..\source\CompoGen.cpp" />
<ClCompile Include="..\source\cPassiveAggressiveMonster.cpp" />
<ClCompile Include="..\source\cPassiveMonster.cpp" />
<ClCompile Include="..\Source\cPawn.cpp" />
@@ -386,16 +388,16 @@
<ClCompile Include="..\source\cWebPlugin_Lua.cpp" />
<ClCompile Include="..\source\cWindow.cpp" />
<ClCompile Include="..\source\cWolf.cpp" />
- <ClCompile Include="..\source\cWorldGenerator.cpp" />
- <ClCompile Include="..\source\cWorldGenerator_Test.cpp" />
<ClCompile Include="..\source\cZombie.cpp" />
<ClCompile Include="..\source\cZombiepigman.cpp" />
+ <ClCompile Include="..\source\FinishGen.cpp" />
<ClCompile Include="..\source\FastNBT.cpp" />
<ClCompile Include="..\source\Globals.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="..\source\HeiGen.cpp" />
<ClCompile Include="..\source\LeakFinder.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
@@ -422,7 +424,6 @@
<ClCompile Include="..\source\cLog.cpp" />
<ClCompile Include="..\source\Bindings.cpp" />
<ClCompile Include="..\source\main.cpp" />
- <ClCompile Include="..\source\NBT.cpp" />
<ClCompile Include="..\source\packets\cPacket.cpp" />
<ClCompile Include="..\source\packets\cPacket_13.cpp" />
<ClCompile Include="..\source\packets\cPacket_AddToInventory.cpp" />
@@ -480,15 +481,17 @@
</ClCompile>
<ClCompile Include="..\source\StringCompression.cpp" />
<ClCompile Include="..\source\StringUtils.cpp" />
+ <ClCompile Include="..\source\StructGen.cpp" />
+ <ClCompile Include="..\source\Trees.cpp" />
<ClCompile Include="..\source\Vector3d.cpp" />
<ClCompile Include="..\source\Vector3f.cpp" />
<ClCompile Include="..\source\Vector3i.cpp" />
- <ClCompile Include="..\source\WGFlat.cpp" />
<ClCompile Include="..\source\WorldStorage.cpp" />
<ClCompile Include="..\source\WSSAnvil.cpp" />
<ClCompile Include="..\source\WSSCompact.cpp" />
</ItemGroup>
<ItemGroup>
+ <ClInclude Include="..\source\BioGen.h" />
<ClInclude Include="..\Source\BlockID.h" />
<ClInclude Include="..\source\cAggressiveMonster.h" />
<ClInclude Include="..\Source\cAuthenticator.h" />
@@ -526,6 +529,7 @@
<ClInclude Include="..\Source\cMonster.h" />
<ClInclude Include="..\source\cMonsterConfig.h" />
<ClInclude Include="..\source\cNoise.h" />
+ <ClInclude Include="..\source\CompoGen.h" />
<ClInclude Include="..\source\cPassiveAggressiveMonster.h" />
<ClInclude Include="..\source\cPassiveMonster.h" />
<ClInclude Include="..\Source\cPawn.h" />
@@ -558,7 +562,6 @@
<ClInclude Include="..\source\cStringMap.h" />
<ClInclude Include="..\source\cSurvivalInventory.h" />
<ClInclude Include="..\Source\cTCPLink.h" />
- <ClInclude Include="..\source\cGenSettings.h" />
<ClInclude Include="..\source\cThread.h" />
<ClInclude Include="..\source\cTimer.h" />
<ClInclude Include="..\source\cTorch.h" />
@@ -571,14 +574,14 @@
<ClInclude Include="..\source\cWindow.h" />
<ClInclude Include="..\source\cWindowOwner.h" />
<ClInclude Include="..\source\cWolf.h" />
- <ClInclude Include="..\source\cWorldGenerator.h" />
- <ClInclude Include="..\source\cWorldGenerator_Test.h" />
<ClInclude Include="..\source\cZombie.h" />
<ClInclude Include="..\source\cZombiepigman.h" />
<ClInclude Include="..\source\Endianness.h" />
<ClInclude Include="..\source\FastNBT.h" />
<ClInclude Include="..\Source\FileDefine.h" />
+ <ClInclude Include="..\source\FinishGen.h" />
<ClInclude Include="..\source\Globals.h" />
+ <ClInclude Include="..\source\HeiGen.h" />
<ClInclude Include="..\source\LeakFinder.h" />
<ClInclude Include="..\source\LightingThread.h" />
<ClInclude Include="..\Source\LuaFunctions.h" />
@@ -605,7 +608,6 @@
<ClInclude Include="..\source\Bindings.h" />
<ClInclude Include="..\source\Defines.h" />
<ClInclude Include="..\source\MCSocket.h" />
- <ClInclude Include="..\source\NBT.h" />
<ClInclude Include="..\Source\PacketID.h" />
<ClInclude Include="..\source\packets\cPacket.h" />
<ClInclude Include="..\source\packets\cPacket_13.h" />
@@ -656,15 +658,15 @@
<ClInclude Include="..\source\packets\cPacket_WindowClick.h" />
<ClInclude Include="..\source\packets\cPacket_WindowClose.h" />
<ClInclude Include="..\source\packets\cPacket_WindowOpen.h" />
- <ClInclude Include="..\source\ptr_cChunk.h" />
<ClInclude Include="..\source\SquirrelBindings.h" />
<ClInclude Include="..\source\StackWalker.h" />
<ClInclude Include="..\source\StringCompression.h" />
<ClInclude Include="..\source\StringUtils.h" />
+ <ClInclude Include="..\source\StructGen.h" />
+ <ClInclude Include="..\source\Trees.h" />
<ClInclude Include="..\source\Vector3d.h" />
<ClInclude Include="..\source\Vector3f.h" />
<ClInclude Include="..\source\Vector3i.h" />
- <ClInclude Include="..\source\WGFlat.h" />
<ClInclude Include="..\source\WorldStorage.h" />
<ClInclude Include="..\source\WSSAnvil.h" />
<ClInclude Include="..\source\WSSCompact.h" />
diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters
index dcbf530a9..4098023bb 100644
--- a/VC2010/MCServer.vcxproj.filters
+++ b/VC2010/MCServer.vcxproj.filters
@@ -217,9 +217,6 @@
<Filter Include="cNoise">
<UniqueIdentifier>{58d6aa00-beab-4654-bbbd-c3b0397a9549}</UniqueIdentifier>
</Filter>
- <Filter Include="cGenSettings">
- <UniqueIdentifier>{731dfafd-78a6-47d4-8494-a8e024f24c07}</UniqueIdentifier>
- </Filter>
<Filter Include="cMakeDir">
<UniqueIdentifier>{102fff22-0eb3-4977-9de3-307534954fbc}</UniqueIdentifier>
</Filter>
@@ -379,9 +376,6 @@
<Filter Include="Packets\cPacket_ItemData">
<UniqueIdentifier>{fcc08e08-8dba-47b4-89b0-5dc255bb9b8a}</UniqueIdentifier>
</Filter>
- <Filter Include="cChunkGenerator">
- <UniqueIdentifier>{0d6f822b-71eb-406f-b17a-d188c4924283}</UniqueIdentifier>
- </Filter>
<Filter Include="cEntity\cPawn\cMonster\Personalities">
<UniqueIdentifier>{b0f7c883-e2ca-4bba-89e3-c36656c3de39}</UniqueIdentifier>
</Filter>
@@ -394,15 +388,9 @@
<Filter Include="cEntity\cPawn\cMonster\Personalities\PassiveAggressive">
<UniqueIdentifier>{71574b1c-a518-4a17-92c1-e3ea340f73bc}</UniqueIdentifier>
</Filter>
- <Filter Include="cWorldGenerator">
- <UniqueIdentifier>{72727ea7-779f-439e-8f30-53bd6985c9e7}</UniqueIdentifier>
- </Filter>
<Filter Include="!LuaPlugins">
<UniqueIdentifier>{4b86e1cf-ea3c-40b6-82b2-82fa9e6eb35e}</UniqueIdentifier>
</Filter>
- <Filter Include="cWorldGenerator\cWorldGenerator_Test">
- <UniqueIdentifier>{c86f4c53-af06-4b42-97dd-ffb7035041ce}</UniqueIdentifier>
- </Filter>
<Filter Include="Simulator">
<UniqueIdentifier>{ad41fc50-2d3d-4f6f-addd-a8bcb15b8518}</UniqueIdentifier>
</Filter>
@@ -433,9 +421,6 @@
<Filter Include="cInventory\cCreativeInventory">
<UniqueIdentifier>{69e6a927-8e49-4d39-af88-f587d17bb8a3}</UniqueIdentifier>
</Filter>
- <Filter Include="!Smart_Pointers">
- <UniqueIdentifier>{9bd7a65c-b60f-4905-ae2b-7c3c7586eaef}</UniqueIdentifier>
- </Filter>
<Filter Include="cPlugin\cPlugin_NewLua">
<UniqueIdentifier>{fb282bd3-cf18-44b3-8ccc-9a5a89701a6d}</UniqueIdentifier>
</Filter>
@@ -445,6 +430,9 @@
<Filter Include="Simulator\cSimulator\cRedstoneSimulator">
<UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier>
</Filter>
+ <Filter Include="Generating">
+ <UniqueIdentifier>{833e49bd-848d-42de-ac60-6cd7656474e3}</UniqueIdentifier>
+ </Filter>
<Filter Include="Storage">
<UniqueIdentifier>{038cf4bd-108e-44e2-bdcb-9d48fb26676e}</UniqueIdentifier>
</Filter>
@@ -664,9 +652,6 @@
<ClCompile Include="..\source\cNoise.cpp">
<Filter>cNoise</Filter>
</ClCompile>
- <ClCompile Include="..\source\cGenSettings.cpp">
- <Filter>cGenSettings</Filter>
- </ClCompile>
<ClCompile Include="..\source\cMakeDir.cpp">
<Filter>cMakeDir</Filter>
</ClCompile>
@@ -835,9 +820,6 @@
<ClCompile Include="..\source\packets\cPacket_ItemData.cpp">
<Filter>Packets\cPacket_ItemData</Filter>
</ClCompile>
- <ClCompile Include="..\source\cChunkGenerator.cpp">
- <Filter>cChunkGenerator</Filter>
- </ClCompile>
<ClCompile Include="..\source\cFluidSimulator.cpp">
<Filter>Simulator\cSimulator\cFluidSimulator</Filter>
</ClCompile>
@@ -850,12 +832,6 @@
<ClCompile Include="..\source\cPassiveMonster.cpp">
<Filter>cEntity\cPawn\cMonster\Personalities\Passive</Filter>
</ClCompile>
- <ClCompile Include="..\source\cWorldGenerator.cpp">
- <Filter>cWorldGenerator</Filter>
- </ClCompile>
- <ClCompile Include="..\source\cWorldGenerator_Test.cpp">
- <Filter>cWorldGenerator\cWorldGenerator_Test</Filter>
- </ClCompile>
<ClCompile Include="..\source\cSimulatorManager.cpp">
<Filter>Simulator</Filter>
</ClCompile>
@@ -896,12 +872,30 @@
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClCompile>
<ClCompile Include="..\source\ChunkSender.cpp" />
- <ClCompile Include="..\source\WGFlat.cpp" />
<ClCompile Include="..\source\packets\cPacket_Player.cpp">
<Filter>Packets</Filter>
</ClCompile>
- <ClCompile Include="..\source\NBT.cpp">
- <Filter>Storage</Filter>
+ <ClCompile Include="..\source\cChunkGenerator.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\Trees.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\CompoGen.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\HeiGen.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\StructGen.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\BioGen.cpp">
+ <Filter>Generating</Filter>
+ </ClCompile>
+ <ClCompile Include="..\source\BlockID.cpp" />
+ <ClCompile Include="..\source\FinishGen.cpp">
+ <Filter>Generating</Filter>
</ClCompile>
<ClCompile Include="..\source\WorldStorage.cpp">
<Filter>Storage</Filter>
@@ -1163,9 +1157,6 @@
<ClInclude Include="..\source\cNoise.h">
<Filter>cNoise</Filter>
</ClInclude>
- <ClInclude Include="..\source\cGenSettings.h">
- <Filter>cGenSettings</Filter>
- </ClInclude>
<ClInclude Include="..\source\cMakeDir.h">
<Filter>cMakeDir</Filter>
</ClInclude>
@@ -1337,9 +1328,6 @@
<ClInclude Include="..\source\packets\cPacket_ItemData.h">
<Filter>Packets\cPacket_ItemData</Filter>
</ClInclude>
- <ClInclude Include="..\source\cChunkGenerator.h">
- <Filter>cChunkGenerator</Filter>
- </ClInclude>
<ClInclude Include="..\source\cFluidSimulator.h">
<Filter>Simulator\cSimulator\cFluidSimulator</Filter>
</ClInclude>
@@ -1352,12 +1340,6 @@
<ClInclude Include="..\source\cPassiveMonster.h">
<Filter>cEntity\cPawn\cMonster\Personalities\Passive</Filter>
</ClInclude>
- <ClInclude Include="..\source\cWorldGenerator.h">
- <Filter>cWorldGenerator</Filter>
- </ClInclude>
- <ClInclude Include="..\source\cWorldGenerator_Test.h">
- <Filter>cWorldGenerator\cWorldGenerator_Test</Filter>
- </ClInclude>
<ClInclude Include="..\source\cSimulatorManager.h">
<Filter>Simulator</Filter>
</ClInclude>
@@ -1379,9 +1361,6 @@
<ClInclude Include="..\source\cSurvivalInventory.h">
<Filter>cInventory\cSurvivalInventory</Filter>
</ClInclude>
- <ClInclude Include="..\source\ptr_cChunk.h">
- <Filter>!Smart_Pointers</Filter>
- </ClInclude>
<ClInclude Include="..\source\cPlugin_NewLua.h">
<Filter>cPlugin\cPlugin_NewLua</Filter>
</ClInclude>
@@ -1401,13 +1380,30 @@
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClInclude>
<ClInclude Include="..\source\ChunkSender.h" />
- <ClInclude Include="..\source\WGFlat.h" />
<ClInclude Include="..\source\ChunkDef.h" />
<ClInclude Include="..\source\packets\cPacket_Player.h">
<Filter>Packets</Filter>
</ClInclude>
- <ClInclude Include="..\source\NBT.h">
- <Filter>Storage</Filter>
+ <ClInclude Include="..\source\cChunkGenerator.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\StructGen.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\Trees.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\CompoGen.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\HeiGen.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\BioGen.h">
+ <Filter>Generating</Filter>
+ </ClInclude>
+ <ClInclude Include="..\source\FinishGen.h">
+ <Filter>Generating</Filter>
</ClInclude>
<ClInclude Include="..\source\WorldStorage.h">
<Filter>Storage</Filter>
diff --git a/WebServer/Socket.cpp b/WebServer/Socket.cpp
index 59efc8127..430461fe8 100644
--- a/WebServer/Socket.cpp
+++ b/WebServer/Socket.cpp
@@ -37,6 +37,7 @@
#ifndef _WIN32
#include <cstring>
#include <sys/time.h>
+ #include <unistd.h>
#define SD_SEND 0x01
#else
#define MSG_NOSIGNAL (0)
diff --git a/WebServer/WebServer.cpp b/WebServer/WebServer.cpp
index aca585a88..8be690bd1 100644
--- a/WebServer/WebServer.cpp
+++ b/WebServer/WebServer.cpp
@@ -47,7 +47,15 @@
#include "UrlHelper.h"
#include "base64.h"
-webserver::request_func webserver::request_func_=0;
+
+
+
+
+webserver::request_func webserver::request_func_ = NULL;
+
+
+
+
static std::string EatLine( std::string& a_String )
{
@@ -76,6 +84,10 @@ static std::string EatLine( std::string& a_String )
return RetVal;
}
+
+
+
+
// Turns
// "blabla my string with \"quotes\"!"
// into
@@ -116,6 +128,10 @@ static std::string GetQuotedString( const std::string& a_String )
return RetVal;
}
+
+
+
+
void ParseMultipartFormData( webserver::http_request& req, Socket* s)
{
static const std::string multipart_form_data = "multipart/form-data";
@@ -229,6 +245,10 @@ void ParseMultipartFormData( webserver::http_request& req, Socket* s)
}
}
+
+
+
+
#ifdef _WIN32
unsigned webserver::Request(void* ptr_s)
#else
@@ -395,6 +415,10 @@ void* webserver::Request(void* ptr_s)
return 0;
}
+
+
+
+
void webserver::Stop()
{
m_bStop = true;
@@ -402,46 +426,72 @@ void webserver::Stop()
m_Events->Wait();
}
-void webserver::Begin()
+
+
+
+
+bool webserver::Begin()
{
+ if (!m_Socket->IsValid())
+ {
+ LOGINFO("WebAdmin: The server socket is invalid. Terminating WebAdmin.");
+ return false;
+ }
m_bStop = false;
while ( !m_bStop )
{
- Socket* ptr_s=m_Socket->Accept();
- if( m_bStop )
+ Socket * ptr_s = m_Socket->Accept();
+ if (m_bStop)
{
- if( ptr_s != 0 )
+ if (ptr_s != 0)
{
ptr_s->Close();
delete ptr_s;
}
break;
}
+ if (ptr_s == NULL)
+ {
+ LOGINFO("WebAdmin: Accepted socket is NULL. Terminating WebAdmin to avoid busywait.");
+ return false;
+ }
- // unused variable 'ret'
- //_beginthreadex(0,0,Request,(void*) ptr_s,0,&ret);
- // Thanks to Frank M. Hoffmann for fixing a HANDLE leak
#ifdef _WIN32
unsigned ret;
- HANDLE hHandle = reinterpret_cast<HANDLE>(_beginthreadex(0,0,Request,(void*) ptr_s,0,&ret));
+ HANDLE hHandle = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0, Request, (void *)ptr_s, 0, &ret));
CloseHandle(hHandle);
#else
- pthread_t* hHandle = new pthread_t;
+ // Mattes: TODO: this handle probably leaks!
+ pthread_t * hHandle = new pthread_t;
pthread_create( hHandle, NULL, Request, ptr_s);
#endif
}
m_Events->Set();
+ return true;
}
-webserver::webserver(unsigned int port_to_listen, request_func r) {
- m_Socket = new SocketServer(port_to_listen,1);
+
+
+
+
+webserver::webserver(unsigned int port_to_listen, request_func r)
+{
+ m_Socket = new SocketServer(port_to_listen, 1);
request_func_ = r;
m_Events = new cEvents();
}
+
+
+
+
webserver::~webserver()
{
delete m_Socket;
delete m_Events;
}
+
+
+
+
diff --git a/WebServer/WebServer.h b/WebServer/WebServer.h
index 90b38d407..d9d4b2fd1 100644
--- a/WebServer/WebServer.h
+++ b/WebServer/WebServer.h
@@ -89,16 +89,18 @@ public:
webserver(unsigned int port_to_listen, request_func);
~webserver();
- void Begin();
+ bool Begin();
void Stop();
private:
bool m_bStop;
-#ifdef _WIN32
+
+ #ifdef _WIN32
static unsigned __stdcall Request(void*);
-#else
+ #else
static void* Request(void*);
-#endif
+ #endif
+
static request_func request_func_;
cEvents * m_Events;
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index 26af3e663..a8a35f28b 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 03/25/12 16:23:48.
+** Generated automatically by tolua++-1.0.92 on 05/24/12 19:47:53.
*/
#ifndef __cplusplus
@@ -2344,7 +2344,7 @@ static int tolua_set_AllToLua_g_BlockLightValue(lua_State* tolua_S)
if (tolua_index<0)
tolua_error(tolua_S,"array indexing out of range.",NULL);
#endif
- g_BlockLightValue[tolua_index] = ((char) tolua_tonumber(tolua_S,3,0));
+ g_BlockLightValue[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0));
return 0;
}
#endif //#ifndef TOLUA_DISABLE
@@ -2388,7 +2388,7 @@ static int tolua_set_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S)
if (tolua_index<0)
tolua_error(tolua_S,"array indexing out of range.",NULL);
#endif
- g_BlockSpreadLightFalloff[tolua_index] = ((char) tolua_tonumber(tolua_S,3,0));
+ g_BlockSpreadLightFalloff[tolua_index] = ((unsigned char) tolua_tonumber(tolua_S,3,0));
return 0;
}
#endif //#ifndef TOLUA_DISABLE
@@ -2897,6 +2897,15 @@ static int tolua_get_cChatColor_White(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* get function: Funky of class cChatColor */
+#ifndef TOLUA_DISABLE_tolua_get_cChatColor_Funky
+static int tolua_get_cChatColor_Funky(lua_State* tolua_S)
+{
+ tolua_pushcppstring(tolua_S,(const char*)cChatColor::Funky);
+ return 1;
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: MakeColor of class cChatColor */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cChatColor_MakeColor00
static int tolua_AllToLua_cChatColor_MakeColor00(lua_State* tolua_S)
@@ -3090,6 +3099,38 @@ static int tolua_AllToLua_cClientHandle_SetViewDistance00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetViewDistance of class cClientHandle */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetViewDistance00
+static int tolua_AllToLua_cClientHandle_GetViewDistance00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cClientHandle",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cClientHandle* self = (cClientHandle*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetViewDistance'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetViewDistance();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetViewDistance'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: GetUniqueID of class cClientHandle */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cClientHandle_GetUniqueID00
static int tolua_AllToLua_cClientHandle_GetUniqueID00(lua_State* tolua_S)
@@ -3612,6 +3653,102 @@ static int tolua_AllToLua_cEntity_GetLookVector00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetChunkX of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkX00
+static int tolua_AllToLua_cEntity_GetChunkX00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkX'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkX();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkX'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkY of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkY00
+static int tolua_AllToLua_cEntity_GetChunkY00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkY'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkY();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkY'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetChunkZ of class cEntity */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_GetChunkZ00
+static int tolua_AllToLua_cEntity_GetChunkZ00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetChunkZ'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetChunkZ();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetChunkZ'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: SetPosX of class cEntity */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cEntity_SetPosX00
static int tolua_AllToLua_cEntity_SetPosX00(lua_State* tolua_S)
@@ -6381,7 +6518,7 @@ static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S)
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) ||
!tolua_isstring(tolua_S,2,0,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
@@ -6389,7 +6526,7 @@ static int tolua_AllToLua_cPluginManager_GetPlugin00(lua_State* tolua_S)
else
#endif
{
- cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0);
const char* a_Plugin = ((const char*) tolua_tostring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPlugin'", NULL);
@@ -6516,14 +6653,14 @@ static int tolua_AllToLua_cPluginManager_GetNumPlugins00(lua_State* tolua_S)
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
+ !tolua_isusertype(tolua_S,1,"const cPluginManager",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
+ const cPluginManager* self = (const cPluginManager*) tolua_tousertype(tolua_S,1,0);
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlugins'", NULL);
#endif
@@ -9855,6 +9992,76 @@ static int tolua_AllToLua_cWorld_UpdateSign00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: RegenerateChunk of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00
+static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'RegenerateChunk'", NULL);
+#endif
+ {
+ self->RegenerateChunk(a_ChunkX,a_ChunkZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'RegenerateChunk'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GenerateChunk of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GenerateChunk00
+static int tolua_AllToLua_cWorld_GenerateChunk00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_ChunkX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_ChunkZ = ((int) tolua_tonumber(tolua_S,3,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GenerateChunk'", NULL);
+#endif
+ {
+ self->GenerateChunk(a_ChunkX,a_ChunkZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GenerateChunk'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: SetBlock of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetBlock00
static int tolua_AllToLua_cWorld_SetBlock00(lua_State* tolua_S)
@@ -10409,14 +10616,14 @@ static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S)
#endif
{
cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
- int a_X = ((int) tolua_tonumber(tolua_S,2,0));
- int a_Y = ((int) tolua_tonumber(tolua_S,3,0));
- int a_Z = ((int) tolua_tonumber(tolua_S,4,0));
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTree'", NULL);
#endif
{
- self->GrowTree(a_X,a_Y,a_Z);
+ self->GrowTree(a_BlockX,a_BlockY,a_BlockZ);
}
}
return 0;
@@ -10428,33 +10635,113 @@ static int tolua_AllToLua_cWorld_GrowTree00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetWorldSeed of class cWorld */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetWorldSeed00
-static int tolua_AllToLua_cWorld_GetWorldSeed00(lua_State* tolua_S)
+/* method: GrowTreeFromSapling of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeFromSapling00
+static int tolua_AllToLua_cWorld_GrowTreeFromSapling00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
- !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,5,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,6,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
- const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+ char a_SaplingMeta = ((char) tolua_tonumber(tolua_S,5,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL);
+#endif
+ {
+ self->GrowTreeFromSapling(a_BlockX,a_BlockY,a_BlockZ,a_SaplingMeta);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowTreeFromSapling'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GrowTreeByBiome of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GrowTreeByBiome00
+static int tolua_AllToLua_cWorld_GrowTreeByBiome00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,4,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,5,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockY = ((int) tolua_tonumber(tolua_S,3,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,4,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeByBiome'", NULL);
+#endif
+ {
+ self->GrowTreeByBiome(a_BlockX,a_BlockY,a_BlockZ);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GrowTreeByBiome'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetBiomeAt of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetBiomeAt00
+static int tolua_AllToLua_cWorld_GetBiomeAt00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,3,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,4,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
+ int a_BlockX = ((int) tolua_tonumber(tolua_S,2,0));
+ int a_BlockZ = ((int) tolua_tonumber(tolua_S,3,0));
#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorldSeed'", NULL);
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetBiomeAt'", NULL);
#endif
{
- unsigned int tolua_ret = (unsigned int) self->GetWorldSeed();
+ int tolua_ret = (int) self->GetBiomeAt(a_BlockX,a_BlockZ);
tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetWorldSeed'.",&tolua_err);
+ tolua_error(tolua_S,"#ferror in function 'GetBiomeAt'.",&tolua_err);
return 0;
#endif
}
@@ -17174,6 +17461,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_BLOCK_GRASS",E_BLOCK_GRASS);
tolua_constant(tolua_S,"E_BLOCK_DIRT",E_BLOCK_DIRT);
tolua_constant(tolua_S,"E_BLOCK_COBBLESTONE",E_BLOCK_COBBLESTONE);
+ tolua_constant(tolua_S,"E_BLOCK_PLANKS",E_BLOCK_PLANKS);
tolua_constant(tolua_S,"E_BLOCK_WOOD",E_BLOCK_WOOD);
tolua_constant(tolua_S,"E_BLOCK_SAPLING",E_BLOCK_SAPLING);
tolua_constant(tolua_S,"E_BLOCK_BEDROCK",E_BLOCK_BEDROCK);
@@ -17260,6 +17548,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_BLOCK_SOULSAND",E_BLOCK_SOULSAND);
tolua_constant(tolua_S,"E_BLOCK_GLOWSTONE",E_BLOCK_GLOWSTONE);
tolua_constant(tolua_S,"E_BLOCK_PORT",E_BLOCK_PORT);
+ tolua_constant(tolua_S,"E_BLOCK_NETHER_PORTAL",E_BLOCK_NETHER_PORTAL);
tolua_constant(tolua_S,"E_BLOCK_JACK_O_LANTERN",E_BLOCK_JACK_O_LANTERN);
tolua_constant(tolua_S,"E_BLOCK_CAKE",E_BLOCK_CAKE);
tolua_constant(tolua_S,"E_BLOCK_REDSTONE_REPEATER_OFF",E_BLOCK_REDSTONE_REPEATER_OFF);
@@ -17291,6 +17580,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_BLOCK_END_PORTAL",E_BLOCK_END_PORTAL);
tolua_constant(tolua_S,"E_BLOCK_END_PORTAL_FRAME",E_BLOCK_END_PORTAL_FRAME);
tolua_constant(tolua_S,"E_BLOCK_END_STONE",E_BLOCK_END_STONE);
+ tolua_constant(tolua_S,"E_BLOCK_DRAGON_EGG",E_BLOCK_DRAGON_EGG);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_OFF",E_BLOCK_REDSTONE_LAMP_OFF);
+ tolua_constant(tolua_S,"E_BLOCK_REDSTONE_LAMP_ON",E_BLOCK_REDSTONE_LAMP_ON);
tolua_constant(tolua_S,"E_BLOCK_",E_BLOCK_);
tolua_constant(tolua_S,"E_ITEM_EMPTY",E_ITEM_EMPTY);
tolua_constant(tolua_S,"E_ITEM_STONE",E_ITEM_STONE);
@@ -17544,6 +17836,25 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_constant(tolua_S,"E_ITEM_STRAD_DISC",E_ITEM_STRAD_DISC);
tolua_constant(tolua_S,"E_ITEM_WARD_DISC",E_ITEM_WARD_DISC);
tolua_constant(tolua_S,"E_ITEM_11_DISC",E_ITEM_11_DISC);
+ tolua_constant(tolua_S,"E_META_PLANKS_APPLE",E_META_PLANKS_APPLE);
+ tolua_constant(tolua_S,"E_META_PLANKS_CONIFER",E_META_PLANKS_CONIFER);
+ tolua_constant(tolua_S,"E_META_PLANKS_BIRCH",E_META_PLANKS_BIRCH);
+ tolua_constant(tolua_S,"E_META_PLANKS_JUNGLE",E_META_PLANKS_JUNGLE);
+ tolua_constant(tolua_S,"E_META_LOG_APPLE",E_META_LOG_APPLE);
+ tolua_constant(tolua_S,"E_META_LOG_CONIFER",E_META_LOG_CONIFER);
+ tolua_constant(tolua_S,"E_META_LOG_BIRCH",E_META_LOG_BIRCH);
+ tolua_constant(tolua_S,"E_META_LOG_JUNGLE",E_META_LOG_JUNGLE);
+ tolua_constant(tolua_S,"E_META_LEAVES_APPLE",E_META_LEAVES_APPLE);
+ tolua_constant(tolua_S,"E_META_LEAVES_CONIFER",E_META_LEAVES_CONIFER);
+ tolua_constant(tolua_S,"E_META_LEAVES_BIRCH",E_META_LEAVES_BIRCH);
+ tolua_constant(tolua_S,"E_META_LEAVES_JUNGLE",E_META_LEAVES_JUNGLE);
+ tolua_constant(tolua_S,"E_META_SAPLING_APPLE",E_META_SAPLING_APPLE);
+ tolua_constant(tolua_S,"E_META_SAPLING_CONIFER",E_META_SAPLING_CONIFER);
+ tolua_constant(tolua_S,"E_META_SAPLING_BIRCH",E_META_SAPLING_BIRCH);
+ tolua_constant(tolua_S,"E_META_SAPLING_JUNGLE",E_META_SAPLING_JUNGLE);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_DEAD_SHRUB",E_META_TALL_GRASS_DEAD_SHRUB);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_GRASS",E_META_TALL_GRASS_GRASS);
+ tolua_constant(tolua_S,"E_META_TALL_GRASS_FERN",E_META_TALL_GRASS_FERN);
tolua_constant(tolua_S,"E_KEEP_ALIVE",E_KEEP_ALIVE);
tolua_constant(tolua_S,"E_LOGIN",E_LOGIN);
tolua_constant(tolua_S,"E_HANDSHAKE",E_HANDSHAKE);
@@ -17638,6 +17949,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_variable(tolua_S,"LightPurple",tolua_get_cChatColor_LightPurple,NULL);
tolua_variable(tolua_S,"Yellow",tolua_get_cChatColor_Yellow,NULL);
tolua_variable(tolua_S,"White",tolua_get_cChatColor_White,NULL);
+ tolua_variable(tolua_S,"Funky",tolua_get_cChatColor_Funky,NULL);
tolua_function(tolua_S,"MakeColor",tolua_AllToLua_cChatColor_MakeColor00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cClientHandle","cClientHandle","",NULL);
@@ -17647,6 +17959,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetUsername",tolua_AllToLua_cClientHandle_GetUsername00);
tolua_function(tolua_S,"GetPing",tolua_AllToLua_cClientHandle_GetPing00);
tolua_function(tolua_S,"SetViewDistance",tolua_AllToLua_cClientHandle_SetViewDistance00);
+ tolua_function(tolua_S,"GetViewDistance",tolua_AllToLua_cClientHandle_GetViewDistance00);
tolua_function(tolua_S,"GetUniqueID",tolua_AllToLua_cClientHandle_GetUniqueID00);
tolua_endmodule(tolua_S);
#ifdef __cplusplus
@@ -17673,6 +17986,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetPitch",tolua_AllToLua_cEntity_GetPitch00);
tolua_function(tolua_S,"GetRoll",tolua_AllToLua_cEntity_GetRoll00);
tolua_function(tolua_S,"GetLookVector",tolua_AllToLua_cEntity_GetLookVector00);
+ tolua_function(tolua_S,"GetChunkX",tolua_AllToLua_cEntity_GetChunkX00);
+ tolua_function(tolua_S,"GetChunkY",tolua_AllToLua_cEntity_GetChunkY00);
+ tolua_function(tolua_S,"GetChunkZ",tolua_AllToLua_cEntity_GetChunkZ00);
tolua_function(tolua_S,"SetPosX",tolua_AllToLua_cEntity_SetPosX00);
tolua_function(tolua_S,"SetPosY",tolua_AllToLua_cEntity_SetPosY00);
tolua_function(tolua_S,"SetPosZ",tolua_AllToLua_cEntity_SetPosZ00);
@@ -17909,6 +18225,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cWorld_GetNumPlayers00);
tolua_function(tolua_S,"GetPlayer",tolua_AllToLua_cWorld_GetPlayer00);
tolua_function(tolua_S,"UpdateSign",tolua_AllToLua_cWorld_UpdateSign00);
+ tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00);
+ tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00);
tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00);
tolua_function(tolua_S,"FastSetBlock",tolua_AllToLua_cWorld_FastSetBlock00);
tolua_function(tolua_S,"GetBlock",tolua_AllToLua_cWorld_GetBlock00);
@@ -17925,7 +18243,9 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetSpawnZ",tolua_AllToLua_cWorld_GetSpawnZ00);
tolua_function(tolua_S,"GetBlockEntity",tolua_AllToLua_cWorld_GetBlockEntity00);
tolua_function(tolua_S,"GrowTree",tolua_AllToLua_cWorld_GrowTree00);
- tolua_function(tolua_S,"GetWorldSeed",tolua_AllToLua_cWorld_GetWorldSeed00);
+ tolua_function(tolua_S,"GrowTreeFromSapling",tolua_AllToLua_cWorld_GrowTreeFromSapling00);
+ tolua_function(tolua_S,"GrowTreeByBiome",tolua_AllToLua_cWorld_GrowTreeByBiome00);
+ tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00);
tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00);
tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cWorld_SaveAllChunks00);
tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00);
diff --git a/source/Bindings.h b/source/Bindings.h
index fc265fb0b..20634c36b 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 03/25/12 16:23:48.
+** Generated automatically by tolua++-1.0.92 on 05/24/12 19:47:53.
*/
/* Exported function */
diff --git a/source/BioGen.cpp b/source/BioGen.cpp
new file mode 100644
index 000000000..c87a6ec1b
--- /dev/null
+++ b/source/BioGen.cpp
@@ -0,0 +1,89 @@
+
+// BioGen.cpp
+
+// Implements the various biome generators
+
+#include "Globals.h"
+#include "BioGen.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenConstant:
+
+void cBioGenConstant::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ a_BiomeMap[i] = m_Biome;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenDistortedVoronoi:
+
+void cBioGenDistortedVoronoi::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ // TODO: Replace this placeholder
+ ASSERT(!"Not implemented yet");
+ for (int i = 0; i < ARRAYCOUNT(a_BiomeMap); i++)
+ {
+ a_BiomeMap[i] = (EMCSBiome)(a_ChunkX + a_ChunkZ);
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cBioGenCheckerboard:
+
+void cBioGenCheckerboard::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ // The list of biomes we will generate in the checkerboard:
+ static EMCSBiome Biomes[] =
+ {
+ biOcean,
+ biPlains,
+ biDesert,
+ biExtremeHills,
+ biForest,
+ biTaiga,
+ biSwampland,
+ biRiver,
+ biFrozenOcean,
+ biFrozenRiver,
+ biIcePlains,
+ biIceMountains,
+ biMushroomIsland,
+ biMushroomShore,
+ biBeach,
+ biDesertHills,
+ biForestHills,
+ biTaigaHills,
+ biExtremeHillsEdge,
+ biJungle,
+ biJungleHills,
+ } ;
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Base = cChunkDef::Width * a_ChunkZ + z;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Add = cChunkDef::Width * a_ChunkX + x;
+ a_BiomeMap[x + cChunkDef::Width * z] = Biomes[(Base / m_BiomeSize + Add / m_BiomeSize) % ARRAYCOUNT(Biomes)];
+ }
+ }
+}
+
+
+
+
diff --git a/source/BioGen.h b/source/BioGen.h
new file mode 100644
index 000000000..316399436
--- /dev/null
+++ b/source/BioGen.h
@@ -0,0 +1,75 @@
+
+// BioGen.h
+
+/*
+Interfaces to the various biome generators:
+ - cBioGenConstant
+ - cBioGenCheckerboard
+ - cBioGenDistortedVoronoi
+*/
+
+
+
+
+
+#pragma once
+
+#include "cChunkGenerator.h"
+
+
+
+
+
+class cBioGenConstant :
+ public cBiomeGen
+{
+public:
+ cBioGenConstant(EMCSBiome a_Biome) : m_Biome(a_Biome) {}
+
+protected:
+
+ EMCSBiome m_Biome;
+
+ // cBiomeGen override:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+} ;
+
+
+
+
+
+class cBioGenDistortedVoronoi :
+ public cBiomeGen
+{
+public:
+ cBioGenDistortedVoronoi(int a_Seed) : m_Seed(a_Seed) {}
+
+protected:
+
+ int m_Seed;
+
+ // cBiomeGen override:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+} ;
+
+
+
+
+
+class cBioGenCheckerboard :
+ public cBiomeGen
+{
+public:
+ cBioGenCheckerboard(int a_BiomeSize) : m_BiomeSize((a_BiomeSize < 8) ? 8 : a_BiomeSize) {}
+
+protected:
+
+ int m_BiomeSize;
+
+ // cBiomeGen override:
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+} ;
+
+
+
+
diff --git a/source/BlockID.cpp b/source/BlockID.cpp
new file mode 100644
index 000000000..2e2484f21
--- /dev/null
+++ b/source/BlockID.cpp
@@ -0,0 +1,186 @@
+
+// BlockID.cpp
+
+// Implements the helper functions for converting Block ID string to int etc.
+
+#include "Globals.h"
+#include "BlockID.h"
+#include "../iniFile/iniFile.h"
+
+
+
+
+
+NIBBLETYPE g_BlockLightValue[256];
+NIBBLETYPE g_BlockSpreadLightFalloff[256];
+bool g_BlockTransparent[256];
+bool g_BlockOneHitDig[256];
+bool g_BlockPistonBreakable[256];
+
+
+
+
+
+class cBlockIDMap
+{
+public:
+ cBlockIDMap(void) : m_Ini("items.ini")
+ {
+ m_Ini.ReadFile();
+ }
+
+ int Resolve(const AString & a_ItemName)
+ {
+ return m_Ini.GetValueI("Items", a_ItemName, -1);
+ }
+
+protected:
+ cIniFile m_Ini;
+} ;
+
+
+
+
+
+static cBlockIDMap gsBlockIDMap;
+
+
+
+
+
+int BlockStringToType(const AString & a_BlockTypeString)
+{
+ int res = atoi(a_BlockTypeString.c_str());
+ if ((res != 0) || (a_BlockTypeString.compare("0") == 0))
+ {
+ // It was a valid number, return that
+ return res;
+ }
+
+ return gsBlockIDMap.Resolve(a_BlockTypeString);
+}
+
+
+
+
+// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor:
+class cBlockPropertiesInitializer
+{
+public:
+ cBlockPropertiesInitializer(void)
+ {
+ memset( g_BlockLightValue, 0x00, sizeof( g_BlockLightValue ) );
+ memset( g_BlockSpreadLightFalloff, 0x0f, sizeof( g_BlockSpreadLightFalloff ) ); // 0x0f means total falloff
+ memset( g_BlockTransparent, 0x00, sizeof( g_BlockTransparent ) );
+ memset( g_BlockOneHitDig, 0x00, sizeof( g_BlockOneHitDig ) );
+ memset( g_BlockPistonBreakable, 0x00, sizeof( g_BlockPistonBreakable ) );
+
+ // Emissive blocks
+ g_BlockLightValue[E_BLOCK_FIRE] = 15;
+ g_BlockLightValue[E_BLOCK_GLOWSTONE] = 15;
+ g_BlockLightValue[E_BLOCK_JACK_O_LANTERN] = 15;
+ g_BlockLightValue[E_BLOCK_LAVA] = 15;
+ g_BlockLightValue[E_BLOCK_STATIONARY_LAVA] = 15;
+ g_BlockLightValue[E_BLOCK_END_PORTAL] = 15;
+ g_BlockLightValue[E_BLOCK_REDSTONE_LAMP_ON] = 15;
+ g_BlockLightValue[E_BLOCK_TORCH] = 14;
+ g_BlockLightValue[E_BLOCK_BURNING_FURNACE] = 13;
+ g_BlockLightValue[E_BLOCK_NETHER_PORTAL] = 11;
+ g_BlockLightValue[E_BLOCK_REDSTONE_ORE_GLOWING] = 9;
+ g_BlockLightValue[E_BLOCK_REDSTONE_REPEATER_ON] = 9;
+ g_BlockLightValue[E_BLOCK_REDSTONE_TORCH_ON] = 7;
+ g_BlockLightValue[E_BLOCK_BREWING_STAND] = 1;
+ g_BlockLightValue[E_BLOCK_BROWN_MUSHROOM] = 1;
+ g_BlockLightValue[E_BLOCK_DRAGON_EGG] = 1;
+
+ // Spread blocks
+ g_BlockSpreadLightFalloff[E_BLOCK_AIR] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_TORCH] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_FIRE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_LAVA] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_LAVA] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_WATER] = 4; // Light in water dissapears faster
+ g_BlockSpreadLightFalloff[E_BLOCK_STATIONARY_WATER] = 4;
+ g_BlockSpreadLightFalloff[E_BLOCK_LEAVES] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_GLASS] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_GLOWSTONE] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_SIGN_POST] = 1;
+ g_BlockSpreadLightFalloff[E_BLOCK_WALLSIGN] = 1;
+
+ // Transparent blocks
+ g_BlockTransparent[E_BLOCK_AIR] = true;
+ g_BlockTransparent[E_BLOCK_GLASS] = true;
+ g_BlockTransparent[E_BLOCK_FIRE] = true;
+ g_BlockTransparent[E_BLOCK_ICE] = true;
+ g_BlockTransparent[E_BLOCK_TORCH] = true;
+ g_BlockTransparent[E_BLOCK_SIGN_POST] = true;
+ g_BlockTransparent[E_BLOCK_WALLSIGN] = true;
+ g_BlockTransparent[E_BLOCK_TALL_GRASS] = true;
+ g_BlockTransparent[E_BLOCK_YELLOW_FLOWER] = true;
+ g_BlockTransparent[E_BLOCK_RED_ROSE] = true;
+ g_BlockTransparent[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockTransparent[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockTransparent[E_BLOCK_SNOW] = true;
+
+ // TODO: Any other transparent blocks?
+
+ // One hit break blocks
+ g_BlockOneHitDig[E_BLOCK_SAPLING] = true;
+ g_BlockOneHitDig[E_BLOCK_YELLOW_FLOWER] = true;
+ g_BlockOneHitDig[E_BLOCK_RED_ROSE] = true;
+ g_BlockOneHitDig[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockOneHitDig[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockOneHitDig[E_BLOCK_TNT] = true;
+ g_BlockOneHitDig[E_BLOCK_TORCH] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
+ g_BlockOneHitDig[E_BLOCK_CROPS] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_OFF] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_TORCH_ON] = true;
+ g_BlockOneHitDig[E_BLOCK_REEDS] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_WIRE] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_OFF] = true;
+ g_BlockOneHitDig[E_BLOCK_REDSTONE_REPEATER_ON] = true;
+ g_BlockOneHitDig[E_BLOCK_LOCKED_CHEST] = true;
+ g_BlockOneHitDig [E_BLOCK_FIRE] = true;
+
+ // Blocks that breaks when pushed by piston
+ g_BlockPistonBreakable[E_BLOCK_AIR] = true;
+ g_BlockPistonBreakable[E_BLOCK_STATIONARY_WATER] = false; //This gave pistons the ability to drop water :D
+ g_BlockPistonBreakable[E_BLOCK_WATER] = false;
+ g_BlockPistonBreakable[E_BLOCK_STATIONARY_LAVA] = false;
+ g_BlockPistonBreakable[E_BLOCK_LAVA] = false;
+ g_BlockPistonBreakable[E_BLOCK_BED] = true;
+ g_BlockPistonBreakable[E_BLOCK_COBWEB] = true;
+ g_BlockPistonBreakable[E_BLOCK_TALL_GRASS] = true;
+ g_BlockPistonBreakable[E_BLOCK_YELLOW_FLOWER] = true;
+ g_BlockPistonBreakable[E_BLOCK_BROWN_MUSHROOM] = true;
+ g_BlockPistonBreakable[E_BLOCK_RED_ROSE] = true;
+ g_BlockPistonBreakable[E_BLOCK_RED_MUSHROOM] = true;
+ g_BlockPistonBreakable[E_BLOCK_DEAD_BUSH] = true;
+ g_BlockPistonBreakable[E_BLOCK_TORCH] = true;
+ g_BlockPistonBreakable[E_BLOCK_FIRE] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_WIRE] = true;
+ g_BlockPistonBreakable[E_BLOCK_CROPS] = true;
+ g_BlockPistonBreakable[E_BLOCK_LADDER] = true;
+ g_BlockPistonBreakable[E_BLOCK_WOODEN_DOOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_IRON_DOOR] = true;
+ g_BlockPistonBreakable[E_BLOCK_LEVER] = true;
+ g_BlockPistonBreakable[E_BLOCK_STONE_BUTTON] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_ON] = true;
+ g_BlockPistonBreakable[E_BLOCK_REDSTONE_TORCH_OFF] = true;
+ g_BlockPistonBreakable[E_BLOCK_SNOW] = true;
+ g_BlockPistonBreakable[E_BLOCK_REEDS] = true;
+ g_BlockPistonBreakable[E_BLOCK_PUMPKIN_STEM] = true;
+ g_BlockPistonBreakable[E_BLOCK_MELON_STEM] = true;
+ g_BlockPistonBreakable[E_BLOCK_MELON] = true;
+ g_BlockPistonBreakable[E_BLOCK_PUMPKIN] = true;
+ g_BlockPistonBreakable[E_BLOCK_JACK_O_LANTERN] = true;
+ g_BlockPistonBreakable[E_BLOCK_VINES] = true;
+ g_BlockPistonBreakable[E_BLOCK_STONE_PRESSURE_PLATE] = true;
+ g_BlockPistonBreakable[E_BLOCK_WOODEN_PRESSURE_PLATE] = true;
+ }
+} BlockPropertiesInitializer;
+
+
+
+
diff --git a/source/BlockID.h b/source/BlockID.h
index 3fa4ff867..323319fcb 100644
--- a/source/BlockID.h
+++ b/source/BlockID.h
@@ -8,7 +8,8 @@ enum ENUM_BLOCK_ID
E_BLOCK_GRASS = 2,
E_BLOCK_DIRT = 3,
E_BLOCK_COBBLESTONE = 4,
- E_BLOCK_WOOD = 5,
+ E_BLOCK_PLANKS = 5,
+ E_BLOCK_WOOD = E_BLOCK_PLANKS,
E_BLOCK_SAPLING = 6,
E_BLOCK_BEDROCK = 7,
E_BLOCK_WATER = 8,
@@ -93,7 +94,8 @@ enum ENUM_BLOCK_ID
E_BLOCK_BLOODSTONE = 87,
E_BLOCK_SOULSAND = 88,
E_BLOCK_GLOWSTONE = 89,
- E_BLOCK_PORT = 90,
+ E_BLOCK_PORT = 90, // Deprecated, use E_BLOCK_NETHER_PORTAL instead
+ E_BLOCK_NETHER_PORTAL = 90,
E_BLOCK_JACK_O_LANTERN = 91,
E_BLOCK_CAKE = 92,
E_BLOCK_REDSTONE_REPEATER_OFF = 93,
@@ -125,6 +127,9 @@ enum ENUM_BLOCK_ID
E_BLOCK_END_PORTAL = 119,
E_BLOCK_END_PORTAL_FRAME = 120,
E_BLOCK_END_STONE = 121,
+ E_BLOCK_DRAGON_EGG = 122,
+ E_BLOCK_REDSTONE_LAMP_OFF = 123,
+ E_BLOCK_REDSTONE_LAMP_ON = 124,
E_BLOCK_ = 121,
};
//tolua_end
@@ -394,38 +399,60 @@ enum ENUM_ITEM_ID
E_ITEM_WARD_DISC = 2265,
E_ITEM_11_DISC = 2266,
};
-//tolua_end
-/// Biome IDs, as stored in the Anvil format and sent in the MapChunk packet
-enum eBiomeID
+
+enum
{
- biOcean = 0,
- biPlains = 1,
- biDesert = 2,
- biExtremeHills = 3,
- biForest = 4,
- biTaiga = 5,
- biSwampland = 6,
- biRiver = 7,
- biHell = 8, // Nether?
- biSky = 9,
- biFrozenOcean = 10,
- biFrozenRiver = 11,
- biIcePlains = 12,
- biIceMountains = 13,
- biMushroomIsland = 14,
- biMushroomShore = 15,
- biBeach = 16,
- biDesertHills = 17,
- biForestHills = 18,
- biTaigaHills = 19,
- biExtremeHillsEdge = 20,
- biJungle = 21,
- biJungleHills = 22,
+ // E_BLOCK_PLANKS metas:
+ E_META_PLANKS_APPLE = 0,
+ E_META_PLANKS_CONIFER = 1,
+ E_META_PLANKS_BIRCH = 2,
+ E_META_PLANKS_JUNGLE = 3,
+
+ // E_BLOCK_LOG metas:
+ E_META_LOG_APPLE = 0,
+ E_META_LOG_CONIFER = 1,
+ E_META_LOG_BIRCH = 2,
+ E_META_LOG_JUNGLE = 3,
+
+ // E_BLOCK_LEAVES metas:
+ E_META_LEAVES_APPLE = 0,
+ E_META_LEAVES_CONIFER = 1,
+ E_META_LEAVES_BIRCH = 2,
+ E_META_LEAVES_JUNGLE = 3,
+
+ // E_BLOCK_SAPLING metas (lowest 3 bits):
+ E_META_SAPLING_APPLE = 0,
+ E_META_SAPLING_CONIFER = 1,
+ E_META_SAPLING_BIRCH = 2,
+ E_META_SAPLING_JUNGLE = 3,
+
+ // E_BLOCK_TALL_GRASS metas:
+ E_META_TALL_GRASS_DEAD_SHRUB = 0,
+ E_META_TALL_GRASS_GRASS = 1,
+ E_META_TALL_GRASS_FERN = 2,
} ;
+//tolua_end
+
+
+
+
+// Translates a blocktype string into blocktype. Takes either a number or an items.ini alias as input. Returns -1 on failure.
+extern int BlockStringToType(const AString & a_BlockTypeString);
+
+
+
+
+
+// Block properties:
+extern NIBBLETYPE g_BlockLightValue[256];
+extern NIBBLETYPE g_BlockSpreadLightFalloff[256];
+extern bool g_BlockTransparent[256];
+extern bool g_BlockOneHitDig[256];
+extern bool g_BlockPistonBreakable[256];
diff --git a/source/ChunkDef.h b/source/ChunkDef.h
index 837a515b5..039fcd6b0 100644
--- a/source/ChunkDef.h
+++ b/source/ChunkDef.h
@@ -41,9 +41,51 @@ typedef std::list<cBlockEntity *> cBlockEntityList;
-/// The datatype used by blockdata, metadata, blocklight and skylight
+/// The datatype used by blockdata
typedef char BLOCKTYPE;
-typedef char BIOMETYPE;
+
+/// The datatype used by nibbledata (meta, light, skylight)
+typedef unsigned char NIBBLETYPE;
+
+/// The type used by the heightmap
+typedef unsigned char HEIGHTTYPE;
+
+
+
+
+
+/** Biome IDs
+The first batch corresponds to the clientside biomes, used by MineCraft.
+BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
+When adding or deleting, you might want to edit the cBioGenCheckerBoard in BioGen.cpp to
+include / exclude that biome in that generator.
+*/
+enum EMCSBiome
+{
+ biOcean = 0,
+ biPlains = 1,
+ biDesert = 2,
+ biExtremeHills = 3,
+ biForest = 4,
+ biTaiga = 5,
+ biSwampland = 6,
+ biRiver = 7,
+ biHell = 8, // Nether?
+ biSky = 9,
+ biFrozenOcean = 10,
+ biFrozenRiver = 11,
+ biIcePlains = 12,
+ biIceMountains = 13,
+ biMushroomIsland = 14,
+ biMushroomShore = 15,
+ biBeach = 16,
+ biDesertHills = 17,
+ biForestHills = 18,
+ biTaigaHills = 19,
+ biExtremeHillsEdge = 20,
+ biJungle = 21,
+ biJungleHills = 22,
+} ;
@@ -65,7 +107,20 @@ public:
static const unsigned int INDEX_OUT_OF_RANGE = 0xffffffff;
- typedef unsigned char HeightMap[Width * Width];
+ /// The type used for any heightmap operations and storage; idx = x + Width * z
+ typedef HEIGHTTYPE HeightMap[Width * Width];
+
+ /** The type used for any biomemap operations and storage inside MCServer,
+ using MCServer biomes (need not correspond to client representation!)
+ idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
+ */
+ typedef EMCSBiome BiomeMap[Width * Width];
+
+ /// The type used for block type operations and storage, AXIS_ORDER ordering
+ typedef BLOCKTYPE BlockTypes[NumBlocks];
+
+ /// The type used for block data in nibble format, AXIS_ORDER ordering
+ typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
/// Converts absolute block coords into relative (chunk + block) coords:
@@ -134,7 +189,49 @@ public:
}
- static BLOCKTYPE GetNibble(BLOCKTYPE * a_Buffer, int a_BlockIdx)
+ inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type)
+ {
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Y >= 0) && (a_Y < Height));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
+ a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type;
+ }
+
+
+ inline static BLOCKTYPE GetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z)
+ {
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Y >= 0) && (a_Y < Height));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
+ return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)];
+ }
+
+
+ inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
+ {
+ return a_HeightMap[a_X + Width * a_Z];
+ }
+
+
+ inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
+ {
+ a_HeightMap[a_X + Width * a_Z] = a_Height;
+ }
+
+
+ inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
+ {
+ return a_BiomeMap[a_X + Width * a_Z];
+ }
+
+
+ inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
+ {
+ a_BiomeMap[a_X + Width * a_Z] = a_Biome;
+ }
+
+
+ static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx)
{
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
{
@@ -144,7 +241,7 @@ public:
}
- static BLOCKTYPE GetNibble(BLOCKTYPE * a_Buffer, int x, int y, int z)
+ static NIBBLETYPE GetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z)
{
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
{
@@ -155,7 +252,7 @@ public:
}
- static void SetNibble(BLOCKTYPE * a_Buffer, int a_BlockIdx, BLOCKTYPE a_Nibble)
+ static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
{
if ((a_BlockIdx > -1) && (a_BlockIdx < cChunkDef::NumBlocks))
{
@@ -167,7 +264,7 @@ public:
}
- static void SetNibble(BLOCKTYPE * a_Buffer, int x, int y, int z, BLOCKTYPE a_Nibble)
+ static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
{
if ((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1))
{
@@ -180,13 +277,13 @@ public:
}
- inline static BLOCKTYPE GetNibble(BLOCKTYPE * a_Buffer, const Vector3i & a_BlockPos )
+ inline static char GetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
{
return GetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
}
- inline static void SetNibble(BLOCKTYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
+ inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, char a_Value )
{
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
}
@@ -207,17 +304,23 @@ public:
/// Called once to provide heightmap data
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) {};
+ /// Called once to provide biome data
+ virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) {};
+
/// Called once to export block types
virtual void BlockTypes (const BLOCKTYPE * a_Type) {};
/// Called once to export block meta
- virtual void BlockMeta (const BLOCKTYPE * a_Meta) {};
+ virtual void BlockMeta (const NIBBLETYPE * a_Meta) {};
+ /// Called once to let know if the chunk lighting is valid. Return value is used to control if BlockLight() and BlockSkyLight() are called next
+ virtual bool LightIsValid(bool a_IsLightValid) {return true; };
+
/// Called once to export block light
- virtual void BlockLight (const BLOCKTYPE * a_Meta) {};
+ virtual void BlockLight (const NIBBLETYPE * a_Meta) {};
/// Called once to export sky light
- virtual void BlockSkyLight(const BLOCKTYPE * a_Meta) {};
+ virtual void BlockSkyLight(const NIBBLETYPE * a_Meta) {};
/// Called for each entity in the chunk
virtual void Entity(cEntity * a_Entity) {};
@@ -237,29 +340,30 @@ class cChunkDataCollector :
{
public:
- BLOCKTYPE m_BlockData[cChunkDef::BlockDataSize];
+ // Must be char instead of BLOCKTYPE or NIBBLETYPE, because it houses both.
+ char m_BlockData[cChunkDef::BlockDataSize];
protected:
virtual void BlockTypes(const BLOCKTYPE * a_BlockTypes) override
{
- memcpy(m_BlockData, a_BlockTypes, cChunkDef::NumBlocks);
+ memcpy(m_BlockData, a_BlockTypes, sizeof(cChunkDef::BlockTypes));
}
- virtual void BlockMeta(const BLOCKTYPE * a_BlockMeta) override
+ virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
{
memcpy(m_BlockData + cChunkDef::NumBlocks, a_BlockMeta, cChunkDef::NumBlocks / 2);
}
- virtual void BlockLight(const BLOCKTYPE * a_BlockLight) override
+ virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
{
memcpy(m_BlockData + 3 * cChunkDef::NumBlocks / 2, a_BlockLight, cChunkDef::NumBlocks / 2);
}
- virtual void BlockSkyLight(const BLOCKTYPE * a_BlockSkyLight) override
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
{
memcpy(m_BlockData + 2 * cChunkDef::NumBlocks, a_BlockSkyLight, cChunkDef::NumBlocks / 2);
}
@@ -276,12 +380,10 @@ class cChunkDataSeparateCollector :
{
public:
- BLOCKTYPE m_BlockTypes[cChunkDef::NumBlocks];
-
- // TODO: These should be NIBBLETYPE:
- BLOCKTYPE m_BlockMetas[cChunkDef::NumBlocks / 2];
- BLOCKTYPE m_BlockLight[cChunkDef::NumBlocks / 2];
- BLOCKTYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
+ cChunkDef::BlockTypes m_BlockTypes;
+ cChunkDef::BlockNibbles m_BlockMetas;
+ cChunkDef::BlockNibbles m_BlockLight;
+ cChunkDef::BlockNibbles m_BlockSkyLight;
protected:
@@ -291,19 +393,19 @@ protected:
}
- virtual void BlockMeta(const BLOCKTYPE * a_BlockMeta) override
+ virtual void BlockMeta(const NIBBLETYPE * a_BlockMeta) override
{
memcpy(m_BlockMetas, a_BlockMeta, sizeof(m_BlockMetas));
}
- virtual void BlockLight(const BLOCKTYPE * a_BlockLight) override
+ virtual void BlockLight(const NIBBLETYPE * a_BlockLight) override
{
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
}
- virtual void BlockSkyLight(const BLOCKTYPE * a_BlockSkyLight) override
+ virtual void BlockSkyLight(const NIBBLETYPE * a_BlockSkyLight) override
{
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
}
@@ -334,12 +436,20 @@ struct sSetBlock
{
int x, y, z;
int ChunkX, ChunkZ;
- char BlockType, BlockMeta;
-
- sSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); // absolute block position
+ BLOCKTYPE BlockType;
+ NIBBLETYPE BlockMeta;
+
+ sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ); // absolute block position
+ sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
+ ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
+ x(a_X), y(a_Y), z(a_Z),
+ BlockType(a_BlockType),
+ BlockMeta(a_BlockMeta)
+ {}
};
-typedef std::list< sSetBlock > sSetBlockList;
+typedef std::list<sSetBlock> sSetBlockList;
+typedef std::vector<sSetBlock> sSetBlockVector;
@@ -366,3 +476,13 @@ typedef std::list<cChunkCoords> cChunkCoordsList;
+/// Interface class used as a callback for operations that involve chunk coords
+class cChunkCoordCallback
+{
+public:
+ virtual void Call(int a_ChunkX, int a_ChunkZ) = 0;
+} ;
+
+
+
+
diff --git a/source/ChunkSender.cpp b/source/ChunkSender.cpp
index 232ce423c..8c65eece6 100644
--- a/source/ChunkSender.cpp
+++ b/source/ChunkSender.cpp
@@ -18,11 +18,27 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cNotifyChunkSender:
+
+void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ)
+{
+ m_ChunkSender->ChunkReady(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cChunkSender:
+
cChunkSender::cChunkSender(void) :
super("ChunkSender"),
- m_World(NULL)
+ m_World(NULL),
+ m_Notify(NULL)
{
- memset(m_BiomeData, biPlains, sizeof(m_BiomeData));
+ m_Notify.SetChunkSender(this);
}
@@ -182,18 +198,32 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHa
}
}
+ // If the chunk has no clients, no need to packetize it:
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ))
{
return;
}
+ // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
+ if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
+ {
+ return;
+ }
+
+ // If the chunk is not lighted, queue it for relighting and get notified when it's ready:
+ if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
+ {
+ m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify);
+ return;
+ }
+
// Prepare MapChunk packets:
if( !m_World->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, *this) )
{
return;
}
cPacket_PreChunk PreChunk(a_ChunkX, a_ChunkZ, true);
- cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, m_BlockData, m_BiomeData);
+ cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, m_BlockData, m_BiomeMap);
// Send:
if (a_Client == NULL)
@@ -247,3 +277,24 @@ void cChunkSender::Entity(cEntity * a_Entity)
+
+void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ }
+ } // for i - m_BiomeMap[]
+}
+
+
+
+
diff --git a/source/ChunkSender.h b/source/ChunkSender.h
index ae9a8b5c5..196db1dfa 100644
--- a/source/ChunkSender.h
+++ b/source/ChunkSender.h
@@ -40,6 +40,33 @@ class cClientHandle;
+// fwd:
+class cChunkSender;
+
+
+
+
+
+/// Callback that can be used to notify chunk sender upon another chunkcoord notification
+class cNotifyChunkSender :
+ public cChunkCoordCallback
+{
+ virtual void Call(int a_ChunkX, int a_ChunkZ) override;
+
+ cChunkSender * m_ChunkSender;
+public:
+ cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
+
+ void SetChunkSender(cChunkSender * a_ChunkSender)
+ {
+ m_ChunkSender = a_ChunkSender;
+ }
+} ;
+
+
+
+
+
class cChunkSender:
public cIsThread,
public cChunkDataCollector
@@ -101,9 +128,11 @@ protected:
cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
+ cNotifyChunkSender m_Notify; // Used for chunks that don't have a valid lighting - they will be re-queued after lightcalc
+
// Data about the chunk that is being sent:
// NOTE that m_BlockData[] is inherited from the cChunkDataCollector
- BIOMETYPE m_BiomeData[cChunkDef::Width * cChunkDef::Width];
+ unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];
PacketList m_Packets; // Accumulator for the entity-packets to send
// cIsThread override:
@@ -111,6 +140,7 @@ protected:
// cChunkDataCollector overrides:
// (Note that they are called while the ChunkMap's CS is locked - don't do heavy calculations here!)
+ virtual void BiomeData (const cChunkDef::BiomeMap * a_BiomeMap) override;
virtual void Entity (cEntity * a_Entity) override;
virtual void BlockEntity (cBlockEntity * a_Entity) override;
diff --git a/source/CompoGen.cpp b/source/CompoGen.cpp
new file mode 100644
index 000000000..9d98d2332
--- /dev/null
+++ b/source/CompoGen.cpp
@@ -0,0 +1,174 @@
+
+// CompoGen.cpp
+
+/* Implements the various terrain composition generators:
+ - cCompoGenSameBlock
+ - cCompoGenDebugBiomes
+ - cCompoGenClassic
+*/
+
+#include "Globals.h"
+#include "CompoGen.h"
+#include "BlockID.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenSameBlock:
+
+void cCompoGenSameBlock::ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+)
+{
+ memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
+ memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Start;
+ if (m_IsBedrocked)
+ {
+ a_BlockTypes[cChunkDef::MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;
+ Start = 1;
+ }
+ else
+ {
+ Start = 0;
+ }
+ for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= Start; y--)
+ {
+ a_BlockTypes[cChunkDef::MakeIndex(x, y, z)] = m_BlockType;
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenDebugBiomes:
+
+void cCompoGenDebugBiomes::ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+)
+{
+ memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
+ memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
+
+ cChunkDef::BiomeMap BiomeMap;
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, BiomeMap);
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ BLOCKTYPE BlockType = (BLOCKTYPE)cChunkDef::GetBiome(BiomeMap, x, z);
+ for (int y = a_HeightMap[x + cChunkDef::Width * z]; y >= 0; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, BlockType);
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cCompoGenClassic:
+
+cCompoGenClassic::cCompoGenClassic(int a_SeaLevel, int a_BeachHeight, int a_BeachDepth) :
+ m_SeaLevel(a_SeaLevel),
+ m_BeachHeight(a_BeachHeight),
+ m_BeachDepth(a_BeachDepth)
+{
+}
+
+
+
+
+
+void cCompoGenClassic::ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+)
+{
+ /* The classic composition means:
+ - 1 layer of grass, 3 of dirt and the rest stone, if the height > sealevel + beachheight
+ - 3 sand and a 1 sandstone, rest stone if between sealevel and sealevel + beachheight
+ - water from waterlevel to height, then 3 sand, 1 sandstone, the rest stone, if water depth < beachdepth
+ - water from waterlevel, then 3 dirt, the rest stone otherwise
+ - bedrock at the bottom
+ */
+
+ memset(a_BlockTypes, E_BLOCK_AIR, sizeof(a_BlockTypes));
+ memset(a_BlockMeta, 0, sizeof(a_BlockMeta));
+
+ // The patterns to use for different situations, must be same length!
+ static const BLOCKTYPE PatternGround[] = {E_BLOCK_GRASS, E_BLOCK_DIRT, E_BLOCK_DIRT, E_BLOCK_DIRT} ;
+ static const BLOCKTYPE PatternBeach[] = {E_BLOCK_SAND, E_BLOCK_SAND, E_BLOCK_SAND, E_BLOCK_SANDSTONE} ;
+ static const BLOCKTYPE PatternOcean[] = {E_BLOCK_DIRT, E_BLOCK_DIRT, E_BLOCK_DIRT, E_BLOCK_STONE} ;
+ static int PatternLength = ARRAYCOUNT(PatternGround);
+ ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternBeach));
+ ASSERT(ARRAYCOUNT(PatternGround) == ARRAYCOUNT(PatternOcean));
+
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int Height = a_HeightMap[x + cChunkDef::Width * z];
+ const BLOCKTYPE * Pattern;
+ if (Height > m_SeaLevel + m_BeachHeight)
+ {
+ Pattern = PatternGround;
+ }
+ else if (Height > m_SeaLevel - m_BeachDepth)
+ {
+ Pattern = PatternBeach;
+ }
+ else
+ {
+ Pattern = PatternOcean;
+ }
+
+ // Fill water from sealevel down to height (if any):
+ for (int y = m_SeaLevel; y >= Height; --y)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_STATIONARY_WATER);
+ }
+
+ // Fill from height till the bottom:
+ for (int y = Height; y >= 1; y--)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, (Height - y < PatternLength) ? Pattern[Height - y] : E_BLOCK_STONE);
+ }
+
+ // The last layer is always bedrock:
+ cChunkDef::SetBlock(a_BlockTypes, x, 0, z, E_BLOCK_BEDROCK);
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/source/CompoGen.h b/source/CompoGen.h
new file mode 100644
index 000000000..a3a524533
--- /dev/null
+++ b/source/CompoGen.h
@@ -0,0 +1,101 @@
+
+// CompoGen.h
+
+/* Interfaces to the various terrain composition generators:
+ - cCompoGenSameBlock
+ - cCompoGenDebugBiomes
+ - cCompoGenClassic
+*/
+
+
+
+
+
+#pragma once
+
+#include "cChunkGenerator.h"
+
+
+
+
+
+class cCompoGenSameBlock :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenSameBlock(BLOCKTYPE a_BlockType, bool a_IsBedrocked) :
+ m_BlockType(a_BlockType),
+ m_IsBedrocked(a_IsBedrocked)
+ {}
+
+protected:
+
+ BLOCKTYPE m_BlockType;
+ bool m_IsBedrocked;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+ ) override;
+} ;
+
+
+
+
+
+class cCompoGenDebugBiomes :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenDebugBiomes(cBiomeGen * a_BiomeGen) : m_BiomeGen(a_BiomeGen) {}
+
+protected:
+
+ cBiomeGen * m_BiomeGen;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+ ) override;
+} ;
+
+
+
+
+
+class cCompoGenClassic :
+ public cTerrainCompositionGen
+{
+public:
+ cCompoGenClassic(int a_SeaLevel, int a_BeachHeight, int a_BeachDepth);
+
+protected:
+
+ int m_SeaLevel;
+ int m_BeachHeight;
+ int m_BeachDepth;
+
+ // cTerrainCompositionGen overrides:
+ virtual void ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+ ) override;
+} ;
+
+
+
+
diff --git a/source/Defines.h b/source/Defines.h
index 5f49362b2..2c9729223 100644
--- a/source/Defines.h
+++ b/source/Defines.h
@@ -4,9 +4,9 @@
//tolua_begin
// emissive blocks
-extern char g_BlockLightValue[];
+extern unsigned char g_BlockLightValue[];
// whether blocks allow spreading
-extern char g_BlockSpreadLightFalloff[];
+extern unsigned char g_BlockSpreadLightFalloff[];
// whether blocks are transparent (light can shine though)
extern bool g_BlockTransparent[];
// one hit break blocks
diff --git a/source/FastNBT.h b/source/FastNBT.h
index a0fe7a863..f38bfcdbd 100644
--- a/source/FastNBT.h
+++ b/source/FastNBT.h
@@ -259,7 +259,7 @@ protected:
{
// Compound: add the type and name:
m_Result.push_back((char)a_Type);
- WriteString(a_Name.c_str(), a_Name.length());
+ WriteString(a_Name.c_str(), (short)a_Name.length());
}
else
{
diff --git a/source/FinishGen.cpp b/source/FinishGen.cpp
new file mode 100644
index 000000000..b2f4eab78
--- /dev/null
+++ b/source/FinishGen.cpp
@@ -0,0 +1,154 @@
+
+// FinishGen.cpp
+
+/* Implements the various finishing generators:
+ - cFinishGenSnow
+ - cFinishGenIce
+ - cFinishGenSprinkleFoliage
+*/
+
+#include "Globals.h"
+
+#include "FinishGen.h"
+#include "cNoise.h"
+#include "BlockID.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSprinkleFoliage:
+
+void cFinishGenSprinkleFoliage::GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ )
+{
+ // Generate small foliage (1-block):
+
+ // TODO: Update heightmap with 1-block-tall foliage
+ cNoise Noise(m_Seed);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int BlockZ = a_ChunkZ * cChunkDef::Width + z;
+ const float zz = (float)BlockZ;
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ int BlockX = a_ChunkX * cChunkDef::Width + x;
+ if (((Noise.IntNoise2DInt(BlockX, BlockZ) / 8) % 128) < 124)
+ {
+ continue;
+ }
+ int Top = cChunkDef::GetHeight(a_HeightMap, x, z);
+ if (Top > 250)
+ {
+ // Nothing grows above Y=250
+ continue;
+ }
+ if (cChunkDef::GetBlock(a_BlockTypes, x, Top + 1, z) != E_BLOCK_AIR)
+ {
+ // Space already taken by something else, don't grow here
+ // WEIRD, since we're using heightmap, so there should NOT be anything above it
+ continue;
+ }
+
+ const float xx = (float)BlockX;
+ float val1 = Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
+ float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
+ switch (cChunkDef::GetBlock(a_BlockTypes, x, Top, z))
+ {
+ case E_BLOCK_GRASS:
+ {
+ float val3 = Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
+ float val4 = Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
+ if (val1 + val2 > 0.2f)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_YELLOW_FLOWER);
+ }
+ else if (val2 + val3 > 0.2f)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_RED_ROSE);
+ }
+ else if (val3 + val4 > 0.2f)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_RED_MUSHROOM);
+ }
+ else if (val1 + val4 > 0.2f)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_BROWN_MUSHROOM);
+ }
+ else if (val1 + val2 + val3 + val4 < -0.1)
+ {
+ cChunkDef::SetBlock (a_BlockTypes, x, ++Top, z, E_BLOCK_TALL_GRASS);
+ cChunkDef::SetNibble(a_BlockMeta, x, Top, z, E_META_TALL_GRASS_GRASS);
+ }
+ break;
+ } // case E_BLOCK_GRASS
+
+ case E_BLOCK_SAND:
+ {
+ if (val1 + val2 > 0.f)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_CACTUS);
+ if (val1 > val2)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, ++Top, z, E_BLOCK_CACTUS);
+ }
+ }
+ break;
+ }
+ } // switch (TopBlock)
+ cChunkDef::SetHeight(a_HeightMap, x, z, Top);
+ } // for y
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenSnow:
+
+void cFinishGenSnow::GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ )
+{
+ // TODO: Add snow block in snowy biomes onto blocks that can be snowed over
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenIce:
+
+void cFinishGenIce::GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ )
+{
+ // TODO: Turn surface water into ice in icy biomes
+}
+
+
+
+
diff --git a/source/FinishGen.h b/source/FinishGen.h
new file mode 100644
index 000000000..2391d1638
--- /dev/null
+++ b/source/FinishGen.h
@@ -0,0 +1,82 @@
+
+// FinishGen.h
+
+/* Interfaces to the various finishing generators:
+ - cFinishGenSnow
+ - cFinishGenIce
+ - cFinishGenSprinkleFoliage
+*/
+
+
+
+
+
+#include "cChunkGenerator.h"
+
+
+
+
+
+class cFinishGenSnow :
+ public cFinishGen
+{
+protected:
+ // cFinishGen override:
+ virtual void GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+} ;
+
+
+
+
+
+class cFinishGenIce :
+ public cFinishGen
+{
+protected:
+ // cFinishGen override:
+ virtual void GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+} ;
+
+
+
+
+class cFinishGenSprinkleFoliage :
+ public cFinishGen
+{
+public:
+ cFinishGenSprinkleFoliage(int a_Seed) : m_Seed(a_Seed) {}
+
+protected:
+ int m_Seed;
+
+ // cFinishGen override:
+ virtual void GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+} ;
+
+
+
+
diff --git a/source/Globals.h b/source/Globals.h
index 3a9c6e9db..df72b25a8 100644
--- a/source/Globals.h
+++ b/source/Globals.h
@@ -138,7 +138,7 @@ typedef short Int16;
-// Common headers:
+// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "cSleep.h"
#include "cCriticalSection.h"
@@ -187,3 +187,11 @@ public:
+
+// Common headers (part 2, with macros):
+#include "ChunkDef.h"
+#include "BlockID.h"
+
+
+
+
diff --git a/source/HeiGen.cpp b/source/HeiGen.cpp
new file mode 100644
index 000000000..a0744ed19
--- /dev/null
+++ b/source/HeiGen.cpp
@@ -0,0 +1,91 @@
+
+// HeiGen.cpp
+
+// Implements the various terrain height generators
+
+#include "Globals.h"
+#include "HeiGen.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenFlat:
+
+void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ for (int i = 0; i < ARRAYCOUNT(a_HeightMap); i++)
+ {
+ a_HeightMap[i] = m_Height;
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cHeiGenClassic:
+
+cHeiGenClassic::cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3) :
+ m_Seed(a_Seed),
+ m_Noise(a_Seed),
+ m_HeightFreq1(a_HeightFreq1),
+ m_HeightAmp1 (a_HeightAmp1),
+ m_HeightFreq2(a_HeightFreq2),
+ m_HeightAmp2 (a_HeightAmp2),
+ m_HeightFreq3(a_HeightFreq3),
+ m_HeightAmp3 (a_HeightAmp3)
+{
+ // Nothing needed yet
+}
+
+
+
+
+
+float cHeiGenClassic::GetNoise(float x, float y)
+{
+ float oct1 = m_Noise.CubicNoise2D(x * m_HeightFreq1, y * m_HeightFreq1) * m_HeightAmp1;
+ float oct2 = m_Noise.CubicNoise2D(x * m_HeightFreq2, y * m_HeightFreq2) * m_HeightAmp2;
+ float oct3 = m_Noise.CubicNoise2D(x * m_HeightFreq3, y * m_HeightFreq3) * m_HeightAmp3;
+
+ float height = m_Noise.CubicNoise2D(x * 0.1f, y * 0.1f ) * 2;
+
+ float flatness = ((m_Noise.CubicNoise2D(x * 0.5f, y * 0.5f) + 1.f) * 0.5f) * 1.1f; // 0 ... 1.5
+ flatness *= flatness * flatness;
+
+ return (oct1 + oct2 + oct3) * flatness + height;
+}
+
+
+
+
+
+void cHeiGenClassic::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
+{
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
+
+ int hei = 64 + (int)(GetNoise(xx * 0.05f, zz * 0.05f) * 16);
+ if (hei < 10)
+ {
+ hei = 10;
+ }
+ if (hei > 250)
+ {
+ hei = 250;
+ }
+ cChunkDef::SetHeight(a_HeightMap, x , z, hei);
+ } // for x
+ } // for z
+}
+
+
+
+
diff --git a/source/HeiGen.h b/source/HeiGen.h
new file mode 100644
index 000000000..aca2fd593
--- /dev/null
+++ b/source/HeiGen.h
@@ -0,0 +1,64 @@
+
+// HeiGen.h
+
+/*
+Interfaces to the various height generators:
+ - cHeiGenFlat
+ - cHeiGenClassic
+ - cHeiGenBiomal
+*/
+
+
+
+
+
+#pragma once
+
+#include "cChunkGenerator.h"
+#include "cNoise.h"
+
+
+
+
+
+class cHeiGenFlat :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenFlat(int a_Height) : m_Height(a_Height) {}
+
+protected:
+
+ int m_Height;
+
+ // cTerrainHeightGen override:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+} ;
+
+
+
+
+
+class cHeiGenClassic :
+ public cTerrainHeightGen
+{
+public:
+ cHeiGenClassic(int a_Seed, float a_HeightFreq1, float a_HeightAmp1, float a_HeightFreq2, float a_HeightAmp2, float a_HeightFreq3, float a_HeightAmp3);
+
+protected:
+
+ int m_Seed;
+ cNoise m_Noise;
+ float m_HeightFreq1, m_HeightAmp1;
+ float m_HeightFreq2, m_HeightAmp2;
+ float m_HeightFreq3, m_HeightAmp3;
+
+ float GetNoise(float x, float y);
+
+ // cTerrainHeightGen override:
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override;
+} ;
+
+
+
+
diff --git a/source/LightingThread.cpp b/source/LightingThread.cpp
index 1bddb81bf..c2f876e32 100644
--- a/source/LightingThread.cpp
+++ b/source/LightingThread.cpp
@@ -5,6 +5,67 @@
#include "Globals.h"
#include "LightingThread.h"
+#include "cChunkMap.h"
+#include "cWorld.h"
+
+
+
+
+
+/// If more than this many chunks are in the queue, a warning is printed to the log
+#define WARN_ON_QUEUE_SIZE 800
+
+
+
+
+
+/// Chunk data callback that takes the chunk data and puts them into cLightingThread's m_BlockTypes[] / m_HeightMap[]:
+class cReader :
+ public cChunkDataCallback
+{
+ virtual void BlockTypes(const BLOCKTYPE * a_Type) override
+ {
+ // ROW is a block of 16 Blocks, one whole row is copied at a time (hopefully the compiler will optimize that)
+ // C++ doesn't permit copying arrays, but arrays as a part of a struct is ok :)
+ typedef struct {BLOCKTYPE m_Row[16]; } ROW;
+ ROW * InputRows = (ROW *)a_Type;
+ ROW * OutputRows = (ROW *)m_BlockTypes;
+ int InputIdx = 0;
+ int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ OutputRows[OutputIdx] = InputRows[InputIdx++];
+ OutputIdx += 3;
+ } // for z
+ // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
+ // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
+ OutputIdx += cChunkDef::Width * 6;
+ } // for y
+ } // BlockTypes()
+
+
+ virtual void HeightMap(const cChunkDef::HeightMap * a_Heightmap) override
+ {
+ typedef struct {HEIGHTTYPE m_Row[16]; } ROW;
+ ROW * InputRows = (ROW *)a_Heightmap;
+ ROW * OutputRows = (ROW *)m_HeightMap;
+ int InputIdx = 0;
+ int OutputIdx = m_ReadingChunkX + m_ReadingChunkZ * cChunkDef::Width * 3;
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ OutputRows[OutputIdx] = InputRows[InputIdx++];
+ OutputIdx += 3;
+ } // for z
+ }
+
+public:
+ int m_ReadingChunkX; // 0, 1 or 2; x-offset of the chunk we're reading from the BlockTypes start
+ int m_ReadingChunkZ; // 0, 1 or 2; z-offset of the chunk we're reading from the BlockTypes start
+ BLOCKTYPE * m_BlockTypes; // 3x3 chunks of block types, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
+ HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
+} ;
@@ -14,7 +75,8 @@
// cLightingThread:
cLightingThread::cLightingThread(void) :
- super("cLightingThread")
+ super("cLightingThread"),
+ m_World(NULL)
{
}
@@ -31,17 +93,26 @@ cLightingThread::~cLightingThread()
+bool cLightingThread::Start(cWorld * a_World)
+{
+ ASSERT(m_World == NULL); // Not started yet
+ m_World = a_World;
+
+ return super::Start();
+}
+
+
+
+
+
void cLightingThread::Stop(void)
{
{
cCSLock Lock(m_CS);
- for (cLightingBufferQueue::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
- {
- delete *itr;
- } // for itr - m_Queue[]
+ m_Queue.clear();
}
m_ShouldTerminate = true;
- m_Event.Set();
+ m_evtItemAdded.Set();
Wait();
}
@@ -50,9 +121,89 @@ void cLightingThread::Stop(void)
-void cLightingThread::QueueLighting(cWorld * a_World, int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ)
+void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter)
{
- // TODO
+ ASSERT(m_World != NULL); // Did you call Start() properly?
+
+ cChunkStay * ChunkStay = new cChunkStay(m_World);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ);
+ ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
+ ChunkStay->Enable();
+ ChunkStay->Load();
+ cCSLock Lock(m_CS);
+ m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter));
+ if (m_Queue.size() > WARN_ON_QUEUE_SIZE)
+ {
+ LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size());
+ }
+ m_evtItemAdded.Set();
+}
+
+
+
+
+
+void cLightingThread::WaitForQueueEmpty(void)
+{
+ cCSLock Lock(m_CS);
+ while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty()))
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtQueueEmpty.Wait();
+ }
+}
+
+
+
+
+
+size_t cLightingThread::GetQueueLength(void)
+{
+ cCSLock Lock(m_CS);
+ return m_Queue.size() + m_PostponedQueue.size();
+}
+
+
+
+
+
+void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
+{
+ // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue
+
+ bool NewlyAdded = false;
+ {
+ cCSLock Lock(m_CS);
+ for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); )
+ {
+ if (
+ (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) &&
+ (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1)
+ )
+ {
+ // It is a neighbor
+ m_Queue.push_back(*itr);
+ itr = m_PostponedQueue.erase(itr);
+ NewlyAdded = true;
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_PostponedQueue[]
+ } // Lock(m_CS)
+
+ if (NewlyAdded)
+ {
+ m_evtItemAdded.Set(); // Notify the thread it has some work to do
+ }
}
@@ -61,52 +212,318 @@ void cLightingThread::QueueLighting(cWorld * a_World, int a_MinX, int a_MaxX, in
void cLightingThread::Execute(void)
{
- // TODO
+ while (true)
+ {
+ {
+ cCSLock Lock(m_CS);
+ if (m_Queue.size() == 0)
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtItemAdded.Wait();
+ }
+ }
+
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+
+ // Process one items from the queue:
+ sItem Item;
+ {
+ cCSLock Lock(m_CS);
+ if (m_Queue.empty())
+ {
+ continue;
+ }
+ Item = m_Queue.front();
+ m_Queue.pop_front();
+ if (m_Queue.empty())
+ {
+ m_evtQueueEmpty.Set();
+ }
+ } // CSLock(m_CS)
+
+ LightChunk(Item);
+ }
}
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cLightingThread::cLightingBuffer:
-cLightingThread::cLightingBuffer::cLightingBuffer(int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ) :
- m_MinX(a_MinX),
- m_MaxX(a_MaxX),
- m_MinY(a_MinY),
- m_MaxY(a_MaxY),
- m_MinZ(a_MinZ),
- m_MaxZ(a_MaxZ)
+void cLightingThread::LightChunk(cLightingThread::sItem & a_Item)
{
- // TODO: initialize strides
+ cChunkDef::BlockNibbles BlockLight, SkyLight;
+
+ if (!ReadChunks(a_Item.x, a_Item.z))
+ {
+ // Neighbors not available. Re-queue in the postponed queue
+ cCSLock Lock(m_CS);
+ m_PostponedQueue.push_back(a_Item);
+ return;
+ }
+
+ /*
+ // DEBUG: torch somewhere:
+ m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH;
+ // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++;
+ */
+
+ PrepareBlockLight();
+ CalcLight(m_BlockLight);
+
+ PrepareSkyLight();
+ CalcLight(m_SkyLight);
+
+ CompressLight(m_BlockLight, BlockLight);
+ CompressLight(m_SkyLight, SkyLight);
+
+ /*
+ // DEBUG:
+ {
+ cFile f("chunk_BlockTypes.dat", cFile::fmWrite);
+ if (f.IsOpen())
+ {
+ f.Write(m_BlockTypes, sizeof(m_BlockTypes));
+ }
+ }
+
+ // DEBUG:
+ {
+ cFile f("Chunk_SkyLight.dat", cFile::fmWrite);
+ if (f.IsOpen())
+ {
+ f.Write(m_SkyLight, sizeof(m_SkyLight));
+ }
+ }
+
+ // DEBUG:
+ {
+ cFile f("Chunk_BlockLight.dat", cFile::fmWrite);
+ if (f.IsOpen())
+ {
+ f.Write(m_BlockLight, sizeof(m_BlockLight));
+ }
+ }
+ */
+
+ m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight);
+
+ if (a_Item.m_Callback != NULL)
+ {
+ a_Item.m_Callback->Call(a_Item.x, a_Item.z);
+ }
+ delete a_Item.m_ChunkStay;
+}
+
+
+
+
+
+bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
+{
+ cReader Reader;
+ Reader.m_BlockTypes = m_BlockTypes;
+ Reader.m_HeightMap = m_HeightMap;
+
+ for (int z = 0; z < 3; z++)
+ {
+ Reader.m_ReadingChunkZ = z;
+ for (int x = 0; x < 3; x++)
+ {
+ Reader.m_ReadingChunkX = x;
+ if (!m_World->GetChunkData(a_ChunkX + x - 1, ZERO_CHUNK_Y, a_ChunkZ + z - 1, Reader))
+ {
+ return false;
+ }
+ } // for z
+ } // for x
+
+ memset(m_BlockLight, 0, sizeof(m_BlockLight));
+ memset(m_SkyLight, 0, sizeof(m_SkyLight));
+ return true;
+}
+
+
+
+
+
+void cLightingThread::PrepareSkyLight(void)
+{
+ // Clear seeds:
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ m_NumSeeds = 0;
+
+ // Walk every column that has all XZ neighbors
+ for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
+ {
+ int BaseZ = z * cChunkDef::Width * 3;
+ for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
+ {
+ int idx = BaseZ + x;
+ int Current = m_HeightMap[idx] + 1;
+ int Neighbor1 = m_HeightMap[idx + 1] + 1; // X + 1
+ int Neighbor2 = m_HeightMap[idx - 1] + 1; // X - 1
+ int Neighbor3 = m_HeightMap[idx + cChunkDef::Width * 3] + 1; // Z + 1
+ int Neighbor4 = m_HeightMap[idx - cChunkDef::Width * 3] + 1; // Z - 1
+ int MaxNeighbor = MAX(MAX(Neighbor1, Neighbor2), MAX(Neighbor3, Neighbor4)); // Maximum of the four neighbors
+
+ // TODO: The following cycle can be transofrmed into two separate cycles with no condition inside them, one lighting and the other seeding
+ for (int y = Current, Index = idx + y * BlocksPerYLayer; y < cChunkDef::Height; y++, Index += BlocksPerYLayer)
+ {
+ // If all the XZ neighbors are lower than y, abort for the current column (but light up the rest of it):
+ if (y >= MaxNeighbor)
+ {
+ for (int y2 = y; y2 < cChunkDef::Height; y2++, Index += BlocksPerYLayer)
+ {
+ m_SkyLight[Index] = 15;
+ } // for y2
+ break; // for y
+ }
+
+ // Add current block as a seed:
+ m_IsSeed1[Index] = true;
+ m_SeedIdx1[m_NumSeeds++] = Index;
+
+ // Light it up to full skylight:
+ m_SkyLight[Index] = 15;
+ }
+ }
+ }
+}
+
+
+
+
+
+void cLightingThread::PrepareBlockLight(void)
+{
+ // Clear seeds:
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ m_NumSeeds = 0;
+
+ // Walk every column that has all XZ neighbors, make a seed for each light-emitting block:
+ for (int z = 1; z < cChunkDef::Width * 3 - 1; z++)
+ {
+ int BaseZ = z * cChunkDef::Width * 3;
+ for (int x = 1; x < cChunkDef::Width * 3 - 1; x++)
+ {
+ int idx = BaseZ + x;
+ for (int y = m_HeightMap[idx], Index = idx + y * BlocksPerYLayer; y >= 0; y--, Index -= BlocksPerYLayer)
+ {
+ if (g_BlockLightValue[m_BlockTypes[Index]] == 0)
+ {
+ continue;
+ }
+
+ // Add current block as a seed:
+ m_IsSeed1[Index] = true;
+ m_SeedIdx1[m_NumSeeds++] = Index;
+
+ // Light it up:
+ m_BlockLight[Index] = g_BlockLightValue[m_BlockTypes[Index]];
+ }
+ }
+ }
}
-void cLightingThread::cLightingBuffer::GetFromWorld(cWorld * a_World)
+void cLightingThread::CalcLight(NIBBLETYPE * a_Light)
{
- // TODO: Set m_BlockData, m_SkyLight and m_BlockLight from the world's chunks
+ int NumSeeds2 = 0;
+ while (m_NumSeeds > 0)
+ {
+ // Buffer 1 -> buffer 2
+ memset(m_IsSeed2, 0, sizeof(m_IsSeed2));
+ NumSeeds2 = 0;
+ CalcLightStep(a_Light, m_NumSeeds, m_IsSeed1, m_SeedIdx1, NumSeeds2, m_IsSeed2, m_SeedIdx2);
+ if (NumSeeds2 == 0)
+ {
+ return;
+ }
+
+ // Buffer 2 -> buffer 1
+ memset(m_IsSeed1, 0, sizeof(m_IsSeed1));
+ m_NumSeeds = 0;
+ CalcLightStep(a_Light, NumSeeds2, m_IsSeed2, m_SeedIdx2, m_NumSeeds, m_IsSeed1, m_SeedIdx1);
+ }
}
-void cLightingThread::cLightingBuffer::SetToWorld (cWorld * a_World)
+void cLightingThread::CalcLightStep(
+ NIBBLETYPE * a_Light,
+ int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+)
{
- // TODO: Set world's chunks from m_BlockData, m_SkyLight and m_BlockLight
+ int NumSeedsOut = 0;
+ for (int i = 0; i < a_NumSeedsIn; i++)
+ {
+ int SeedIdx = a_SeedIdxIn[i];
+ int SeedX = SeedIdx % (cChunkDef::Width * 3);
+ int SeedZ = (SeedIdx / (cChunkDef::Width * 3)) % (cChunkDef::Width * 3);
+ int SeedY = SeedIdx / BlocksPerYLayer;
+
+ // Propagate seed:
+ if (SeedX < cChunkDef::Width * 3)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedX > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - 1, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedZ < cChunkDef::Width * 3)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedZ > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedY < cChunkDef::Height)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx + cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ if (SeedY > 0)
+ {
+ PropagateLight(a_Light, SeedIdx, SeedIdx - cChunkDef::Width * cChunkDef::Width * 3 * 3, NumSeedsOut, a_IsSeedOut, a_SeedIdxOut);
+ }
+ } // for i - a_SeedIdxIn[]
+ a_NumSeedsOut = NumSeedsOut;
}
-void cLightingThread::cLightingBuffer::Process(void)
+void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight)
{
- // TODO: Does the actual lighting on this buffer
+ int InIdx = cChunkDef::Width * 49; // Index to the first nibble of the middle chunk in the a_LightArray
+ int OutIdx = 0;
+ for (int y = 0; y < cChunkDef::Height; y++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int x = 0; x < cChunkDef::Width; x += 2)
+ {
+ a_ChunkLight[OutIdx++] = (a_LightArray[InIdx + 1] << 4) | a_LightArray[InIdx];
+ InIdx += 2;
+ }
+ InIdx += cChunkDef::Width * 2;
+ }
+ // Skip into the next y-level in the 3x3 chunk blob; each level has cChunkDef::Width * 9 rows
+ // We've already walked cChunkDef::Width * 3 in the "for z" cycle, that makes cChunkDef::Width * 6 rows left to skip
+ InIdx += cChunkDef::Width * cChunkDef::Width * 6;
+ }
}
diff --git a/source/LightingThread.h b/source/LightingThread.h
index 5b60d34bf..0f8b59441 100644
--- a/source/LightingThread.h
+++ b/source/LightingThread.h
@@ -2,22 +2,48 @@
// LightingThread.h
// Interfaces to the cLightingThread class representing the thread that processes requests for lighting
-// Note that the world generators need direct access to the lighting methods so that they can light the generated chunk
+/*
+Lighting is done on whole chunks. For each chunk to be lighted, the whole 3x3 chunk area around it is read,
+then it is processed, so that the middle chunk area has valid lighting, and the lighting is copied into the ChunkMap.
+Lighting is calculated in full char arrays instead of nibbles, so that accessing the arrays is fast.
+Lighting is calculated in a flood-fill fashion:
+1. Generate seeds from where the light spreads (full skylight / light-emitting blocks)
+2. For each seed:
+ - Spread the light 1 block in each of the 6 cardinal directions, if the blocktype allows
+ - If the recipient block has had lower lighting value than that being spread, make it a new seed
+3. Repeat step 2, until there are no more seeds
+The seeds need two fast operations:
+ - Check if a block at [x, y, z] is already a seed
+ - Get the next seed in the row
+For that reason it is stored in two arrays, one stores a bool saying a seed is in that position,
+the other is an array of seed coords, encoded as a single int.
+Step 2 needs two separate storages for old seeds and new seeds, so there are two actual storages for that purpose,
+their content is swapped after each full step-2-cycle.
+
+The thread has two queues of chunks that are to be lighted.
+The first queue, m_Queue, is the only one that is publicly visible, chunks get queued there by external requests.
+The second one, m_PostponedQueue, is for chunks that have been taken out of m_Queue and didn't have neighbors ready.
+Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors get valid, using the ChunkReady callback.
+*/
#pragma once
#include "cIsThread.h"
+#include "ChunkDef.h"
-// fwd:
+// fwd: "cWorld.h"
class cWorld;
+// fwd: "cChunkMap.h"
+class cChunkStay;
+
@@ -28,53 +54,121 @@ class cLightingThread :
typedef cIsThread super;
public:
-
- class cLightingBuffer
- {
- public:
- cLightingBuffer(int m_MinX, int m_MaxX, int m_MinY, int m_MaxY, int m_MinZ, int m_MaxZ);
-
- /// Copies the world's existing chunks into m_BlockData, m_Skylight and m_BlockLight
- void GetFromWorld(cWorld * a_World);
- void SetToWorld (cWorld * a_World);
-
- void Process(void); // Does the actual lighting on this buffer
-
- protected:
-
- // Block coords:
- int m_MinX, m_MaxX;
- int m_MinY, m_MaxY;
- int m_MinZ, m_MaxZ;
-
- int m_StrideX; // = OffsetOfBlock(x, y, z) - OffsetOfBlock(x + 1, y, z)
- int m_StrideZ; // = OffsetOfBlock(x, y, z) - OffsetOfBlock(x, y, z + 1)
-
- // These buffers actually store 1 block in each direction more than is specified in the coords
- // This way we can throw out a lot of conditions inside the processing cycles
- // And it allows us to light a chunk with regard to its surrounding chunks without much work
- // (So if m_MinX is 16 and m_MaxX is 32, the buffers actually contain data for X in range from 15 to 33 (18 items)
- char * m_BlockData;
- char * m_SkyLight;
- char * m_BlockLight;
- } ;
cLightingThread(void);
~cLightingThread();
+ bool Start(cWorld * a_World);
+
void Stop(void);
- void QueueLighting(cWorld * a_World, int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ); // queues the request
-
+ /// Queues the entire chunk for lighting
+ void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL);
+
+ /// Blocks until the queue is empty or the thread is terminated
+ void WaitForQueueEmpty(void);
+
+ size_t GetQueueLength(void);
+
+ /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue
+ void ChunkReady(int a_ChunkX, int a_ChunkZ);
+
protected:
- typedef std::list<cLightingBuffer *> cLightingBufferQueue;
+ struct sItem
+ {
+ int x, z;
+ cChunkStay * m_ChunkStay;
+ cChunkCoordCallback * m_Callback;
+
+ sItem(void) {} // empty default constructor needed
+ sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) :
+ x(a_X),
+ z(a_Z),
+ m_ChunkStay(a_ChunkStay),
+ m_Callback(a_Callback)
+ {
+ }
+ } ;
+
+ typedef std::list<sItem> sItems;
- cCriticalSection m_CS;
- cLightingBufferQueue m_Queue;
- cEvent m_Event; // Set when queue is appended or to stop the thread
+ cWorld * m_World;
+ cCriticalSection m_CS;
+ sItems m_Queue;
+ sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors
+ cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread
+ cEvent m_evtQueueEmpty; // Set when the queue gets empty
+ // Buffers for the 3x3 chunk data
+ // These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less
+ // Placing the buffers into the object means that this object can light chunks only in one thread!
+ // The blobs are XZY organized as a whole, instead of 3x3 XZY-organized subarrays ->
+ // -> This means data has to be scatterred when reading and gathered when writing!
+ static const int BlocksPerYLayer = cChunkDef::Width * cChunkDef::Width * 3 * 3;
+ BLOCKTYPE m_BlockTypes[BlocksPerYLayer * cChunkDef::Height];
+ NIBBLETYPE m_BlockLight[BlocksPerYLayer * cChunkDef::Height];
+ NIBBLETYPE m_SkyLight [BlocksPerYLayer * cChunkDef::Height];
+ HEIGHTTYPE m_HeightMap [BlocksPerYLayer];
+
+ // Seed management (5.7 MiB)
+ // Two buffers, in each calc step one is set as input and the other as output, then in the next step they're swapped
+ // Each seed is represented twice in this structure - both as a "list" and as a "position".
+ // "list" allows fast traversal from seed to seed
+ // "position" allows fast checking if a coord is already a seed
+ unsigned char m_IsSeed1 [BlocksPerYLayer * cChunkDef::Height];
+ unsigned int m_SeedIdx1[BlocksPerYLayer * cChunkDef::Height];
+ unsigned char m_IsSeed2 [BlocksPerYLayer * cChunkDef::Height];
+ unsigned int m_SeedIdx2[BlocksPerYLayer * cChunkDef::Height];
+ int m_NumSeeds;
+
virtual void Execute(void) override;
+
+ /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk
+ void LightChunk(sItem & a_Item);
+
+ /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays
+ bool ReadChunks(int a_ChunkX, int a_ChunkZ);
+
+ /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight
+ void PrepareSkyLight(void);
+
+ /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight
+ void PrepareBlockLight(void);
+
+ /// Calculates light in the light array specified, using stored seeds
+ void CalcLight(NIBBLETYPE * a_Light);
+
+ /// Does one step in the light calculation - one seed propagation and seed recalculation
+ void CalcLightStep(
+ NIBBLETYPE * a_Light,
+ int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+ );
+
+ /// Compresses from 1-byte-per-block into 2-bytes-per-block:
+ void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight);
+
+ inline void PropagateLight(
+ NIBBLETYPE * a_Light,
+ int a_SrcIdx, int a_DstIdx,
+ int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
+ )
+ {
+ if (a_Light[a_SrcIdx] <= a_Light[a_DstIdx] + g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]])
+ {
+ // The dest block already has enough light than we're offerring
+ return;
+ }
+
+ a_Light[a_DstIdx] = a_Light[a_SrcIdx] - g_BlockSpreadLightFalloff[m_BlockTypes[a_DstIdx]];
+ if (!a_IsSeedOut[a_DstIdx])
+ {
+ a_IsSeedOut[a_DstIdx] = true;
+ a_SeedIdxOut[a_NumSeedsOut++] = a_DstIdx;
+ }
+ }
+
} ;
diff --git a/source/NBT.cpp b/source/NBT.cpp
deleted file mode 100644
index ba2f3d0f6..000000000
--- a/source/NBT.cpp
+++ /dev/null
@@ -1,899 +0,0 @@
-
-// NBT.cpp
-
-// Implements the classes used for NBT representation, parsing and serializing
-
-/*
-This file has been retrofitted from a different project of mine (_Xoft(o)) and melded here, so it is not strictyl MCS-styled
-Also the project used error codes, which MCS doesn't need, so they were declared locally only.
-The code is not strictly safe, it could leak pointers when exceptions are thrown. It could use a RAII redesign.
-*/
-
-#include "Globals.h"
-#include "NBT.h"
-#include "Endianness.h"
-
-
-
-
-
-// Error codes
-// Unused by MCS
-enum
-{
- ERROR_PRIVATE_BASE = 0x00040000,
-
- ERROR_PRIVATE_NBTPARSER_BADTYPE, // The parsed type is not recognized
- ERROR_PRIVATE_NBTPARSER_INVALIDLENGTH, // The parsed name has an invalid length (negative-length string etc.)
- ERROR_PRIVATE_NBTPARSER_UNEXPECTEDTAG, // The parser has encountered a tag that should not be at such a point
- ERROR_PRIVATE_NBTPARSER_TOOSHORT, // The parser was invoked on data that is too short
-
- ERROR_PRIVATE_NBT_UNINITIALIZEDLIST, // NBTList needs its ChildType set before adding items to it
- ERROR_PRIVATE_NBT_TYPEMISMATCH, // NBTList must have all of its children of the same type
- ERROR_PRIVATE_NBT_UNEXPECTEDTAG, // NBTList and NBTCompound cannot contain a TAG_End
- ERROR_PRIVATE_NBT_BADTYPE, // NBTList's children type cannot be set to such a value
- ERROR_PRIVATE_NBT_LISTNOTEMPTY, // NBTList must be empty to allow setting children type
- ERROR_PRIVATE_NBT_INDEXOUTOFRANGE, // Requesting an item with invalid index from a list or a compound
-
- ERROR_PRIVATE_UNKNOWN, // Unknown error
-} ;
-
-#ifndef ERROR_SUCCESS
- // This constant is actually #defined in the WinAPI; it cannot be put into the above enum, but needs to be #defined (for *nix)
- #define ERROR_SUCCESS 0
-#endif // ERROR_SUCCCESS
-
-#ifndef ERROR_NOT_ENOUGH_MEMORY
- // This constant is actually #defined in the WinAPI; it cannot be put into the above enum, but needs to be #defined (for *nix)
- #define ERROR_NOT_ENOUGH_MEMORY 8
-#endif // ERROR_NOT_ENOUGH_MEMORY
-
-
-
-
-
-#define RETURN_INT_IF_FAILED(X) {int r = X; if (r != ERROR_SUCCESS) return r; }
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTTag:
-
-cNBTTag * cNBTTag::CreateTag(cNBTTag * a_Parent, eTagType a_Type, const AString & a_Name)
-{
- // Creates a new instance of a tag specified by a_Type, uses the correct class
- switch (a_Type)
- {
- case TAG_Byte: return new cNBTByte (a_Parent, a_Name);
- case TAG_Short: return new cNBTShort (a_Parent, a_Name);
- case TAG_Int: return new cNBTInt (a_Parent, a_Name);
- case TAG_Long: return new cNBTLong (a_Parent, a_Name);
- case TAG_Float: return new cNBTFloat (a_Parent, a_Name);
- case TAG_Double: return new cNBTDouble (a_Parent, a_Name);
- case TAG_ByteArray: return new cNBTByteArray(a_Parent, a_Name);
- case TAG_String: return new cNBTString (a_Parent, a_Name);
- case TAG_List: return new cNBTList (a_Parent, a_Name, TAG_End);
- case TAG_Compound: return new cNBTCompound (a_Parent, a_Name);
- default:
- {
- ASSERT(!"Unknown TAG type requested");
- return NULL;
- }
- }
-}
-
-
-
-
-
-const cNBTTag * cNBTTag::FindChildByPath(const AString & iPath) const
-{
- size_t PrevIdx = 0;
- size_t idx = iPath.find('\\');
- const cNBTTag * res = this;
- while ((res != NULL) && (idx != AString::npos))
- {
- res = res->FindChildByName(AString(iPath, PrevIdx, idx - PrevIdx));
- PrevIdx = idx + 1;
- idx = iPath.find('\\', PrevIdx);
- }
- if (res != NULL)
- {
- res = res->FindChildByName(AString(iPath, PrevIdx));
- }
- return res;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTList:
-
-void cNBTList::Clear(void)
-{
- for (cNBTTags::iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr)
- {
- delete *itr;
- }
- m_Children.clear();
-}
-
-
-
-
-int cNBTList::Add(cNBTTag * iTag)
-{
- // Catch usage errors while debugging:
- ASSERT(m_ChildrenType != TAG_End); // Didn't call SetChildrenType()
- ASSERT(iTag->GetType() == m_ChildrenType); // Child of different type
-
- // Catch errors while running:
- if (m_ChildrenType == TAG_End)
- {
- return ERROR_PRIVATE_NBT_UNINITIALIZEDLIST;
- }
- if (iTag->GetType() != m_ChildrenType)
- {
- return ERROR_PRIVATE_NBT_TYPEMISMATCH;
- }
-
- m_Children.push_back(iTag);
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTList::SetChildrenType(cNBTTag::eTagType a_Type)
-{
- // Catch usage errors while debugging:
- ASSERT(a_Type != TAG_End); // Invalid, though not specifically in the NBT spec
- ASSERT(m_Children.size() == 0); // Can change only when empty
-
- // Catch runtime errors:
- if (a_Type == TAG_End)
- {
- return ERROR_PRIVATE_NBT_BADTYPE;
- }
- if (m_Children.size() != 0)
- {
- return ERROR_PRIVATE_NBT_LISTNOTEMPTY;
- }
-
- m_ChildrenType = a_Type;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-cNBTTag * cNBTList::GetChildByIdx(size_t iIndex)
-{
- // Catch usage errors while debugging:
- ASSERT((iIndex >= 0) && (iIndex < m_Children.size()));
-
- // Catch runtime errors:
- if ((iIndex < 0) || (iIndex >= m_Children.size()))
- {
- return NULL;
- }
-
- return m_Children[iIndex];
-}
-
-
-
-
-
-cNBTTag * cNBTList::FindChildByName(const AString & a_Name) const
-{
- for (cNBTTags::const_iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr)
- {
- if ((*itr)->GetName() == a_Name)
- {
- return *itr;
- }
- }
- return NULL;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTCompound:
-
-void cNBTCompound::Clear(void)
-{
- for (cNBTTags::iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr)
- {
- delete *itr;
- }
- m_Children.clear();
-}
-
-
-
-
-
-int cNBTCompound::Add(cNBTTag * iTag)
-{
- // Catch usage errors while debugging:
- ASSERT(iTag->GetType() != TAG_End);
-
- // Catch runtime errors:
- if (iTag->GetType() == TAG_End)
- {
- return ERROR_PRIVATE_NBT_UNEXPECTEDTAG;
- }
-
- m_Children.push_back(iTag);
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-cNBTTag * cNBTCompound::GetChildByIdx(size_t iIndex)
-{
- // Catch usage errors while debugging:
- ASSERT((iIndex >= 0) && (iIndex < m_Children.size()));
-
- // Catch runtime errors:
- if ((iIndex < 0) || (iIndex >= m_Children.size()))
- {
- return NULL;
- }
-
- return m_Children[iIndex];
-}
-
-
-
-
-
-cNBTTag * cNBTCompound::FindChildByName(const AString & a_Name) const
-{
- for (cNBTTags::const_iterator itr = m_Children.begin(); itr != m_Children.end(); ++itr)
- {
- if ((*itr)->GetName() == a_Name)
- {
- return *itr;
- }
- }
- return NULL;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTParser:
-
-int cNBTParser::ReadByte(const char ** a_Data, int * a_Length, char & a_Value)
-{
- if (*a_Length < 1)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_Value = **a_Data;
- *a_Data += 1;
- *a_Length -= 1;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadInt16(const char ** a_Data, int * a_Length, Int16 & a_Value)
-{
- if (*a_Length < 2)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_Value = ntohs(*((Int16 *)*a_Data));
- *a_Data += 2;
- *a_Length -= 2;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadInt32(const char ** a_Data, int * a_Length, Int32 & a_Value)
-{
- if (*a_Length < 4)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_Value = ntohl(*((Int32 *)*a_Data));
- *a_Data += 4;
- *a_Length -= 4;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadInt64(const char ** a_Data, int * a_Length, Int64 & a_Value)
-{
- if (*a_Length < 8)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_Value = NetworkToHostLong8(*a_Data);
- *a_Data += 8;
- *a_Length -= 8;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadFloat(const char ** a_Data, int * a_Length, float & a_Value)
-{
- if (*a_Length < 4)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- // Read as a 32-bit integer, converting endianness, then reinterpret as float:
- Int32 tmp = ntohl(*((Int32 *)*a_Data));
- a_Value = *((float *)&tmp);
- *a_Data += 4;
- *a_Length -= 4;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadDouble(const char ** a_Data, int * a_Length, double & a_Value)
-{
- if (*a_Length < 8)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_Value = NetworkToHostDouble8(*a_Data);
- *a_Data += 8;
- *a_Length -= 8;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadByteArray(const char ** a_Data, int * a_Length, AString & a_String)
-{
- // Reads the short-counted string, adjusts a_Data and a_Length accordingly
- Int32 Len;
- RETURN_INT_IF_FAILED(ReadInt32(a_Data, a_Length, Len));
- if (Len < 0)
- {
- return ERROR_PRIVATE_NBTPARSER_INVALIDLENGTH;
- }
- if (*a_Length < Len)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_String.assign(*a_Data, Len);
- *a_Data += Len;
- *a_Length -= Len;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadString(const char ** a_Data, int * a_Length, AString & a_String)
-{
- // Reads the short-counted string, adjusts a_Data and a_Length accordingly
- if (*a_Length < 2)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- Int16 val = *((Int16 *)*a_Data);
- Int16 Len = ntohs(val);
- if (Len < 0)
- {
- return ERROR_PRIVATE_NBTPARSER_INVALIDLENGTH;
- }
- *a_Data += 2;
- *a_Length -= 2;
- if (*a_Length < Len)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- a_String.assign(*a_Data, Len);
- *a_Data += Len;
- *a_Length -= Len;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadList(const char ** a_Data, int * a_Length, cNBTList * a_List)
-{
- // Reads a_List's contents from a_Data; up to a_Length bytes may be used; adjusts a_Data and a_Length for after the list
- Int32 ItemCount;
- RETURN_INT_IF_FAILED(ReadInt32(a_Data, a_Length, ItemCount));
- for (Int32 i = 0; i < ItemCount; i++)
- {
- cNBTTag * child = NULL;
- RETURN_INT_IF_FAILED(ReadTag(a_Data, a_Length, a_List->GetChildrenType(), "", a_List, &child));
- if (child == NULL)
- {
- return ERROR_PRIVATE_UNKNOWN;
- }
- RETURN_INT_IF_FAILED(a_List->Add(child));
- } // for i - Items[]
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-int cNBTParser::ReadCompound(const char ** a_Data, int * a_Length, cNBTCompound * a_Compound)
-{
- // Reads a_Compound's contents from a_Data; up to a_Length bytes may be used; adjusts a_Data and a_Length for after the compound
- while (true)
- {
- char TagType = **a_Data;
- *a_Data += 1;
- *a_Length -= 1;
- if (TagType == cNBTTag::TAG_End)
- {
- return ERROR_SUCCESS;
- }
- AString Name;
- RETURN_INT_IF_FAILED(ReadString(a_Data, a_Length, Name));
- cNBTTag * child = NULL;
- RETURN_INT_IF_FAILED(ReadTag(a_Data, a_Length, (cNBTTag::eTagType)TagType, Name, a_Compound, &child));
- if (child == NULL)
- {
- return ERROR_PRIVATE_UNKNOWN;
- }
- RETURN_INT_IF_FAILED(a_Compound->Add(child));
- } // while (true)
-}
-
-
-
-
-int cNBTParser::ReadIntArray(const char ** a_Data, int * a_Length, cNBTIntArray * a_Array)
-{
- if (*a_Length < 4)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- int Count = ntohl(*((int *)*a_Data));
- *a_Data += 4;
- *a_Length -= 4;
- if (*a_Length < 4 * Count)
- {
- return ERROR_PRIVATE_NBTPARSER_TOOSHORT;
- }
- for (int i = 0; i < Count; i++)
- {
- int Value = ntohl(*((int *)*a_Data));
- a_Array->Add(Value);
- *a_Data += 4;
- }
- *a_Length -= 4 * Count;
- return ERROR_SUCCESS;
-}
-
-
-
-
-
-#define CASE_SIMPLE_TAG(TAGTYPE,CTYPE,FUNC) \
- case cNBTTag::TAG_##TAGTYPE: \
- { \
- CTYPE val; \
- RETURN_INT_IF_FAILED(Read##FUNC(a_Data, a_Length, val)); \
- *a_Tag = new cNBT##TAGTYPE(a_Parent, a_Name, val); \
- if (*a_Tag == NULL) \
- { \
- return ERROR_NOT_ENOUGH_MEMORY; \
- } \
- return ERROR_SUCCESS;\
- }
-
-int cNBTParser::ReadTag(const char ** a_Data, int * a_Length, cNBTTag::eTagType a_Type, const AString & a_Name, cNBTTag * a_Parent, cNBTTag ** a_Tag)
-{
- switch (a_Type)
- {
- CASE_SIMPLE_TAG(Byte, char, Byte)
- CASE_SIMPLE_TAG(Short, Int16, Int16)
- CASE_SIMPLE_TAG(Int, Int32, Int32)
- CASE_SIMPLE_TAG(Long, Int64, Int64)
- CASE_SIMPLE_TAG(Float, float, Float)
- CASE_SIMPLE_TAG(Double, double, Double)
- CASE_SIMPLE_TAG(ByteArray, AString, ByteArray)
- CASE_SIMPLE_TAG(String, AString, String)
-
- case cNBTTag::TAG_List:
- {
- char ItemType;
- RETURN_INT_IF_FAILED(ReadByte (a_Data, a_Length, ItemType));
- cNBTList * List = new cNBTList(a_Parent, a_Name, (cNBTTag::eTagType)ItemType);
- if (List == NULL)
- {
- return ERROR_NOT_ENOUGH_MEMORY;
- }
- RETURN_INT_IF_FAILED(ReadList(a_Data, a_Length, List));
- *a_Tag = List;
- return ERROR_SUCCESS;
- }
-
- case cNBTTag::TAG_Compound:
- {
- cNBTCompound * Compound = new cNBTCompound(a_Parent, a_Name);
- if (Compound == NULL)
- {
- return ERROR_NOT_ENOUGH_MEMORY;
- }
- RETURN_INT_IF_FAILED(ReadCompound(a_Data, a_Length, Compound));
- *a_Tag = Compound;
- return ERROR_SUCCESS;
- }
-
- case cNBTTag::TAG_IntArray:
- {
- cNBTIntArray * Array = new cNBTIntArray(a_Parent, a_Name);
- if (Array == NULL)
- {
- return ERROR_NOT_ENOUGH_MEMORY;
- }
- RETURN_INT_IF_FAILED(ReadIntArray(a_Data, a_Length, Array));
- *a_Tag = Array;
- return ERROR_SUCCESS;
- }
-
- default:
- {
- #if (defined(_DEBUG) && defined(_WIN32))
- OutputDebugString("Unhandled NBT tag type\n");
- cNBTTag * Parent = a_Parent, * Cur = a_Parent;
- while (Parent != NULL)
- {
- OutputDebugString("Parent:\n");
- Cur = Parent;
- Parent = Parent->GetParent();
- DumpTree(Cur);
- }
- OutputDebugString("Done\n");
- #endif // _DEBUG
- ASSERT(!"Unhandled NBT tag type");
- break;
- }
- } // switch (iType)
-
- return ERROR_PRIVATE_NBTPARSER_BADTYPE;
-}
-
-#undef CASE_SIMPLE_TAG
-
-
-
-
-cNBTTree * cNBTParser::Parse(const char * a_Data, int a_Length)
-{
- // Creates a NBT from a_Data
- if (a_Length < 3)
- {
- return NULL;
- }
- if (a_Data[0] != cNBTTag::TAG_Compound)
- {
- return NULL;
- }
- a_Data++;
- a_Length--;
- AString Name;
- if (ReadString(&a_Data, &a_Length, Name) != 0)
- {
- return NULL;
- }
- std::auto_ptr<cNBTCompound> Root(new cNBTCompound(NULL, Name));
- if (Root.get() == NULL)
- {
- return NULL;
- }
- if (ReadCompound(&a_Data, &a_Length, Root.get()) == 0)
- {
- return Root.release();
- }
- return NULL;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cNBTSerializer:
-
-void cNBTSerializer::WriteByte(AString & a_Out, const char a_Value)
-{
- a_Out.push_back(a_Value);
-}
-
-
-
-
-
-void cNBTSerializer::WriteInt16(AString & a_Out, const Int16 a_Value)
-{
- Int16 Val = htons(a_Value);
- a_Out.append((char *)&Val, 2);
-}
-
-
-
-
-
-void cNBTSerializer::WriteInt32(AString & a_Out, const Int32 a_Value)
-{
- Int32 Val = htonl(a_Value);
- a_Out.append((char *)&Val, 4);
-}
-
-
-
-
-
-void cNBTSerializer::WriteInt64(AString & a_Out, const Int64 a_Value)
-{
- Int64 Val = HostToNetwork8(&a_Value);
- a_Out.append((char *)&Val, 8);
-}
-
-
-
-
-
-void cNBTSerializer::WriteFloat(AString & a_Out, const float a_Value)
-{
- Int32 Val = htonl(*((u_long *)&a_Value));
- a_Out.append((char *)&Val, 4);
-}
-
-
-
-
-
-void cNBTSerializer::WriteDouble(AString & a_Out, const double a_Value)
-{
- Int64 Val = HostToNetwork8(&a_Value);
- a_Out.append((char *)&Val, 8);
-}
-
-
-
-
-
-void cNBTSerializer::WriteByteArray(AString & a_Out, const AString & a_Value)
-{
- WriteInt32(a_Out, a_Value.size());
- a_Out.append(a_Value);
-}
-
-
-
-
-
-void cNBTSerializer::WriteString(AString & a_Out, const AString & a_String)
-{
- WriteInt16(a_Out, a_String.length());
- a_Out.append(a_String);
-}
-
-
-
-
-
-void cNBTSerializer::WriteList(AString & a_Out, const cNBTList * a_List)
-{
- WriteInt32(a_Out, a_List->GetChildrenCount());
- const cNBTTags & Children = a_List->GetChildren();
- for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr)
- {
- WriteTag(a_Out, *itr);
- } // for itr - Children[]
-}
-
-
-
-
-
-void cNBTSerializer::WriteIntArray(AString & a_Out, const cNBTIntArray * a_Array)
-{
- WriteInt32(a_Out, a_Array->GetValues().size());
- const std::vector<int> & Values = a_Array->GetValues();
- for (std::vector<int>::const_iterator itr = Values.begin(); itr != Values.end(); ++itr)
- {
- WriteInt32(a_Out, *itr);
- } // for itr - Values[]
-}
-
-
-
-
-
-void cNBTSerializer::WriteCompound(AString & a_Out, const cNBTCompound * a_Compound)
-{
- const cNBTTags & Children = a_Compound->GetChildren();
- for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr)
- {
- a_Out.push_back((*itr)->GetType());
- WriteString(a_Out, (*itr)->GetName());
- WriteTag(a_Out, *itr);
- } // for itr - Children[]
- a_Out.push_back(cNBTTag::TAG_End);
-}
-
-
-
-
-
-#define CASE_SIMPLE_TAG(TAGTYPE,CTYPE,FUNC) \
- case cNBTTag::TAG_##TAGTYPE: \
- { \
- Write##FUNC(a_Out, ((const cNBT##TAGTYPE *)a_Tag)->m_Value ); \
- return;\
- }
-
-void cNBTSerializer::WriteTag(AString & a_Out, const cNBTTag * a_Tag)
-{
- switch (a_Tag->GetType())
- {
- CASE_SIMPLE_TAG(Byte, char, Byte)
- CASE_SIMPLE_TAG(Short, Int16, Int16)
- CASE_SIMPLE_TAG(Int, Int32, Int32)
- CASE_SIMPLE_TAG(Long, Int64, Int64)
- CASE_SIMPLE_TAG(Float, float, Float)
- CASE_SIMPLE_TAG(Double, double, Double)
- CASE_SIMPLE_TAG(ByteArray, AString, ByteArray)
- CASE_SIMPLE_TAG(String, AString, String)
-
- case cNBTTag::TAG_List:
- {
- a_Out.push_back((char)((const cNBTList *)a_Tag)->GetChildrenType());
- WriteList(a_Out, (const cNBTList *)a_Tag);
- return;
- }
-
- case cNBTTag::TAG_Compound:
- {
- WriteCompound(a_Out, (const cNBTCompound *)a_Tag);
- return;
- }
-
- case cNBTTag::TAG_IntArray:
- {
- WriteIntArray(a_Out, (const cNBTIntArray *)a_Tag);
- return;
- }
- } // switch (iType)
-
- ASSERT(!"Unhandled NBT tag type");
-}
-
-#undef CASE_SIMPLE_TAG
-
-
-
-
-void cNBTSerializer::Serialize(const cNBTTree * a_Tree, AString & a_Out)
-{
- a_Out.clear();
- a_Out.push_back(cNBTTag::TAG_Compound);
- WriteString(a_Out, a_Tree->GetName());
- WriteCompound(a_Out, (const cNBTCompound *)a_Tree);
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Dumping the NBT tree (debug-only)
-
-#if (defined(_DEBUG) && defined(_WIN32))
-
-#define CASE_SIMPLE_TAG(TYPE,FMT) \
- case cNBTTag::TAG_##TYPE: \
- { \
- AString out; \
- Printf(out, "%sTAG_" TEXT(#TYPE) TEXT("(\"%hs\"): %") TEXT(FMT) TEXT("\n"), Indent.c_str(), a_Tree->GetName().c_str(), ((cNBT##TYPE *)a_Tree)->m_Value); \
- OutputDebugString(out.c_str()); \
- break; \
- }
-
-void DumpTree(const cNBTTree * a_Tree, int a_Level)
-{
- AString Indent(a_Level, TEXT(' '));
- switch (a_Tree->GetType())
- {
- CASE_SIMPLE_TAG(Byte, "d")
- CASE_SIMPLE_TAG(Short, "d")
- CASE_SIMPLE_TAG(Int, "d")
- CASE_SIMPLE_TAG(Long, "I64d")
- CASE_SIMPLE_TAG(Float, "f")
- CASE_SIMPLE_TAG(Double, "f")
-
- case cNBTTag::TAG_ByteArray:
- {
- AString out;
- Printf(out, "%sTAG_ByteArray(\"%hs\"): %d bytes\n", Indent.c_str(), a_Tree->GetName().c_str(), ((cNBTByteArray *)a_Tree)->m_Value.size());
- OutputDebugString(out.c_str());
- break;
- }
-
- case cNBTTag::TAG_String:
- {
- AString out;
- Printf(out, "%sTAG_String(\"%hs\"): %d bytes: \"%hs\"\n", Indent.c_str(), a_Tree->GetName().c_str(), ((cNBTString *)a_Tree)->m_Value.size(), ((cNBTString *)a_Tree)->m_Value.c_str());
- OutputDebugString(out.c_str());
- break;
- }
-
- case cNBTTag::TAG_List:
- {
- const cNBTTags & Children = ((cNBTList *)a_Tree)->GetChildren();
- AString out;
- Printf(out, "%sTAG_List(\"%hs\"): %d items of type %d\n%s{\n", Indent.c_str(), a_Tree->GetName().c_str(), Children.size(), ((cNBTList *)a_Tree)->GetChildrenType(), Indent.c_str());
- OutputDebugString(out.c_str());
- for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr)
- {
- DumpTree(*itr, a_Level + 1);
- } // for itr - Children[]
- Printf(out, "%s}\n", Indent.c_str());
- OutputDebugString(out.c_str());
- break;
- }
-
- case cNBTTag::TAG_Compound:
- {
- const cNBTTags & Children = ((cNBTCompound *)a_Tree)->GetChildren();
- AString out;
- Printf(out, "%sTAG_Compound(\"%hs\"): %d items\n%s{\n", Indent.c_str(), a_Tree->GetName().c_str(), Children.size(), Indent.c_str());
- OutputDebugString(out.c_str());
- for (cNBTTags::const_iterator itr = Children.begin(); itr != Children.end(); ++itr)
- {
- DumpTree(*itr, a_Level + 1);
- } // for itr - Children[]
- Printf(out, "%s}\n", Indent.c_str());
- OutputDebugString(out.c_str());
- break;
- }
- }
-}
-
-#undef CASE_SIMPLE_TAG
-
-#endif // (_DEBUG && _WIN32)
-
-
-
-
diff --git a/source/NBT.h b/source/NBT.h
deleted file mode 100644
index 39b3c7252..000000000
--- a/source/NBT.h
+++ /dev/null
@@ -1,232 +0,0 @@
-
-// NBT.h
-
-// Interfaces to the classes used for NBT representation, parsing and serializing
-
-
-
-
-
-#pragma once
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Representation classes:
-
-class cNBTTag abstract // The base class for all NBT tags
-{
-public:
- enum eTagType
- {
- TAG_Min = 0, // The minimum value for a tag type
- TAG_End = 0,
- TAG_Byte = 1,
- TAG_Short = 2,
- TAG_Int = 3,
- TAG_Long = 4,
- TAG_Float = 5,
- TAG_Double = 6,
- TAG_ByteArray = 7,
- TAG_String = 8,
- TAG_List = 9,
- TAG_Compound = 10,
- TAG_IntArray = 11,
- TAG_Max = 11, // The maximum value for a tag type
- } ;
-
-protected:
- cNBTTag * m_Parent;
- eTagType m_Type;
- AString m_Name; // tag name, in UTF-8
-
-public:
- cNBTTag(cNBTTag * a_Parent, eTagType a_Type) : m_Parent(a_Parent), m_Type(a_Type) {}
- cNBTTag(cNBTTag * a_Parent, eTagType a_Type, const AString & a_Name) : m_Parent(a_Parent), m_Type(a_Type), m_Name(a_Name) {}
- virtual ~cNBTTag() {} // Force a virtual destructor
-
- cNBTTag * GetParent(void) const {return m_Parent; }
- eTagType GetType (void) const {return m_Type; }
- const AString & GetName (void) const {return m_Name; }
- void SetName (const AString & a_Name) {m_Name = a_Name; }
-
- static cNBTTag * CreateTag(cNBTTag * a_Parent, eTagType a_Type, const AString & a_Name); // Creates a new instance of a tag specified by iType, uses the correct class
-
- virtual cNBTTag * FindChildByName(const AString & a_Name) const {return NULL; }
- const cNBTTag * FindChildByPath(const AString & a_Path) const;
-} ;
-
-typedef cNBTTag cNBTTree;
-typedef std::vector<cNBTTag *> cNBTTags;
-
-
-
-
-
-#define DECLARE_SIMPLE_TAG(TAG,CTYPE) \
-class cNBT##TAG : \
- public cNBTTag \
-{ \
-public: \
- cNBT##TAG(cNBTTag * a_Parent) : cNBTTag(a_Parent, TAG_##TAG) {} \
- cNBT##TAG(cNBTTag * a_Parent, const AString & a_Name) : cNBTTag(a_Parent, TAG_##TAG, a_Name) {} \
- cNBT##TAG(cNBTTag * a_Parent, const AString & a_Name, const CTYPE & a_Value) : cNBTTag(a_Parent, TAG_##TAG, a_Name), m_Value(a_Value) {} \
- CTYPE m_Value; \
-}
-
-
-
-
-
-DECLARE_SIMPLE_TAG(Byte, char);
-DECLARE_SIMPLE_TAG(Short, Int16);
-DECLARE_SIMPLE_TAG(Int, Int32);
-DECLARE_SIMPLE_TAG(Long, Int64);
-DECLARE_SIMPLE_TAG(Float, float);
-DECLARE_SIMPLE_TAG(Double, double);
-DECLARE_SIMPLE_TAG(ByteArray, AString); // Represent the array as a string for easier manipulation
-DECLARE_SIMPLE_TAG(String, AString);
-
-
-
-
-
-
-class cNBTList :
- public cNBTTag
-{
- cNBTTags m_Children;
- eTagType m_ChildrenType;
-
-public:
- cNBTList(cNBTTag * a_Parent, eTagType a_ChildrenType) : cNBTTag(a_Parent, TAG_List), m_ChildrenType(a_ChildrenType) {}
- cNBTList(cNBTTag * a_Parent, const AString & a_Name, eTagType a_ChildrenType) : cNBTTag(a_Parent, TAG_List, a_Name), m_ChildrenType(a_ChildrenType) {}
- virtual ~cNBTList() {Clear(); }
-
- void Clear (void);
- int Add (cNBTTag * a_Tag);
- cNBTTag * GetChildByIdx (size_t a_Index);
- const cNBTTags & GetChildren (void) const {return m_Children; }
- size_t GetChildrenCount(void) const {return m_Children.size(); }
- virtual cNBTTag * FindChildByName (const AString & a_Name) const override;
-
- int SetChildrenType(eTagType a_Type); // Only valid when list empty
- eTagType GetChildrenType(void) const {return m_ChildrenType; }
-} ;
-
-
-
-
-
-class cNBTCompound :
- public cNBTTag
-{
- cNBTTags m_Children;
-public:
- cNBTCompound(cNBTTag * a_Parent) : cNBTTag(a_Parent, TAG_Compound) {}
- cNBTCompound(cNBTTag * a_Parent, const AString & a_Name) : cNBTTag(a_Parent, TAG_Compound, a_Name) {}
- virtual ~cNBTCompound() {Clear(); }
-
- void Clear (void);
- int Add (cNBTTag * a_Tag);
- cNBTTag * GetChildByIdx (size_t a_Index);
- const cNBTTags & GetChildren (void) const {return m_Children; }
- size_t GetChildrenCount(void) const {return m_Children.size(); }
- virtual cNBTTag * FindChildByName (const AString & a_Name) const override;
-} ;
-
-
-
-
-
-class cNBTIntArray :
- public cNBTTag
-{
- typedef cNBTTag super;
-
- std::vector<int> m_Values;
-
-public:
- cNBTIntArray(cNBTTag * a_Parent) : super(a_Parent, TAG_IntArray) {}
- cNBTIntArray(cNBTTag * a_Parent, const AString & a_Name) : super(a_Parent, TAG_IntArray, a_Name) {}
-
- void Clear(void) {m_Values.clear(); }
- void Add (int a_Value) {m_Values.push_back(a_Value); }
- int Get (int a_Index) const {return m_Values[a_Index]; }
- int Size (void) const {return m_Values.size(); }
- const std::vector<int> & GetValues(void) const {return m_Values; }
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// The parser:
-
-class cNBTParser
-{
- static int ReadTag (const char ** Data, int * Length, cNBTTag::eTagType iType, const AString & a_Name, cNBTTag * iParent, cNBTTag ** oTag); // Helper
-
- static int ReadByte (const char ** Data, int * Length, char & a_Value);
- static int ReadInt16 (const char ** Data, int * Length, Int16 & a_Value);
- static int ReadInt32 (const char ** Data, int * Length, Int32 & a_Value);
- static int ReadInt64 (const char ** Data, int * Length, Int64 & a_Value);
- static int ReadFloat (const char ** Data, int * Length, float & a_Value);
- static int ReadDouble (const char ** Data, int * Length, double & a_Value);
- static int ReadByteArray(const char ** Data, int * Length, AString & a_Value);
- static int ReadString (const char ** Data, int * Length, AString & a_Value);
- static int ReadList (const char ** Data, int * Length, cNBTList * a_List);
- static int ReadCompound (const char ** Data, int * Length, cNBTCompound * a_Compound);
- static int ReadIntArray (const char ** Data, int * Length, cNBTIntArray * a_Array);
-
-public:
-
- /// Returns the parsed tree, or NULL on failure
- static cNBTTree * Parse(const char * a_Data, int a_Length);
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// The serializer:
-
-class cNBTSerializer
-{
- static void WriteTag (AString & a_Out, const cNBTTag * a_Tag);
-
- static void WriteByte (AString & a_Out, const char a_Value);
- static void WriteInt16 (AString & a_Out, const Int16 a_Value);
- static void WriteInt32 (AString & a_Out, const Int32 a_Value);
- static void WriteInt64 (AString & a_Out, const Int64 a_Value);
- static void WriteFloat (AString & a_Out, const float a_Value);
- static void WriteDouble (AString & a_Out, const double a_Value);
- static void WriteByteArray(AString & a_Out, const AString & a_Value);
- static void WriteString (AString & a_Out, const AString & a_Value);
- static void WriteList (AString & a_Out, const cNBTList * a_List);
- static void WriteCompound (AString & a_Out, const cNBTCompound * a_Compound);
- static void WriteIntArray (AString & a_Out, const cNBTIntArray * a_Array);
-
-public:
-
- static void Serialize(const cNBTTree * a_Tree, AString & a_Out);
-} ;
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Dumping the tree (DEBUG-only)
-
-#if (defined(_DEBUG) && defined(_WIN32))
-void DumpTree(const cNBTTree * a_Tree, int a_Level = 0);
-#endif // (_DEBUG && _WIN32)
-
-
-
-
diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp
index 46c20b3d8..97571f521 100644
--- a/source/StringUtils.cpp
+++ b/source/StringUtils.cpp
@@ -38,23 +38,15 @@ AString & AppendVPrintf(AString & str, const char *format, va_list args)
#endif // _MSC_VER
// Allocate a buffer and printf into it:
- std::auto_ptr<char> tmp(new char[len + 1]);
- ASSERT(tmp.get() != NULL); // Why not alloced? Is the length reasonable?
- if (tmp.get() == NULL)
- {
- throw std::bad_alloc();
- }
+ str.resize(len + 1);
+ // HACK: we're accessing AString's internal buffer in a way that is NOT guaranteed to always work. But it works on all STL implementations tested.
+ // I can't think of any other way that is safe, doesn't allocate twice as much space as needed and doesn't use C++11 features like the move constructor
#ifdef _MSC_VER
- if ((len = vsprintf_s(tmp.get(), len + 1, format, args)) != -1)
- {
- str.append(tmp.get(), len);
- }
- ASSERT(len != -1);
+ vsprintf_s((char *)str.data(), len + 1, format, args);
#else // _MSC_VER
- vsnprintf(tmp.get(), len + 1, format, args);
- str.append(tmp.get(), len);
+ vsnprintf((char *)str.data(), len + 1, format, args);
#endif // else _MSC_VER
-
+ str.resize(len);
return str;
}
diff --git a/source/StructGen.cpp b/source/StructGen.cpp
new file mode 100644
index 000000000..a15b0bcb9
--- /dev/null
+++ b/source/StructGen.cpp
@@ -0,0 +1,463 @@
+
+// StructGen.h
+
+#include "Globals.h"
+#include "StructGen.h"
+#include "BlockID.h"
+#include "Trees.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenOreNests configuration:
+
+const int MAX_HEIGHT_COAL = 127;
+const int NUM_NESTS_COAL = 60;
+const int NEST_SIZE_COAL = 10;
+
+const int MAX_HEIGHT_IRON = 70;
+const int NUM_NESTS_IRON = 30;
+const int NEST_SIZE_IRON = 6;
+
+const int MAX_HEIGHT_REDSTONE = 40;
+const int NUM_NESTS_REDSTONE = 10;
+const int NEST_SIZE_REDSTONE = 6;
+
+const int MAX_HEIGHT_GOLD = 35;
+const int NUM_NESTS_GOLD = 6;
+const int NEST_SIZE_GOLD = 6;
+
+const int MAX_HEIGHT_DIAMOND = 16;
+const int NUM_NESTS_DIAMOND = 6;
+const int NEST_SIZE_DIAMOND = 5;
+
+const int MAX_HEIGHT_LAPIS = 30;
+const int NUM_NESTS_LAPIS = 6;
+const int NEST_SIZE_LAPIS = 5;
+
+
+
+
+
+template <typename T> T Clamp(T a_Value, T a_Min, T a_Max)
+{
+ return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenTrees:
+
+void cStructGenTrees::GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMetas, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+)
+{
+ cChunkDef::BlockTypes WorkerBlockTypes;
+ cChunkDef::BlockNibbles WorkerBlockMeta;
+ cChunkDef::HeightMap WorkerHeight;
+
+ cEntityList Entities;
+ cBlockEntityList BlockEntities;
+
+ // Generate trees:
+ for (int x = 0; x <= 2; x++)
+ {
+ int BaseX = a_ChunkX + x - 1;
+ for (int z = 0; z <= 2; z++)
+ {
+ int BaseZ = a_ChunkZ + z - 1;
+ sSetBlockVector Outside;
+
+ cChunkDef::BlockTypes * BlT;
+ cChunkDef::BlockNibbles * BlM;
+ cChunkDef::HeightMap * Hei;
+
+ if ((x != 1) || (z != 1))
+ {
+ BlT = &WorkerBlockTypes;
+ BlM = &WorkerBlockMeta;
+ Hei = &WorkerHeight;
+
+ m_HeightGen->GenHeightMap (BaseX, BaseZ, *Hei);
+ m_CompositionGen->ComposeTerrain(BaseX, BaseZ, *BlT, *BlM, *Hei, Entities, BlockEntities);
+ // TODO: Free the entity lists
+ }
+ else
+ {
+ BlT = &a_BlockTypes;
+ BlM = &a_BlockMetas;
+ Hei = &a_HeightMap;
+ }
+
+ cChunkDef::BiomeMap Biomes;
+ m_BiomeGen->GenBiomes(BaseX, BaseZ, Biomes);
+ int NumTrees = GetNumTrees(BaseX, BaseZ, Biomes);
+
+ for (int i = 0; i < NumTrees; i++)
+ {
+ GenerateSingleTree(BaseX, BaseZ, i, *BlT, *BlM, *Hei, Biomes, Outside);
+ }
+
+ // Integrate blocks in Outside into chunk:
+ for (sSetBlockVector::const_iterator itr = Outside.begin(); itr != Outside.end(); ++itr)
+ {
+ if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
+ {
+ continue;
+ }
+ switch (cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z))
+ {
+ CASE_TREE_OVERWRITTEN_BLOCKS:
+ {
+ cChunkDef::SetBlock (a_BlockTypes, itr->x, itr->y, itr->z, itr->BlockType);
+ cChunkDef::SetNibble(a_BlockMetas, itr->x, itr->y, itr->z, itr->BlockMeta);
+ break;
+ }
+ } // switch (GetBlock())
+ } // for itr - Outside[]
+ } // for z
+ } // for x
+
+ // Update the heightmap:
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ for (int y = cChunkDef::Height - 1; y >= 0; y--)
+ {
+ if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) != E_BLOCK_AIR)
+ {
+ cChunkDef::SetHeight(a_HeightMap, x, z, y);
+ break;
+ }
+ } // for y
+ } // for z
+ } // for x
+}
+
+
+
+
+
+void cStructGenTrees::GenerateSingleTree(
+ int a_ChunkX, int a_ChunkZ, int a_Seq,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::HeightMap & a_Height,
+ const cChunkDef::BiomeMap & a_Biomes,
+ sSetBlockVector & a_Blocks
+)
+{
+ int x = m_Noise.IntNoise3DInt(a_ChunkX + a_ChunkZ, a_ChunkZ, a_Seq) % cChunkDef::Width;
+ int z = m_Noise.IntNoise3DInt(a_ChunkX - a_ChunkZ, a_Seq, a_ChunkZ) % cChunkDef::Width;
+
+ int Height = a_Height[x + cChunkDef::Width * z];
+
+ if ((Height <= 0) || (Height > 240))
+ {
+ return;
+ }
+
+ // Check the block underneath the tree:
+ BLOCKTYPE TopBlock = cChunkDef::GetBlock(a_BlockTypes, x, Height, z);
+ if ((TopBlock != E_BLOCK_DIRT) && (TopBlock != E_BLOCK_GRASS) && (TopBlock != E_BLOCK_SOIL))
+ {
+ return;
+ }
+
+ sSetBlockVector TreeBlocks;
+ GetTreeImageByBiome(a_ChunkX * cChunkDef::Width + x, Height + 1, a_ChunkZ * cChunkDef::Width + z, m_Noise, a_Seq, a_Biomes[x + cChunkDef::Width * z], TreeBlocks);
+
+ // Check if the generated image fits the terrain:
+ for (sSetBlockVector::const_iterator itr = TreeBlocks.begin(); itr != TreeBlocks.end(); ++itr)
+ {
+ if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ) || (itr->BlockType != E_BLOCK_LOG))
+ {
+ // Outside the chunk, or not a log (we don't check non-logs)
+ continue;
+ }
+
+ BLOCKTYPE Block = cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z);
+ switch (Block)
+ {
+ CASE_TREE_ALLOWED_BLOCKS:
+ {
+ break;
+ }
+ default:
+ {
+ // There's something in the way, abort this tree altogether
+ return;
+ }
+ }
+ }
+
+ // Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
+ for (sSetBlockVector::const_iterator itr = TreeBlocks.begin(); itr != TreeBlocks.end(); ++itr)
+ {
+ if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ))
+ {
+ // Inside this chunk, integrate into a_BlockTypes:
+ switch (cChunkDef::GetBlock(a_BlockTypes, itr->x, itr->y, itr->z))
+ {
+ CASE_TREE_OVERWRITTEN_BLOCKS:
+ {
+ cChunkDef::SetBlock (a_BlockTypes, itr->x, itr->y, itr->z, itr->BlockType);
+ cChunkDef::SetNibble(a_BlockMetas, itr->x, itr->y, itr->z, itr->BlockMeta);
+ break;
+ }
+ } // switch (GetBlock())
+ continue;
+ }
+
+ // Outside the chunk, push into a_Blocks; check if already present there:
+ bool Found = false;
+ for (sSetBlockVector::iterator itrB = a_Blocks.begin(); itrB != a_Blocks.end(); ++itrB)
+ {
+ if (
+ (itr->ChunkX == itrB->ChunkX) &&
+ (itr->ChunkZ == itrB->ChunkZ) &&
+ (itr->x == itrB->x) &&
+ (itr->y == itrB->y) &&
+ (itr->z == itrB->z)
+ )
+ {
+ // Same coords already found in a_Blocks, overwrite with wood, if requested:
+ if (itr->BlockType == E_BLOCK_LOG)
+ {
+ itrB->BlockType = itr->BlockType;
+ itrB->BlockMeta = itr->BlockMeta;
+ }
+ Found = true;
+ break;
+ }
+ } // for itrB - a_Blocks[]
+ if (!Found)
+ {
+ a_Blocks.push_back(*itr);
+ }
+ }
+}
+
+
+
+
+
+int cStructGenTrees::GetNumTrees(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BiomeMap & a_Biomes
+)
+{
+ int NumTrees = 0;
+ for (int x = 0; x < cChunkDef::Width; x++) for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Add = 0;
+ switch (a_Biomes[x + cChunkDef::Width * z])
+ {
+ case biPlains: Add = 1; break;
+ case biExtremeHills: Add = 3; break;
+ case biForest: Add = 30; break;
+ case biTaiga: Add = 30; break;
+ case biSwampland: Add = 8; break;
+ case biIcePlains: Add = 1; break;
+ case biIceMountains: Add = 1; break;
+ case biMushroomIsland: Add = 3; break;
+ case biMushroomShore: Add = 3; break;
+ case biForestHills: Add = 20; break;
+ case biTaigaHills: Add = 20; break;
+ case biExtremeHillsEdge: Add = 5; break;
+ case biJungle: Add = 120; break;
+ case biJungleHills: Add = 90; break;
+ }
+ NumTrees += Add;
+ }
+ return NumTrees / 1024;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenMarbleCaves:
+
+static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
+{
+ static const float PI_2 = 1.57079633f;
+ float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f )) * 4;
+
+ oct1 = oct1 * oct1 * oct1;
+ if (oct1 < 0.f) oct1 = PI_2;
+ if (oct1 > PI_2) oct1 = PI_2;
+
+ return oct1;
+}
+
+
+
+
+
+void cStructGenMarbleCaves::GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+)
+{
+ cNoise Noise(m_Seed);
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
+
+ int Top = cChunkDef::GetHeight(a_HeightMap, x, z);
+ for (int y = 1; y < Top; ++y )
+ {
+ if (cChunkDef::GetBlock(a_BlockTypes, x, y, z) != E_BLOCK_STONE)
+ {
+ continue;
+ }
+
+ const float yy = (float)y;
+ const float WaveNoise = 1;
+ if (cosf(GetMarbleNoise(xx, yy * 0.5f, zz, Noise)) * fabs(cosf(yy * 0.2f + WaveNoise * 2) * 0.75f + WaveNoise) > 0.0005f)
+ {
+ if (y > 4)
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_AIR);
+ }
+ else
+ {
+ cChunkDef::SetBlock(a_BlockTypes, x, y, z, E_BLOCK_STATIONARY_LAVA);
+ }
+ }
+ } // for y
+ } // for x
+ } // for z
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cStructGenOreNests:
+
+void cStructGenOreNests::GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+)
+{
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, a_BlockTypes, 1);
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, a_BlockTypes, 2);
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, a_BlockTypes, 3);
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, a_BlockTypes, 4);
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, a_BlockTypes, 5);
+ GenerateOre(a_ChunkX, a_ChunkZ, E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, a_BlockTypes, 6);
+}
+
+
+
+
+
+void cStructGenOreNests::GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq)
+{
+ // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
+ // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
+ // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
+
+ for (int i = 0; i < a_NumNests; i++)
+ {
+ int rnd = m_Noise.IntNoise3DInt(a_ChunkX + i, a_Seq, a_ChunkZ + 64 * i) / 8;
+ int BaseX = rnd % cChunkDef::Width;
+ rnd /= cChunkDef::Width;
+ int BaseZ = rnd % cChunkDef::Width;
+ rnd /= cChunkDef::Width;
+ int BaseY = rnd % a_MaxHeight;
+ rnd /= a_MaxHeight;
+ int NestSize = a_NestSize + (rnd % (a_NestSize / 4)); // The actual nest size may be up to 1/4 larger
+ int Num = 0;
+ while (Num < NestSize)
+ {
+ // Put a cuboid around [BaseX, BaseY, BaseZ]
+ int rnd = m_Noise.IntNoise3DInt(a_ChunkX + 64 * i, 2 * a_Seq + Num, a_ChunkZ + 32 * i) / 8;
+ int xsize = rnd % 2;
+ int ysize = (rnd / 4) % 2;
+ int zsize = (rnd / 16) % 2;
+ rnd >>= 8;
+ for (int x = xsize; x >= 0; --x)
+ {
+ int BlockX = BaseX + x;
+ if ((BlockX < 0) || (BlockX >= cChunkDef::Width))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+ for (int y = ysize; y >= 0; --y)
+ {
+ int BlockY = BaseY + y;
+ if ((BlockY < 0) || (BlockY >= cChunkDef::Height))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+ for (int z = zsize; z >= 0; --z)
+ {
+ int BlockZ = BaseZ + z;
+ if ((BlockZ < 0) || (BlockZ >= cChunkDef::Width))
+ {
+ Num++; // So that the cycle finishes even if the base coords wander away from the chunk
+ continue;
+ }
+
+ int Index = cChunkDef::MakeIndexNoCheck(BlockX, BlockY, BlockZ);
+ if (a_BlockTypes[Index] == E_BLOCK_STONE)
+ {
+ a_BlockTypes[Index] = a_OreType;
+ }
+ Num++;
+ } // for z
+ } // for y
+ } // for x
+
+ // Move the base to a neighbor voxel
+ switch (rnd % 4)
+ {
+ case 0: BaseX--; break;
+ case 1: BaseX++; break;
+ }
+ switch ((rnd >> 3) % 4)
+ {
+ case 0: BaseY--; break;
+ case 1: BaseY++; break;
+ }
+ switch ((rnd >> 6) % 4)
+ {
+ case 0: BaseZ--; break;
+ case 1: BaseZ++; break;
+ }
+ } // while (Num < NumBlocks)
+ } // for i - NumNests
+}
+
+
+
+
diff --git a/source/StructGen.h b/source/StructGen.h
new file mode 100644
index 000000000..3f5c2e2a2
--- /dev/null
+++ b/source/StructGen.h
@@ -0,0 +1,122 @@
+
+// StructGen.h
+
+/* Interfaces to the various structure generators:
+ - cStructGenTrees
+ - cStructGenMarbleCaves
+ - cStructGenOres
+*/
+
+
+
+
+
+#pragma once
+
+#include "cChunkGenerator.h"
+#include "cNoise.h"
+
+
+
+
+
+class cStructGenTrees :
+ public cStructureGen
+{
+public:
+ cStructGenTrees(int a_Seed, cBiomeGen * a_BiomeGen, cTerrainHeightGen * a_HeightGen, cTerrainCompositionGen * a_CompositionGen) :
+ m_Seed(a_Seed),
+ m_Noise(a_Seed),
+ m_BiomeGen(a_BiomeGen),
+ m_HeightGen(a_HeightGen),
+ m_CompositionGen(a_CompositionGen)
+ {}
+
+protected:
+
+ int m_Seed;
+ cNoise m_Noise;
+ cBiomeGen * m_BiomeGen;
+ cTerrainHeightGen * m_HeightGen;
+ cTerrainCompositionGen * m_CompositionGen;
+
+ void GenerateSingleTree(
+ int a_ChunkX, int a_ChunkZ, int a_Seq,
+ cChunkDef::BlockTypes & a_BlockTypes,
+ cChunkDef::BlockNibbles & a_BlockMetas,
+ const cChunkDef::HeightMap & a_Height,
+ const cChunkDef::BiomeMap & a_Biomes,
+ sSetBlockVector & a_Blocks
+ ) ;
+
+ int GetNumTrees(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BiomeMap & a_Biomes
+ );
+
+ // cStructureGen override:
+ virtual void GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMetas, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+} ;
+
+
+
+
+
+class cStructGenMarbleCaves :
+ public cStructureGen
+{
+public:
+ cStructGenMarbleCaves(int a_Seed) : m_Seed(a_Seed) {}
+
+protected:
+
+ int m_Seed;
+
+ // cStructureGen override:
+ virtual void GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+} ;
+
+
+
+
+
+class cStructGenOreNests :
+ public cStructureGen
+{
+public:
+ cStructGenOreNests(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {}
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+
+ // cStructureGen override:
+ virtual void GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) override;
+
+ void GenerateOre(int a_ChunkX, int a_ChunkZ, BLOCKTYPE a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, cChunkDef::BlockTypes & a_BlockTypes, int a_Seq);
+} ;
+
+
+
+
diff --git a/source/Trees.cpp b/source/Trees.cpp
new file mode 100644
index 000000000..1259f1edd
--- /dev/null
+++ b/source/Trees.cpp
@@ -0,0 +1,546 @@
+
+// Trees.cpp
+
+// Implements helper functions used for generating trees
+
+#include "Globals.h"
+#include "Trees.h"
+#include "BlockID.h"
+
+
+
+
+
+#ifndef min
+ #define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+
+
+
+
+typedef struct
+{
+ int x, z;
+} sCoords;
+
+typedef struct
+{
+ int x, z;
+ NIBBLETYPE Meta;
+} sMetaCoords;
+
+static const sCoords Corners[] =
+{
+ {-1, -1},
+ {-1, 1},
+ {1, -1},
+ {1, 1},
+} ;
+
+static const sCoords BigO1[] =
+{
+ {0, -1},
+ {-1, 0}, {1, 0},
+ {0, 1},
+} ;
+
+static const sCoords BigO2[] =
+{
+ {-1, -2}, {0, -2}, {1, -2},
+ {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1},
+ {-2, 0}, {-1, 0}, {1, 0}, {2, 0},
+ {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1},
+ {-1, 2}, {0, 2}, {1, 2},
+} ;
+
+static const sCoords BigO3[] =
+{
+ {-2, -3}, {-1, -3}, {0, -3}, {1, -3}, {2, -3},
+ {-3, -2}, {-2, -2}, {-1, -2}, {0, -2}, {1, -2}, {2, -2}, {3, -2},
+ {-3, -1}, {-2, -1}, {-1, -1}, {0, -1}, {1, -1}, {2, -1}, {3, -1},
+ {-3, 0}, {-2, 0}, {-1, 0}, {1, 0}, {2, 0}, {3, 0},
+ {-3, 1}, {-2, 1}, {-1, 1}, {0, 1}, {1, 1}, {2, 1}, {3, 1},
+ {-3, 2}, {-2, 2}, {-1, 2}, {0, 2}, {1, 2}, {2, 2}, {3, 2},
+ {-2, 3}, {-1, 3}, {0, 3}, {1, 3}, {2, 3},
+} ;
+
+typedef struct
+{
+ const sCoords * Coords;
+ size_t Count;
+} sCoordsArr;
+
+static const sCoordsArr BigOs[] =
+{
+ {BigO1, ARRAYCOUNT(BigO1)},
+ {BigO2, ARRAYCOUNT(BigO2)},
+ {BigO3, ARRAYCOUNT(BigO3)},
+} ;
+
+
+
+
+
+/// Pushes a specified layer of blocks of the same type around (x, h, z) into a_Blocks
+inline void PushCoordBlocks(int a_BlockX, int a_Height, int a_BlockZ, sSetBlockVector & a_Blocks, const sCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
+{
+ for (size_t i = 0; i < a_NumCoords; i++)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX + a_Coords[i].x, a_Height, a_BlockZ + a_Coords[i].z, a_BlockType, a_Meta));
+ }
+}
+
+
+
+
+inline void PushCornerBlocks(int a_BlockX, int a_Height, int a_BlockZ, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, int a_CornersDist, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta)
+{
+ for (size_t i = 0; i < ARRAYCOUNT(Corners); i++)
+ {
+ int x = a_BlockX + Corners[i].x;
+ int z = a_BlockZ + Corners[i].z;
+ if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height, z + 64 * a_Seq) <= a_Chance)
+ {
+ a_Blocks.push_back(sSetBlock(x, a_Height, z, a_BlockType, a_Meta));
+ }
+ } // for i - Corners[]
+}
+
+
+
+
+
+inline void PushSomeColumns(int a_BlockX, int a_Height, int a_BlockZ, int a_ColumnHeight, int a_Seq, cNoise & a_Noise, int a_Chance, sSetBlockVector & a_Blocks, const sMetaCoords * a_Coords, size_t a_NumCoords, BLOCKTYPE a_BlockType)
+{
+ for (size_t i = 0; i < a_NumCoords; i++)
+ {
+ int x = a_BlockX + a_Coords[i].x;
+ int z = a_BlockZ + a_Coords[i].z;
+ if (a_Noise.IntNoise3DInt(x + 64 * a_Seq, a_Height + i, z + 64 * a_Seq) <= a_Chance)
+ {
+ for (int j = 0; j < a_ColumnHeight; j++)
+ {
+ a_Blocks.push_back(sSetBlock(x, a_Height - j, z, a_BlockType, a_Coords[i].Meta));
+ }
+ }
+ } // for i - a_Coords[]
+}
+
+
+
+
+
+void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_Blocks)
+{
+ switch (a_Biome)
+ {
+ case biPlains:
+ case biExtremeHills:
+ case biExtremeHillsEdge:
+ case biForest:
+ case biMushroomIsland:
+ case biMushroomShore:
+ case biForestHills:
+ {
+ // Apple or birch trees:
+ if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff)
+ {
+ GetAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ else
+ {
+ GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ break;
+ }
+
+ case biTaiga:
+ case biIcePlains:
+ case biIceMountains:
+ case biTaigaHills:
+ {
+ // Conifers
+ GetConiferTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ break;
+ }
+
+ case biSwampland:
+ {
+ // Swamp trees:
+ GetSwampTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ break;
+ }
+
+ case biJungle:
+ case biJungleHills:
+ {
+ // Apple bushes, jungle trees
+ if (a_Noise.IntNoise3DInt(a_BlockX, a_BlockY + 16 * a_Seq, a_BlockZ + 16 * a_Seq) < 0x5fffffff)
+ {
+ GetAppleBushImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ else
+ {
+ GetJungleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ }
+ }
+}
+
+
+
+
+
+void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ if (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) < 0x60000000)
+ {
+ GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ else
+ {
+ GetLargeAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+}
+
+
+
+
+
+void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ /* Small apple tree has:
+ - a top plus (no log)
+ - optional BigO1 + random corners (log)
+ - 2 layers of BigO2 + random corners (log)
+ - 1 to 3 blocks of trunk
+ */
+
+ int Random = a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) >> 3;
+
+ // Pre-alloc so that we don't realloc too often later:
+ a_Blocks.reserve(ARRAYCOUNT(BigO2) * 2 + ARRAYCOUNT(BigO1) + ARRAYCOUNT(Corners) * 3 + 3 + 5);
+
+ int Heights[] = {1, 2, 2, 3} ;
+ int Height = 1 + Heights[Random & 3];
+ Random >>= 2;
+
+ // Trunk:
+ for (int i = 0; i < Height; i++)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+ int Hei = a_BlockY + Height;
+
+ // 2 BigO2 + corners layers:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x5000000 - i * 0x10000000, a_Blocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ Hei++;
+ } // for i - 2*
+
+ // Optional BigO1 + corners layer:
+ if ((Random & 1) == 0)
+ {
+ PushCoordBlocks (a_BlockX, Hei, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, Hei, a_BlockZ, a_Seq, a_Noise, 0x6000000, a_Blocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ Hei++;
+ }
+
+ // Top plus:
+ PushCoordBlocks(a_BlockX, Hei, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+}
+
+
+
+
+
+void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // TODO
+}
+
+
+
+
+
+void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ int Height = 5 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3);
+
+ // Prealloc, so that we don't realloc too often later:
+ a_Blocks.reserve(Height + 80);
+
+ // The entire trunk, out of logs:
+ for (int i = Height - 1; i >= 0; --i)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH));
+ }
+ int h = a_BlockY + Height;
+
+ // Top layer - just the Plus:
+ PushCoordBlocks(a_BlockX, h, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ a_Blocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer
+ h--;
+
+ // Second layer - log, Plus and maybe Corners:
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_Blocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+
+ // Third and fourth layers - BigO2 and maybe 2*Corners:
+ for (int Row = 0; Row < 2; Row++)
+ {
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_Blocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+ } // for Row - 2*
+}
+
+
+
+
+
+void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // Half chance for a spruce, half for a pine:
+ if (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) < 0x40000000)
+ {
+ GetSpruceTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+ else
+ {
+ GetPineTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_Blocks);
+ }
+}
+
+
+
+
+
+void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // Spruces have a top section with layer sizes of (0, 1, 0) or only (1, 0),
+ // then 1 - 3 sections of ascending sizes (1, 2) [most often], (1, 3) or (1, 2, 3)
+ // and an optional bottom section of size 1, followed by 1 - 3 clear trunk blocks
+
+ // We'll use bits from this number as partial random numbers; but the noise function has mod8 irregularities
+ // (each of the mod8 remainders has a very different chance of occurrence) - that's why we divide by 8
+ int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY + 32 * a_Seq, a_BlockZ) / 8;
+
+ // Prealloc, so that we don't realloc too often later:
+ a_Blocks.reserve(180);
+
+ // Clear trunk blocks:
+ static const int sHeights[] = {1, 2, 2, 3};
+ int Height = sHeights[MyRandom & 3];
+ MyRandom >>= 2;
+ for (int i = 0; i < Height; i++)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ }
+ Height += a_BlockY;
+
+ // Optional size-1 bottom leaves layer:
+ if ((MyRandom & 1) == 0)
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height++;
+ }
+ MyRandom >>= 1;
+
+ // 1 to 3 sections of leaves layers:
+ static const int sNumSections[] = {1, 2, 2, 3};
+ int NumSections = sNumSections[MyRandom & 3];
+ MyRandom >>= 2;
+ for (int i = 0; i < NumSections; i++)
+ {
+ switch (MyRandom & 3) // SectionType; (1, 2) twice as often as the other two
+ {
+ case 0:
+ case 1:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 2;
+ break;
+ }
+ case 2:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_Blocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 2;
+ break;
+ }
+ case 3:
+ {
+ PushCoordBlocks(a_BlockX, Height, a_BlockZ, a_Blocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 1, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ PushCoordBlocks(a_BlockX, Height + 2, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ Height += 3;
+ break;
+ }
+ } // switch (SectionType)
+ MyRandom >>= 2;
+ } // for i - Sections
+
+ if ((MyRandom & 1) == 0)
+ {
+ // (0, 1, 0) top:
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 2, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ }
+ else
+ {
+ // (1, 0) top:
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ PushCoordBlocks (a_BlockX, Height + 1, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ a_Blocks.push_back(sSetBlock(a_BlockX, Height + 1, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ }
+}
+
+
+
+
+
+void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // Tall, little leaves on top. The top leaves are arranged in a shape of two cones joined by their bases.
+ // There can be one or two layers representing the cone bases (SameSizeMax)
+
+ int MyRandom = a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8;
+ int TrunkHeight = 8 + (MyRandom % 3);
+ int SameSizeMax = ((MyRandom & 8) == 0) ? 1 : 0;
+ MyRandom >>= 3;
+ int NumLeavesLayers = 2 + (MyRandom % 3); // Number of layers that have leaves in them
+ if (NumLeavesLayers == 2)
+ {
+ SameSizeMax = 0;
+ }
+
+ // Pre-allocate the vector:
+ a_Blocks.reserve(TrunkHeight + NumLeavesLayers * 25);
+
+ // The entire trunk, out of logs:
+ for (int i = TrunkHeight; i >= 0; --i)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_CONIFER));
+ }
+ int h = a_BlockY + TrunkHeight + 2;
+
+ // Top layer - just a single leaves block:
+ a_Blocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+ h--;
+
+ // One more layer is above the trunk, push the central leaves:
+ a_Blocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER));
+
+ // Layers expanding in size, then collapsing again:
+ // LOGD("Generating %d layers of pine leaves, SameSizeMax = %d", NumLeavesLayers, SameSizeMax);
+ for (int i = 0; i < NumLeavesLayers; ++i)
+ {
+ int LayerSize = min(i, NumLeavesLayers - i + SameSizeMax - 1);
+ // LOGD("LayerSize %d: %d", i, LayerSize);
+ if (LayerSize < 0)
+ {
+ break;
+ }
+ ASSERT(LayerSize < ARRAYCOUNT(BigOs));
+ PushCoordBlocks(a_BlockX, h, a_BlockZ, a_Blocks, BigOs[LayerSize].Coords, BigOs[LayerSize].Count, E_BLOCK_LEAVES, E_META_LEAVES_CONIFER);
+ h--;
+ }
+}
+
+
+
+
+
+void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // Vines are around the BigO3, but not in the corners; need proper meta for direction
+ static const sMetaCoords Vines[] =
+ {
+ {-2, -4, 1}, {-1, -4, 1}, {0, -4, 1}, {1, -4, 1}, {2, -4, 1}, // North face
+ {-2, 4, 4}, {-1, 4, 4}, {0, 4, 4}, {1, 4, 4}, {2, 4, 4}, // South face
+ {4, -2, 2}, {4, -1, 2}, {4, 0, 2}, {4, 1, 2}, {4, 2, 2}, // East face
+ {-4, -2, 8}, {-4, -1, 8}, {-4, 0, 8}, {-4, 1, 8}, {-4, 2, 8}, // West face
+ } ;
+
+ int Height = 3 + (a_Noise.IntNoise3DInt(a_BlockX + 32 * a_Seq, a_BlockY, a_BlockZ + 32 * a_Seq) / 8) % 3;
+
+ a_Blocks.reserve(2 * ARRAYCOUNT(BigO2) + 2 * ARRAYCOUNT(BigO3) + Height * ARRAYCOUNT(Vines) + 20);
+
+ for (int i = 0; i < Height; i++)
+ {
+ a_Blocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_APPLE));
+ }
+ int hei = a_BlockY + Height - 2;
+
+ // Put vines around the lowermost leaves layer:
+ PushSomeColumns(a_BlockX, hei, a_BlockZ, Height, a_Seq, a_Noise, 0x3fffffff, a_Blocks, Vines, ARRAYCOUNT(Vines), E_BLOCK_VINES);
+
+ // The lower two leaves layers are BigO3 with log in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_Blocks, BigO3, ARRAYCOUNT(BigO3), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_Blocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+ } // for i - 2*
+
+ // The upper two leaves layers are BigO2 with leaves in the middle and possibly corners:
+ for (int i = 0; i < 2; i++)
+ {
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ PushCornerBlocks(a_BlockX, hei, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_Blocks, 3, E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ a_Blocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ hei++;
+ } // for i - 2*
+}
+
+
+
+
+
+void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ a_Blocks.reserve(3 + ARRAYCOUNT(BigO2) + ARRAYCOUNT(BigO1));
+
+ int hei = a_BlockY;
+ a_Blocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LOG, E_META_LOG_JUNGLE));
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_Blocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+
+ a_Blocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+ PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_Blocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
+ hei++;
+
+ a_Blocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
+}
+
+
+
+
+
+void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks)
+{
+ // TODO
+}
+
+
+
+
diff --git a/source/Trees.h b/source/Trees.h
new file mode 100644
index 000000000..84f1c51ea
--- /dev/null
+++ b/source/Trees.h
@@ -0,0 +1,85 @@
+
+// Trees.h
+
+// Interfaces to helper functions used for generating trees
+
+/*
+Note that all of these functions must generate the same tree image for the same input (x, y, z, seq)
+ - cStructGenTrees depends on this
+To generate a random image for the (x, y, z) coords, pass an arbitrary value as (seq).
+*/
+
+
+
+
+
+#pragma once
+
+#include "ChunkDef.h"
+#include "cNoise.h"
+
+
+
+
+
+// Blocks that don't block tree growth:
+#define CASE_TREE_ALLOWED_BLOCKS \
+ case E_BLOCK_AIR: \
+ case E_BLOCK_LEAVES: \
+ case E_BLOCK_SNOW: \
+ case E_BLOCK_TALL_GRASS: \
+ case E_BLOCK_DEAD_BUSH: \
+ case E_BLOCK_SAPLING: \
+ case E_BLOCK_VINES
+
+// Blocks that a tree may overwrite when growing:
+#define CASE_TREE_OVERWRITTEN_BLOCKS \
+ case E_BLOCK_AIR: \
+ case E_BLOCK_LEAVES: \
+ case E_BLOCK_SNOW: \
+ case E_BLOCK_TALL_GRASS: \
+ case E_BLOCK_DEAD_BUSH: \
+ case E_BLOCK_SAPLING: \
+ case E_BLOCK_VINES
+
+
+
+
+
+/// Generates an image of a tree at the specified coords (lowest trunk block) in the specified biome
+void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, EMCSBiome a_Biome, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random apple tree
+void GetAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a small (nonbranching) apple tree
+void GetSmallAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a large (branching) apple tree
+void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random birch tree
+void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random conifer tree
+void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random spruce (short conifer, two layers of leaves)
+void GetSpruceTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random pine (tall conifer, little leaves at top)
+void GetPineTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random swampland tree
+void GetSwampTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random apple bush (for jungles)
+void GetAppleBushImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+/// Generates an image of a random jungle tree
+void GetJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_Blocks);
+
+
+
+
+
diff --git a/source/WGFlat.cpp b/source/WGFlat.cpp
deleted file mode 100644
index cb1392a77..000000000
--- a/source/WGFlat.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-
-// WGFlat.cpp
-
-// Implements the cWGFlat class representing the flat world generator
-
-#include "Globals.h"
-#include "WGFlat.h"
-#include "../iniFile/iniFile.h"
-#include "cWorld.h"
-
-
-
-
-
-cWGFlat::cWGFlat(cWorld * a_World) :
- super(a_World)
-{
- // Load the settings from the INI file:
- cIniFile INI(a_World->GetIniFileName());
- INI.ReadFile();
- m_Height = INI.GetValueI("flat", "height", 5);
- if (m_Height < 1)
- {
- m_Height = 1;
- }
- if (m_Height > 250)
- {
- m_Height = 250;
- }
-}
-
-
-
-
-
-void cWGFlat::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
-{
- int SliceSize = cChunkDef::Width * cChunkDef::Width;
- memset(a_BlockData, E_BLOCK_BEDROCK, SliceSize);
- switch (m_Height)
- {
- case 1:
- {
- // Just the bedrock layer
- break;
- }
- case 2:
- {
- // Bedrock + 1 dirt layer:
- memset(a_BlockData + SliceSize, E_BLOCK_GRASS, SliceSize);
- break;
- }
- case 3:
- {
- // Bedrock + 2 dirt layers:
- memset(a_BlockData + SliceSize, E_BLOCK_DIRT, SliceSize);
- memset(a_BlockData + 2 * SliceSize, E_BLOCK_GRASS, SliceSize);
- break;
- }
- case 4:
- {
- // Bedrock + 3 dirt layers:
- memset(a_BlockData + SliceSize, E_BLOCK_DIRT, 2 * SliceSize);
- memset(a_BlockData + 3 * SliceSize, E_BLOCK_GRASS, SliceSize);
- break;
- }
- default:
- {
- // Bedrock + stone layers + 3 dirt layers:
- memset(a_BlockData + SliceSize, E_BLOCK_STONE, SliceSize * (m_Height - 4));
- memset(a_BlockData + SliceSize * (m_Height - 3), E_BLOCK_DIRT, SliceSize * 2);
- memset(a_BlockData + SliceSize * (m_Height - 1), E_BLOCK_GRASS, SliceSize);
- break;
- }
- }
- memset(a_BlockData + SliceSize * m_Height, E_BLOCK_AIR, cChunkDef::NumBlocks - SliceSize * m_Height);
-
- SliceSize /= 2; // Nibbles from now on
- BLOCKTYPE * Meta = a_BlockData + cChunkDef::NumBlocks;
- memset(Meta, 0, cChunkDef::NumBlocks / 2);
-
- BLOCKTYPE * SkyLight = Meta + cChunkDef::NumBlocks / 2;
- memset(SkyLight, 0, m_Height * SliceSize);
- memset(SkyLight + m_Height * SliceSize, 0xff, cChunkDef::NumBlocks / 2 - m_Height * SliceSize);
-
- BLOCKTYPE * BlockLight = SkyLight + cChunkDef::NumBlocks / 2;
- memset(BlockLight, 0, cChunkDef::NumBlocks / 2);
-}
-
-
-
-
-
-void cWGFlat::PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-{
- // Nothing needed yet, just don't call the parent
-}
-
-
-
-
diff --git a/source/WGFlat.h b/source/WGFlat.h
deleted file mode 100644
index a1f2d2244..000000000
--- a/source/WGFlat.h
+++ /dev/null
@@ -1,35 +0,0 @@
-
-// WGFlat.h
-
-// Interfaces to the cWGFlat class representing the flat world generator
-
-
-
-
-
-#pragma once
-
-#include "cWorldGenerator.h"
-
-
-
-
-
-class cWGFlat :
- public cWorldGenerator
-{
- typedef cWorldGenerator super;
-
-protected:
- int m_Height;
-
- virtual void GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities) override;
- virtual void PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) override;
-
-public:
- cWGFlat(cWorld * a_World);
-} ;
-
-
-
-
diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp
index fae1556ea..abb31d050 100644
--- a/source/WSSAnvil.cpp
+++ b/source/WSSAnvil.cpp
@@ -7,7 +7,6 @@
#include "WSSAnvil.h"
#include "cWorld.h"
#include "zlib.h"
-#include "NBT.h"
#include "BlockID.h"
#include "cChestEntity.h"
#include "cItem.h"
@@ -26,7 +25,7 @@ Since only the header is actually in the memory, this number can be high, but st
*/
#define MAX_MCA_FILES 32
-/// The maximum size of an inflated chunk
+/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities
#define CHUNK_INFLATE_MAX 256 KiB
@@ -45,7 +44,8 @@ public:
m_Writer(a_Writer),
m_IsTagOpen(false),
m_HasHadEntity(false),
- m_HasHadBlockEntity(false)
+ m_HasHadBlockEntity(false),
+ m_IsLightValid(false)
{
}
@@ -59,6 +59,9 @@ public:
}
}
+
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
protected:
/* From cChunkDataSeparateCollector we inherit:
@@ -75,6 +78,7 @@ protected:
bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
bool m_HasHadEntity; // True if any Entity has already been received and processed
bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
+ bool m_IsLightValid; // True if the chunk lighting is valid
void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
@@ -89,10 +93,10 @@ protected:
void AddItem(cItem * a_Item, int a_Slot)
{
m_Writer.BeginCompound("");
- m_Writer.AddShort("id", a_Item->m_ItemID);
+ m_Writer.AddShort("id", (short)(a_Item->m_ItemID));
m_Writer.AddShort("Damage", a_Item->m_ItemHealth);
m_Writer.AddByte ("Count", a_Item->m_ItemCount);
- m_Writer.AddByte ("Slot", a_Slot);
+ m_Writer.AddByte ("Slot", (unsigned char)a_Slot);
m_Writer.EndCompound();
}
@@ -116,6 +120,13 @@ protected:
}
+ virtual bool LightIsValid(bool a_IsLightValid) override
+ {
+ m_IsLightValid = a_IsLightValid;
+ return a_IsLightValid; // We want lighting only if it's valid, otherwise don't bother
+ }
+
+
virtual void Entity(cEntity * a_Entity) override
{
// TODO: Add entity into NBT:
@@ -363,12 +374,6 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
return false;
}
Writer.Finish();
-
- #ifdef _DEBUG
- cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
- ASSERT(TestParse.IsValid());
- #endif // _DEBUG
-
CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data);
return true;
}
@@ -380,13 +385,15 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT)
{
// The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
- BLOCKTYPE BlockData[cChunkDef::BlockDataSize];
- BLOCKTYPE * MetaData = BlockData + cChunkDef::MetaOffset;
- BLOCKTYPE * BlockLight = BlockData + cChunkDef::LightOffset;
- BLOCKTYPE * SkyLight = BlockData + cChunkDef::SkyLightOffset;
+ cChunkDef::BlockTypes BlockTypes;
+ cChunkDef::BlockNibbles MetaData;
+ cChunkDef::BlockNibbles BlockLight;
+ cChunkDef::BlockNibbles SkyLight;
- memset(BlockData, E_BLOCK_AIR, sizeof(BlockData) - cChunkDef::NumBlocks / 2);
- memset(SkyLight, 0xff, cChunkDef::NumBlocks / 2); // By default, data not present in the NBT means air, which means full skylight
+ memset(BlockTypes, E_BLOCK_AIR, sizeof(BlockTypes));
+ memset(MetaData, 0, sizeof(MetaData));
+ memset(SkyLight, 0xff, sizeof(SkyLight)); // By default, data not present in the NBT means air, which means full skylight
+ memset(BlockLight, 0x00, sizeof(BlockLight));
// Load the blockdata, blocklight and skylight:
int Level = a_NBT.FindChildByName(0, "Level");
@@ -412,56 +419,26 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
{
continue;
}
- CopyNBTData(a_NBT, Child, "Blocks", &(BlockData[y * 4096]), 4096);
- CopyNBTData(a_NBT, Child, "Data", &(MetaData[y * 2048]), 2048);
- CopyNBTData(a_NBT, Child, "SkyLight", &(SkyLight[y * 2048]), 2048);
- CopyNBTData(a_NBT, Child, "BlockLight", &(BlockLight[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "Blocks", (char *)&(BlockTypes[y * 4096]), 4096);
+ CopyNBTData(a_NBT, Child, "Data", (char *)&(MetaData[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "SkyLight", (char *)&(SkyLight[y * 2048]), 2048);
+ CopyNBTData(a_NBT, Child, "BlockLight", (char *)&(BlockLight[y * 2048]), 2048);
} // for itr - LevelSections[]
- cEntityList Entities;
- cBlockEntityList BlockEntities;
+ // Load the biomes from NBT, if present and valid:
+ cChunkDef::BiomeMap BiomeMap;
+ cChunkDef::BiomeMap * Biomes = LoadBiomeMapFromNBT(&BiomeMap, a_NBT, a_NBT.FindChildByName(Level, "Biomes"));
// Load the entities from NBT:
+ cEntityList Entities;
+ cBlockEntityList BlockEntities;
LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities"));
LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"));
- #if (AXIS_ORDER == AXIS_ORDER_YZX)
- // Reorder the chunk data - walk the MCA-formatted data sequentially and copy it into the right place in the ChunkData:
- BLOCKTYPE ChunkData[cChunkDef::BlockDataSize];
- memset(ChunkData, 0, sizeof(ChunkData));
- int Index = 0; // Index into the MCA-formatted data, incremented sequentially
- for (int y = 0; y < cChunkDef::Height; y++) for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
- {
- ChunkData[cChunk::MakeIndex(x, y, z)] = BlockData[Index];
- Index++;
- } // for y/z/x
- BLOCKTYPE * ChunkMeta = ChunkData + cChunkDef::NumBlocks;
- Index = 0;
- for (int y = 0; y < cChunkDef::Height; y++) for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunk::SetNibble(ChunkMeta, x, y, z, MetaData[Index / 2] >> ((Index % 2) * 4));
- Index++;
- } // for y/z/x
- BLOCKTYPE * ChunkBlockLight = ChunkMeta + cChunkDef::NumBlocks / 2;
- Index = 0;
- for (int y = 0; y < cChunkDef::Height; y++) for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunk::SetNibble(ChunkBlockLight, x, y, z, BlockLight[Index / 2] >> ((Index % 2) * 4));
- Index++;
- } // for y/z/x
- BLOCKTYPE * ChunkSkyLight = ChunkBlockLight + cChunkDef::NumBlocks / 2;
- Index = 0;
- for (int y = 0; y < cChunkDef::Height; y++) for (int z = 0; z < cChunkDef::Width; z++) for (int x = 0; x < cChunkDef::Width; x++)
- {
- cChunk::SetNibble(ChunkSkyLight, x, y, z, SkyLight[Index / 2] >> ((Index % 2) * 4));
- Index++;
- } // for y/z/x
- #else // AXIS_ORDER_YZX
- BLOCKTYPE * ChunkData = BlockData;
- #endif // else AXIS_ORDER_YZX
+ bool IsLightValid = (a_NBT.FindChildByName(Level, "MCSIsLightValid") > 0);
/*
- // Delete the comment above for really cool stuff :)
+ // Uncomment this block for really cool stuff :)
// DEBUG magic: Invert the underground, so that we can see the MC generator in action :)
bool ShouldInvert[cChunkDef::Width * cChunkDef::Width];
memset(ShouldInvert, 0, sizeof(ShouldInvert));
@@ -484,15 +461,14 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
memset(ChunkData + cChunkDef::SkyLightOffset, 0xff, cChunkDef::NumBlocks / 2);
//*/
- m_World->ChunkDataLoaded(
+ m_World->SetChunkData(
a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ,
- ChunkData,
- ChunkData + cChunkDef::MetaOffset,
- ChunkData + cChunkDef::LightOffset,
- ChunkData + cChunkDef::SkyLightOffset,
- NULL,
- Entities,
- BlockEntities
+ BlockTypes, MetaData,
+ IsLightValid ? BlockLight : NULL,
+ IsLightValid ? SkyLight : NULL,
+ NULL, Biomes,
+ Entities, BlockEntities,
+ false
);
return true;
}
@@ -525,24 +501,37 @@ bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_
}
Serializer.Finish(); // Close NBT tags
- // TODO: Save biomes:
+ // TODO: Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray):
// Level->Add(new cNBTByteArray(Level, "Biomes", AString(Serializer.m_Biomes, sizeof(Serializer.m_Biomes));
+ // Level->Add(new cNBTByteArray(Level, "MCSBiomes", AString(Serializer.m_Biomes, sizeof(Serializer.m_Biomes));
// Save blockdata:
a_Writer.BeginList("Sections", TAG_Compound);
int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16;
int SliceSizeNibble = SliceSizeBlock / 2;
+ const char * BlockTypes = (const char *)(Serializer.m_BlockTypes);
+ const char * BlockMetas = (const char *)(Serializer.m_BlockMetas);
+ const char * BlockLight = (const char *)(Serializer.m_BlockLight);
+ const char * BlockSkyLight = (const char *)(Serializer.m_BlockSkyLight);
for (int Y = 0; Y < 16; Y++)
{
a_Writer.BeginCompound("");
- a_Writer.AddByteArray("Blocks", Serializer.m_BlockTypes + Y * SliceSizeBlock, SliceSizeBlock);
- a_Writer.AddByteArray("Data", Serializer.m_BlockMetas + Y * SliceSizeNibble, SliceSizeNibble);
- a_Writer.AddByteArray("SkyLight", Serializer.m_BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble);
- a_Writer.AddByteArray("BlockLight", Serializer.m_BlockLight + Y * SliceSizeNibble, SliceSizeNibble);
- a_Writer.AddByte("Y", Y);
+ a_Writer.AddByteArray("Blocks", BlockTypes + Y * SliceSizeBlock, SliceSizeBlock);
+ a_Writer.AddByteArray("Data", BlockMetas + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("SkyLight", BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("BlockLight", BlockLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByte("Y", (unsigned char)Y);
a_Writer.EndCompound();
}
a_Writer.EndList(); // "Sections"
+
+ // Store the information that the lighting is valid.
+ // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading.
+ if (Serializer.IsLightValid())
+ {
+ a_Writer.AddByte("MCSIsLightValid", 1);
+ }
+
a_Writer.EndCompound(); // "Level"
return true;
}
@@ -551,6 +540,33 @@ bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_
+cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_ByteArray))
+ {
+ return NULL;
+ }
+ if (a_NBT.GetDataLength(a_TagIdx) != sizeof(*a_BiomeMap))
+ {
+ // The biomes stored don't match in size
+ return NULL;
+ }
+ memcpy(a_BiomeMap, a_NBT.GetData(a_TagIdx), sizeof(*a_BiomeMap));
+ for (int i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
+ {
+ if ((*a_BiomeMap)[i] == 0xff)
+ {
+ // Unassigned biomes
+ return NULL;
+ }
+ }
+ return a_BiomeMap;
+}
+
+
+
+
+
void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_TagIdx)
{
// TODO: Load the entities
@@ -672,12 +688,35 @@ bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int
cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_RegionZ) :
m_RegionX(a_RegionX),
m_RegionZ(a_RegionZ),
- m_File(a_FileName, cFile::fmReadWrite),
m_FileName(a_FileName)
{
- if (!m_File.IsOpen())
+}
+
+
+
+
+
+bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading)
+{
+ if (m_File.IsOpen())
{
- return;
+ // Already open
+ return true;
+ }
+
+ if (a_IsForReading)
+ {
+ if (!cFile::Exists(m_FileName))
+ {
+ // We want to read and the file doesn't exist. Fail.
+ return false;
+ }
+ }
+
+ if (!m_File.Open(m_FileName, cFile::fmReadWrite))
+ {
+ // The file failed to open
+ return false;
}
// Load the header:
@@ -687,15 +726,16 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R
// Try writing a NULL header (both chunk offsets and timestamps):
memset(m_Header, 0, sizeof(m_Header));
if (
- (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) ||
- (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header))
+ (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets
+ (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps
)
{
LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str());
m_File.Close();
- return;
+ return false;
}
}
+ return true;
}
@@ -704,10 +744,11 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R
bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data)
{
- if (!m_File.IsOpen())
+ if (!OpenFile(true))
{
return false;
}
+
int LocalX = a_Chunk.m_ChunkX % 32;
if (LocalX < 0)
{
@@ -720,7 +761,6 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a
}
unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]);
unsigned ChunkOffset = ChunkLocation >> 8;
- unsigned ChunkLen = ChunkLocation & 0xff;
m_File.Seek(ChunkOffset * 4096);
@@ -753,10 +793,11 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a
bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AString & a_Data)
{
- if (!m_File.IsOpen())
+ if (!OpenFile(false))
{
return false;
}
+
int LocalX = a_Chunk.m_ChunkX % 32;
if (LocalX < 0)
{
@@ -782,7 +823,7 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
{
return false;
}
- if (m_File.Write(a_Data.data(), a_Data.size()) != a_Data.size())
+ if (m_File.Write(a_Data.data(), a_Data.size()) != (int)(a_Data.size()))
{
return false;
}
diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h
index 85b4fcaf1..c9f08aeee 100644
--- a/source/WSSAnvil.h
+++ b/source/WSSAnvil.h
@@ -35,6 +35,7 @@ enum
class cNBTTag;
class cNBTList;
class cNBTCompound;
+class cNBTByteArray;
@@ -81,6 +82,9 @@ protected:
/// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number.
unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data);
+
+ /// Opens a MCA file either for a Read operation (fails if doesn't exist) or for a Write operation (creates new if not found)
+ bool OpenFile(bool a_IsForReading);
} ;
typedef std::list<cMCAFile *> cMCAFiles;
@@ -105,6 +109,9 @@ protected:
/// Saves the chunk into NBT data using a_Writer; returns true on success
bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer);
+ /// Loads the chunk's biome map; returns a_BiomeMap if biomes present and valid, NULL otherwise
+ cChunkDef::BiomeMap * LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_BiomeMap, const cParsedNBT & a_NBT, int a_TagIdx);
+
/// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1)
void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag);
diff --git a/source/WSSCompact.cpp b/source/WSSCompact.cpp
index 8c27dba51..1064528bc 100644
--- a/source/WSSCompact.cpp
+++ b/source/WSSCompact.cpp
@@ -44,6 +44,73 @@ const int MAX_DIRTY_CHUNKS = 16;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cJsonChunkSerializer:
+
+cJsonChunkSerializer::cJsonChunkSerializer(void) :
+ m_HasJsonData(false)
+{
+}
+
+
+
+
+
+void cJsonChunkSerializer::Entity(cEntity * a_Entity)
+{
+ // TODO: a_Entity->SaveToJson(m_Root);
+}
+
+
+
+
+
+void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
+{
+ const char * SaveInto = NULL;
+ switch (a_BlockEntity->GetBlockType())
+ {
+ case E_BLOCK_CHEST: SaveInto = "Chests"; break;
+ case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
+ case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
+ case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
+
+ default:
+ {
+ ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON");
+ break;
+ }
+ } // switch (BlockEntity->GetBlockType())
+ if (SaveInto == NULL)
+ {
+ return;
+ }
+
+ Json::Value val;
+ a_BlockEntity->SaveToJson(val);
+ m_Root[SaveInto].append(val);
+ m_HasJsonData = true;
+}
+
+
+
+
+
+bool cJsonChunkSerializer::LightIsValid(bool a_IsLightValid)
+{
+ if (!a_IsLightValid)
+ {
+ return false;
+ }
+ m_Root["IsLightValid"] = true;
+ m_HasJsonData = true;
+ return true;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWSSCompact:
cWSSCompact::~cWSSCompact()
@@ -564,7 +631,7 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
Offset += Header->m_CompressedSize;
// Crude data integrity check:
- int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2
+ const int ExpectedSize = (16*256*16)*2 + (16*256*16)/2; // For version 2
if (UncompressedSize < ExpectedSize)
{
LOGWARNING("Chunk [%d, %d] has too short decompressed data (%d bytes out of %d needed), erasing",
@@ -600,18 +667,18 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
continue;
}
- std::auto_ptr<char> ConvertedData(new char[ ExpectedSize ]);
- memset( ConvertedData.get(), 0, ExpectedSize );
+ char ConvertedData[ExpectedSize];
+ memset(ConvertedData, 0, ExpectedSize);
// Cannot use cChunk::MakeIndex because it might change again?????????
// For compatibility, use what we know is current
-#define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) )
-#define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) )
+ #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) )
+ #define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) )
unsigned int InChunkOffset = 0;
for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y ) // YZX Loop order is important, in 1.1 Y was first then Z then X
{
- ConvertedData.get()[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset];
+ ConvertedData[ MAKE_3_INDEX(x, y, z) ] = UncompressedData[InChunkOffset];
++InChunkOffset;
} // for y, z, x
@@ -619,29 +686,29 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
unsigned int index2 = 0;
for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
{
- ConvertedData.get()[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
++index2;
}
- InChunkOffset += index2/2;
+ InChunkOffset += index2 / 2;
index2 = 0;
for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
{
- ConvertedData.get()[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
++index2;
}
- InChunkOffset += index2/2;
+ InChunkOffset += index2 / 2;
index2 = 0;
for( int x = 0; x < 16; ++x ) for( int z = 0; z < 16; ++z ) for( int y = 0; y < 256; ++y )
{
- ConvertedData.get()[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
+ ConvertedData[ InChunkOffset + MAKE_3_INDEX(x, y, z)/2 ] |= ( (UncompressedData[ InChunkOffset + index2/2 ] >> ((index2&1)*4) ) & 0x0f ) << ((x&1)*4);
++index2;
}
- InChunkOffset += index2/2;
+ InChunkOffset += index2 / 2;
index2 = 0;
- AString Converted(ConvertedData.get(), ExpectedSize);
+ AString Converted(ConvertedData, ExpectedSize);
// Add JSON data afterwards
if (UncompressedData.size() > InChunkOffset)
@@ -717,6 +784,7 @@ bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_Uncomp
cEntityList Entities;
cBlockEntityList BlockEntities;
+ bool IsLightValid = false;
if (a_UncompressedSize > cChunkDef::BlockDataSize)
{
@@ -731,20 +799,23 @@ bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_Uncomp
else
{
LoadEntitiesFromJson(root, Entities, BlockEntities, a_World);
+ IsLightValid = root.get("IsLightValid", false).asBool();
}
}
- BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data();
+ BLOCKTYPE * BlockData = (BLOCKTYPE *)UncompressedData.data();
+ NIBBLETYPE * MetaData = (NIBBLETYPE *)(BlockData + cChunkDef::MetaOffset);
+ NIBBLETYPE * BlockLight = (NIBBLETYPE *)(BlockData + cChunkDef::LightOffset);
+ NIBBLETYPE * SkyLight = (NIBBLETYPE *)(BlockData + cChunkDef::SkyLightOffset);
- a_World->ChunkDataLoaded(
+ a_World->SetChunkData(
a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ,
- BlockData,
- BlockData + cChunkDef::MetaOffset,
- BlockData + cChunkDef::LightOffset,
- BlockData + cChunkDef::SkyLightOffset,
- NULL,
- Entities,
- BlockEntities
+ BlockData, MetaData,
+ IsLightValid ? BlockLight : NULL,
+ IsLightValid ? SkyLight : NULL,
+ NULL, NULL,
+ Entities, BlockEntities,
+ false
);
return true;
diff --git a/source/WSSCompact.h b/source/WSSCompact.h
index cd753ce9c..512cb7e5b 100644
--- a/source/WSSCompact.h
+++ b/source/WSSCompact.h
@@ -18,6 +18,36 @@
+/// Helper class for serializing a chunk into Json
+class cJsonChunkSerializer :
+ public cChunkDataCollector
+{
+public:
+
+ cJsonChunkSerializer(void);
+
+ Json::Value & GetRoot (void) {return m_Root; }
+ BLOCKTYPE * GetBlockData(void) {return m_BlockData; }
+ bool HasJsonData (void) const {return m_HasJsonData; }
+
+protected:
+
+ // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array
+
+ // Entities and BlockEntities are serialized to Json
+ Json::Value m_Root;
+ bool m_HasJsonData;
+
+ // cChunkDataCollector overrides:
+ virtual void Entity (cEntity * a_Entity) override;
+ virtual void BlockEntity (cBlockEntity * a_Entity) override;
+ virtual bool LightIsValid (bool a_IsLightValid) override;
+} ;
+
+
+
+
+
class cWSSCompact :
public cWSSchema
{
diff --git a/source/WorldStorage.cpp b/source/WorldStorage.cpp
index e0e9e7c7b..16439d162 100644
--- a/source/WorldStorage.cpp
+++ b/source/WorldStorage.cpp
@@ -38,61 +38,6 @@ protected:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cJsonChunkSerializer:
-
-cJsonChunkSerializer::cJsonChunkSerializer(void) :
- m_HasJsonData(false)
-{
- m_Root["Chests"] = m_AllChests;
- m_Root["Furnaces"] = m_AllFurnaces;
- m_Root["Signs"] = m_AllSigns;
-}
-
-
-
-
-
-void cJsonChunkSerializer::Entity(cEntity * a_Entity)
-{
- // TODO: a_Entity->SaveToJson(m_Root);
-}
-
-
-
-
-
-void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
-{
- const char * SaveInto = NULL;
- switch (a_BlockEntity->GetBlockType())
- {
- case E_BLOCK_CHEST: SaveInto = "Chests"; break;
- case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
- case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
- case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
-
- default:
- {
- ASSERT(!"Unhandled blocktype in BlockEntities list while saving to JSON");
- break;
- }
- } // switch (BlockEntity->GetBlockType())
- if (SaveInto == NULL)
- {
- return;
- }
-
- Json::Value val;
- a_BlockEntity->SaveToJson(val);
- m_Root[SaveInto].append(val);
- m_HasJsonData = true;
-}
-
-
-
-
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorldStorage:
cWorldStorage::cWorldStorage(void) :
@@ -332,13 +277,13 @@ bool cWorldStorage::LoadOneChunk(void)
bool ShouldLoad = false;
{
cCSLock Lock(m_CSQueues);
- if (m_LoadQueue.size() > 0)
+ if (!m_LoadQueue.empty())
{
ToLoad = m_LoadQueue.front();
m_LoadQueue.pop_front();
ShouldLoad = true;
}
- HasMore = (m_LoadQueue.size() > 0);
+ HasMore = !m_LoadQueue.empty();
}
if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ))
@@ -346,7 +291,7 @@ bool cWorldStorage::LoadOneChunk(void)
if (ToLoad.m_Generate)
{
// The chunk couldn't be loaded, generate it:
- m_World->GetGenerator().GenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
+ m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
}
else
{
@@ -368,13 +313,13 @@ bool cWorldStorage::SaveOneChunk(void)
bool ShouldSave = false;
{
cCSLock Lock(m_CSQueues);
- if (m_SaveQueue.size() > 0)
+ if (!m_SaveQueue.empty())
{
Save = m_SaveQueue.front();
m_SaveQueue.pop_front();
ShouldSave = true;
}
- HasMore = (m_SaveQueue.size() > 0);
+ HasMore = !m_SaveQueue.empty();
}
if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkY, Save.m_ChunkZ))
{
diff --git a/source/WorldStorage.h b/source/WorldStorage.h
index da78b69f2..0c725a460 100644
--- a/source/WorldStorage.h
+++ b/source/WorldStorage.h
@@ -51,38 +51,6 @@ typedef std::list<cWSSchema *> cWSSchemaList;
-/// Helper class for serializing a chunk into Json
-class cJsonChunkSerializer :
- public cChunkDataCollector
-{
-public:
-
- cJsonChunkSerializer(void);
-
- Json::Value & GetRoot (void) {return m_Root; }
- BLOCKTYPE * GetBlockData(void) {return m_BlockData; }
- bool HasJsonData (void) const {return m_HasJsonData; }
-
-protected:
-
- // NOTE: block data is serialized into inherited cChunkDataCollector's m_BlockData[] array
-
- // Entities and BlockEntities are serialized to Json
- Json::Value m_Root;
- Json::Value m_AllChests;
- Json::Value m_AllFurnaces;
- Json::Value m_AllSigns;
- bool m_HasJsonData;
-
- // cChunkDataCollector overrides:
- virtual void Entity (cEntity * a_Entity) override;
- virtual void BlockEntity (cBlockEntity * a_Entity) override;
-} ;
-
-
-
-
-
/// The actual world storage class
class cWorldStorage :
public cIsThread
diff --git a/source/cAuthenticator.cpp b/source/cAuthenticator.cpp
index 3865e46c3..d45eaa043 100644
--- a/source/cAuthenticator.cpp
+++ b/source/cAuthenticator.cpp
@@ -125,7 +125,7 @@ void cAuthenticator::Execute(void)
{
return;
}
- ASSERT(m_Queue.size() > 0);
+ ASSERT(!m_Queue.empty());
int ClientID = m_Queue.front().mClientID;
AString UserName = m_Queue.front().mName;
diff --git a/source/cChatColor.cpp b/source/cChatColor.cpp
index 061148197..57cc4bbfb 100644
--- a/source/cChatColor.cpp
+++ b/source/cChatColor.cpp
@@ -3,26 +3,31 @@
#include "cChatColor.h"
-const std::string cChatColor::Color = "\xa7"; // Old color was "\xc2\xa7" or in other words: "§"
+const std::string cChatColor::Color = "\xa7"; // Old color was "\xc2\xa7" or in other words: "§"
const std::string cChatColor::Delimiter = "\xa7";
-const std::string cChatColor::Black = cChatColor::Color + "0";
-const std::string cChatColor::Navy = cChatColor::Color + "1";
-const std::string cChatColor::Green = cChatColor::Color + "2";
-const std::string cChatColor::Blue = cChatColor::Color + "3";
-const std::string cChatColor::Red = cChatColor::Color + "4";
-const std::string cChatColor::Purple = cChatColor::Color + "5";
-const std::string cChatColor::Gold = cChatColor::Color + "6";
-const std::string cChatColor::LightGray = cChatColor::Color + "7";
-const std::string cChatColor::Gray = cChatColor::Color + "8";
-const std::string cChatColor::DarkPurple = cChatColor::Color + "9";
-const std::string cChatColor::LightGreen = cChatColor::Color + "a";
-const std::string cChatColor::LightBlue = cChatColor::Color + "b";
-const std::string cChatColor::Rose = cChatColor::Color + "c";
-const std::string cChatColor::LightPurple = cChatColor::Color + "d";
-const std::string cChatColor::Yellow = cChatColor::Color + "e";
-const std::string cChatColor::White = cChatColor::Color + "f";
+const std::string cChatColor::Black = cChatColor::Color + "0";
+const std::string cChatColor::Navy = cChatColor::Color + "1";
+const std::string cChatColor::Green = cChatColor::Color + "2";
+const std::string cChatColor::Blue = cChatColor::Color + "3";
+const std::string cChatColor::Red = cChatColor::Color + "4";
+const std::string cChatColor::Purple = cChatColor::Color + "5";
+const std::string cChatColor::Gold = cChatColor::Color + "6";
+const std::string cChatColor::LightGray = cChatColor::Color + "7";
+const std::string cChatColor::Gray = cChatColor::Color + "8";
+const std::string cChatColor::DarkPurple = cChatColor::Color + "9";
+const std::string cChatColor::LightGreen = cChatColor::Color + "a";
+const std::string cChatColor::LightBlue = cChatColor::Color + "b";
+const std::string cChatColor::Rose = cChatColor::Color + "c";
+const std::string cChatColor::LightPurple = cChatColor::Color + "d";
+const std::string cChatColor::Yellow = cChatColor::Color + "e";
+const std::string cChatColor::White = cChatColor::Color + "f";
+const std::string cChatColor::Funky = cChatColor::Color + "k";
const std::string cChatColor::MakeColor( char a_Color )
{
return cChatColor::Color + a_Color;
-} \ No newline at end of file
+}
+
+
+
+
diff --git a/source/cChatColor.h b/source/cChatColor.h
index 217a0afc3..867a8e6ba 100644
--- a/source/cChatColor.h
+++ b/source/cChatColor.h
@@ -1,6 +1,9 @@
+
#pragma once
-#include <string>
+
+
+
// tolua_begin
class cChatColor
@@ -25,6 +28,7 @@ public:
static const std::string LightPurple;
static const std::string Yellow;
static const std::string White;
+ static const std::string Funky;
static const std::string MakeColor( char a_Color );
};
diff --git a/source/cChunk.cpp b/source/cChunk.cpp
index 7b3d20911..0ef2ab7b0 100644
--- a/source/cChunk.cpp
+++ b/source/cChunk.cpp
@@ -24,7 +24,6 @@
#include "cItem.h"
#include "cNoise.h"
#include "cRoot.h"
-#include "cWorldGenerator.h"
#include "cBlockToPickup.h"
#include "MersenneTwister.h"
#include "cPlayer.h"
@@ -32,7 +31,6 @@
#include "packets/cPacket_DestroyEntity.h"
#include "packets/cPacket_PreChunk.h"
#include "packets/cPacket_BlockChange.h"
-#include "packets/cPacket_MapChunk.h"
#include "packets/cPacket_MultiBlock.h"
#include <json/json.h>
@@ -50,10 +48,10 @@ extern bool g_bWaterPhysics;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sSetBlock:
-sSetBlock::sSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ) // absolute block position
- : x( a_X )
- , y( a_Y )
- , z( a_Z )
+sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) // absolute block position
+ : x( a_BlockX )
+ , y( a_BlockY )
+ , z( a_BlockZ )
, BlockType( a_BlockType )
, BlockMeta( a_BlockMeta )
{
@@ -68,8 +66,7 @@ sSetBlock::sSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockM
// cChunk:
cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World)
- : m_bCalculateLighting( false )
- , m_PosX( a_ChunkX )
+ : m_PosX( a_ChunkX )
, m_PosY( a_ChunkY )
, m_PosZ( a_ChunkZ )
, m_BlockTickNum( 0 )
@@ -79,6 +76,7 @@ cChunk::cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap,
, m_World( a_World )
, m_ChunkMap(a_ChunkMap)
, m_IsValid(false)
+ , m_IsLightValid(false)
, m_IsDirty(false)
, m_IsSaving(false)
, m_StayCount(0)
@@ -203,8 +201,10 @@ void cChunk::MarkLoadFailed(void)
void cChunk::GetAllData(cChunkDataCallback & a_Callback)
{
a_Callback.HeightMap (&m_HeightMap);
+ a_Callback.BiomeData (&m_BiomeMap);
a_Callback.BlockTypes (m_BlockTypes);
a_Callback.BlockMeta (m_BlockMeta);
+ a_Callback.LightIsValid (m_IsLightValid);
a_Callback.BlockLight (m_BlockLight);
a_Callback.BlockSkyLight(m_BlockSkyLight);
@@ -224,24 +224,35 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback)
void cChunk::SetAllData(
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
- const HeightMap * a_HeightMap,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
+ const HeightMap * a_HeightMap,
+ const BiomeMap & a_BiomeMap,
cEntityList & a_Entities,
cBlockEntityList & a_BlockEntities
)
{
+ memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap));
+
if (a_HeightMap != NULL)
{
memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap));
}
- memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
- memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta));
- memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
- memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
+ memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
+ memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta));
+ if (a_BlockLight != NULL)
+ {
+ memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
+ }
+ if (a_BlockSkyLight != NULL)
+ {
+ memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
+ }
+
+ m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
if (a_HeightMap == NULL)
{
@@ -290,6 +301,22 @@ void cChunk::SetAllData(
+void cChunk::SetLight(
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+)
+{
+ // TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
+ // Postponing until we see how bad it is :)
+ memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
+ memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight));
+ m_IsLightValid = true;
+}
+
+
+
+
+
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
{
memcpy(a_BlockTypes, m_BlockTypes, NumBlocks);
@@ -345,11 +372,6 @@ void cChunk::Stay(bool a_Stay)
void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
{
- if (m_bCalculateLighting)
- {
- CalculateLighting();
- }
-
cCSLock Lock(m_CSBlockLists);
unsigned int PendingSendBlocks = m_PendingSendBlocks.size();
if( PendingSendBlocks > 1 )
@@ -407,7 +429,7 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
unsigned int NumTickBlocks = m_ToTickBlocks.size();
Lock2.Unlock();
- if( NumTickBlocks > 0 )
+ if ( NumTickBlocks > 0 )
{
Lock2.Lock();
std::deque< unsigned int > ToTickBlocks = m_ToTickBlocks;
@@ -415,32 +437,34 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
Lock2.Unlock();
bool isRedstone = false;
- for( std::deque< unsigned int >::iterator itr = ToTickBlocks.begin(); itr != ToTickBlocks.end(); ++itr )
+ for ( std::deque< unsigned int >::iterator itr = ToTickBlocks.begin(); itr != ToTickBlocks.end(); ++itr )
{
unsigned int index = (*itr);
Vector3i BlockPos = IndexToCoordinate( index );
char BlockID = GetBlock( index );
- switch( BlockID )
+ switch ( BlockID )
{
- case E_BLOCK_REDSTONE_REPEATER_OFF:
- case E_BLOCK_REDSTONE_REPEATER_ON:
- case E_BLOCK_REDSTONE_WIRE:
+ case E_BLOCK_REDSTONE_REPEATER_OFF:
+ case E_BLOCK_REDSTONE_REPEATER_ON:
+ case E_BLOCK_REDSTONE_WIRE:
{
isRedstone = true;
+ // fallthrough
}
- case E_BLOCK_CACTUS:
- case E_BLOCK_REEDS:
- case E_BLOCK_WOODEN_PRESSURE_PLATE:
- case E_BLOCK_STONE_PRESSURE_PLATE:
- case E_BLOCK_MINECART_TRACKS:
- case E_BLOCK_SIGN_POST:
- case E_BLOCK_CROPS:
- case E_BLOCK_SAPLING:
- case E_BLOCK_YELLOW_FLOWER:
- case E_BLOCK_RED_ROSE:
- case E_BLOCK_RED_MUSHROOM:
- case E_BLOCK_BROWN_MUSHROOM: // Stuff that drops when block below is destroyed
+
+ case E_BLOCK_CACTUS:
+ case E_BLOCK_REEDS:
+ case E_BLOCK_WOODEN_PRESSURE_PLATE:
+ case E_BLOCK_STONE_PRESSURE_PLATE:
+ case E_BLOCK_MINECART_TRACKS:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_CROPS:
+ case E_BLOCK_SAPLING:
+ case E_BLOCK_YELLOW_FLOWER:
+ case E_BLOCK_RED_ROSE:
+ case E_BLOCK_RED_MUSHROOM:
+ case E_BLOCK_BROWN_MUSHROOM: // Stuff that drops when block below is destroyed
{
if( GetBlock( BlockPos.x, BlockPos.y-1, BlockPos.z ) == E_BLOCK_AIR )
{
@@ -453,12 +477,17 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
cPickup* Pickup = new cPickup( WorldPos.x * 32 + 16, WorldPos.y * 32 + 16, WorldPos.z * 32 + 16, cItem( cBlockToPickup::ToPickup( (ENUM_ITEM_ID)BlockID, E_ITEM_EMPTY) , 1 ) );
Pickup->Initialize( m_World );
}
+ break;
}
- break;
- case E_BLOCK_REDSTONE_TORCH_OFF:
- case E_BLOCK_REDSTONE_TORCH_ON:
+
+ case E_BLOCK_REDSTONE_TORCH_OFF:
+ case E_BLOCK_REDSTONE_TORCH_ON:
+ {
isRedstone = true;
- case E_BLOCK_TORCH:
+ // fallthrough
+ }
+
+ case E_BLOCK_TORCH:
{
char Dir = cTorch::MetaDataToDirection( GetNibble( m_BlockMeta, BlockPos ) );
Vector3i WorldPos = PositionToWorldPosition( BlockPos );
@@ -474,9 +503,10 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
cPickup* Pickup = new cPickup( WorldPos.x * 32 + 16, WorldPos.y * 32 + 16, WorldPos.z * 32 + 16, cItem( cBlockToPickup::ToPickup( (ENUM_ITEM_ID)BlockID, E_ITEM_EMPTY) , 1 ) );
Pickup->Initialize( m_World );
}
+ break;
}
- break;
- case E_BLOCK_LADDER:
+
+ case E_BLOCK_LADDER:
{
char Dir = cLadder::MetaDataToDirection( GetNibble( m_BlockMeta, BlockPos ) );
Vector3i WorldPos = PositionToWorldPosition( BlockPos );
@@ -488,20 +518,35 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
cPickup* Pickup = new cPickup( WorldPos.x * 32 + 16, WorldPos.y * 32 + 16, WorldPos.z * 32 + 16, cItem( (ENUM_ITEM_ID)BlockID, 1 ) );
Pickup->Initialize( m_World );
}
+ break;
}
- break;
- default:
- break;
- };
+ } // switch (BlockType)
+ } // for itr - ToTickBlocks[]
+ }
+
+ TickBlocks(a_TickRandom);
+
+ // Tick block entities (furnaces)
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ if ((*itr)->GetBlockType() == E_BLOCK_FURNACE)
+ {
+ ((cFurnaceEntity *)(*itr))->Tick( a_Dt );
}
}
+}
+
+
+
+void cChunk::TickBlocks(MTRand & a_TickRandom)
+{
// Tick dem blocks
int RandomX = a_TickRandom.randInt();
int RandomY = a_TickRandom.randInt();
int RandomZ = a_TickRandom.randInt();
- for(int i = 0; i < 50; i++)
+ for (int i = 0; i < 50; i++)
{
m_BlockTickX = (m_BlockTickX + RandomX) % Width;
m_BlockTickY = (m_BlockTickY + RandomY) % Height;
@@ -533,38 +578,38 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom)
case E_BLOCK_GRASS:
{
-#if AXIS_ORDER == AXIS_ORDER_YZX
- char AboveBlock = GetBlock( Index+1 );
-#elif AXIS_ORDER == AXIS_ORDER_XZY
- char AboveBlock = GetBlock( Index + (Width*Width) );
-#endif
- if (!( (AboveBlock == 0) || (g_BlockOneHitDig[AboveBlock]) || (g_BlockTransparent[AboveBlock]) ) ) //changed to not allow grass if any one hit object is on top
+ char AboveBlock = GetBlock( Index + (Width * Width) );
+ if (!( (AboveBlock == E_BLOCK_AIR) || (g_BlockOneHitDig[AboveBlock]) || (g_BlockTransparent[AboveBlock]) ) )
{
FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_DIRT, GetNibble( m_BlockMeta, Index ) );
}
+ break;
}
- break;
- case E_BLOCK_SAPLING: //todo: check meta of sapling. change m_World->GrowTree to look change trunk and leaves based on meta of sapling
+
+ case E_BLOCK_SAPLING: //todo: check meta of sapling. change m_World->GrowTree to look change trunk and leaves based on meta of sapling
{
- FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_AIR, GetNibble( m_BlockMeta, Index ) );
- m_World->GrowTree( m_BlockTickX + m_PosX*Width, m_BlockTickY, m_BlockTickZ + m_PosZ*Width );
+ // Check the highest bit, if set, grow the tree, if not, set it (1-bit delay):
+ NIBBLETYPE Meta = GetMeta(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
+ if ((Meta & 0x08) != 0)
+ {
+ m_World->GrowTree( m_BlockTickX + m_PosX*Width, m_BlockTickY, m_BlockTickZ + m_PosZ*Width );
+ }
+ else
+ {
+ SetMeta(m_BlockTickX, m_BlockTickY, m_BlockTickZ, Meta | 0x08);
+ }
+ break;
}
- break;
- case E_BLOCK_LEAVES: //todo, http://www.minecraftwiki.net/wiki/Data_values#Leaves
+
+ case E_BLOCK_LEAVES: //todo, http://www.minecraftwiki.net/wiki/Data_values#Leaves
{
+ break;
+ }
+
+ default:
+ {
+ break;
}
- break;
- default:
- break;
- }
- }
-
- // Tick block entities (furnaces)
- for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
- {
- if ((*itr)->GetBlockType() == E_BLOCK_FURNACE)
- {
- ((cFurnaceEntity *)(*itr))->Tick( a_Dt );
}
}
}
@@ -596,7 +641,7 @@ void cChunk::CreateBlockEntities(void)
{
for (int y = 0; y < Height; y++)
{
- ENUM_BLOCK_ID BlockType = (ENUM_BLOCK_ID)m_BlockTypes[ MakeIndexNoCheck( x, y, z ) ];
+ ENUM_BLOCK_ID BlockType = (ENUM_BLOCK_ID)m_BlockTypes[ MakeIndex( x, y, z ) ];
switch ( BlockType )
{
case E_BLOCK_CHEST:
@@ -644,7 +689,7 @@ void cChunk::CalculateHeightmap()
{
for (int y = Height - 1; y > -1; y--)
{
- int index = MakeIndexNoCheck( x, y, z );
+ int index = MakeIndex( x, y, z );
if (m_BlockTypes[index] != E_BLOCK_AIR)
{
m_HeightMap[x + z * Width] = (unsigned char)y;
@@ -699,16 +744,13 @@ void cChunk::CalculateLighting()
SpreadLight(m_BlockLight);
MarkDirty();
-
- // Stop it from calculating again :P
- m_bCalculateLighting = false;
}
-void cChunk::SpreadLight(BLOCKTYPE * a_LightBuffer)
+void cChunk::SpreadLight(NIBBLETYPE * a_LightBuffer)
{
// Spread the light
for(int x = 0; x < Width; x++) for(int z = 0; z < Width; z++) for(int y = 0; y < Height; y++)
@@ -735,7 +777,7 @@ void cChunk::SpreadLight(BLOCKTYPE * a_LightBuffer)
// Spread to neighbour chunks X-axis
cChunkPtr LeftChunk = m_ChunkMap->GetChunkNoGen( m_PosX - 1, m_PosY, m_PosZ );
cChunkPtr RightChunk = m_ChunkMap->GetChunkNoGen( m_PosX + 1, m_PosY, m_PosZ );
- BLOCKTYPE * LeftSky = NULL, *RightSky = NULL;
+ NIBBLETYPE * LeftSky = NULL, * RightSky = NULL;
if (LeftChunk->IsValid())
{
LeftSky = (a_LightBuffer == m_BlockSkyLight) ? LeftChunk->m_BlockSkyLight : LeftChunk->m_BlockLight;
@@ -780,7 +822,7 @@ void cChunk::SpreadLight(BLOCKTYPE * a_LightBuffer)
// Spread to neighbour chunks Z-axis
cChunkPtr FrontChunk = m_ChunkMap->GetChunkNoGen( m_PosX, m_PosY, m_PosZ - 1 );
cChunkPtr BackChunk = m_ChunkMap->GetChunkNoGen( m_PosX, m_PosY, m_PosZ + 1 );
- BLOCKTYPE * FrontSky = NULL, * BackSky = NULL;
+ NIBBLETYPE * FrontSky = NULL, * BackSky = NULL;
if (FrontChunk->IsValid())
{
FrontSky = (a_LightBuffer == m_BlockSkyLight) ? FrontChunk->m_BlockSkyLight : FrontChunk->m_BlockLight;
@@ -832,16 +874,16 @@ void cChunk::SpreadLight(BLOCKTYPE * a_LightBuffer)
-void cChunk::SetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta )
+void cChunk::SetBlock( int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta )
{
- if (a_X < 0 || a_X >= Width || a_Y < 0 || a_Y >= Height || a_Z < 0 || a_Z >= Width)
+ if (a_RelX < 0 || a_RelX >= Width || a_RelY < 0 || a_RelY >= Height || a_RelZ < 0 || a_RelZ >= Width)
{
return; // Clip
}
ASSERT(IsValid()); // Is this chunk loaded / generated?
- int index = MakeIndexNoCheck( a_X, a_Y, a_Z );
+ int index = MakeIndexNoCheck( a_RelX, a_RelY, a_RelZ );
BLOCKTYPE OldBlockMeta = GetNibble( m_BlockMeta, index );
BLOCKTYPE OldBlockType = m_BlockTypes[index];
m_BlockTypes[index] = a_BlockType;
@@ -867,38 +909,38 @@ void cChunk::SetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTY
(g_BlockTransparent[ OldBlockType ] != g_BlockTransparent[ a_BlockType ] )
)
{
- RecalculateLighting();
+ m_IsLightValid = false;
}
// Update heightmap, if needed:
- if (a_Y >= m_HeightMap[a_X + a_Z * Width])
+ if (a_RelY >= m_HeightMap[a_RelX + a_RelZ * Width])
{
if (a_BlockType != E_BLOCK_AIR)
{
- m_HeightMap[a_X + a_Z * Width] = (unsigned char)a_Y;
+ SetHeight(m_HeightMap, a_RelX, a_RelZ, a_RelY);
}
else
{
- for (int y = a_Y - 1; y > 0; --y)
+ for (int y = a_RelY - 1; y > 0; --y)
{
- if (m_BlockTypes[MakeIndex(a_X, y, a_Z)] != E_BLOCK_AIR)
+ if (cChunkDef::GetBlock(m_BlockTypes, a_RelX, y, a_RelZ) != E_BLOCK_AIR)
{
- m_HeightMap[a_X + a_Z * Width] = (unsigned char)y;
+ SetHeight(m_HeightMap, a_RelX, a_RelZ, y);
break;
}
} // for y - column in m_BlockData
}
}
- m_ToTickBlocks.push_back( MakeIndex( a_X, a_Y, a_Z ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X+1, a_Y, a_Z ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X-1, a_Y, a_Z ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X, a_Y+1, a_Z ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X, a_Y-1, a_Z ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X, a_Y, a_Z+1 ) );
- m_ToTickBlocks.push_back( MakeIndex( a_X, a_Y, a_Z-1 ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX + 1, a_RelY, a_RelZ ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX - 1, a_RelY, a_RelZ ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY + 1, a_RelZ ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY - 1, a_RelZ ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ + 1 ) );
+ m_ToTickBlocks.push_back( MakeIndex( a_RelX, a_RelY, a_RelZ - 1 ) );
- Vector3i WorldPos = PositionToWorldPosition( a_X, a_Y, a_Z );
+ Vector3i WorldPos = PositionToWorldPosition( a_RelX, a_RelY, a_RelZ );
cBlockEntity* BlockEntity = GetBlockEntity( WorldPos );
if( BlockEntity )
{
@@ -963,7 +1005,7 @@ void cChunk::FastSetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLO
(g_BlockTransparent[ OldBlock ] != g_BlockTransparent[ a_BlockType ] )
)
{
- RecalculateLighting();
+ m_IsLightValid = false;
}
// Update heightmap, if needed:
diff --git a/source/cChunk.h b/source/cChunk.h
index f829341e5..5b3021397 100644
--- a/source/cChunk.h
+++ b/source/cChunk.h
@@ -54,13 +54,15 @@ public:
cChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkMap * a_ChunkMap, cWorld * a_World);
~cChunk();
- bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk is valid (loaded / generated)
+ bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk block data is valid (loaded / generated)
void SetValid(void); // Also wakes up any calls to cChunkMap::GetHeight()
void MarkRegenerating(void); // Marks all clients attached to this chunk as wanting this chunk
bool IsDirty(void) const {return m_IsDirty; } // Returns true if the chunk has changed since it was last saved
bool HasLoadFailed(void) const {return m_HasLoadFailed; } // Returns true if the chunk failed to load and hasn't been generated since then
bool CanUnload(void);
+ bool IsLightValid(void) const {return m_IsLightValid; }
+
/*
To save a chunk, the WSSchema must:
1. Mark the chunk as being saved (MarkSaving() )
@@ -78,15 +80,21 @@ public:
/// Sets all chunk data
void SetAllData(
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap & a_BiomeMap,
cEntityList & a_Entities,
cBlockEntityList & a_BlockEntities
);
+ void SetLight(
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
+ );
+
/// Copies m_BlockData into a_BlockTypes, only the block types
void GetBlockTypes(BLOCKTYPE * a_BlockTypes);
@@ -100,6 +108,7 @@ public:
void Stay(bool a_Stay = true);
void Tick(float a_Dt, MTRand & a_TickRandom);
+ void TickBlocks(MTRand & a_TickRandom);
int GetPosX() { return m_PosX; }
int GetPosY() { return m_PosY; }
@@ -108,12 +117,15 @@ public:
// OBSOLETE void SendTo( cClientHandle * a_Client );
- void SetBlock( int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta );
- void SetBlock( const Vector3i & a_BlockPos, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ) { SetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_BlockType, a_BlockMeta ); }
+ void SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta );
+ // SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense
+ void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); }
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
BLOCKTYPE GetBlock( int a_X, int a_Y, int a_Z );
BLOCKTYPE GetBlock( int a_BlockIdx );
+ EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); }
+
void CollectPickupsByPlayer(cPlayer * a_Player);
void UpdateSign(int a_PosX, int a_PosY, int a_PosZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4); // Also sends update packets to all clients in the chunk
@@ -133,8 +145,6 @@ public:
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
- inline void RecalculateLighting() { m_bCalculateLighting = true; } // Recalculate lighting next tick
-
void CalculateLighting(); // Recalculate right now
void CalculateHeightmap();
@@ -165,19 +175,21 @@ public:
inline void SpreadBlockSkyLight(void) {SpreadLight(m_BlockSkyLight); }
inline void SpreadBlockLight (void) {SpreadLight(m_BlockLight); }
- inline BLOCKTYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
- inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); }
+ inline NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetMeta(int a_BlockIdx) {return cChunkDef::GetNibble(m_BlockMeta, a_BlockIdx); }
+ inline void SetMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ, a_Meta); }
- inline BLOCKTYPE GetLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
- inline BLOCKTYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
+ inline NIBBLETYPE GetSkyLight(int a_RelX, int a_RelY, int a_RelZ) {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
private:
friend class cChunkMap;
- bool m_IsValid; // True if the chunk is loaded / generated
- bool m_IsDirty; // True if the chunk has changed since it was last saved
- bool m_IsSaving; // True if the chunk is being saved
+ bool m_IsValid; // True if the chunk is loaded / generated
+ bool m_IsLightValid; // True if the blocklight and skylight are calculated
+ bool m_IsDirty; // True if the chunk has changed since it was last saved
+ bool m_IsSaving; // True if the chunk is being saved
bool m_HasLoadFailed; // True if chunk failed to load and hasn't been generated yet since then
cCriticalSection m_CSBlockLists;
@@ -193,19 +205,18 @@ private:
/// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded
int m_StayCount;
- bool m_bCalculateLighting;
-
int m_PosX, m_PosY, m_PosZ;
cWorld * m_World;
cChunkMap * m_ChunkMap;
// TODO: Make these pointers and don't allocate what isn't needed
- BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks];
- BLOCKTYPE m_BlockMeta [cChunkDef::NumBlocks / 2];
- BLOCKTYPE m_BlockLight [cChunkDef::NumBlocks / 2];
- BLOCKTYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
+ BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks];
+ NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2];
+ NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2];
+ NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
cChunkDef::HeightMap m_HeightMap;
+ cChunkDef::BiomeMap m_BiomeMap;
unsigned int m_BlockTickNum;
unsigned int m_BlockTickX, m_BlockTickY, m_BlockTickZ;
@@ -215,14 +226,14 @@ private:
cBlockEntity * GetBlockEntity( int a_X, int a_Y, int a_Z );
cBlockEntity * GetBlockEntity( const Vector3i & a_BlockPos ) { return GetBlockEntity( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z ); }
- void SpreadLightOfBlock(BLOCKTYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Falloff);
+ void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff);
void CreateBlockEntities(void);
// Makes a copy of the list
cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
- void SpreadLight(BLOCKTYPE * a_LightBuffer);
+ void SpreadLight(NIBBLETYPE * a_LightBuffer);
};
typedef cChunk * cChunkPtr;
diff --git a/source/cChunk.inl.h b/source/cChunk.inl.h
index 1300f8209..f0353521c 100644
--- a/source/cChunk.inl.h
+++ b/source/cChunk.inl.h
@@ -11,7 +11,7 @@
__C_CHUNK_INLINE__
-void cChunk::SpreadLightOfBlock(BLOCKTYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Falloff)
+void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff)
{
unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z );
cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
diff --git a/source/cChunkGenerator.cpp b/source/cChunkGenerator.cpp
index 67b004a69..321246298 100644
--- a/source/cChunkGenerator.cpp
+++ b/source/cChunkGenerator.cpp
@@ -3,9 +3,12 @@
#include "cChunkGenerator.h"
#include "cWorld.h"
-#include "cWorldGenerator.h"
-#include "cWorldGenerator_Test.h"
-#include "WGFlat.h"
+#include "../iniFile/iniFile.h"
+#include "BioGen.h"
+#include "HeiGen.h"
+#include "CompoGen.h"
+#include "StructGen.h"
+#include "FinishGen.h"
@@ -24,7 +27,9 @@ const int QUEUE_SKIP_LIMIT = 500;
cChunkGenerator::cChunkGenerator(void)
: super("cChunkGenerator")
, m_World(NULL)
- , m_pWorldGenerator(NULL)
+ , m_BiomeGen(NULL)
+ , m_HeightGen(NULL)
+ , m_CompositionGen(NULL)
{
}
@@ -41,22 +46,24 @@ cChunkGenerator::~cChunkGenerator()
-bool cChunkGenerator::Start(cWorld * a_World, const AString & a_WorldGeneratorName)
+bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
{
+ MTRand rnd;
m_World = a_World;
+ m_Seed = a_IniFile.GetValueI("Seed", "Seed", rnd.randInt());
- if (NoCaseCompare(a_WorldGeneratorName, "Test") == 0 )
- {
- m_pWorldGenerator = new cWorldGenerator_Test(a_World);
- }
- else if (NoCaseCompare(a_WorldGeneratorName, "flat") == 0)
- {
- m_pWorldGenerator = new cWGFlat(a_World);
- }
- else // Default
- {
- m_pWorldGenerator = new cWorldGenerator(a_World);
- }
+ // TODO: Remove this after INI file interface changes ( http://forum.mc-server.org/showthread.php?tid=427 )
+ a_IniFile.DeleteValue("Seed", "Seed");
+
+ a_IniFile.SetValueI("Seed", "Seed", m_Seed);
+
+ InitBiomeGen(a_IniFile);
+ InitHeightGen(a_IniFile);
+ InitCompositionGen(a_IniFile);
+ InitStructureGens(a_IniFile);
+ InitFinishGens(a_IniFile);
+
+ a_IniFile.WriteFile();
return super::Start();
}
@@ -71,16 +78,201 @@ void cChunkGenerator::Stop(void)
m_Event.Set();
m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
Wait();
+
+ // Delete the generating composition:
+ for (cFinishGenList::const_iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_FinishGens.clear();
+ for (cStructureGenList::const_iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ delete *itr;
+ }
+ m_StructureGens.clear();
+ delete m_CompositionGen;
+ m_CompositionGen = NULL;
+ delete m_HeightGen;
+ m_HeightGen = NULL;
+ delete m_BiomeGen;
+ m_BiomeGen = NULL;
+}
+
+
+
+
+
+void cChunkGenerator::InitBiomeGen(cIniFile & a_IniFile)
+{
+ AString BiomeGenName = a_IniFile.GetValue("Generator", "BiomeGen", "");
+ if (BiomeGenName.empty())
+ {
+ LOGWARN("[Generator]::BiomeGen value not found in world.ini, using \"constant\".");
+ BiomeGenName = "constant";
+ }
- delete m_pWorldGenerator;
- m_pWorldGenerator = NULL;
+ if (NoCaseCompare(BiomeGenName, "constant") == 0)
+ {
+ int Biome = a_IniFile.GetValueI("Generator", "ConstantBiome", biPlains);
+ m_BiomeGen = new cBioGenConstant((EMCSBiome)Biome);
+ }
+ else if (NoCaseCompare(BiomeGenName, "checkerboard") == 0)
+ {
+ int BiomeSize = a_IniFile.GetValueI("Generator", "CheckerboardBiomeSize", 64);
+ m_BiomeGen = new cBioGenCheckerboard(BiomeSize);
+ }
+ else
+ {
+ if (NoCaseCompare(BiomeGenName, "distortedvoronoi") != 0)
+ {
+ LOGWARNING("Unknown BiomeGen \"%s\", using \"distortedvoronoi\" instead.", BiomeGenName.c_str());
+ }
+ m_BiomeGen = new cBioGenDistortedVoronoi(m_Seed);
+ }
+}
+
+
+
+
+
+void cChunkGenerator::InitHeightGen(cIniFile & a_IniFile)
+{
+ AString HeightGenName = a_IniFile.GetValue("Generator", "HeightGen", "");
+ if (HeightGenName.empty())
+ {
+ LOGWARN("[Generator]::HeightGen value not found in world.ini, using \"classic\".");
+ HeightGenName = "classic";
+ }
+
+ if (NoCaseCompare(HeightGenName, "flat") == 0)
+ {
+ int Height = a_IniFile.GetValueI("Generator", "FlatHeight", 5);
+ m_HeightGen = new cHeiGenFlat(Height);
+ }
+ else // "classic" or <not found>
+ {
+ if (NoCaseCompare(HeightGenName, "classic") != 0)
+ {
+ LOGWARN("Unknown HeightGen \"%s\", using \"classic\" instead.", HeightGenName.c_str());
+ }
+ // These used to be in terrain.ini, but now they are in world.ini (so that multiple worlds can have different values):
+ float HeightFreq1 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightFreq1", 0.1);
+ float HeightFreq2 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightFreq2", 1.0);
+ float HeightFreq3 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightFreq3", 2.0);
+ float HeightAmp1 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightAmp1", 1.0);
+ float HeightAmp2 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightAmp2", 0.5);
+ float HeightAmp3 = (float)a_IniFile.GetValueF("Generator", "ClassicHeightAmp3", 0.5);
+ m_HeightGen = new cHeiGenClassic(m_Seed, HeightFreq1, HeightAmp1, HeightFreq2, HeightAmp2, HeightFreq3, HeightAmp3);
+ }
+}
+
+
+
+
+
+void cChunkGenerator::InitCompositionGen(cIniFile & a_IniFile)
+{
+ AString CompoGenName = a_IniFile.GetValue("Generator", "CompositionGen", "");
+ if (CompoGenName.empty())
+ {
+ LOGWARN("[Generator]::CompositionGen value not found in world.ini, using \"classic\".");
+ CompoGenName = "classic";
+ }
+ if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ {
+ AString BlockType = a_IniFile.GetValue("Generator", "SameBlockType", "");
+ if (BlockType.empty())
+ {
+ LOGWARN("[Generator]::SameBlockType value not found in world.ini, using \"stone\".");
+ BlockType = "stone";
+ }
+ int Block = BlockStringToType(BlockType);
+ if (Block < 0)
+ {
+ LOGWARN("World.ini: [Generator]::SameBlockType value \"%s\" not parseable (use a number or alias from items.ini), using \"stone\" (1).", BlockType.c_str());
+ Block = E_BLOCK_STONE;
+ }
+ bool Bedrocked = (a_IniFile.GetValueI("Generator", "SameBlockBedrocked", 1) != 0);
+ m_CompositionGen = new cCompoGenSameBlock((BLOCKTYPE)Block, Bedrocked);
+ }
+ else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ {
+ m_CompositionGen = new cCompoGenDebugBiomes(m_BiomeGen);
+ }
+ else
+ {
+ if (NoCaseCompare(CompoGenName, "classic") != 0)
+ {
+ LOGWARN("Unknown CompositionGen \"%s\", using \"classic\" instead.", CompoGenName.c_str());
+ }
+ int SeaLevel = a_IniFile.GetValueI("Generator", "ClassicSeaLevel", 60);
+ int BeachHeight = a_IniFile.GetValueI("Generator", "ClassicBeachHeight", 2);
+ int BeachDepth = a_IniFile.GetValueI("Generator", "ClassicBeachDepth", 4);
+ m_CompositionGen = new cCompoGenClassic(SeaLevel, BeachHeight, BeachDepth);
+ }
+}
+
+
+
+
+
+void cChunkGenerator::InitStructureGens(cIniFile & a_IniFile)
+{
+ AString Structures = a_IniFile.GetValue("Generator", "Structures", "Trees,MarbleCaves");
+
+ AStringVector Str = StringSplit(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ if (NoCaseCompare(*itr, "trees") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenTrees(m_Seed, m_BiomeGen, m_HeightGen, m_CompositionGen));
+ }
+ else if (NoCaseCompare(*itr, "marblecaves") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenMarbleCaves(m_Seed));
+ }
+ else if (NoCaseCompare(*itr, "orenests") == 0)
+ {
+ m_StructureGens.push_back(new cStructGenOreNests(m_Seed));
+ }
+ else
+ {
+ LOGWARNING("Unknown structure generator: \"%s\". Ignoring.", itr->c_str());
+ }
+ } // for itr - Str[]
}
-void cChunkGenerator::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkGenerator::InitFinishGens(cIniFile & a_IniFile)
+{
+ AString Structures = a_IniFile.GetValue("Generator", "Finishers", "SprinkleFoliage");
+
+ AStringVector Str = StringSplit(Structures, ",");
+ for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
+ {
+ if (NoCaseCompare(*itr, "SprinkleFoliage") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSprinkleFoliage(m_Seed));
+ }
+ else if (NoCaseCompare(*itr, "Snow") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenSnow);
+ }
+ else if (NoCaseCompare(*itr, "Ice") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenIce);
+ }
+ } // for itr - Str[]
+}
+
+
+
+
+
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
{
cCSLock Lock(m_CS);
@@ -110,6 +302,15 @@ void cChunkGenerator::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+}
+
+
+
+
+
void cChunkGenerator::WaitForQueueEmpty(void)
{
cCSLock Lock(m_CS);
@@ -134,6 +335,20 @@ int cChunkGenerator::GetQueueLength(void)
+EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
+{
+ cChunkDef::BiomeMap Biomes;
+ int Y = 0;
+ int ChunkX, ChunkZ;
+ cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ);
+ m_BiomeGen->GenBiomes(ChunkX, ChunkZ, Biomes);
+ return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ);
+}
+
+
+
+
+
void cChunkGenerator::Execute(void)
{
while (!m_ShouldTerminate)
@@ -182,22 +397,34 @@ void cChunkGenerator::Execute(void)
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
- BLOCKTYPE BlockData[cChunkDef::BlockDataSize];
+ cChunkDef::BiomeMap BiomeMap;
+ cChunkDef::BlockTypes BlockTypes;
+ cChunkDef::BlockNibbles BlockMeta;
+ cChunkDef::HeightMap HeightMap;
cEntityList Entities;
cBlockEntityList BlockEntities;
- m_pWorldGenerator->GenerateChunk(a_ChunkX, a_ChunkY, a_ChunkZ, BlockData, Entities, BlockEntities);
- m_World->ChunkDataGenerated(
+ // Use the composed generator:
+ m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, BiomeMap);
+ m_HeightGen->GenHeightMap(a_ChunkX, a_ChunkZ, HeightMap);
+ m_CompositionGen->ComposeTerrain(a_ChunkX, a_ChunkZ, BlockTypes, BlockMeta, HeightMap, Entities, BlockEntities);
+ for (cStructureGenList::iterator itr = m_StructureGens.begin(); itr != m_StructureGens.end(); ++itr)
+ {
+ (*itr)->GenStructures(a_ChunkX, a_ChunkZ, BlockTypes, BlockMeta, HeightMap, Entities, BlockEntities);
+ } // for itr - m_StructureGens[]
+ for (cFinishGenList::iterator itr = m_FinishGens.begin(); itr != m_FinishGens.end(); ++itr)
+ {
+ (*itr)->GenFinish(a_ChunkX, a_ChunkZ, BlockTypes, BlockMeta, HeightMap, BiomeMap, Entities, BlockEntities);
+ } // for itr - m_FinishGens[]
+
+ m_World->SetChunkData(
a_ChunkX, a_ChunkY, a_ChunkZ,
- BlockData,
- BlockData + cChunkDef::MetaOffset,
- BlockData + cChunkDef::LightOffset,
- BlockData + cChunkDef::SkyLightOffset,
- NULL,
- Entities, BlockEntities
+ BlockTypes, BlockMeta,
+ NULL, NULL, // We don't have lighting, chunk will be lighted when needed
+ &HeightMap, &BiomeMap,
+ Entities, BlockEntities,
+ true
);
-
- m_pWorldGenerator->PostGenerateChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
}
diff --git a/source/cChunkGenerator.h b/source/cChunkGenerator.h
index 1a2403c80..0cf11b74c 100644
--- a/source/cChunkGenerator.h
+++ b/source/cChunkGenerator.h
@@ -3,12 +3,20 @@
// Interfaces to the cChunkGenerator class representing the thread that generates chunks
-// The object takes requests for generating chunks and processes them in a separate thread one by one.
-// The requests are not added to the queue if there is already a request with the same coords
-// Before generating, the thread checks if the chunk hasn't been already generated.
-// It is theoretically possible to have multiple generator threads by having multiple instances of this object
-// but then it MAY happen that the chunk is generated twice
-// If the generator queue is overloaded, the generator skips chunks with no clients in them
+/*
+The object takes requests for generating chunks and processes them in a separate thread one by one.
+The requests are not added to the queue if there is already a request with the same coords
+Before generating, the thread checks if the chunk hasn't been already generated.
+It is theoretically possible to have multiple generator threads by having multiple instances of this object,
+but then it MAY happen that the chunk is generated twice.
+If the generator queue is overloaded, the generator skips chunks with no clients in them
+
+Generating works by composing several algorithms:
+Biome, TerrainHeight, TerrainComposition, Ores, Structures and SmallFoliage
+Each algorithm may be chosen from a pool of available algorithms in the same class and combined with others,
+based on user's preferences in the world.ini.
+See http://forum.mc-server.org/showthread.php?tid=409 for details.
+*/
@@ -23,13 +31,130 @@
+// fwd:
class cWorld;
+class cIniFile;
+
+// TODO: remove this:
class cWorldGenerator;
+/** The interface that a biome generator must implement
+A biome generator takes chunk coords on input and outputs an array of biome indices for that chunk on output.
+The output array is sequenced in the same way as the MapChunk packet's biome data.
+*/
+class cBiomeGen
+{
+public:
+ virtual ~cBiomeGen() {} // Force a virtual destructor in descendants
+
+ /// Generates biomes for the given chunk
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
+} ;
+
+
+
+
+
+/** The interface that a terrain height generator must implement
+A terrain height generator takes chunk coords on input and outputs an array of terrain heights for that chunk.
+The output array is sequenced in the same way as the BiomeGen's biome data.
+The generator may request biome information from the underlying BiomeGen, it may even request information for
+other chunks than the one it's currently generating (possibly neighbors - for averaging)
+*/
+class cTerrainHeightGen
+{
+public:
+ virtual ~cTerrainHeightGen() {} // Force a virtual destructor in descendants
+
+ /// Generates heightmap for the given chunk
+ virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) = 0;
+} ;
+
+
+
+
+
+/** The interface that a terrain composition generator must implement
+Terrain composition takes chunk coords on input and outputs the blockdata for that entire chunk, along with
+the list of entities. It is supposed to make use of the underlying TerrainHeightGen and BiomeGen for that purpose,
+but it may request information for other chunks than the one it's currently generating from them.
+*/
+class cTerrainCompositionGen
+{
+public:
+ virtual ~cTerrainCompositionGen() {} // Force a virtual destructor in descendants
+
+ virtual void ComposeTerrain(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // BlockTypes to be generated (the whole array gets initialized, even air)
+ cChunkDef::BlockNibbles & a_BlockMeta, // BlockMetas to be generated (the whole array gets initialized)
+ const cChunkDef::HeightMap & a_HeightMap, // The height map to fit
+ cEntityList & a_Entities, // Entitites may be generated along with the terrain
+ cBlockEntityList & a_BlockEntities // Block entitites may be generated (chests / furnaces / ...)
+ ) = 0;
+} ;
+
+
+
+
+
+/** The interface that a structure generator must implement
+Structures are generated after the terrain composition took place. It should modify the blocktype data to account
+for whatever structures the generator is generating.
+Note that ores are considered structures too, at least from the interface point of view.
+Also note that a worldgenerator may contain multiple structure generators, one for each type of structure
+*/
+class cStructureGen
+{
+public:
+ virtual ~cStructureGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenStructures(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) = 0;
+} ;
+
+typedef std::list<cStructureGen *> cStructureGenList;
+
+
+
+
+
+/** The interface that a finisher must implement
+Finisher implements small additions after all structures have been generated.
+*/
+class cFinishGen
+{
+public:
+ virtual ~cFinishGen() {} // Force a virtual destructor in descendants
+
+ virtual void GenFinish(
+ int a_ChunkX, int a_ChunkZ,
+ cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change
+ cChunkDef::BlockNibbles & a_BlockMeta, // Block meta to read and change
+ cChunkDef::HeightMap & a_HeightMap, // Height map to read and change by the current data
+ const cChunkDef::BiomeMap & a_BiomeMap, // Biomes to adhere to
+ cEntityList & a_Entities, // Entities may be added or deleted
+ cBlockEntityList & a_BlockEntities // Block entities may be added or deleted
+ ) = 0;
+} ;
+
+typedef std::list<cFinishGen *> cFinishGenList;
+
+
+
+
+
+/// The chunk generator itself
class cChunkGenerator :
cIsThread
{
@@ -40,25 +165,55 @@ public:
cChunkGenerator (void);
~cChunkGenerator();
- bool Start(cWorld * a_World, const AString & a_WorldGeneratorName);
+ bool Start(cWorld * a_World, cIniFile & a_IniFile);
void Stop(void);
- void GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Queues the chunk for generation; removes duplicate requests
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Queues the chunk for generation; removes duplicate requests
+
+ /// Generates the biomes for the specified chunk (directly, not in a separate thread)
+ void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
void WaitForQueueEmpty(void);
int GetQueueLength(void);
+
+ int GetSeed(void) const { return m_Seed; }
+
+ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
private:
cWorld * m_World;
- cWorldGenerator * m_pWorldGenerator;
+
+ // The generation composition:
+ cBiomeGen * m_BiomeGen;
+ cTerrainHeightGen * m_HeightGen;
+ cTerrainCompositionGen * m_CompositionGen;
+ cStructureGenList m_StructureGens;
+ cFinishGenList m_FinishGens;
+
+ int m_Seed;
cCriticalSection m_CS;
cChunkCoordsList m_Queue;
cEvent m_Event; // Set when an item is added to the queue or the thread should terminate
cEvent m_evtRemoved; // Set when an item is removed from the queue
+ /// Reads the biome gen settings from the ini and initializes m_BiomeGen accordingly
+ void InitBiomeGen(cIniFile & a_IniFile);
+
+ /// Reads the HeightGen settings from the ini and initializes m_HeightGen accordingly
+ void InitHeightGen(cIniFile & a_IniFile);
+
+ /// Reads the CompositionGen settings from the ini and initializes m_CompositionGen accordingly
+ void InitCompositionGen(cIniFile & a_IniFile);
+
+ /// Reads the structures to generate from the ini and initializes m_StructureGens accordingly
+ void InitStructureGens(cIniFile & a_IniFile);
+
+ /// Reads the finishers from the ini and initializes m_FinishGens accordingly
+ void InitFinishGens(cIniFile & a_IniFile);
+
// cIsThread override:
virtual void Execute(void) override;
diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp
index 58cdd6f07..bddec5656 100644
--- a/source/cChunkMap.cpp
+++ b/source/cChunkMap.cpp
@@ -10,6 +10,7 @@
#include "cItem.h"
#include "cPickup.h"
#include "cChunk.h"
+#include "Trees.h" // used in cChunkMap::ReplaceTreeBlocks() for tree block discrimination
#ifndef _WIN32
#include <cstdlib> // abs
@@ -276,15 +277,17 @@ void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-void cChunkMap::ChunkDataLoaded(
+void cChunkMap::SetChunkData(
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
const cChunkDef::HeightMap * a_HeightMap,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ const cChunkDef::BiomeMap & a_BiomeMap,
+ cEntityList & a_Entities,
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
)
{
cCSLock Lock(m_CSLayers);
@@ -293,37 +296,32 @@ void cChunkMap::ChunkDataLoaded(
{
return;
}
- Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_Entities, a_BlockEntities);
- Chunk->MarkLoaded();
+ Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_Entities, a_BlockEntities);
+ Chunk->SetValid();
+
+ if (a_MarkDirty)
+ {
+ Chunk->MarkDirty();
+ }
}
-void cChunkMap::ChunkDataGenerated(
- int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
- const cChunkDef::HeightMap * a_HeightMap,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+void cChunkMap::ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
if (Chunk == NULL)
{
return;
}
- Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_Entities, a_BlockEntities);
-
- // TODO: This has to go - lighting takes way too long to execute in a locked ChunkMap!
- Chunk->CalculateLighting();
-
- Chunk->SetValid();
+ Chunk->SetLight(a_BlockLight, a_SkyLight);
Chunk->MarkDirty();
}
@@ -594,9 +592,9 @@ void cChunkMap::SetBlockMeta(int a_X, int a_Y, int a_Z, char a_BlockMeta)
-void cChunkMap::SetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
+void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta)
{
- int ChunkX, ChunkZ, X = a_X, Y = a_Y, Z = a_Z;
+ int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
cCSLock Lock(m_CSLayers);
@@ -611,6 +609,100 @@ void cChunkMap::SetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCK
+void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
+{
+ cCSLock Lock(m_CSLayers);
+ for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType)
+ {
+ Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ }
+ }
+}
+
+
+
+
+
+void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
+{
+ cCSLock Lock(m_CSLayers);
+ for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ switch (Chunk->GetBlock(itr->x, itr->y, itr->z))
+ {
+ CASE_TREE_OVERWRITTEN_BLOCKS:
+ {
+ Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
+ break;
+ }
+ }
+ } // for itr - a_Blocks[]
+}
+
+
+
+
+
+EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
+{
+ int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ;
+ cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ );
+
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ );
+ if ((Chunk != NULL) && Chunk->IsValid())
+ {
+ return Chunk->GetBiomeAt(X, Z);
+ }
+ else
+ {
+ return m_World->GetGenerator().GetBiomeAt(a_BlockX, a_BlockZ);
+ }
+}
+
+
+
+
+
+bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
+{
+ bool res = true;
+ cCSLock Lock(m_CSLayers);
+ for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
+ {
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ );
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ if (!a_ContinueOnFailure)
+ {
+ return false;
+ }
+ res = false;
+ continue;
+ }
+ int idx = cChunkDef::MakeIndexNoCheck(itr->x, itr->y, itr->z);
+ itr->BlockType = Chunk->GetBlock(idx);
+ itr->BlockMeta = Chunk->GetMeta(idx);
+ }
+ return res;
+}
+
+
+
+
+
bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z, cItem & a_PickupItem)
{
int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ;
@@ -913,6 +1005,40 @@ void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
+bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ)
+{
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ // Not present
+ return false;
+ }
+ return Chunk->IsLightValid();
+}
+
+
+
+
+
+void cChunkMap::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty)
+{
+ a_NumChunksValid = 0;
+ a_NumChunksDirty = 0;
+ cCSLock Lock(m_CSLayers);
+ for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
+ {
+ int NumValid = 0, NumDirty = 0;
+ (*itr)->GetChunkStats(NumValid, NumDirty);
+ a_NumChunksValid += NumValid;
+ a_NumChunksDirty += NumDirty;
+ } // for itr - m_Layers[]
+}
+
+
+
+
+
void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom )
{
cCSLock Lock(m_CSLayers);
@@ -1053,6 +1179,30 @@ int cChunkMap::cChunkLayer::GetNumChunksLoaded(void) const
+void cChunkMap::cChunkLayer::GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const
+{
+ int NumValid = 0;
+ int NumDirty = 0;
+ for ( int i = 0; i < ARRAYCOUNT(m_Chunks); ++i )
+ {
+ if (m_Chunks[i] == NULL)
+ {
+ continue;
+ }
+ NumValid++;
+ if (m_Chunks[i]->IsDirty())
+ {
+ NumDirty++;
+ }
+ } // for i - m_Chunks[]
+ a_NumChunksValid = NumValid;
+ a_NumChunksDirty = NumDirty;
+}
+
+
+
+
+
void cChunkMap::cChunkLayer::Save(void)
{
cWorld * World = m_Parent->GetWorld();
@@ -1178,7 +1328,7 @@ void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
m_Chunks.erase(itr);
return;
}
- } // for itr - Chunks[]
+ } // for itr - m_Chunks[]
}
@@ -1197,6 +1347,18 @@ void cChunkStay::Enable(void)
+void cChunkStay::Load(void)
+{
+ for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ } // for itr - m_Chunks[]
+}
+
+
+
+
+
void cChunkStay::Disable(void)
{
ASSERT(m_IsEnabled);
diff --git a/source/cChunkMap.h b/source/cChunkMap.h
index 4364b421d..716f5ac93 100644
--- a/source/cChunkMap.h
+++ b/source/cChunkMap.h
@@ -49,26 +49,29 @@ public:
void MarkChunkSaving (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- void ChunkDataLoaded(
+ /** Sets the chunk data as either loaded from the storage or generated.
+ a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted.
+ a_BiomeMap is optional, if not present, biomes will be calculated by the generator
+ a_HeightMap is optional, if not present, will be calculated.
+ If a_MarkDirty is set, the chunk is set as dirty (used after generating)
+ */
+ void SetChunkData(
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap & a_BiomeMap,
cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
);
- void ChunkDataGenerated (
- int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
- const cChunkDef::HeightMap * a_HeightMap,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ void ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
);
bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback);
@@ -90,6 +93,18 @@ public:
BLOCKTYPE GetBlockSkyLight (int a_X, int a_Y, int a_Z);
void SetBlockMeta (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockMeta);
void SetBlock (int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta);
+
+ /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
+
+ /// Special function used for growing trees, replaces only blocks that tree may overwrite
+ void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks);
+
+ EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ);
+
+ /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read.
+ bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
+
bool DigBlock (int a_X, int a_Y, int a_Z, cItem & a_PickupItem);
void SendBlockTo (int a_X, int a_Y, int a_Z, cPlayer * a_Player);
@@ -130,6 +145,11 @@ public:
/// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() )
void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ);
+
+ bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
+
+ /// Returns the number of valid chunks and the number of dirty chunks
+ void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty);
void Tick( float a_Dt, MTRand & a_TickRand );
@@ -160,6 +180,8 @@ private:
int GetNumChunksLoaded(void) const ;
+ void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty) const;
+
void Save(void);
void UnloadUnusedChunks(void);
@@ -218,6 +240,9 @@ public:
void Enable(void);
void Disable(void);
+ /// Queues each chunk in m_Chunks[] for loading / generating
+ void Load(void);
+
// Allow cChunkStay be passed to functions expecting a const cChunkCoordsList &
operator const cChunkCoordsList(void) const {return m_Chunks; }
diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp
index 0e9fd76b7..f64f1fe44 100644
--- a/source/cClientHandle.cpp
+++ b/source/cClientHandle.cpp
@@ -436,9 +436,11 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
if (World->AddChunkClient(a_ChunkX, a_ChunkY, a_ChunkZ, this))
{
- cCSLock Lock(m_CSChunkLists);
- m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
- m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ {
+ cCSLock Lock(m_CSChunkLists);
+ m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ }
World->SendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, this);
}
}
@@ -1708,6 +1710,7 @@ void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* =
}
}
+ // Filter out map chunks that the client doesn't want anymore:
if (a_Packet.m_PacketID == E_MAP_CHUNK)
{
// Check chunks being sent, erase them from m_ChunksToSend:
@@ -1727,7 +1730,33 @@ void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* =
} // for itr - m_ChunksToSend[]
if (!Found)
{
- LOGD("Refusing to send chunk [%d, %d] - no longer wanted by client \"%s\".", ChunkX, ChunkZ, m_Username.c_str());
+ // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
+ // It's not a big issue anyway, just means that some chunks may be compressed several times
+ // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
+ return;
+ }
+ }
+
+ // Filter out pre chunks that the client doesn't want anymore:
+ if ((a_Packet.m_PacketID == E_PRE_CHUNK) && ((cPacket_PreChunk &)a_Packet).m_bLoad)
+ {
+ int ChunkX = ((cPacket_PreChunk &)a_Packet).m_PosX;
+ int ChunkZ = ((cPacket_PreChunk &)a_Packet).m_PosZ;
+ bool Found = false;
+ cCSLock Lock(m_CSChunkLists);
+ for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
+ {
+ Found = true;
+ break;
+ }
+ } // for itr - m_ChunksToSend[]
+ if (!Found)
+ {
+ // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
+ // It's not a big issue anyway, just means that some chunks may be compressed several times
+ // LOGD("Refusing to send PREchunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
return;
}
}
diff --git a/source/cFireSimulator.cpp b/source/cFireSimulator.cpp
index 1af6deb69..0d6b59422 100644
--- a/source/cFireSimulator.cpp
+++ b/source/cFireSimulator.cpp
@@ -85,7 +85,7 @@ bool cFireSimulator::IsForeverBurnable( char a_BlockID )
bool cFireSimulator::IsBurnable( char a_BlockID )
{
- return a_BlockID == E_BLOCK_WOOD
+ return a_BlockID == E_BLOCK_PLANKS
|| a_BlockID == E_BLOCK_LEAVES
|| a_BlockID == E_BLOCK_LOG
|| a_BlockID == E_BLOCK_WHITE_CLOTH
diff --git a/source/cFireSimulator.h b/source/cFireSimulator.h
index 1a3bbd314..f66fb2019 100644
--- a/source/cFireSimulator.h
+++ b/source/cFireSimulator.h
@@ -1,10 +1,20 @@
+
#pragma once
+
#include "cSimulator.h"
#include "cBlockEntity.h"
-#include <list>
+
+
+
+
class Vector3i;
class cWorld;
+
+
+
+
+
class cFireSimulator : public cSimulator
{
public:
@@ -30,4 +40,8 @@ protected:
BlockList *m_Buffer;
BlockList *m_BurningBlocks;
-}; \ No newline at end of file
+};
+
+
+
+
diff --git a/source/cFurnaceRecipe.h b/source/cFurnaceRecipe.h
index 7489b09e6..75e569099 100644
--- a/source/cFurnaceRecipe.h
+++ b/source/cFurnaceRecipe.h
@@ -1,8 +1,16 @@
+
#pragma once
-#include <list>
+
+
+
class cItem;
+
+
+
+
+
class cFurnaceRecipe
{
public:
@@ -31,4 +39,8 @@ private:
struct sFurnaceRecipeState;
sFurnaceRecipeState* m_pState;
-}; \ No newline at end of file
+};
+
+
+
+
diff --git a/source/cGenSettings.cpp b/source/cGenSettings.cpp
deleted file mode 100644
index 2edd55a6a..000000000
--- a/source/cGenSettings.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cGenSettings.h"
-
-
-float cGenSettings::HeightFreq1 = 0.1f;
-float cGenSettings::HeightFreq2 = 1.f;
-float cGenSettings::HeightFreq3 = 2.f;
-
-float cGenSettings::HeightAmp1 = 1.f;
-float cGenSettings::HeightAmp2 = 0.5f;
-float cGenSettings::HeightAmp3 = 0.5f; \ No newline at end of file
diff --git a/source/cGenSettings.h b/source/cGenSettings.h
deleted file mode 100644
index ba76f55e9..000000000
--- a/source/cGenSettings.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-class cGenSettings
-{
-public:
- static float HeightFreq1, HeightAmp1;
- static float HeightFreq2, HeightAmp2;
- static float HeightFreq3, HeightAmp3;
-}; \ No newline at end of file
diff --git a/source/cGroup.h b/source/cGroup.h
index a097ea94d..02fb7f05c 100644
--- a/source/cGroup.h
+++ b/source/cGroup.h
@@ -1,8 +1,9 @@
+
#pragma once
-#include <string>
-#include <map>
-#include <list>
+
+
+
class cGroup //tolua_export
{ //tolua_export
diff --git a/source/cHeartBeat.h b/source/cHeartBeat.h
index 8a5373af4..0adaf6b59 100644
--- a/source/cHeartBeat.h
+++ b/source/cHeartBeat.h
@@ -1,8 +1,11 @@
+
#pragma once
#include "cTCPLink.h"
-#include <string>
+
+
+
class cHeartBeat : public cTCPLink
{
@@ -18,4 +21,8 @@ private:
void SendUpdate();
std::string m_ServerID;
-}; \ No newline at end of file
+};
+
+
+
+
diff --git a/source/cLuaCommandBinder.h b/source/cLuaCommandBinder.h
index bafc16ee8..b623c5c92 100644
--- a/source/cLuaCommandBinder.h
+++ b/source/cLuaCommandBinder.h
@@ -1,12 +1,14 @@
-#pragma once
-#include <vector>
-#include <string>
-#include <map>
+#pragma once
struct lua_State;
class cPlugin;
class cPlayer;
+
+
+
+
+
class cLuaCommandBinder
{
public:
@@ -32,5 +34,9 @@ private:
typedef std::map< std::string, BoundFunction > CommandMap;
CommandMap m_BoundCommands;
-
};
+
+
+
+
+
diff --git a/source/cMonster.h b/source/cMonster.h
index 62a1049c1..4a292d193 100644
--- a/source/cMonster.h
+++ b/source/cMonster.h
@@ -1,12 +1,21 @@
+
#pragma once
+
#include "cPawn.h"
#include "Defines.h"
#include "cWorld.h"
#include "BlockID.h"
-#include <string.h>
+
+
+
+
class Vector3f;
class cClientHandle;
+
+
+
+
class cMonster : public cPawn //tolua_export
{ //tolua_export
public:
diff --git a/source/cNoise.h b/source/cNoise.h
index a0283fc8d..2169e0a13 100644
--- a/source/cNoise.h
+++ b/source/cNoise.h
@@ -37,6 +37,11 @@ public:
__NOISE_INLINE__ float IntNoise2D( int a_X, int a_Y ) const;
__NOISE_INLINE__ float IntNoise3D( int a_X, int a_Y, int a_Z ) const;
+ // Note: These functions have a mod8-irregular chance - each of the mod8 remainders has different chance of occurrence. Divide by 8 to rectify.
+ __NOISE_INLINE__ int IntNoise1DInt( int a_X ) const;
+ __NOISE_INLINE__ int IntNoise2DInt( int a_X, int a_Y ) const;
+ __NOISE_INLINE__ int IntNoise3DInt( int a_X, int a_Y, int a_Z ) const;
+
float LinearNoise1D( float a_X ) const;
float CosineNoise1D( float a_X ) const;
float CubicNoise1D( float a_X ) const;
diff --git a/source/cNoise.inc b/source/cNoise.inc
index fd52fef37..cde1f1609 100644
--- a/source/cNoise.inc
+++ b/source/cNoise.inc
@@ -1,34 +1,85 @@
+
#ifndef __C_NOISE_INC__
#define __C_NOISE_INC__
#include <math.h>
+
+
+
+
/****************
* Random value generator
**/
+
float cNoise::IntNoise( int a_X ) const
{
int x = ((a_X*m_Seed)<<13) ^ a_X;
- return ( 1.0f - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
+ return ( 1.0f - ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
}
+
+
+
+
float cNoise::IntNoise2D( int a_X, int a_Y ) const
{
int n = a_X + a_Y * 57 + m_Seed*57*57;
- n = (n<<13) ^ n;
- return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
+ n = (n<<13) ^ n;
+ return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
}
+
+
+
+
float cNoise::IntNoise3D( int a_X, int a_Y, int a_Z ) const
{
int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57;
- n = (n<<13) ^ n;
- return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
+ n = (n<<13) ^ n;
+ return ( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
+}
+
+
+
+
+
+int cNoise::IntNoise1DInt( int a_X ) const
+{
+ int x = ((a_X*m_Seed)<<13) ^ a_X;
+ return ( (x * (x * x * 15731 + 789221) + 1376312589) & 0x7fffffff);
+}
+
+
+
+
+
+int cNoise::IntNoise2DInt( int a_X, int a_Y ) const
+{
+ int n = a_X + a_Y * 57 + m_Seed*57*57;
+ n = (n<<13) ^ n;
+ return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
}
+
+
+
+
+int cNoise::IntNoise3DInt( int a_X, int a_Y, int a_Z ) const
+{
+ int n = a_X + a_Y * 57 + a_Z * 57*57 + m_Seed*57*57*57;
+ n = (n<<13) ^ n;
+ return ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff);
+}
+
+
+
+
+
/****************
* Interpolation functions
**/
+
float cNoise::CubicInterpolate( float a_A, float a_B, float a_C, float a_D, float a_Pct ) const
{
float P = (a_D - a_C) - (a_A - a_B);
@@ -39,6 +90,10 @@ float cNoise::CubicInterpolate( float a_A, float a_B, float a_C, float a_D, floa
return ((P * a_Pct + Q) * a_Pct + R) * a_Pct + S;
}
+
+
+
+
float cNoise::CosineInterpolate( float a_A, float a_B, float a_Pct ) const
{
const float ft = a_Pct * 3.1415927f;
@@ -46,9 +101,21 @@ float cNoise::CosineInterpolate( float a_A, float a_B, float a_Pct ) const
return a_A*(1-f) + a_B*f;
}
+
+
+
+
float cNoise::LinearInterpolate( float a_A, float a_B, float a_Pct ) const
{
return a_A*(1.f-a_Pct) + a_B*a_Pct;
}
-#endif \ No newline at end of file
+
+
+
+
+#endif
+
+
+
+
diff --git a/source/cPlugin.h b/source/cPlugin.h
index af0d99134..5e9ee1601 100644
--- a/source/cPlugin.h
+++ b/source/cPlugin.h
@@ -1,10 +1,5 @@
#pragma once
-#include "MemoryLeak.h"
-
-#include <vector>
-#include <string>
-
class cPacket_BlockPlace;
class cPacket_PickupSpawn;
class cPacket_EntityEquipment;
diff --git a/source/cPlugin_Lua.h b/source/cPlugin_Lua.h
index e84ac8451..1ce582a29 100644
--- a/source/cPlugin_Lua.h
+++ b/source/cPlugin_Lua.h
@@ -1,7 +1,5 @@
-#pragma once
-#include <string>
-#include <list>
+#pragma once
class cPickup;
class cPlayer;
diff --git a/source/cPlugin_NewLua.h b/source/cPlugin_NewLua.h
index f778cddfa..971f79b80 100644
--- a/source/cPlugin_NewLua.h
+++ b/source/cPlugin_NewLua.h
@@ -1,12 +1,19 @@
+
#pragma once
#include "cPlugin.h"
-#include <string>
-#include <list>
+
+
+
+
typedef struct lua_State lua_State;
class cWebPlugin_Lua;
+
+
+
+
class cPlugin_NewLua : public cPlugin //tolua_export
{ //tolua_export
public: //tolua_export
diff --git a/source/cReferenceManager.h b/source/cReferenceManager.h
index 397527de1..3142d5d5a 100644
--- a/source/cReferenceManager.h
+++ b/source/cReferenceManager.h
@@ -1,8 +1,16 @@
+
#pragma once
-#include <list>
+
+
+
class cEntity;
+
+
+
+
+
class cReferenceManager
{
public:
@@ -19,4 +27,8 @@ public:
private:
ENUM_REFERENCE_MANAGER_TYPE m_Type;
std::list< cEntity** > m_References;
-}; \ No newline at end of file
+};
+
+
+
+
diff --git a/source/cRoot.cpp b/source/cRoot.cpp
index 69127f78e..d110c96a3 100644
--- a/source/cRoot.cpp
+++ b/source/cRoot.cpp
@@ -13,7 +13,6 @@
#include "cSleep.h"
#include "cThread.h"
#include "cFileFormatUpdater.h"
-#include "cGenSettings.h"
#include "cRedstone.h"
#include "../iniFile/iniFile.h"
@@ -193,31 +192,6 @@ void cRoot::LoadGlobalSettings()
{
cRedstone::s_UseRedstone = IniFile.GetValueB("Redstone", "SimulateRedstone", true );
}
-
-
- // I think this should be removed? I can't believe anybody is using it anyway
- cIniFile GenSettings("terrain.ini");
- if( GenSettings.ReadFile() )
- {
-#define READ_INI_TERRAIN_VAL( var, type ) cGenSettings::var = (type)GenSettings.GetValueF("Terrain", #var, cGenSettings::var )
- READ_INI_TERRAIN_VAL( HeightFreq1, float );
- READ_INI_TERRAIN_VAL( HeightFreq2, float );
- READ_INI_TERRAIN_VAL( HeightFreq3, float );
- READ_INI_TERRAIN_VAL( HeightAmp1, float );
- READ_INI_TERRAIN_VAL( HeightAmp2, float );
- READ_INI_TERRAIN_VAL( HeightAmp3, float );
- }
- else
- {
-#define SET_INI_TERRAIN_VAL( var ) GenSettings.SetValueF("Terrain", #var, cGenSettings::var )
- SET_INI_TERRAIN_VAL( HeightFreq1 );
- SET_INI_TERRAIN_VAL( HeightFreq2 );
- SET_INI_TERRAIN_VAL( HeightFreq3 );
- SET_INI_TERRAIN_VAL( HeightAmp1 );
- SET_INI_TERRAIN_VAL( HeightAmp2 );
- SET_INI_TERRAIN_VAL( HeightAmp3 );
- GenSettings.WriteFile();
- }
}
@@ -237,15 +211,15 @@ void cRoot::LoadWorlds()
// Then load the other worlds
unsigned int KeyNum = IniFile.FindKey("Worlds");
unsigned int NumWorlds = IniFile.GetNumValues( KeyNum );
- if( NumWorlds > 0 )
+ if ( NumWorlds > 0 )
{
- for(unsigned int i = 0; i < NumWorlds; i++)
+ for (unsigned int i = 0; i < NumWorlds; i++)
{
std::string ValueName = IniFile.GetValueName(KeyNum, i );
if( ValueName.compare("World") == 0 )
{
std::string WorldName = IniFile.GetValue(KeyNum, i );
- if( WorldName.size() > 0 )
+ if (!WorldName.empty())
{
cWorld* NewWorld = new cWorld( WorldName.c_str() );
NewWorld->InitializeSpawn();
diff --git a/source/cRoot.h b/source/cRoot.h
index 702768b0e..3f65305db 100644
--- a/source/cRoot.h
+++ b/source/cRoot.h
@@ -56,6 +56,7 @@ public:
void TickWorlds( float a_Dt );
+ /// Returns the number of chunks loaded
int GetTotalChunkCount(void); // tolua_export
/// Saves all chunks in all worlds
diff --git a/source/cSandSimulator.h b/source/cSandSimulator.h
index 0a38172e8..c99092726 100644
--- a/source/cSandSimulator.h
+++ b/source/cSandSimulator.h
@@ -1,10 +1,20 @@
+
#pragma once
+
#include "cSimulator.h"
#include "cBlockEntity.h"
-#include <list>
#include "Vector3i.h"
+
+
+
+
class cWorld;
+
+
+
+
+
class cSandSimulator : public cSimulator
{
public:
@@ -23,4 +33,8 @@ protected:
typedef std::list <Vector3i> BlockList;
BlockList * m_Blocks;
BlockList * m_Buffer;
-}; \ No newline at end of file
+};
+
+
+
+
diff --git a/source/cServer.cpp b/source/cServer.cpp
index 332631b47..36183f3bc 100644
--- a/source/cServer.cpp
+++ b/source/cServer.cpp
@@ -475,77 +475,92 @@ void cServer::ServerCommand( const char * a_Cmd )
{
AString Command( a_Cmd );
AStringVector split = StringSplit( Command, " " );
- if( split.size() > 0 )
+ if( split.empty())
{
- if( split[0].compare( "help" ) == 0 )
- {
- printf("================== ALL COMMANDS ===================\n");
- printf("help - Shows this message\n");
- printf("save-all - Saves all loaded chunks to disk\n");
- printf("list - Lists all players currently in server\n");
- printf("unload - Unloads all unused chunks\n");
- printf("numchunks - Shows number of chunks currently loaded\n");
- printf("say - Sends a chat message to all players\n");
- printf("restart - Kicks all clients, and saves everything\n");
- printf(" and clears memory\n");
- printf("stop - Saves everything and closes server\n");
- printf("===================================================\n");
- return;
- }
- if( split[0].compare( "stop" ) == 0 || split[0].compare( "restart" ) == 0 )
- {
- return;
- }
- if( split[0].compare( "save-all" ) == 0 )
- {
- cRoot::Get()->SaveAllChunks(); // TODO - Force ALL worlds to save their chunks
- return;
- }
- if (split[0].compare("unload") == 0)
- {
- LOG("Num loaded chunks before: %i", cRoot::Get()->GetTotalChunkCount() );
- cRoot::Get()->GetDefaultWorld()->UnloadUnusedChunks(); // TODO: Iterate through ALL worlds
- LOG("Num loaded chunks after: %i", cRoot::Get()->GetTotalChunkCount() );
- return;
- }
- if( split[0].compare( "list" ) == 0 )
+ return;
+ }
+
+ if( split[0].compare( "help" ) == 0 )
+ {
+ printf("================== ALL COMMANDS ===================\n");
+ printf("help - Shows this message\n");
+ printf("save-all - Saves all loaded chunks to disk\n");
+ printf("list - Lists all players currently in server\n");
+ printf("unload - Unloads all unused chunks\n");
+ printf("numchunks - Shows number of chunks currently loaded\n");
+ printf("chunkstats - Shows chunks statistics\n");
+ printf("say - Sends a chat message to all players\n");
+ printf("restart - Kicks all clients, and saves everything\n");
+ printf(" and clears memory\n");
+ printf("stop - Saves everything and closes server\n");
+ printf("===================================================\n");
+ return;
+ }
+ if( split[0].compare( "stop" ) == 0 || split[0].compare( "restart" ) == 0 )
+ {
+ return;
+ }
+ if( split[0].compare( "save-all" ) == 0 )
+ {
+ cRoot::Get()->SaveAllChunks(); // TODO - Force ALL worlds to save their chunks
+ return;
+ }
+ if (split[0].compare("unload") == 0)
+ {
+ LOG("Num loaded chunks before: %i", cRoot::Get()->GetTotalChunkCount() );
+ cRoot::Get()->GetDefaultWorld()->UnloadUnusedChunks(); // TODO: Iterate through ALL worlds
+ LOG("Num loaded chunks after: %i", cRoot::Get()->GetTotalChunkCount() );
+ return;
+ }
+ if( split[0].compare( "list" ) == 0 )
+ {
+ class cPlayerLogger : public cPlayerListCallback
{
- class cPlayerLogger : public cPlayerListCallback
+ virtual bool Item(cPlayer * a_Player) override
{
- virtual bool Item(cPlayer * a_Player) override
- {
- LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetSocket().GetIPString().c_str());
- return false;
- }
- } Logger;
- cRoot::Get()->ForEachPlayer(Logger);
- return;
- }
- if( split[0].compare( "numchunks" ) == 0 )
- {
- LOG("Num loaded chunks: %i", cRoot::Get()->GetTotalChunkCount() );
- return;
- }
-
- if(split[0].compare("monsters") == 0 )
+ LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetSocket().GetIPString().c_str());
+ return false;
+ }
+ } Logger;
+ cRoot::Get()->ForEachPlayer(Logger);
+ return;
+ }
+ if( split[0].compare( "numchunks" ) == 0 )
+ {
+ LOG("Num loaded chunks: %i", cRoot::Get()->GetTotalChunkCount() );
+ return;
+ }
+ if (split[0].compare("chunkstats") == 0)
+ {
+ // TODO: For each world
+ int NumValid = 0;
+ int NumDirty = 0;
+ int NumInLighting = 0;
+ cRoot::Get()->GetDefaultWorld()->GetChunkStats(NumValid, NumDirty, NumInLighting);
+ LOG("Num loaded chunks: %d", NumValid);
+ LOG("Num dirty chunks: %d", NumDirty);
+ LOG("Num chunks in lighting queue: %d", NumInLighting);
+ return;
+ }
+
+ if(split[0].compare("monsters") == 0 )
+ {
+ // TODO: cWorld::ListMonsters();
+ return;
+ }
+
+ if(split.size() > 1)
+ {
+ if( split[0].compare( "say" ) == 0 )
{
- // TODO: cWorld::ListMonsters();
+ std::string Message = cChatColor::Purple + "[SERVER] " + Command.substr( Command.find_first_of("say") + 4 );
+ LOG("%s", Message.c_str() );
+ Broadcast( cPacket_Chat(Message) );
return;
}
-
- if(split.size() > 1)
- {
- if( split[0].compare( "say" ) == 0 )
- {
- std::string Message = cChatColor::Purple + "[SERVER] " + Command.substr( Command.find_first_of("say") + 4 );
- LOG("%s", Message.c_str() );
- Broadcast( cPacket_Chat(Message) );
- return;
- }
- }
- printf("Unknown command, type 'help' for all commands.\n");
}
- //LOG("You didn't enter anything? (%s)", a_Cmd.c_str() );
+ printf("Unknown command, type 'help' for all commands.\n");
+ // LOG("You didn't enter anything? (%s)", a_Cmd.c_str() );
}
diff --git a/source/cSocket.cpp b/source/cSocket.cpp
index d3f0a07eb..595b423b8 100644
--- a/source/cSocket.cpp
+++ b/source/cSocket.cpp
@@ -249,7 +249,7 @@ cSocket cSocket::Accept()
return SClient;
}
-
+
diff --git a/source/cWebAdmin.cpp b/source/cWebAdmin.cpp
index 57aea3146..d9a89b961 100644
--- a/source/cWebAdmin.cpp
+++ b/source/cWebAdmin.cpp
@@ -310,6 +310,10 @@ bool cWebAdmin::Init( int a_Port )
return true;
}
+
+
+
+
#ifdef _WIN32
DWORD WINAPI cWebAdmin::ListenThread(LPVOID lpParam)
#else
@@ -319,7 +323,10 @@ void *cWebAdmin::ListenThread( void *lpParam )
cWebAdmin* self = (cWebAdmin*)lpParam;
self->m_WebServer = new webserver(self->m_Port, Request_Handler );
- self->m_WebServer->Begin();
+ if (!self->m_WebServer->Begin())
+ {
+ LOGWARN("WebServer failed to start! WebAdmin is disabled");
+ }
self->m_Event->Set();
return 0;
@@ -341,20 +348,8 @@ std::string cWebAdmin::GetTemplate()
return "";
}
- // obtain file size:
- int lSize = f.GetSize();
-
- // allocate memory to contain the whole file:
- std::auto_ptr<char> buffer(new char[lSize]); // auto_ptr deletes the memory in its destructor
-
// copy the file into the buffer:
- if (f.Read(buffer.get(), lSize) != lSize)
- {
- LOG ("WEBADMIN: Could not read file \"%s\"", SourceFile);
- return "";
- }
-
- retVal.assign(buffer.get(), lSize );
+ f.ReadRestOfFile(retVal);
return retVal;
}
diff --git a/source/cWorld.cpp b/source/cWorld.cpp
index a2c651d44..9abfb963e 100644
--- a/source/cWorld.cpp
+++ b/source/cWorld.cpp
@@ -36,12 +36,11 @@
#include "cCavespider.h" //cavespider
#include "cGhast.h" //Ghast
#include "cZombiepigman.h" //Zombiepigman
-#include "cGenSettings.h"
#include "cMakeDir.h"
#include "cChunkGenerator.h"
#include "MersenneTwister.h"
-#include "cWorldGenerator_Test.h"
#include "cTracer.h"
+#include "Trees.h"
#include "packets/cPacket_TimeUpdate.h"
@@ -71,12 +70,6 @@ const int MAX_LIGHTING_SPREAD_PER_TICK = 10;
float cWorld::m_Time = 0.f;
-char g_BlockLightValue[128];
-char g_BlockSpreadLightFalloff[128];
-bool g_BlockTransparent[128];
-bool g_BlockOneHitDig[128];
-bool g_BlockPistonBreakable[128];
-
@@ -96,6 +89,12 @@ public:
Start();
}
+ void Stop(void)
+ {
+ m_ShouldTerminate = true;
+ Wait();
+ }
+
protected:
cWorld * m_World;
@@ -127,6 +126,53 @@ protected:
+/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
+class cWorldLightingProgress :
+ public cIsThread
+{
+public:
+ cWorldLightingProgress(cLightingThread * a_Lighting) :
+ cIsThread("cWorldLightingProgress"),
+ m_Lighting(a_Lighting)
+ {
+ Start();
+ }
+
+ void Stop(void)
+ {
+ m_ShouldTerminate = true;
+ Wait();
+ }
+
+protected:
+
+ cLightingThread * m_Lighting;
+
+ virtual void Execute(void) override
+ {
+ for (;;)
+ {
+ LOG("%d chunks remaining to light", m_Lighting->GetQueueLength()
+ );
+
+ // Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
+ for (int i = 0; i < 20; i++)
+ {
+ cSleep::MilliSleep(100);
+ if (m_ShouldTerminate)
+ {
+ return;
+ }
+ }
+ } // for (-ever)
+ }
+
+} ;
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld:
@@ -182,21 +228,6 @@ cWorld::cWorld( const AString & a_WorldName )
, m_RSList ( 0 )
, m_Weather ( eWeather_Sunny )
{
- /*
- // DEBUG:
- DWORD Tick = GetTickCount();
- for (int i = 0; i < 3000; i++)
- {
- BLOCKTYPE Playground[cChunkDef::NumBlocks / 2];
- for (int x = 0; x < 16; x++) for (int z = 0; z < 16; z++) for (int y = 0; y < 256; y++)
- {
- cChunkDef::SetNibble(Playground, x, y, z, x);
- } // for x, y, z
- } // for i
- Tick = GetTickCount() - Tick;
- LOGINFO("3000 chunkfulls of SetNibble() took %d ticks", Tick);
- //*/
-
LOG("cWorld::cWorld(%s)", a_WorldName.c_str());
m_WorldName = a_WorldName;
m_IniFileName = m_WorldName + "/world.ini";
@@ -207,10 +238,8 @@ cWorld::cWorld( const AString & a_WorldName )
m_SpawnX = (double)((r1.randInt()%1000)-500);
m_SpawnY = cChunkDef::Height;
m_SpawnZ = (double)((r1.randInt()%1000)-500);
- m_WorldSeed = r1.randInt();
m_GameMode = eGameMode_Creative;
- AString GeneratorName;
AString StorageSchema("Default");
cIniFile IniFile(m_IniFileName);
@@ -219,9 +248,7 @@ cWorld::cWorld( const AString & a_WorldName )
m_SpawnX = IniFile.GetValueF("SpawnPosition", "X", m_SpawnX );
m_SpawnY = IniFile.GetValueF("SpawnPosition", "Y", m_SpawnY );
m_SpawnZ = IniFile.GetValueF("SpawnPosition", "Z", m_SpawnZ );
- m_WorldSeed = IniFile.GetValueI("Seed", "Seed", m_WorldSeed );
m_GameMode = (eGameMode)IniFile.GetValueI("GameMode", "GameMode", m_GameMode );
- GeneratorName = IniFile.GetValue("Generator", "GeneratorName", GeneratorName);
StorageSchema = IniFile.GetValue("Storage", "Schema", StorageSchema);
}
else
@@ -229,19 +256,17 @@ cWorld::cWorld( const AString & a_WorldName )
IniFile.SetValueF("SpawnPosition", "X", m_SpawnX );
IniFile.SetValueF("SpawnPosition", "Y", m_SpawnY );
IniFile.SetValueF("SpawnPosition", "Z", m_SpawnZ );
- IniFile.SetValueI("Seed", "Seed", m_WorldSeed );
IniFile.SetValueI("GameMode", "GameMode", m_GameMode );
- IniFile.SetValue("Generator", "GeneratorName", GeneratorName);
IniFile.SetValue("Storage", "Schema", StorageSchema);
if( !IniFile.WriteFile() )
{
LOG("WARNING: Could not write to %s", m_IniFileName.c_str());
}
}
- LOGINFO("Seed: %i", m_WorldSeed );
-
+
+ m_Lighting.Start(this);
m_Storage.Start(this, StorageSchema);
- m_Generator.Start(this, GeneratorName);
+ m_Generator.Start(this, IniFile);
m_bAnimals = true;
m_SpawnMonsterRate = 10;
@@ -277,105 +302,6 @@ cWorld::cWorld( const AString & a_WorldName )
m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
m_SimulatorManager->RegisterSimulator(m_FireSimulator, 10);
m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
-
- memset( g_BlockLightValue, 0x0, sizeof( g_BlockLightValue ) );
- memset( g_BlockSpreadLightFalloff, 0xf, sizeof( g_BlockSpreadLightFalloff ) ); // 0xf means total falloff
- memset( g_BlockTransparent, 0x0, sizeof( g_BlockTransparent ) );
- memset( g_BlockOneHitDig, 0x0, sizeof( g_BlockOneHitDig ) );
- memset( g_BlockPistonBreakable, 0x0, sizeof( g_BlockPistonBreakable ) );
-
- // Emissive blocks
- g_BlockLightValue[ E_BLOCK_TORCH ] = 14;
- g_BlockLightValue[ E_BLOCK_FIRE ] = 15;
- g_BlockLightValue[ E_BLOCK_LAVA ] = 15;
- g_BlockLightValue[ E_BLOCK_STATIONARY_LAVA ] = 15;
- g_BlockLightValue[ E_BLOCK_GLOWSTONE ] = 15;
-
- // Spread blocks
- g_BlockSpreadLightFalloff[ E_BLOCK_AIR ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_TORCH ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_FIRE ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_LAVA ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_STATIONARY_LAVA ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_WATER ] = 4; // Light in water dissapears faster
- g_BlockSpreadLightFalloff[ E_BLOCK_STATIONARY_WATER ] = 4;
- g_BlockSpreadLightFalloff[ E_BLOCK_LEAVES ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_GLASS ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_GLOWSTONE ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_SIGN_POST ] = 1;
- g_BlockSpreadLightFalloff[ E_BLOCK_WALLSIGN ] = 1;
-
- // Transparent blocks
- g_BlockTransparent[ E_BLOCK_AIR ] = true;
- g_BlockTransparent[ E_BLOCK_GLASS ] = true;
- g_BlockTransparent[ E_BLOCK_FIRE ] = true;
- g_BlockTransparent[ E_BLOCK_ICE ] = true;
- g_BlockTransparent[ E_BLOCK_TORCH ] = true;
- g_BlockTransparent[ E_BLOCK_SIGN_POST ] = true;
- g_BlockTransparent[ E_BLOCK_WALLSIGN ] = true;
- g_BlockTransparent[ E_BLOCK_TALL_GRASS ] = true;
- g_BlockTransparent[ E_BLOCK_YELLOW_FLOWER ] = true;
- g_BlockTransparent[ E_BLOCK_RED_ROSE ] = true;
- g_BlockTransparent[ E_BLOCK_RED_MUSHROOM ] = true;
- g_BlockTransparent[ E_BLOCK_BROWN_MUSHROOM ] = true;
- g_BlockTransparent[ E_BLOCK_SNOW ] = true;
-
- // TODO: Any other transparent blocks?
-
- // One hit break blocks
- g_BlockOneHitDig[ E_BLOCK_SAPLING ] = true;
- g_BlockOneHitDig[ E_BLOCK_YELLOW_FLOWER ] = true;
- g_BlockOneHitDig[ E_BLOCK_RED_ROSE ] = true;
- g_BlockOneHitDig[ E_BLOCK_BROWN_MUSHROOM ] = true;
- g_BlockOneHitDig[ E_BLOCK_RED_MUSHROOM ] = true;
- g_BlockOneHitDig[ E_BLOCK_TNT ] = true;
- g_BlockOneHitDig[ E_BLOCK_TORCH ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_WIRE ] = true;
- g_BlockOneHitDig[ E_BLOCK_CROPS ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_TORCH_OFF ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_TORCH_ON ] = true;
- g_BlockOneHitDig[ E_BLOCK_REEDS ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_WIRE ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_REPEATER_OFF ] = true;
- g_BlockOneHitDig[ E_BLOCK_REDSTONE_REPEATER_ON ] = true;
- g_BlockOneHitDig[ E_BLOCK_LOCKED_CHEST ] = true;
- g_BlockOneHitDig [ E_BLOCK_FIRE ] = true;
-
- // Blocks that breaks when pushed by piston
- g_BlockPistonBreakable[ E_BLOCK_AIR ] = true;
- g_BlockPistonBreakable[ E_BLOCK_STATIONARY_WATER ] = false; //This gave pistons the ability to drop water :D
- g_BlockPistonBreakable[ E_BLOCK_WATER ] = false;
- g_BlockPistonBreakable[ E_BLOCK_STATIONARY_LAVA ] = false;
- g_BlockPistonBreakable[ E_BLOCK_LAVA ] = false;
- g_BlockPistonBreakable[ E_BLOCK_BED ] = true;
- g_BlockPistonBreakable[ E_BLOCK_COBWEB ] = true;
- g_BlockPistonBreakable[ E_BLOCK_TALL_GRASS ] = true;
- g_BlockPistonBreakable[ E_BLOCK_YELLOW_FLOWER ] = true;
- g_BlockPistonBreakable[ E_BLOCK_BROWN_MUSHROOM ] = true;
- g_BlockPistonBreakable[ E_BLOCK_RED_ROSE ] = true;
- g_BlockPistonBreakable[ E_BLOCK_RED_MUSHROOM ] = true;
- g_BlockPistonBreakable[ E_BLOCK_DEAD_BUSH ] = true;
- g_BlockPistonBreakable[ E_BLOCK_TORCH ] = true;
- g_BlockPistonBreakable[ E_BLOCK_FIRE ] = true;
- g_BlockPistonBreakable[ E_BLOCK_REDSTONE_WIRE ] = true;
- g_BlockPistonBreakable[ E_BLOCK_CROPS ] = true;
- g_BlockPistonBreakable[ E_BLOCK_LADDER ] = true;
- g_BlockPistonBreakable[ E_BLOCK_WOODEN_DOOR ] = true;
- g_BlockPistonBreakable[ E_BLOCK_IRON_DOOR ] = true;
- g_BlockPistonBreakable[ E_BLOCK_LEVER ] = true;
- g_BlockPistonBreakable[ E_BLOCK_STONE_BUTTON ] = true;
- g_BlockPistonBreakable[ E_BLOCK_REDSTONE_TORCH_ON ] = true;
- g_BlockPistonBreakable[ E_BLOCK_REDSTONE_TORCH_OFF ]= true;
- g_BlockPistonBreakable[ E_BLOCK_SNOW ] = true;
- g_BlockPistonBreakable[ E_BLOCK_REEDS ] = true;
- g_BlockPistonBreakable[ E_BLOCK_PUMPKIN_STEM ] = true;
- g_BlockPistonBreakable[ E_BLOCK_MELON_STEM ] = true;
- g_BlockPistonBreakable[ E_BLOCK_MELON ] = true;
- g_BlockPistonBreakable[ E_BLOCK_PUMPKIN ] = true;
- g_BlockPistonBreakable[ E_BLOCK_JACK_O_LANTERN ] = true;
- g_BlockPistonBreakable[ E_BLOCK_VINES ] = true;
- g_BlockPistonBreakable[ E_BLOCK_STONE_PRESSURE_PLATE ] = true;
- g_BlockPistonBreakable[ E_BLOCK_WOODEN_PRESSURE_PLATE ] = true;
}
@@ -434,7 +360,7 @@ void cWorld::CastThunderbolt ( int a_X, int a_Y, int a_Z )
-void cWorld::InitializeSpawn()
+void cWorld::InitializeSpawn(void)
{
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ );
@@ -446,24 +372,51 @@ void cWorld::InitializeSpawn()
int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
#endif // _DEBUG
- LOG("Preparing spawn area in world \"%s\"", m_WorldName.c_str());
+ LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str());
for (int x = 0; x < ViewDist; x++)
{
for (int z = 0; z < ViewDist; z++)
{
- m_ChunkMap->TouchChunk( x + ChunkX-(ViewDist - 1) / 2, 0, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader
+ m_ChunkMap->TouchChunk( x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader
}
}
- // Display progress during this process:
- cWorldLoadProgress Progress(this);
+ {
+ // Display progress during this process:
+ cWorldLoadProgress Progress(this);
+
+ // Wait for the loader to finish loading
+ m_Storage.WaitForQueuesEmpty();
+
+ // Wait for the generator to finish generating
+ m_Generator.WaitForQueueEmpty();
+
+ Progress.Stop();
+ }
- // Wait for the loader to finish loading
- m_Storage.WaitForQueuesEmpty();
+ // Light all chunks that have been newly generated:
+ LOG("Lighting spawn area in world \"%s\"...", m_WorldName.c_str());
- // Wait for the generator to finish generating
- m_Generator.WaitForQueueEmpty();
+ for (int x = 0; x < ViewDist; x++)
+ {
+ int ChX = x + ChunkX-(ViewDist - 1) / 2;
+ for (int z = 0; z < ViewDist; z++)
+ {
+ int ChZ = z + ChunkZ-(ViewDist - 1) / 2;
+ if (!m_ChunkMap->IsChunkLighted(ChX, ChZ))
+ {
+ m_Lighting.QueueChunk(ChX, ChZ); // Queue the chunk in the lighting thread
+ }
+ } // for z
+ } // for x
+ {
+ cWorldLightingProgress Progress(&m_Lighting);
+ m_Lighting.WaitForQueueEmpty();
+ Progress.Stop();
+ }
+
+ // TODO: Better spawn detection - move spawn out of the water if it isn't set in the INI already
m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height
}
@@ -508,8 +461,6 @@ void cWorld::Tick(float a_Dt)
}
}
- TickLighting();
-
m_ChunkMap->Tick(a_Dt, m_TickRand);
GetSimulatorManager()->Simulate(a_Dt);
@@ -523,7 +474,7 @@ void cWorld::Tick(float a_Dt)
std::swap(FastSetBlockQueueCopy, m_FastSetBlockQueue);
}
m_ChunkMap->FastSetBlocks(FastSetBlockQueueCopy);
- if (FastSetBlockQueueCopy.size() > 0)
+ if (!FastSetBlockQueueCopy.empty())
{
// Some blocks failed, store them for next tick:
cCSLock Lock(m_CSFastSetBlock);
@@ -716,30 +667,17 @@ void cWorld::TickSpawnMobs(float a_Dt)
-void cWorld::TickLighting(void)
+void cWorld::GrowTree( int a_X, int a_Y, int a_Z )
{
- // To avoid a deadlock, we lock the spread queue only long enough to pick the chunk coords to spread
- // The spreading itself will run unlocked
- cChunkCoordsList SpreadQueue;
+ if (GetBlock(a_X, a_Y, a_Z) == E_BLOCK_SAPLING)
{
- cCSLock Lock(m_CSLighting);
- if (m_SpreadQueue.size() == 0)
- {
- return;
- }
- if (m_SpreadQueue.size() >= MAX_LIGHTING_SPREAD_PER_TICK )
- {
- LOGWARN("cWorld: Lots of lighting to do! Still %i chunks left!", m_SpreadQueue.size() );
- }
- // Move up to MAX_LIGHTING_SPREAD_PER_TICK elements from m_SpreadQueue out into SpreadQueue:
- cChunkCoordsList::iterator itr = m_SpreadQueue.begin();
- std::advance(itr, MIN(m_SpreadQueue.size(), MAX_LIGHTING_SPREAD_PER_TICK));
- SpreadQueue.splice(SpreadQueue.begin(), m_SpreadQueue, m_SpreadQueue.begin(), itr);
+ // There is a sapling here, grow a tree according to its type:
+ GrowTreeFromSapling(a_X, a_Y, a_Z, GetBlockMeta(a_X, a_Y, a_Z));
}
-
- for (cChunkCoordsList::iterator itr = SpreadQueue.begin(); itr != SpreadQueue.end(); ++itr)
+ else
{
- m_ChunkMap->SpreadChunkLighting(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ // There is nothing here, grow a tree based on the current biome here:
+ GrowTreeByBiome(a_X, a_Y, a_Z);
}
}
@@ -747,73 +685,84 @@ void cWorld::TickLighting(void)
-void cWorld::GrowTree( int a_X, int a_Y, int a_Z )
+void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, char a_SaplingMeta)
{
- // new tree code, looks much better
- // with help from seanj
- // converted from php to lua then lua to c++
-
- // build trunk
- MTRand r1;
- int trunk = r1.randInt() % (7 - 5 + 1) + 5;
- for (int i = 0; i < trunk; i++)
+ cNoise Noise(m_Generator.GetSeed());
+ sSetBlockVector Blocks;
+ switch (a_SaplingMeta & 0x07)
{
- FastSetBlock( a_X, a_Y + i, a_Z, E_BLOCK_LOG, 0 );
+ case E_META_SAPLING_APPLE: GetAppleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Blocks); break;
+ case E_META_SAPLING_BIRCH: GetBirchTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Blocks); break;
+ case E_META_SAPLING_CONIFER: GetConiferTreeImage(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Blocks); break;
+ case E_META_SAPLING_JUNGLE: GetJungleTreeImage (a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), Blocks); break;
}
+
+ GrowTreeImage(Blocks);
+}
+
+
+
+
+
+void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z)
+{
+ cNoise Noise(m_Generator.GetSeed());
+ sSetBlockVector Blocks;
+ GetTreeImageByBiome(a_X, a_Y, a_Z, Noise, (int)(m_WorldTime & 0xffffffff), (EMCSBiome)GetBiomeAt(a_X, a_Z), Blocks);
+ GrowTreeImage(Blocks);
+}
- // build tree
- for (int j = 0; j < trunk; j++)
+
+
+
+
+void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
+{
+ // Check that the tree has place to grow
+
+ // Make a copy of the log blocks:
+ sSetBlockVector b2;
+ for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- int radius = trunk - j;
- if (radius < 4)
+ if (itr->BlockType == E_BLOCK_LOG)
{
- if (radius > 2)
- {
- radius = 2;
- }
- for (int i = a_X - radius; i <= a_X + radius; i++)
+ b2.push_back(*itr);
+ }
+ } // for itr - a_Blocks[]
+
+ // Query blocktypes and metas at those log blocks:
+ if (!GetBlocks(b2, false))
+ {
+ return;
+ }
+
+ // Check that at each log's coord there's an block allowed to be overwritten:
+ for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr)
+ {
+ switch (itr->BlockType)
+ {
+ CASE_TREE_ALLOWED_BLOCKS:
{
- for (int k = a_Z-radius; k <= a_Z + radius; k++)
- {
- // small chance to be missing a block to add a little random
- if (k != a_Z || i != a_X && (r1.randInt() % 100 + 1) > 20)
- {
- if( GetBlock( i, a_Y + j, k ) == E_BLOCK_AIR )
- {
- FastSetBlock(i, a_Y+j, k, E_BLOCK_LEAVES, 0 );
- }
- }
- else
- {
- //if( m_BlockType[ MakeIndex(i, TopY+j, k) ] == E_BLOCK_AIR )
- // m_BlockType[ MakeIndex(i, TopY+j, k) ] = E_BLOCK_LEAVES;
- }
- }
+ break;
}
- if (GetBlock( a_X, a_Y+j, a_Z ) == E_BLOCK_AIR )
+ default:
{
- FastSetBlock( a_X, a_Y+j, a_Z, E_BLOCK_LOG, 0 );
+ return;
}
}
- }
-
- // do the top
- if( GetBlock( a_X+1, a_Y+trunk, a_Z ) == E_BLOCK_AIR )
- FastSetBlock( a_X+1, a_Y+trunk, a_Z, E_BLOCK_LEAVES, 0 );
+ } // for itr - b2[]
+
+ // All ok, replace blocks with the tree image:
+ m_ChunkMap->ReplaceTreeBlocks(a_Blocks);
+}
- if( GetBlock( a_X-1, a_Y+trunk, a_Z ) == E_BLOCK_AIR )
- FastSetBlock( a_X-1, a_Y+trunk, a_Z, E_BLOCK_LEAVES, 0 );
- if( GetBlock( a_X, a_Y+trunk, a_Z+1 ) == E_BLOCK_AIR )
- FastSetBlock( a_X, a_Y+trunk, a_Z+1, E_BLOCK_LEAVES, 0 );
- if( GetBlock( a_X, a_Y+trunk, a_Z-1 ) == E_BLOCK_AIR )
- FastSetBlock( a_X, a_Y+trunk, a_Z-1, E_BLOCK_LEAVES, 0 );
- if( GetBlock( a_X, a_Y+trunk, a_Z ) == E_BLOCK_AIR )
- FastSetBlock( a_X, a_Y+trunk, a_Z, E_BLOCK_LEAVES, 0 );
- // end new tree code
+int cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ)
+{
+ return m_ChunkMap->GetBiomeAt(a_BlockX, a_BlockZ);
}
@@ -905,6 +854,24 @@ char cWorld::GetBlockSkyLight( int a_X, int a_Y, int a_Z )
+void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
+{
+ m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType);
+}
+
+
+
+
+
+bool cWorld::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
+{
+ return m_ChunkMap->GetBlocks(a_Blocks, a_ContinueOnFailure);
+}
+
+
+
+
+
bool cWorld::DigBlock( int a_X, int a_Y, int a_Z, cItem & a_PickupItem )
{
bool res = m_ChunkMap->DigBlock(a_X, a_Y, a_Z, a_PickupItem);
@@ -1018,38 +985,58 @@ void cWorld::MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-void cWorld::ChunkDataLoaded(
+void cWorld::SetChunkData(
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap * a_BiomeMap,
cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
)
{
- m_ChunkMap->ChunkDataLoaded(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_Entities, a_BlockEntities);
- m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkY, a_ChunkZ);
+ // Validate biomes, if needed:
+ cChunkDef::BiomeMap BiomeMap;
+ const cChunkDef::BiomeMap * Biomes = a_BiomeMap;
+ if (a_BiomeMap == NULL)
+ {
+ // The biomes are not assigned, get them from the generator:
+ Biomes = &BiomeMap;
+ m_Generator.GenerateBiomes(a_ChunkX, a_ChunkZ, BiomeMap);
+ }
+
+ m_ChunkMap->SetChunkData(
+ a_ChunkX, a_ChunkY, a_ChunkZ,
+ a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight,
+ a_HeightMap, *Biomes,
+ a_Entities, a_BlockEntities,
+ a_MarkDirty
+ );
+
+ // If a client is requesting this chunk, send it to them:
+ if (m_ChunkMap->HasChunkAnyClients(a_ChunkX, a_ChunkY, a_ChunkZ))
+ {
+ m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkY, a_ChunkZ);
+ }
+
+ // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk):
+ m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ);
}
-void cWorld::ChunkDataGenerated(
- int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
- const cChunkDef::HeightMap * a_HeightMap,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+void cWorld::ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
)
{
- m_ChunkMap->ChunkDataGenerated(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_Entities, a_BlockEntities);
- m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkY, a_ChunkZ);
+ m_ChunkMap->ChunkLighted(a_ChunkX, a_ChunkZ, a_BlockLight, a_SkyLight);
}
@@ -1431,14 +1418,41 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
// Trick: use Y=1 to force the chunk generation even though the chunk data is already present
- m_Generator.GenerateChunk(a_ChunkX, 1, a_ChunkZ);
+ m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ)
+{
+ m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback)
+{
+ m_Lighting.QueueChunk(a_ChunkX, a_ChunkZ, a_Callback);
+}
+
+
+
+
+
+bool cWorld::IsChunkLighted(int a_ChunkX, int a_ChunkZ)
+{
+ return m_ChunkMap->IsChunkLighted(a_ChunkX, a_ChunkZ);
}
-void cWorld::SaveAllChunks()
+void cWorld::SaveAllChunks(void)
{
LOG("Saving all chunks...");
m_LastSave = m_Time;
@@ -1514,3 +1528,13 @@ int cWorld::GetNumChunks(void) const
+
+void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue)
+{
+ m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty);
+ a_NumInLightingQueue = (int) m_Lighting.GetQueueLength();
+}
+
+
+
+
diff --git a/source/cWorld.h b/source/cWorld.h
index d5916807a..e030f3570 100644
--- a/source/cWorld.h
+++ b/source/cWorld.h
@@ -18,6 +18,7 @@
#include "Vector3f.h"
#include "ChunkSender.h"
#include "Defines.h"
+#include "LightingThread.h"
@@ -72,26 +73,29 @@ public:
void MarkChunkSaving(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void MarkChunkSaved (int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- void ChunkDataLoaded(
+ /** Sets the chunk data as either loaded from the storage or generated.
+ a_BlockLight and a_BlockSkyLight are optional, if not present, chunk will be marked as unlighted.
+ a_BiomeMap is optional, if not present, biomes will be calculated by the generator
+ a_HeightMap is optional, if not present, will be calculated.
+ If a_MarkDirty is set, the chunk is set as dirty (used after generating)
+ */
+ void SetChunkData(
int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
+ const BLOCKTYPE * a_BlockTypes,
+ const NIBBLETYPE * a_BlockMeta,
+ const NIBBLETYPE * a_BlockLight,
+ const NIBBLETYPE * a_BlockSkyLight,
const cChunkDef::HeightMap * a_HeightMap,
+ const cChunkDef::BiomeMap * a_BiomeMap,
cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ cBlockEntityList & a_BlockEntities,
+ bool a_MarkDirty
);
- void ChunkDataGenerated (
- int a_ChunkX, int a_ChunkY, int a_ChunkZ,
- const BLOCKTYPE * a_BlockTypes,
- const BLOCKTYPE * a_BlockMeta,
- const BLOCKTYPE * a_BlockLight,
- const BLOCKTYPE * a_BlockSkyLight,
- const cChunkDef::HeightMap * a_HeightMap,
- cEntityList & a_Entities,
- cBlockEntityList & a_BlockEntities
+ void ChunkLighted(
+ int a_ChunkX, int a_ChunkZ,
+ const cChunkDef::BlockNibbles & a_BlockLight,
+ const cChunkDef::BlockNibbles & a_SkyLight
);
bool GetChunkData (int a_ChunkX, int a_ChunkY, int a_ChunkZ, cChunkDataCallback & a_Callback);
@@ -153,7 +157,7 @@ public:
/// Removes the client from all chunks it is present in
void RemoveClientFromChunks(cClientHandle * a_Client);
- /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is ignored (ChunkSender will send that chunk when it becomes valid)
+ /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted)
void SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
/// Removes client from ChunkSender's queue of chunks to be sent
@@ -178,6 +182,14 @@ public:
/// Regenerate the given chunk:
void RegenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export
+
+ /// Generates the given chunk, if not already generated
+ void GenerateChunk(int a_ChunkX, int a_ChunkZ); //tolua_export
+
+ /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted
+ void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL);
+
+ bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
// TODO: Export to Lua
bool DoWithEntity( int a_UniqueID, cEntityCallback & a_Callback );
@@ -191,6 +203,14 @@ public:
void SetBlockMeta( int a_X, int a_Y, int a_Z, char a_MetaData ); //tolua_export
void SetBlockMeta( const Vector3i & a_Pos, char a_MetaData ) { SetBlockMeta( a_Pos.x, a_Pos.y, a_Pos.z, a_MetaData ); } //tolua_export
char GetBlockSkyLight( int a_X, int a_Y, int a_Z ); //tolua_export
+ // TODO: char GetBlockActualLight(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
+
+ /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
+
+ /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read.
+ bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
+
bool DigBlock( int a_X, int a_Y, int a_Z, cItem & a_PickupItem ); //tolua_export
void SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer* a_Player ); //tolua_export
@@ -209,9 +229,14 @@ public:
/// a_Player is using block entity at [x, y, z], handle that:
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) {m_ChunkMap->UseBlockEntity(a_Player, a_X, a_Y, a_Z); }
- void GrowTree( int a_X, int a_Y, int a_Z ); //tolua_export
+ void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
+ void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, char a_SaplingMeta); // tolua_export
+ void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
+
+ void GrowTreeImage(const sSetBlockVector & a_Blocks);
+
+ int GetBiomeAt (int a_BlockX, int a_BlockZ); // tolua_export
- unsigned int GetWorldSeed(void) const { return m_WorldSeed; } //tolua_export
const AString & GetName(void) const { return m_WorldName; } //tolua_export
const AString & GetIniFileName(void) const {return m_IniFileName; }
@@ -242,9 +267,15 @@ public:
if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--;
}
- void SaveAllChunks(); //tolua_export
+ void SaveAllChunks(void); //tolua_export
+
+ /// Returns the number of chunks loaded
int GetNumChunks() const; //tolua_export
+ /// Returns the number of chunks loaded and dirty, and in the lighting queue
+ void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
+
+
void Tick(float a_Dt);
void ReSpreadLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
@@ -263,7 +294,7 @@ public:
private:
friend class cRoot;
-
+
// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe)
MTRand m_TickRand;
@@ -306,8 +337,6 @@ private:
float m_SpawnMonsterTime;
float m_SpawnMonsterRate;
- unsigned int m_WorldSeed;
-
eWeather m_Weather;
cEntityList m_RemoveEntityQueue;
@@ -324,6 +353,7 @@ private:
cChunkGenerator m_Generator;
cChunkSender m_ChunkSender;
+ cLightingThread m_Lighting;
AString m_WorldName;
AString m_IniFileName;
@@ -333,7 +363,6 @@ private:
void TickWeather(float a_Dt); // Handles weather each tick
void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick
- void TickLighting(void); // Handles lighting re-spreading
void RemoveEntity( cEntity * a_Entity );
}; //tolua_export
diff --git a/source/cWorldGenerator.cpp b/source/cWorldGenerator.cpp
deleted file mode 100644
index cdb3951b8..000000000
--- a/source/cWorldGenerator.cpp
+++ /dev/null
@@ -1,438 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cWorldGenerator.h"
-#include "cNoise.h"
-#include "cWorld.h"
-#include "cGenSettings.h"
-
-#include "BlockID.h"
-#include "Vector3i.h"
-
-
-
-
-
-// An array describing an 8-way neighbor coords deltas
-static struct
-{
- int m_X;
- int m_Z;
-} g_NeighborCoords[] =
-{
- {-1, -1},
- {-1, 0},
- {-1, 1},
- {0, -1},
- {0, 1},
- {1, -1},
- {1, 0},
- {1, 1},
-} ;
-
-
-
-
-
-// You can use GLASS for these instead for debugging ore generation ;)
-// Beware though, client has problems with this much glass!
-const char BLOCK_STONE = E_BLOCK_STONE;
-const char BLOCK_DIRT = E_BLOCK_DIRT;
-const char BLOCK_GRASS = E_BLOCK_GRASS;
-
-const int MAX_HEIGHT_COAL = 127;
-const int NUM_NESTS_COAL = 40;
-const int NEST_SIZE_COAL = 10;
-
-const int MAX_HEIGHT_IRON = 70;
-const int NUM_NESTS_IRON = 10;
-const int NEST_SIZE_IRON = 6;
-
-const int MAX_HEIGHT_REDSTONE = 40;
-const int NUM_NESTS_REDSTONE = 10;
-const int NEST_SIZE_REDSTONE = 6;
-
-const int MAX_HEIGHT_GOLD = 35;
-const int NUM_NESTS_GOLD = 6;
-const int NEST_SIZE_GOLD = 6;
-
-const int MAX_HEIGHT_DIAMOND = 16;
-const int NUM_NESTS_DIAMOND = 6;
-const int NEST_SIZE_DIAMOND = 5;
-
-const int MAX_HEIGHT_LAPIS = 16;
-const int NUM_NESTS_LAPIS = 6;
-const int NEST_SIZE_LAPIS = 5;
-
-
-
-
-
-cWorldGenerator::cWorldGenerator(cWorld * a_World) :
- m_World(a_World)
-{
-}
-
-
-
-
-
-cWorldGenerator::~cWorldGenerator()
-{
-}
-
-
-
-
-
-void cWorldGenerator::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
-{
- GenerateTerrain(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData);
-}
-
-
-
-
-
-void cWorldGenerator::PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-{
- // Check the chunk just generated and all its 8-way neighbors
-
- // Make the chunks stay loaded in the surrounding 5x5 area:
- cChunkStay Stay(m_World);
- Stay.Add(a_ChunkX, a_ChunkY, a_ChunkZ);
- for (int x = -2; x <= 2; x++)
- {
- for (int z = -2; z <= 2; z++)
- {
- Stay.Add(a_ChunkX + x, a_ChunkY, a_ChunkZ + z);
- } // for z
- } // for x
- Stay.Enable();
-
- m_World->LoadChunks(Stay);
-
- CheckNeighbors(a_ChunkX, a_ChunkY, a_ChunkZ);
- for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
- {
- CheckNeighbors(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z);
- } // for i - g_NeighborCoords[]
-}
-
-
-
-
-
-void cWorldGenerator::CheckNeighbors(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-{
- if (!m_World->IsChunkValid(a_ChunkX, a_ChunkY, a_ChunkZ))
- {
- return;
- }
-
- // Check all 8-way neighbors, if they are all valid, generate foliage in this chunk:
- int NumNeighbors = 0;
- for (int i = 0; i < ARRAYCOUNT(g_NeighborCoords); i++)
- {
- if (m_World->IsChunkValid(a_ChunkX + g_NeighborCoords[i].m_X, a_ChunkY, a_ChunkZ + g_NeighborCoords[i].m_Z))
- {
- NumNeighbors++;
- }
- } // for i - g_NeighborCoords[]
- if (NumNeighbors == 8)
- {
- GenerateFoliage(a_ChunkX, a_ChunkY, a_ChunkZ);
- }
-}
-
-
-
-
-
-static float GetNoise( float x, float y, cNoise & a_Noise )
-{
- float oct1 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq1, y*cGenSettings::HeightFreq1 )*cGenSettings::HeightAmp1;
- float oct2 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq2, y*cGenSettings::HeightFreq2 )*cGenSettings::HeightAmp2;
- float oct3 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq3, y*cGenSettings::HeightFreq3 )*cGenSettings::HeightAmp3;
-
- float height = a_Noise.CubicNoise2D( x*0.1f, y*0.1f )*2;
-
- float flatness = ((a_Noise.CubicNoise2D( x*0.5f, y*0.5f ) + 1.f ) * 0.5f) * 1.1f; // 0 ... 1.5
- flatness *= flatness * flatness;
-
- return (oct1 + oct2 + oct3) * flatness + height;
-}
-
-
-
-
-
-#define PI_2 (1.57079633f)
-static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise )
-{
- float oct1 = (a_Noise.CubicNoise3D( x*0.1f, y*0.1f, z*0.1f ))*4;
-
- oct1 = oct1 * oct1 * oct1;
- if( oct1 < 0.f ) oct1 = PI_2;
- if( oct1 > PI_2 ) oct1 = PI_2;
-
- return oct1;
-}
-
-
-
-
-
-unsigned int cWorldGenerator::MakeIndex(int x, int y, int z )
-{
- ASSERT((x < cChunkDef::Width) && (x > -1) && (y < cChunkDef::Height) && (y > -1) && (z < cChunkDef::Width) && (z > -1));
-
- return cChunkDef::MakeIndexNoCheck( x, y, z );
-}
-
-
-
-
-
-void cWorldGenerator::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData)
-{
- const int WATER_LEVEL = 60;
- const int SAND_LEVEL = 3;
-
- memset(a_BlockData, E_BLOCK_AIR, cChunkDef::BlockDataSize);
-
- cNoise Noise(m_World->GetWorldSeed());
-
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- const float zz = (float)(a_ChunkZ * cChunkDef::Width + z);
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- // Place bedrock on bottom layer
- a_BlockData[MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;
-
- const float xx = (float)(a_ChunkX * cChunkDef::Width + x);
-
- int Height = (int)(GetNoise( xx * 0.05f, zz * 0.05f, Noise ) * 16);
- const int Lower = 64;
- if ( Height + Lower > 127 )
- {
- Height = 127 - Lower;
- }
- if (Height < -63)
- {
- Height = -63;
- }
- const int Top = Lower + Height;
- const float WaveNoise = 1; // m_Noise.CubicNoise2D( xx*0.01f, zz*0.01f ) + 0.5f;
- for( int y = 1; y < Top; ++y )
- {
- const float yy = (float)y;
- // V prevent caves from getting too close to the surface
- if( (Top - y > (WaveNoise*2) ) && cosf(GetMarbleNoise( xx, yy*0.5f, zz, Noise )) * fabs( cosf( yy*0.2f + WaveNoise*2 )*0.75f + WaveNoise ) > 0.5f )
- {
- if( y > 4 )
- {
- a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_AIR;
- if( z > 0 ) a_BlockData[ MakeIndex(x, y, z-1) ] = E_BLOCK_AIR;
- if( z < 15 ) a_BlockData[ MakeIndex(x, y, z+1) ] = E_BLOCK_AIR;
- if( x > 0 ) a_BlockData[ MakeIndex(x-1, y, z) ] = E_BLOCK_AIR;
- if( x < 15 ) a_BlockData[ MakeIndex(x+1, y, z) ] = E_BLOCK_AIR;
- }
- else
- {
- a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_STATIONARY_LAVA;
- }
- }
- else if ((y < 61) && (Top - y < SAND_LEVEL ))
- {
- a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_SAND;
- }
- else if ((y < 61) && (Top - y < 4 ))
- {
- a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_SANDSTONE;
- }
- else if (Top - y > ((WaveNoise + 1.5f) * 1.5f)) // rock and ores between 1.5 .. 4.5 deep
- {
- {
- a_BlockData[ MakeIndex(x, y, z) ] = BLOCK_STONE;
- }
- }
- else
- {
- a_BlockData[ MakeIndex(x, y, z) ] = BLOCK_DIRT;
- }
- } // for y
-
- if (Top + 1 >= WATER_LEVEL + SAND_LEVEL)
- {
- // Replace top dirt with grass:
- a_BlockData[MakeIndex(x, Top - 1, z)] = BLOCK_GRASS;
-
- // Generate small foliage (1-block):
- int TopY = Top - 1;
- float val1 = Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
- float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
- float val3 = Noise.CubicNoise2D(xx * 0.01f + 10, zz * 0.01f + 10 );
- float val4 = Noise.CubicNoise2D(xx * 0.05f + 20, zz * 0.05f + 20 );
- if ((val3 > 0.2f) && ((r1.randInt()%128) > 124))
- {
- a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_YELLOW_FLOWER;
- }
- else if ((val4 > 0.2f) && ((r1.randInt() % 128) > 124))
- {
- a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_RED_ROSE;
- }
- else if ((val1 + val2 + val3 + val4 > 0.2f) && ((r1.randInt() % 128) > 124))
- {
- a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_RED_MUSHROOM;
- }
- else if ((val1 + val2 + val3 + val4 > 0.2f) && ((r1.randInt() % 128) > 124))
- {
- a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_BROWN_MUSHROOM;
- }
- } // if (Top above beach-level)
- else if (Top > WATER_LEVEL)
- {
- // This is the sandy shore, generate cacti here
- int TopY = Top - 1;
- float val1 = Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f );
- float val2 = Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f );
- if ((val1 + val2 > 0.f) && ((r1.randInt() % 128) > 124))
- {
- a_BlockData[ MakeIndex(x, TopY + 1, z) ] = E_BLOCK_CACTUS;
- if ((r1.randInt() & 3) == 3)
- {
- a_BlockData[ MakeIndex(x, TopY + 2, z) ] = E_BLOCK_CACTUS;
- }
- continue;
- }
- }
- else
- {
- // Add water up to the WATER_LEVEL:
- for (int y = Top; y < WATER_LEVEL; ++y )
- {
- a_BlockData[ MakeIndex(x, y, z) ] = E_BLOCK_STATIONARY_WATER;
- }
- } // else (Top is under waterlevel)
- } // for x
- } // for z
-
- // Generate ores:
- GenerateOre(E_BLOCK_COAL_ORE, MAX_HEIGHT_COAL, NUM_NESTS_COAL, NEST_SIZE_COAL, a_BlockData);
- GenerateOre(E_BLOCK_IRON_ORE, MAX_HEIGHT_IRON, NUM_NESTS_IRON, NEST_SIZE_IRON, a_BlockData);
- GenerateOre(E_BLOCK_REDSTONE_ORE, MAX_HEIGHT_REDSTONE, NUM_NESTS_REDSTONE, NEST_SIZE_REDSTONE, a_BlockData);
- GenerateOre(E_BLOCK_GOLD_ORE, MAX_HEIGHT_GOLD, NUM_NESTS_GOLD, NEST_SIZE_GOLD, a_BlockData);
- GenerateOre(E_BLOCK_DIAMOND_ORE, MAX_HEIGHT_DIAMOND, NUM_NESTS_DIAMOND, NEST_SIZE_DIAMOND, a_BlockData);
- GenerateOre(E_BLOCK_LAPIS_ORE, MAX_HEIGHT_LAPIS, NUM_NESTS_LAPIS, NEST_SIZE_LAPIS, a_BlockData);
-}
-
-
-
-
-
-void cWorldGenerator::GenerateOre(char a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, BLOCKTYPE * a_BlockData)
-{
- // This function generates several "nests" of ore, each nest consisting of number of ore blocks relatively adjacent to each other.
- // It does so by making a random XYZ walk and adding ore along the way in cuboids of different (random) sizes
- // Only stone gets replaced with ore, all other blocks stay (so the nest can actually be smaller than specified).
- for (int i = 0; i < a_NumNests; i++)
- {
- int BaseX = r1.randInt(cChunkDef::Width);
- int BaseY = r1.randInt(a_MaxHeight);
- int BaseZ = r1.randInt(cChunkDef::Width);
- sSetBlockList OreBlocks;
- size_t NestSize = (size_t)(a_NestSize + r1.randInt(a_NestSize / 4)); // The actual nest size may be up to 1/4 larger
- while (OreBlocks.size() < NestSize)
- {
- // Put a cuboid around [BaseX, BaseY, BaseZ]
- for (int x = r1.randInt(2); x >= 0; --x)
- {
- for (int y = r1.randInt(2); y >= 0; --y)
- {
- for (int z = r1.randInt(2); z >= 0; --z)
- {
- if (OreBlocks.size() < NestSize)
- {
- OreBlocks.push_back(sSetBlock(BaseX + x, BaseY + y, BaseZ + z, a_OreType, 0));
- }
- } // for z
- } // for y
- } // for x
-
- // Move the base to a neighbor voxel
- switch (r1.randInt(4))
- {
- case 0: BaseX--; break;
- case 1: BaseX++; break;
- }
- switch (r1.randInt(4))
- {
- case 0: BaseY--; break;
- case 1: BaseY++; break;
- }
- switch (r1.randInt(4))
- {
- case 0: BaseZ--; break;
- case 1: BaseZ++; break;
- }
- } // while (OreBlocks.size() < NumBlocks)
-
- // Replace stone with the queued ore blocks:
- for (sSetBlockList::iterator itr = OreBlocks.begin(); itr != OreBlocks.end(); ++itr)
- {
- if ((itr->x < 0) || (itr->y < 0) || (itr->z < 0) || (itr->x >= cChunkDef::Width) || (itr->y >= cChunkDef::Height-1) || (itr->z >= cChunkDef::Width))
- {
- continue;
- }
- int Index = MakeIndex(itr->x, itr->y, itr->z);
- if (a_BlockData[Index] == BLOCK_STONE)
- {
- a_BlockData[Index] = a_OreType;
- }
- } // for itr - OreBlocks[]
- OreBlocks.clear();
- } // for i
-}
-
-
-
-
-
-void cWorldGenerator::GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-{
- BLOCKTYPE BlockType[cChunkDef::NumBlocks];
-
- if (!m_World->GetChunkBlockTypes(a_ChunkX, a_ChunkY, a_ChunkZ, BlockType))
- {
- LOGWARNING("Cannot generate foliage on chunk [%d, %d]", a_ChunkX, a_ChunkZ);
- return;
- }
-
- cNoise Noise(m_World->GetWorldSeed());
- for (int z = 0; z < cChunkDef::Width; z++)
- {
- int zz = z + a_ChunkZ * cChunkDef::Width;
- for (int x = 0; x < cChunkDef::Width; x++)
- {
- int xx = x + a_ChunkX * cChunkDef::Width;
-
- int TopY = m_World->GetHeight(xx, zz);
- int index = cChunkDef::MakeIndexNoCheck(x, MAX(TopY - 1, 0), z);
- if (BlockType[index] == BLOCK_GRASS)
- {
- float val1 = Noise.CubicNoise2D( xx * 0.1f, zz * 0.1f );
- float val2 = Noise.CubicNoise2D( xx * 0.01f, zz * 0.01f );
- if ((val1 + val2 > 0.2f) && ((r1.randInt() % 128) > 124))
- {
- m_World->GrowTree( xx, TopY, zz );
- }
- } // if (Grass)
- } // for x
- } // for z
-}
-
-
-
-
diff --git a/source/cWorldGenerator.h b/source/cWorldGenerator.h
deleted file mode 100644
index 157f93d83..000000000
--- a/source/cWorldGenerator.h
+++ /dev/null
@@ -1,53 +0,0 @@
-
-#pragma once
-
-
-
-
-
-#include "ChunkDef.h"
-#include "MersenneTwister.h"
-
-
-
-
-
-// fwd:
-class cWorld;
-
-
-
-
-
-class cWorldGenerator
-{
-public:
- cWorldGenerator(cWorld * a_World);
- ~cWorldGenerator();
-
- virtual void GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities);
-
- virtual void PostGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Called when the chunk has been already generated and set valid
-
-protected:
-
- cWorld * m_World;
-
- // Thread-unsafe:
- MTRand r1;
-
- void GenerateOre(char a_OreType, int a_MaxHeight, int a_NumNests, int a_NestSize, BLOCKTYPE * a_BlockData);
-
- static unsigned int MakeIndex(int x, int y, int z );
-
- virtual void GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData);
-
- virtual void GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
-
- /// Checks if the chunk has all 8 neighbors valid, if so, foliage is generated on that chunk
- void CheckNeighbors(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
-};
-
-
-
-
diff --git a/source/cWorldGenerator_Test.cpp b/source/cWorldGenerator_Test.cpp
deleted file mode 100644
index b7257f35f..000000000
--- a/source/cWorldGenerator_Test.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cWorldGenerator_Test.h"
-#include "BlockID.h"
-
-
-
-
-
-void cWorldGenerator_Test::GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData)
-{
- memset(a_BlockData, E_BLOCK_DIRT, cChunkDef::NumBlocks);
- for(int x = 0; x < cChunkDef::Width; x++)
- {
- for(int z = 0; z < cChunkDef::Width; z++)
- {
- a_BlockData[MakeIndex(x, 0, z)] = E_BLOCK_BEDROCK;
- }
- }
-}
-
-
-
-
-
-void cWorldGenerator_Test::GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-{
- (void)a_ChunkX;
- (void)a_ChunkY;
- (void)a_ChunkZ;
-}
-
-
-
-
diff --git a/source/cWorldGenerator_Test.h b/source/cWorldGenerator_Test.h
deleted file mode 100644
index 966ce4a8d..000000000
--- a/source/cWorldGenerator_Test.h
+++ /dev/null
@@ -1,25 +0,0 @@
-
-#pragma once
-
-#include "cWorldGenerator.h"
-
-
-
-
-
-class cWorldGenerator_Test :
- public cWorldGenerator
-{
-public:
-
- cWorldGenerator_Test(cWorld * a_World) : cWorldGenerator(a_World) {}
-
-protected:
-
- virtual void GenerateTerrain(int a_ChunkX, int a_ChunkY, int a_ChunkZ, BLOCKTYPE * a_BlockData) override;
- virtual void GenerateFoliage(int a_ChunkX, int a_ChunkY, int a_ChunkZ) override;
-};
-
-
-
-
diff --git a/source/packets/cPacket.cpp b/source/packets/cPacket.cpp
index 284e80903..ec7181762 100644
--- a/source/packets/cPacket.cpp
+++ b/source/packets/cPacket.cpp
@@ -195,13 +195,14 @@ void cPacket::AppendString(AString & a_Dst, const AString & a_String)
void cPacket::AppendString16(AString & a_Dst, const AString & a_String)
{
AppendShort(a_Dst, (unsigned short)a_String.size());
- std::auto_ptr<char> UTF16(new char[a_String.size() * sizeof( short ) ]);
+ AString UTF16;
+ UTF16.resize(a_String.size() * sizeof(short));
for( unsigned int i = 0; i < a_String.size(); ++i )
{
- UTF16.get()[i * sizeof( short )] = 0x00;
- UTF16.get()[i * sizeof( short ) + 1] = a_String[i];
+ UTF16[i * sizeof( short )] = 0x00;
+ UTF16[i * sizeof( short ) + 1] = a_String[i];
}
- a_Dst.append(UTF16.get(), a_String.size() * sizeof(short));
+ a_Dst.append(UTF16.data(), a_String.size() * sizeof(short));
}
diff --git a/source/packets/cPacket_MapChunk.cpp b/source/packets/cPacket_MapChunk.cpp
index 501e4df2d..279dee808 100644
--- a/source/packets/cPacket_MapChunk.cpp
+++ b/source/packets/cPacket_MapChunk.cpp
@@ -19,7 +19,7 @@ cPacket_MapChunk::~cPacket_MapChunk()
-cPacket_MapChunk::cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const BIOMETYPE * a_BiomeData)
+cPacket_MapChunk::cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const unsigned char * a_BiomeData)
{
m_PacketID = E_MAP_CHUNK;
diff --git a/source/packets/cPacket_MapChunk.h b/source/packets/cPacket_MapChunk.h
index 27e1ec8e5..0daf5af34 100644
--- a/source/packets/cPacket_MapChunk.h
+++ b/source/packets/cPacket_MapChunk.h
@@ -24,7 +24,7 @@ public:
{ m_PacketID = E_MAP_CHUNK; }
cPacket_MapChunk( const cPacket_MapChunk & a_Copy );
- cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const BIOMETYPE * a_BiomeData);
+ cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const unsigned char * a_BiomeData);
~cPacket_MapChunk();
virtual cPacket* Clone() const { return new cPacket_MapChunk(*this); }