Merged the composable_generator branch into the trunk
git-svn-id: http://mc-server.googlecode.com/svn/trunk@504 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
parent
5f29a3e134
commit
a4a418a679
@ -264,6 +264,10 @@
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\source\BlockID.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\BlockID.h"
|
||||
>
|
||||
@ -336,14 +340,6 @@
|
||||
RelativePath="..\source\cFurnaceRecipe.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cGenSettings.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cGenSettings.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cGroup.cpp"
|
||||
>
|
||||
@ -462,22 +458,6 @@
|
||||
RelativePath="..\source\cPiston.h"
|
||||
>
|
||||
</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"
|
||||
>
|
||||
@ -1659,6 +1639,14 @@
|
||||
RelativePath="..\source\cLuaCommandBinder.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cPlugin.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cPlugin.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cPlugin_Lua.cpp"
|
||||
>
|
||||
@ -1675,6 +1663,14 @@
|
||||
RelativePath="..\source\cPlugin_NewLua.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cPluginManager.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cPluginManager.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cStringMap.cpp"
|
||||
>
|
||||
@ -1747,14 +1743,6 @@
|
||||
RelativePath="..\source\FastNBT.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\NBT.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\NBT.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\WorldStorage.cpp"
|
||||
>
|
||||
@ -1783,6 +1771,14 @@
|
||||
<Filter
|
||||
Name="Generating"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\source\BioGen.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\BioGen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cChunkGenerator.cpp"
|
||||
>
|
||||
@ -1792,31 +1788,87 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cWorldGenerator.cpp"
|
||||
RelativePath="..\source\CompoGen.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cWorldGenerator.h"
|
||||
RelativePath="..\source\CompoGen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cWorldGenerator_Test.cpp"
|
||||
RelativePath="..\source\FinishGen.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\cWorldGenerator_Test.h"
|
||||
RelativePath="..\source\FinishGen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\WGFlat.cpp"
|
||||
RelativePath="..\source\HeiGen.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\WGFlat.h"
|
||||
RelativePath="..\source\HeiGen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\StructGen.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\StructGen.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\source\Trees.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
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"
|
||||
>
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -37,6 +37,7 @@
|
||||
#ifndef _WIN32
|
||||
#include <cstring>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#define SD_SEND 0x01
|
||||
#else
|
||||
#define MSG_NOSIGNAL (0)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 'GetWorldSeed'", NULL);
|
||||
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GrowTreeFromSapling'", NULL);
|
||||
#endif
|
||||
{
|
||||
unsigned int tolua_ret = (unsigned int) self->GetWorldSeed();
|
||||
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 'GetBiomeAt'", NULL);
|
||||
#endif
|
||||
{
|
||||
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);
|
||||
|
@ -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 */
|
||||
|
89
source/BioGen.cpp
Normal file
89
source/BioGen.cpp
Normal file
@ -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)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
75
source/BioGen.h
Normal file
75
source/BioGen.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
186
source/BlockID.cpp
Normal file
186
source/BlockID.cpp
Normal file
@ -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;
|
||||
|
||||
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
// 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
|
||||
|
||||
|
||||
|
||||
|
||||
/// Biome IDs, as stored in the Anvil format and sent in the MapChunk packet
|
||||
enum eBiomeID
|
||||
{
|
||||
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,
|
||||
} ;
|
||||
// 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];
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
|
||||
sSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); // absolute block position
|
||||
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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
174
source/CompoGen.cpp
Normal file
174
source/CompoGen.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
101
source/CompoGen.h
Normal file
101
source/CompoGen.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
154
source/FinishGen.cpp
Normal file
154
source/FinishGen.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
82
source/FinishGen.h
Normal file
82
source/FinishGen.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
91
source/HeiGen.cpp
Normal file
91
source/HeiGen.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
64
source/HeiGen.h
Normal file
64
source/HeiGen.h
Normal file
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLightingThread::cLightingBuffer::GetFromWorld(cWorld * a_World)
|
||||
bool cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
|
||||
{
|
||||
// TODO: Set m_BlockData, m_SkyLight and m_BlockLight from the world's chunks
|
||||
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::cLightingBuffer::SetToWorld (cWorld * a_World)
|
||||
void cLightingThread::PrepareSkyLight(void)
|
||||
{
|
||||
// TODO: Set world's chunks from m_BlockData, m_SkyLight and m_BlockLight
|
||||
// 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::cLightingBuffer::Process(void)
|
||||
void cLightingThread::PrepareBlockLight(void)
|
||||
{
|
||||
// TODO: Does the actual lighting on this buffer
|
||||
// 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::CalcLight(NIBBLETYPE * a_Light)
|
||||
{
|
||||
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::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
|
||||
)
|
||||
{
|
||||
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::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
} ;
|
||||
|
||||
cCriticalSection m_CS;
|
||||
cLightingBufferQueue m_Queue;
|
||||
cEvent m_Event; // Set when queue is appended or to stop the thread
|
||||
typedef std::list<sItem> sItems;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
899
source/NBT.cpp
899
source/NBT.cpp
@ -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)
|
||||
|
||||
|
||||
|
||||
|
232
source/NBT.h
232
source/NBT.h
@ -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)
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
463
source/StructGen.cpp
Normal file
463
source/StructGen.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
122
source/StructGen.h
Normal file
122
source/StructGen.h
Normal file
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
546
source/Trees.cpp
Normal file
546
source/Trees.cpp
Normal file
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
85
source/Trees.h
Normal file
85
source/Trees.h
Normal file
@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -43,6 +43,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:
|
||||
|
||||
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -37,61 +37,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:
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 );
|
||||
};
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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) ) );
|
||||
|
@ -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 m_pWorldGenerator;
|
||||
m_pWorldGenerator = NULL;
|
||||
|
||||
// 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::GenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
|
||||
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";
|
||||
}
|
||||
|
||||
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::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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,8 +1,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
|
||||
|
||||
|
||||
|
||||
class cItem;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cFurnaceRecipe
|
||||
{
|
||||
public:
|
||||
@ -31,4 +39,8 @@ private:
|
||||
|
||||
struct sFurnaceRecipeState;
|
||||
sFurnaceRecipeState* m_pState;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class cGenSettings
|
||||
{
|
||||
public:
|
||||
static float HeightFreq1, HeightAmp1;
|
||||
static float HeightFreq2, HeightAmp2;
|
||||
static float HeightFreq3, HeightAmp3;
|
||||
};
|
@ -1,8 +1,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
|
||||
|
||||
|
||||
class cGroup //tolua_export
|
||||
{ //tolua_export
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,10 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "MemoryLeak.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class cPacket_BlockPlace;
|
||||
class cPacket_PickupSpawn;
|
||||
class cPacket_EntityEquipment;
|
||||
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#pragma once
|
||||
|
||||
class cPickup;
|
||||
class cPlayer;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
class cPlayerLogger : public cPlayerListCallback
|
||||
{
|
||||
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 )
|
||||
{
|
||||
// TODO: cWorld::ListMonsters();
|
||||
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");
|
||||
return;
|
||||
}
|
||||
//LOG("You didn't enter anything? (%s)", a_Cmd.c_str() );
|
||||
|
||||
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
|
||||
{
|
||||
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("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 )
|
||||
{
|
||||
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() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,7 +249,7 @@ cSocket cSocket::Accept()
|
||||
return SClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
// build tree
|
||||
for (int j = 0; j < trunk; j++)
|
||||
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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)
|
||||
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:
|
||||
{
|
||||
radius = 2;
|
||||
break;
|
||||
}
|
||||
for (int i = a_X - radius; i <= a_X + radius; i++)
|
||||
default:
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (GetBlock( a_X, a_Y+j, a_Z ) == E_BLOCK_AIR )
|
||||
{
|
||||
FastSetBlock( a_X, a_Y+j, a_Z, E_BLOCK_LOG, 0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for itr - b2[]
|
||||
|
||||
// All ok, replace blocks with the tree image:
|
||||
m_ChunkMap->ReplaceTreeBlocks(a_Blocks);
|
||||
}
|
||||
|
||||
// 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 );
|
||||
|
||||
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::SaveAllChunks()
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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); }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user