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