1
0
Fork 0

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:
madmaxoft@gmail.com 2012-05-25 07:18:52 +00:00
parent 5f29a3e134
commit a4a418a679
82 changed files with 5422 additions and 2961 deletions

View File

@ -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"
>

View File

@ -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" />

View File

@ -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>

View File

@ -37,6 +37,7 @@
#ifndef _WIN32
#include <cstring>
#include <sys/time.h>
#include <unistd.h>
#define SD_SEND 0x01
#else
#define MSG_NOSIGNAL (0)

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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
View 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
View 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
View 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;

View File

@ -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];

View File

@ -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;
} ;

View File

@ -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[]
}

View File

@ -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
View 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
View 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;
} ;

View File

@ -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

View File

@ -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
View 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
View 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;
} ;

View File

@ -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
View 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
View 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;
} ;

View File

@ -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;
}
}

View File

@ -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;
}
}
} ;

View File

@ -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)

View File

@ -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)

View File

@ -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
View 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
View 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
View 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
View 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);

View File

@ -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
}

View File

@ -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);
} ;

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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
{

View File

@ -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))
{

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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 );
};

View File

@ -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:

View File

@ -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;

View File

@ -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) ) );

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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; }

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
};
};

View File

@ -1,8 +1,16 @@
#pragma once
#include <list>
class cItem;
class cFurnaceRecipe
{
public:
@ -31,4 +39,8 @@ private:
struct sFurnaceRecipeState;
sFurnaceRecipeState* m_pState;
};
};

View File

@ -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;

View File

@ -1,9 +0,0 @@
#pragma once
class cGenSettings
{
public:
static float HeightFreq1, HeightAmp1;
static float HeightFreq2, HeightAmp2;
static float HeightFreq3, HeightAmp3;
};

View File

@ -1,8 +1,9 @@
#pragma once
#include <string>
#include <map>
#include <list>
class cGroup //tolua_export
{ //tolua_export

View File

@ -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;
};
};

View File

@ -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;
};

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -1,10 +1,5 @@
#pragma once
#include "MemoryLeak.h"
#include <vector>
#include <string>
class cPacket_BlockPlace;
class cPacket_PickupSpawn;
class cPacket_EntityEquipment;

View File

@ -1,7 +1,5 @@
#pragma once
#include <string>
#include <list>
#pragma once
class cPickup;
class cPlayer;

View File

@ -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

View File

@ -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;
};
};

View File

@ -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();

View File

@ -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

View File

@ -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;
};
};

View File

@ -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() );
}

View File

@ -249,7 +249,7 @@ cSocket cSocket::Accept()
return SClient;
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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

View File

@ -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
}

View File

@ -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);
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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));
}

View File

@ -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;

View File

@ -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); }