From 4f17362aeb80e5339c58a5d3b0fbaeb88d9e701c Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 13 Feb 2012 21:47:03 +0000 Subject: Rewritten most of the code for multithreading; still not 100%, but getting there. If this commit proves to be too problematic, we can always undo it. git-svn-id: http://mc-server.googlecode.com/svn/trunk@251 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Bindings.cpp | 182 +++-- source/Bindings.h | 2 +- source/Globals.h | 21 + source/LeakFinder.cpp | 1040 +++++++++++++++++++++++++++ source/LeakFinder.h | 145 ++++ source/MCSocket.h | 35 - source/PacketID.h | 104 +-- source/StackWalker.cpp | 1345 +++++++++++++++++++++++++++++++++++ source/StackWalker.h | 214 ++++++ source/WSSCompact.cpp | 415 +++++++++++ source/WSSCompact.h | 84 +++ source/WorldStorage.cpp | 256 +++++++ source/WorldStorage.h | 94 +++ source/cAuthenticator.cpp | 11 + source/cAuthenticator.h | 1 + source/cBlockEntity.h | 26 +- source/cBlockingTCPLink.cpp | 7 +- source/cBlockingTCPLink.h | 5 + source/cChestEntity.cpp | 56 +- source/cChestEntity.h | 31 +- source/cChunk.cpp | 704 +++++++++--------- source/cChunk.h | 129 +++- source/cChunkGenerator.cpp | 162 ++--- source/cChunkGenerator.h | 62 +- source/cChunkMap.cpp | 568 +++------------ source/cChunkMap.h | 89 ++- source/cClientHandle.cpp | 700 ++++++++++-------- source/cClientHandle.h | 76 +- source/cCriticalSection.cpp | 22 +- source/cCriticalSection.h | 27 +- source/cEntity.cpp | 276 ++++--- source/cEntity.h | 63 +- source/cFile.cpp | 23 +- source/cFile.h | 5 +- source/cFurnaceEntity.cpp | 62 +- source/cFurnaceEntity.h | 38 +- source/cGroup.h | 2 +- source/cIsThread.cpp | 9 +- source/cIsThread.h | 1 - source/cItem.h | 4 +- source/cMonster.cpp | 133 ++-- source/cMonster.h | 5 +- source/cPawn.cpp | 134 +++- source/cPawn.h | 24 +- source/cPickup.cpp | 99 +-- source/cPickup.h | 33 +- source/cPiston.cpp | 76 +- source/cPiston.h | 12 +- source/cPlayer.cpp | 200 +++--- source/cPlayer.h | 12 +- source/cPluginManager.h | 4 +- source/cRoot.h | 7 +- source/cServer.cpp | 34 +- source/cServer.h | 4 +- source/cSignEntity.cpp | 14 +- source/cSignEntity.h | 18 +- source/cSimulatorManager.cpp | 31 +- source/cSimulatorManager.h | 29 +- source/cSocket.cpp | 28 + source/cSocket.h | 2 + source/cSocketThreads.cpp | 18 +- source/cSocketThreads.h | 1 + source/cTCPLink.cpp | 90 +-- source/cTCPLink.h | 6 +- source/cWebAdmin.cpp | 33 +- source/cWorld.cpp | 855 +++++++++++----------- source/cWorld.h | 161 +++-- source/cWorldGenerator.cpp | 42 +- source/cWorldGenerator.h | 27 +- source/cWorldGenerator_Test.cpp | 4 +- source/cWorldGenerator_Test.h | 18 +- source/main.cpp | 55 +- source/packets/cPacket_MapChunk.cpp | 3 +- source/packets/cPacket_MapChunk.h | 16 +- source/packets/cPacket_Metadata.cpp | 14 + source/packets/cPacket_Metadata.h | 1 + source/ptr_cChunk.h | 37 - 77 files changed, 6689 insertions(+), 2687 deletions(-) create mode 100644 source/LeakFinder.cpp create mode 100644 source/LeakFinder.h delete mode 100644 source/MCSocket.h create mode 100644 source/StackWalker.cpp create mode 100644 source/StackWalker.h create mode 100644 source/WSSCompact.cpp create mode 100644 source/WSSCompact.h create mode 100644 source/WorldStorage.cpp create mode 100644 source/WorldStorage.h delete mode 100644 source/ptr_cChunk.h (limited to 'source') diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 4b0bb9f46..0c3cad075 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 02/08/12 12:55:58. +** Generated automatically by tolua++-1.0.92 on 02/13/12 11:40:58. */ #ifndef __cplusplus @@ -3160,14 +3160,14 @@ static int tolua_AllToLua_cEntity_GetWorld00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); #endif @@ -3192,14 +3192,14 @@ static int tolua_AllToLua_cEntity_GetPosition00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosition'", NULL); #endif @@ -3224,14 +3224,14 @@ static int tolua_AllToLua_cEntity_GetPosX00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosX'", NULL); #endif @@ -3256,14 +3256,14 @@ static int tolua_AllToLua_cEntity_GetPosY00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosY'", NULL); #endif @@ -3288,14 +3288,14 @@ static int tolua_AllToLua_cEntity_GetPosZ00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPosZ'", NULL); #endif @@ -3320,14 +3320,14 @@ static int tolua_AllToLua_cEntity_GetRot00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRot'", NULL); #endif @@ -3352,14 +3352,14 @@ static int tolua_AllToLua_cEntity_GetRotation00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRotation'", NULL); #endif @@ -3384,14 +3384,14 @@ static int tolua_AllToLua_cEntity_GetPitch00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetPitch'", NULL); #endif @@ -3416,14 +3416,14 @@ static int tolua_AllToLua_cEntity_GetRoll00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetRoll'", NULL); #endif @@ -3792,14 +3792,14 @@ static int tolua_AllToLua_cEntity_GetUniqueID00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetUniqueID'", NULL); #endif @@ -3824,14 +3824,14 @@ static int tolua_AllToLua_cEntity_IsDestroyed00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cEntity",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + const cEntity* self = (const cEntity*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsDestroyed'", NULL); #endif @@ -3929,12 +3929,12 @@ static int tolua_AllToLua_cEntity_SpawnOn00(lua_State* tolua_S) #endif { cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); - cClientHandle* a_Target = ((cClientHandle*) tolua_tousertype(tolua_S,2,0)); + cClientHandle* a_Client = ((cClientHandle*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SpawnOn'", NULL); #endif { - self->SpawnOn(a_Target); + self->SpawnOn(a_Client); } } return 0; @@ -4001,20 +4001,6 @@ public: return ( void )0; }; }; - void SpawnOn( cClientHandle* a_Target) { - if (push_method("SpawnOn", tolua_AllToLua_cEntity_SpawnOn00)) { - tolua_pushusertype(lua_state, (void*)a_Target, "cClientHandle"); - ToluaBase::dbcall(lua_state, 2, 0); - } else { - if (lua_state) - LOG("pure-virtual method cEntity::SpawnOn not implemented."); - else { - LOG("pure-virtual method cEntity::SpawnOn called with no lua_state. Aborting"); - ::abort(); - }; - return ( void )0; - }; - }; void cEntity__Initialize( cWorld* a_World) { return ( void )cEntity::Initialize(a_World); @@ -4662,20 +4648,6 @@ public: return ( void )0; }; }; - void SpawnOn( cClientHandle* a_Target) { - if (push_method("SpawnOn", tolua_AllToLua_cEntity_SpawnOn00)) { - tolua_pushusertype(lua_state, (void*)a_Target, "cClientHandle"); - ToluaBase::dbcall(lua_state, 2, 0); - } else { - if (lua_state) - LOG("pure-virtual method cPawn::SpawnOn not implemented."); - else { - LOG("pure-virtual method cPawn::SpawnOn called with no lua_state. Aborting"); - ::abort(); - }; - return ( void )0; - }; - }; void cPawn__TeleportTo( cEntity* a_Entity) { return ( void )cPawn::TeleportTo(a_Entity); @@ -5646,19 +5618,19 @@ static int tolua_AllToLua_cPlayer_GetColor00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cPlayer",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cPlayer",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + const cPlayer* self = (const cPlayer*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); #endif { - std::string tolua_ret = (std::string) self->GetColor(); + AString tolua_ret = (AString) self->GetColor(); tolua_pushcppstring(tolua_S,(const char*)tolua_ret); } } @@ -6058,20 +6030,6 @@ public: return ( void )0; }; }; - void SpawnOn( cClientHandle* a_Target) { - if (push_method("SpawnOn", tolua_AllToLua_cEntity_SpawnOn00)) { - tolua_pushusertype(lua_state, (void*)a_Target, "cClientHandle"); - ToluaBase::dbcall(lua_state, 2, 0); - } else { - if (lua_state) - LOG("pure-virtual method cPlayer::SpawnOn not implemented."); - else { - LOG("pure-virtual method cPlayer::SpawnOn called with no lua_state. Aborting"); - ::abort(); - }; - return ( void )0; - }; - }; void cPlayer__Initialize( cWorld* a_World) { return ( void )cPlayer::Initialize(a_World); @@ -10117,14 +10075,14 @@ static int tolua_AllToLua_cWorld_GetWorldSeed00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorldSeed'", NULL); #endif @@ -10149,20 +10107,20 @@ static int tolua_AllToLua_cWorld_GetName00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetName'", NULL); #endif { - const char* tolua_ret = (const char*) self->GetName(); - tolua_pushstring(tolua_S,(const char*)tolua_ret); + const AString tolua_ret = (const AString) self->GetName(); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); } } return 1; @@ -10212,14 +10170,14 @@ static int tolua_AllToLua_cWorld_GetNumChunks00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0); + const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumChunks'", NULL); #endif @@ -10743,14 +10701,14 @@ static int tolua_AllToLua_cItem_IsEmpty00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsEmpty'", NULL); #endif @@ -10775,7 +10733,7 @@ static int tolua_AllToLua_cItem_Equals00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"cItem",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"const cItem",0,&tolua_err) || (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"cItem",0,&tolua_err)) || !tolua_isnoobj(tolua_S,3,&tolua_err) ) @@ -10783,7 +10741,7 @@ static int tolua_AllToLua_cItem_Equals00(lua_State* tolua_S) else #endif { - cItem* self = (cItem*) tolua_tousertype(tolua_S,1,0); + const cItem* self = (const cItem*) tolua_tousertype(tolua_S,1,0); cItem* a_Item = ((cItem*) tolua_tousertype(tolua_S,2,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Equals'", NULL); @@ -11763,20 +11721,6 @@ public: return ( void )0; }; }; - void SpawnOn( cClientHandle* a_Target) { - if (push_method("SpawnOn", tolua_AllToLua_cEntity_SpawnOn00)) { - tolua_pushusertype(lua_state, (void*)a_Target, "cClientHandle"); - ToluaBase::dbcall(lua_state, 2, 0); - } else { - if (lua_state) - LOG("pure-virtual method cPickup::SpawnOn not implemented."); - else { - LOG("pure-virtual method cPickup::SpawnOn called with no lua_state. Aborting"); - ::abort(); - }; - return ( void )0; - }; - }; bool cPickup__CollectedBy( cPlayer* a_Dest) { return ( bool )cPickup::CollectedBy(a_Dest); @@ -12120,7 +12064,7 @@ static int tolua_AllToLua_cRoot_GetWorld00(lua_State* tolua_S) if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetWorld'", NULL); #endif { - cWorld* tolua_ret = (cWorld*) self->GetWorld(); + OBSOLETE cWorld* tolua_ret = (OBSOLETE cWorld*) self->GetWorld(); tolua_pushusertype(tolua_S,(void*)tolua_ret,"cWorld"); } } @@ -12387,6 +12331,38 @@ static int tolua_AllToLua_cRoot_ServerCommand00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: GetTotalChunkCount of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetTotalChunkCount00 +static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetTotalChunkCount'", NULL); +#endif + { + int tolua_ret = (int) self->GetTotalChunkCount(); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetTotalChunkCount'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: delete of class cTCPLink */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cTCPLink_delete00 static int tolua_AllToLua_cTCPLink_delete00(lua_State* tolua_S) @@ -12424,7 +12400,7 @@ static int tolua_AllToLua_cTCPLink_Connect00(lua_State* tolua_S) tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cTCPLink",0,&tolua_err) || - !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,0,&tolua_err) || !tolua_isnumber(tolua_S,3,0,&tolua_err) || !tolua_isnoobj(tolua_S,4,&tolua_err) ) @@ -12433,7 +12409,7 @@ static int tolua_AllToLua_cTCPLink_Connect00(lua_State* tolua_S) #endif { cTCPLink* self = (cTCPLink*) tolua_tousertype(tolua_S,1,0); - const char* a_Address = ((const char*) tolua_tostring(tolua_S,2,0)); + const AString a_Address = ((const AString) tolua_tocppstring(tolua_S,2,0)); unsigned int a_Port = ((unsigned int) tolua_tonumber(tolua_S,3,0)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Connect'", NULL); @@ -12441,9 +12417,10 @@ static int tolua_AllToLua_cTCPLink_Connect00(lua_State* tolua_S) { bool tolua_ret = (bool) self->Connect(a_Address,a_Port); tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Address); } } - return 1; + return 2; #ifndef TOLUA_RELEASE tolua_lerror: tolua_error(tolua_S,"#ferror in function 'Connect'.",&tolua_err); @@ -12470,7 +12447,7 @@ static int tolua_AllToLua_cTCPLink_Send00(lua_State* tolua_S) #endif { cTCPLink* self = (cTCPLink*) tolua_tousertype(tolua_S,1,0); - char* a_Data = ((char*) tolua_tostring(tolua_S,2,0)); + const char* a_Data = ((const char*) tolua_tostring(tolua_S,2,0)); unsigned int a_Size = ((unsigned int) tolua_tonumber(tolua_S,3,0)); int a_Flags = ((int) tolua_tonumber(tolua_S,4,0)); #ifndef TOLUA_RELEASE @@ -15919,7 +15896,7 @@ static int tolua_AllToLua_cGroup_GetColor00(lua_State* tolua_S) if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetColor'", NULL); #endif { - std::string tolua_ret = (std::string) self->GetColor(); + const AString tolua_ret = (const AString) self->GetColor(); tolua_pushcppstring(tolua_S,(const char*)tolua_ret); } } @@ -17752,6 +17729,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); tolua_function(tolua_S,"ServerCommand",tolua_AllToLua_cRoot_ServerCommand00); + tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00); tolua_endmodule(tolua_S); #ifdef __cplusplus tolua_cclass(tolua_S,"cTCPLink","cTCPLink","",tolua_collect_cTCPLink); diff --git a/source/Bindings.h b/source/Bindings.h index 941263827..369018ad5 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 02/08/12 12:55:58. +** Generated automatically by tolua++-1.0.92 on 02/13/12 11:40:58. */ /* Exported function */ diff --git a/source/Globals.h b/source/Globals.h index 36d174548..2718b8509 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -29,6 +29,8 @@ #include #include #include + #include + #include #include #include #include @@ -40,6 +42,7 @@ #include #include #include + #include #endif @@ -49,6 +52,7 @@ // CRT stuff: #include #include +#include @@ -95,11 +99,28 @@ #ifdef _MSC_VER #define OBSOLETE __declspec(deprecated) + #define ABSTRACT abstract #else // TODO: how do other compilers mark functions as obsolete, so that their usage results in a compile-time warning? #define OBSOLETE + // TODO: Can other compilers explicitly mark classes as abstract (no instances can be created)? + #define ABSTRACT #endif +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) ) + + + + + +/// A generic interface used in ForEach() functions +template class cListCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; diff --git a/source/LeakFinder.cpp b/source/LeakFinder.cpp new file mode 100644 index 000000000..1490025f8 --- /dev/null +++ b/source/LeakFinder.cpp @@ -0,0 +1,1040 @@ + +// LeakFinder.cpp + +// Finds memory leaks rather effectively + +// _X: downloaded from http://www.codeproject.com/Articles/3134/Memory-Leak-and-Exception-Trace-CRT-and-COM-Leaks - the real link is in the comments, RC11 version + + + + + +/********************************************************************** + * + * LEAKFINDER.CPP + * + * + * + * History: + * 2010-04-15 RC10 - Updated to VC10 RTM + * Fixed Bug: Application Verifier, thanks to handsinmypocket! + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3439751#xx3439751xx + * 2008-08-04 RC6 - Updated to VC9 RTM + * Fixed Bug: Missing "ole32.lib" LIB + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2253980#xx2253980xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Removed "#pragma init_seg (compiler)" from h-file + * + * 2005-12-30 RC5 - Now again VC8 RTM compatible + * - Added Xml-Output (like in the old Leakfinder) + * YOu need to define XML_LEAK_FINDER to activate it + * So you can use the LeakAnalyseTool from + * http://www.codeproject.com/tools/leakfinder.asp + * + * 2005-12-13 RC4 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-08-01 RC3 - Merged with the new "StackWalker"-project on + * http://www.codeproject.com/threads/StackWalker.asp + * + * 2005-07-05 RC2 - First version with x86, IA64 and x64 support + * + * 2005-07-04 RC1 - Added "OutputOptions" + * - New define "INIT_LEAK_FINDER_VERBOSE" to + * display more info (for error reporting) + * + * 2005-07-01 Beta3 - Workaround for a bug in the new dbghelp.dll + * (version 6.5.3.7 from 2005-05-30; StakWalk64 no + * refused to produce an callstack on x86 systems + * if the context is NULL or has some registers set + * to 0 (for example Esp). This is against the + * documented behaviour of StackWalk64...) + * - First version with x64-support + * + * 2005-06-16 Beta1 First public release with the following features: + * - Completely rewritten in C++ (object oriented) + * - CRT-Leak-Report + * - COM-Leak-Report + * - Report is done via "OutputDebugString" so + * the line can directly selected in the debugger + * and is opening the corresponding file/line of + * the allocation + * - Tried to support x64 systems, bud had some + * trouble wih StackWalk64 + * See: http://blog.kalmbachnet.de/?postid=43 + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +#include +#include // Needed if compiled with "WIN32_LEAN_AND_MEAN" +#include +#include +#include + +#include +#include + + +#include "LeakFinder.h" + +// Currently only tested with MS VC++ 5 to 10 +#if (_MSC_VER < 1100) || (_MSC_VER > 1600) +#error Only MS VC++ 5/6/7/7.1/8/9 supported. Check if the '_CrtMemBlockHeader' has not changed with this compiler! +#endif + + +// Controlling the callstack depth +#define MAX_CALLSTACK_LEN_BUF 0x2000 + +#define IGNORE_CRT_ALLOC + +// disable 64-bit compatibility-checks (because we explicite have here either x86 or x64!) +#pragma warning(disable:4312) // warning C4312: 'type cast' : conversion from 'DWORD' to 'LPCVOID' of greater size +#pragma warning(disable:4826) + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + +static std::string SimpleXMLEncode(LPCSTR szText) +{ + std::string szRet; + for (size_t i=0; i': + szRet.append(">"); + break; + case '"': + szRet.append("""); + break; + case '\'': + szRet.append("'"); + break; + default: + szRet += szText[i]; + } + } + return szRet; +} + + +LeakFinderOutput::LeakFinderOutput(int options, LPCSTR szSymPath) + : StackWalker(options, szSymPath) +{ +} +void LeakFinderOutput::OnLeakSearchStart(LPCSTR szLeakFinderName) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "######## %s ########\n", szLeakFinderName); + this->OnOutput(buffer); +} +void LeakFinderOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + CHAR buffer[1024]; + _snprintf_s(buffer, 1024, "--------------- Key: %s, %d bytes ---------\n", szKeyName, nDataSize); + this->OnOutput(buffer); +} +void LeakFinderOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if ( ((this->m_options & LeakFinderShowCompleteCallstack) == 0) && ( + (strstr(entry.lineFileName, "afxmem.cpp") != NULL) || + (strstr(entry.lineFileName, "dbgheap.c") != NULL) || + (strstr(entry.lineFileName, "new.cpp") != NULL) || + (strstr(entry.lineFileName, "newop.cpp") != NULL) || + (strstr(entry.lineFileName, "leakfinder.cpp") != NULL) || + (strstr(entry.lineFileName, "stackwalker.cpp") != NULL) + ) ) + { + return; + } + } + StackWalker::OnCallstackEntry(eType, entry); +} + + +// #################################################################### +// XML-Output +LeakFinderXmlOutput::LeakFinderXmlOutput() +{ + TCHAR szXMLFileName[1024]; + + GetModuleFileName(NULL, szXMLFileName, sizeof(szXMLFileName) / sizeof(TCHAR)); + _tcscat_s(szXMLFileName, _T(".mem.xml-leaks")); +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szXMLFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szXMLFileName, _T("w")); +#endif + if (m_fXmlFile != NULL) + { + SYSTEMTIME st; + GetLocalTime(&st); + fprintf(m_fXmlFile, "\n", + st.wMonth, st.wDay, st.wYear, + st.wHour, st.wMinute, st.wSecond); + } + else + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } +} +LeakFinderXmlOutput::LeakFinderXmlOutput(LPCTSTR szFileName) +{ +#if _MSC_VER < 1400 + m_fXmlFile = _tfopen(szFileName, _T("w")); +#else + m_fXmlFile = NULL; + _tfopen_s(&m_fXmlFile, szFileName, _T("w")); +#endif + if (m_fXmlFile == NULL) + { + MessageBox(NULL, _T("Could not open xml-logfile for leakfinder!"), _T("Warning"), MB_ICONHAND); + } +} +LeakFinderXmlOutput::~LeakFinderXmlOutput() +{ + if (m_fXmlFile != NULL) + { + // Write the ending-tags and close the file + fprintf(m_fXmlFile, "\n"); + fclose(m_fXmlFile); + } + m_fXmlFile = NULL; +} +void LeakFinderXmlOutput::OnLeakSearchStart(LPCSTR sszLeakFinderName) +{ +} +void LeakFinderXmlOutput::OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize) +{ + if (m_fXmlFile != NULL) + { + fprintf(m_fXmlFile, " \n", SimpleXMLEncode(szKeyName).c_str(), nDataSize); + } +} +void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + if (m_fXmlFile != NULL) + { + if (eType != lastEntry) + { + fprintf(m_fXmlFile, " \n"); + } + else + { + fprintf(m_fXmlFile, " \n"); + } + } +} + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Base class for storing contexts in a hashtable +template class ContextHashtableBase +{ +public: + ContextHashtableBase(SIZE_T sizeOfHastable, LPCSTR finderName) + { + SIZE_T s = sizeOfHastable*sizeof(AllocHashEntryType); + m_hHeap = HeapCreate(0, 10*1024 + s, 0); + if (m_hHeap == NULL) + throw; + pAllocHashTable = (AllocHashEntryType*) own_malloc(s); + sAllocEntries = sizeOfHastable; + m_finderName = own_strdup(finderName); + } + +protected: + virtual ~ContextHashtableBase() + { + if (pAllocHashTable != NULL) + own_free(pAllocHashTable); + pAllocHashTable = NULL; + + own_free(m_finderName); + m_finderName = NULL; + + if (m_hHeap != NULL) + HeapDestroy(m_hHeap); + } + + __inline LPVOID own_malloc(SIZE_T size) + { + return HeapAlloc(m_hHeap, HEAP_ZERO_MEMORY, size); + } + __inline VOID own_free(LPVOID memblock) + { + HeapFree(m_hHeap, 0, memblock); + } + __inline CHAR *own_strdup(const char *str) + { + size_t len = strlen(str)+1; + CHAR *c = (CHAR*)own_malloc(len); +#if _MSC_VER >= 1400 + strcpy_s(c, len, str); +#else + strcpy(c, str); +#endif + return c; + } + + // Disables this leak-finder + virtual LONG Disable() = 0; + // enables the leak-finder again... + virtual LONG Enable() = 0; + +private: + // Entry for each allocation + typedef struct AllocHashEntryType { + HASHTABLE_KEY key; + SIZE_T nDataSize; // Size of the allocated memory + struct AllocHashEntryType *Next; + CONTEXT c; + PVOID pStackBaseAddr; + SIZE_T nMaxStackSize; + + PVOID pCallstackOffset; + SIZE_T nCallstackLen; + char pcCallstackAddr[MAX_CALLSTACK_LEN_BUF]; // min of both values... + } AllocHashEntryType; + +protected: + virtual SIZE_T HashFunction(HASHTABLE_KEY &key) = 0; + virtual BOOL IsKeyEmpty(HASHTABLE_KEY &key) = 0; + virtual VOID SetEmptyKey(HASHTABLE_KEY &key) = 0; + virtual VOID GetKeyAsString(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) = 0; + //virtual SIZE_T GetNativeBytes(HASHTABLE_KEY &key, CHAR *szName, SIZE_T nBufferLen) { return 0; } + +public: + VOID Insert(HASHTABLE_KEY &key, CONTEXT &context, SIZE_T nDataSize) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // generate hash-value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + if (IsKeyEmpty(pHashEntry->key) != FALSE) { + // Entry is empty... + } + else { + // Entry is not empy! make a list of entries for this hash value... + while(pHashEntry->Next != NULL) { + pHashEntry = pHashEntry->Next; + } + + pHashEntry->Next = (AllocHashEntryType*) own_malloc(sizeof(AllocHashEntryType)); + pHashEntry = pHashEntry->Next; + } + pHashEntry->key = key; + pHashEntry->nDataSize = nDataSize; + pHashEntry->Next = NULL; +#ifdef _M_IX86 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp); +#elif _M_X64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp); +#elif _M_IA64 + pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP); +#else +#error "Platform not supported!" +#endif + pHashEntry->c = context; + + // Query the max. stack-area: + MEMORY_BASIC_INFORMATION MemBuffer; + if(VirtualQuery((LPCVOID) pHashEntry->pCallstackOffset, &MemBuffer, sizeof(MemBuffer)) > 0) + { + pHashEntry->pStackBaseAddr = MemBuffer.BaseAddress; + pHashEntry->nMaxStackSize = MemBuffer.RegionSize; + } + else + { + pHashEntry->pStackBaseAddr = 0; + pHashEntry->nMaxStackSize = 0; + } + + SIZE_T bytesToRead = MAX_CALLSTACK_LEN_BUF; + if (pHashEntry->nMaxStackSize > 0) + { + SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset; + bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF); + } + // Now read the callstack: + if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0) + { + // Could not read memory... + pHashEntry->nCallstackLen = 0; + pHashEntry->pCallstackOffset = 0; + } // read callstack + } // Insert + + BOOL Remove(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry, *pHashEntryLast; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntryLast = NULL; + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + // release my memory + if (pHashEntryLast == NULL) { + // It is an entry in the table, so do not release this memory + if (pHashEntry->Next == NULL) { + // It was the last entry, so empty the table entry + SetEmptyKey(pAllocHashTable[HashIdx].key); + //memset(&pAllocHashTable[HashIdx], 0, sizeof(pAllocHashTable[HashIdx])); + } + else { + // There are some more entries, so shorten the list + AllocHashEntryType *pTmp = pHashEntry->Next; + *pHashEntry = *(pHashEntry->Next); + own_free(pTmp); + } + return TRUE; + } + else { + // now, I am in an dynamic allocated entry (it was a collision) + pHashEntryLast->Next = pHashEntry->Next; + own_free(pHashEntry); + return TRUE; + } + } + pHashEntryLast = pHashEntry; + pHashEntry = pHashEntry->Next; + } + + // if we are here, we could not find the RequestID + return FALSE; + } + + AllocHashEntryType *Find(HASHTABLE_KEY &key) + { + SIZE_T HashIdx; + AllocHashEntryType *pHashEntry; + + // get the Hash-Value + HashIdx = HashFunction(key); + + pHashEntry = &pAllocHashTable[HashIdx]; + while(pHashEntry != NULL) { + if (pHashEntry->key == key) { + return pHashEntry; + } + pHashEntry = pHashEntry->Next; + } + + // entry was not found! + return NULL; + } + + // For the followong static-var See comment in "ShowCallstack"... + static BOOL CALLBACK ReadProcessMemoryFromHashEntry64( + HANDLE hProcess, // hProcess must be a pointer to an hash-entry! + DWORD64 lpBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ) + { + *lpNumberOfBytesRead = 0; + AllocHashEntryType *pHashEntry = (AllocHashEntryType*) pUserData; + if (pHashEntry == NULL) + { + return FALSE; + } + + if ( ( (DWORD64)lpBaseAddress >= (DWORD64)pHashEntry->pCallstackOffset) && ((DWORD64)lpBaseAddress <= ((DWORD64)pHashEntry->pCallstackOffset+pHashEntry->nCallstackLen)) ) { + // Memory is located in saved Callstack: + // Calculate the offset + DWORD dwOffset = (DWORD) ((DWORD64)lpBaseAddress - (DWORD64)pHashEntry->pCallstackOffset); + DWORD dwSize = __min(nSize, MAX_CALLSTACK_LEN_BUF-dwOffset); + memcpy(lpBuffer, &(pHashEntry->pcCallstackAddr[dwOffset]), dwSize); + *lpNumberOfBytesRead = dwSize; + if (dwSize != nSize) + { + return FALSE; + } + *lpNumberOfBytesRead = nSize; + return TRUE; + } + + if (*lpNumberOfBytesRead == 0) // Memory could not be found + { + if ( ( (DWORD64)lpBaseAddress < (DWORD64)pHashEntry->pStackBaseAddr) || ((DWORD64)lpBaseAddress > ((DWORD64)pHashEntry->pStackBaseAddr+pHashEntry->nMaxStackSize)) ) + { + // Stackwalking is done by reading the "real memory" (normally this happens when the StackWalk64 tries to read some code) + SIZE_T st = 0; + BOOL bRet = ReadProcessMemory(hProcess, (LPCVOID) lpBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + return bRet; + } + } + + return TRUE; + } + + VOID ShowLeaks(LeakFinderOutput &leakFinderOutput) + { + SIZE_T ulTemp; + AllocHashEntryType *pHashEntry; + ULONG ulCount = 0; + SIZE_T ulLeaksByte = 0; + + leakFinderOutput.OnLeakSearchStart(this->m_finderName); + + // Move throu every entry + CHAR keyName[1024]; + for(ulTemp = 0; ulTemp < this->sAllocEntries; ulTemp++) { + pHashEntry = &pAllocHashTable[ulTemp]; + if (IsKeyEmpty(pHashEntry->key) == FALSE) { + while(pHashEntry != NULL) { + ulCount++; + CONTEXT c; + memcpy(&c, &(pHashEntry->c), sizeof(CONTEXT)); + + this->GetKeyAsString(pHashEntry->key, keyName, 1024); + + leakFinderOutput.OnLeakStartEntry(keyName, pHashEntry->nDataSize); + leakFinderOutput.ShowCallstack(GetCurrentThread(), &c, ReadProcessMemoryFromHashEntry64, pHashEntry); + + // Count the number of leaky bytes + ulLeaksByte += pHashEntry->nDataSize; + + pHashEntry = pHashEntry->Next; + } // while + } + } + } + + AllocHashEntryType *pAllocHashTable; + SIZE_T sAllocEntries; + HANDLE m_hHeap; + LPSTR m_finderName; + bool m_bSupressUselessLines; +}; // template class ContextHashtableBase + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Specialization for CRT-Leaks: +// VC5 has excluded all types in release-builds +#ifdef _DEBUG + +// The follwoing is copied from dbgint.h: +// +/* +* For diagnostic purpose, blocks are allocated with extra information and +* stored in a doubly-linked list. This makes all blocks registered with +* how big they are, when they were allocated, and what they are used for. +*/ + +// forward declaration: +#ifndef _M_CEE_PURE +#define MyAllocHookCallingConvention __cdecl +#endif +#if _MSC_VER >= 1400 +#ifdef _M_CEE +#define MyAllocHookCallingConvention __clrcall +#endif +#endif + +static int MyAllocHookCallingConvention MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 and before + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine); + +static _CRT_ALLOC_HOOK s_pfnOldCrtAllocHook = NULL; +static LONG s_CrtDisableCount = 0; +static LONG s_lMallocCalled = 0; + + +class CRTTable : public ContextHashtableBase +{ +public: + CRTTable() : ContextHashtableBase(1021, "CRT-Leaks") + { + // save the previous alloc hook + s_pfnOldCrtAllocHook = _CrtSetAllocHook(MyAllocHook); + } + + virtual ~CRTTable() + { + _CrtSetAllocHook(s_pfnOldCrtAllocHook); + } + + virtual LONG Disable() + { + return InterlockedIncrement(&s_CrtDisableCount); + } + virtual LONG Enable() + { + return InterlockedDecrement(&s_CrtDisableCount); + } + + virtual SIZE_T HashFunction(LONG &key) + { + // I couldn´t find any better and faster + return key % sAllocEntries; + } + virtual BOOL IsKeyEmpty(LONG &key) + { + if (key == 0) + return TRUE; + return FALSE; + } + virtual VOID SetEmptyKey(LONG &key) + { + key = 0; + } + virtual VOID GetKeyAsString(LONG &key, CHAR *szName, SIZE_T nBufferLen) + { +#if _MSC_VER < 1400 + _snprintf_s(szName, nBufferLen, "%d", key); +#else + _snprintf_s(szName, nBufferLen, nBufferLen, "%d", key); +#endif + } + +protected: + CHAR *m_pBuffer; + SIZE_T m_maxBufferLen; + SIZE_T m_bufferLen; +}; // class CRTTable + + +#define nNoMansLandSize 4 + +typedef struct _CrtMemBlockHeader +{ + struct _CrtMemBlockHeader * pBlockHeaderNext; + struct _CrtMemBlockHeader * pBlockHeaderPrev; + char * szFileName; + int nLine; +#ifdef _WIN64 + /* These items are reversed on Win64 to eliminate gaps in the struct + * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is + * maintained in the debug heap. + */ + int nBlockUse; + size_t nDataSize; +#else /* _WIN64 */ + size_t nDataSize; + int nBlockUse; +#endif /* _WIN64 */ + long lRequest; + unsigned char gap[nNoMansLandSize]; + /* followed by: + * unsigned char data[nDataSize]; + * unsigned char anotherGap[nNoMansLandSize]; + */ +} _CrtMemBlockHeader; +#define pbData(pblock) ((unsigned char *)((_CrtMemBlockHeader *)pblock + 1)) +#define pHdr(pbData) (((_CrtMemBlockHeader *)pbData)-1) +// + +static CRTTable *g_pCRTTable = NULL; + + +// MyAllocHook is Single-Threaded, that means the the calls are serialized in the calling function! +static int MyAllocHook(int nAllocType, void *pvData, + size_t nSize, int nBlockUse, long lRequest, +#if _MSC_VER <= 1100 // Special case for VC 5 + const char * szFileName, +#else + const unsigned char * szFileName, +#endif + int nLine) +{ + //static TCHAR *operation[] = { _T(""), _T("ALLOCATIONG"), _T("RE-ALLOCATING"), _T("FREEING") }; + //static TCHAR *blockType[] = { _T("Free"), _T("Normal"), _T("CRT"), _T("Ignore"), _T("Client") }; + +#ifdef IGNORE_CRT_ALLOC + if (_BLOCK_TYPE(nBlockUse) == _CRT_BLOCK) // Ignore internal C runtime library allocations + return TRUE; +#endif + extern int _crtDbgFlag; + if ( ((_CRTDBG_ALLOC_MEM_DF & _crtDbgFlag) == 0) && ( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) ) ) + { + // Someone has disabled that the runtime should log this allocation + // so we do not log this allocation + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + // Handle the Disable/Enable setting + if (InterlockedExchangeAdd(&s_CrtDisableCount, 0) != 0) + return TRUE; + + // Prevent from reentrat calls + if (InterlockedIncrement(&s_lMallocCalled) > 1) { // I was already called + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + _ASSERT( (nAllocType == _HOOK_ALLOC) || (nAllocType == _HOOK_REALLOC) || (nAllocType == _HOOK_FREE) ); + _ASSERT( ( _BLOCK_TYPE(nBlockUse) >= 0 ) && ( _BLOCK_TYPE(nBlockUse) < 5 ) ); + + if (nAllocType == _HOOK_FREE) { // freeing + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + nSize = pHead->nDataSize; + lRequest = pHead->lRequest; // This is the ID! + + if (pHead->nBlockUse == _IGNORE_BLOCK) + { + InterlockedDecrement(&s_lMallocCalled); + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + } + if (lRequest != 0) { // RequestID was found + g_pCRTTable->Remove(lRequest); + } + } // freeing + + if (nAllocType == _HOOK_REALLOC) { // re-allocating + // Try to get the header information + if (_CrtIsValidHeapPointer(pvData)) { // it is a valid Heap-Pointer + BOOL bRet; + LONG lReallocRequest; + // get the ID + _CrtMemBlockHeader *pHead; + // get a pointer to memory block header + pHead = pHdr(pvData); + // Try to find the RequestID in the Hash-Table, mark it that it was freed + lReallocRequest = pHead->lRequest; + bRet = g_pCRTTable->Remove(lReallocRequest); + } // ValidHeapPointer + } // re-allocating + + //if ( (g_ulShowStackAtAlloc < 3) && (nAllocType == _HOOK_FREE) ) { + if (nAllocType == _HOOK_FREE) { + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; + } + + CONTEXT c; + GET_CURRENT_CONTEXT(c, CONTEXT_FULL); + + // Only insert in the Hash-Table if it is not a "freeing" + if (nAllocType != _HOOK_FREE) { + if(lRequest != 0) // Always a valid RequestID should be provided (see comments in the header) + g_pCRTTable->Insert(lRequest, c, nSize); + } + + InterlockedDecrement(&s_lMallocCalled); + // call the previous alloc hook + if (s_pfnOldCrtAllocHook != NULL) + s_pfnOldCrtAllocHook(nAllocType, pvData, nSize, nBlockUse, lRequest, szFileName, nLine); + return TRUE; // allow the memory operation to proceed +} // MyAllocHook + +#endif // _DEBUG + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Specialization for COM-Leaks: + +// forwards: +class COMTable; +class CMallocSpy : public IMallocSpy +{ +public: + CMallocSpy() { m_cbRequest = 0; m_cRef = 0; m_disableCount = 0; } + virtual ~CMallocSpy() {} + // IUnknown methods + STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppUnk); + STDMETHOD_(ULONG, AddRef) (); + STDMETHOD_(ULONG, Release) (); + // IMallocSpy methods + STDMETHOD_(SIZE_T, PreAlloc) (SIZE_T cbRequest); + STDMETHOD_(void *, PostAlloc) (void *pActual); + STDMETHOD_(void *, PreFree) (void *pRequest, BOOL fSpyed); + STDMETHOD_(void, PostFree) (BOOL fSpyed) { return; }; + STDMETHOD_(SIZE_T, PreRealloc) (void *pRequest, SIZE_T cbRequest, void **ppNewRequest, BOOL fSpyed); + STDMETHOD_(void *, PostRealloc) (void *pActual, BOOL fSpyed); + STDMETHOD_(void *, PreGetSize) (void *pRequest, BOOL fSpyed) { return pRequest; } + STDMETHOD_(SIZE_T, PostGetSize) (SIZE_T cbActual, BOOL fSpyed) { return cbActual; } + STDMETHOD_(void *, PreDidAlloc) (void *pRequest, BOOL fSpyed) { return pRequest; } + STDMETHOD_(BOOL, PostDidAlloc) (void *pRequest, BOOL fSpyed, BOOL fActual) { return fActual; } + STDMETHOD_(void, PreHeapMinimize) (void) { return; } + STDMETHOD_(void, PostHeapMinimize) (void) { return; } +private: + LONG m_cRef; + SIZE_T m_cbRequest; +protected: + COMTable *m_pComTable; + LONG m_disableCount; + friend COMTable; +}; + +class COMTable : public ContextHashtableBase +{ +public: + COMTable() : ContextHashtableBase(1021, "COM-Leaks") + { + m_pMallocSpy = new CMallocSpy(); // wird später durch Release freigegeben + if (m_pMallocSpy != NULL) + { + m_pMallocSpy->m_pComTable = this; + // CoInitilize(); // ??? Is this necessary ? + HRESULT hr = CoRegisterMallocSpy(m_pMallocSpy); + if FAILED(hr) + { + _tprintf(_T("\nCoRegisterMallocSpay failed with %.8x"), hr); + } + } + } + + virtual ~COMTable() + { + if (m_pMallocSpy != NULL) + m_pMallocSpy->m_pComTable = NULL; + CoRevokeMallocSpy(); + } + + virtual LONG Disable() + { + return InterlockedIncrement(&(m_pMallocSpy->m_disableCount)); + } + virtual LONG Enable() + { + return InterlockedDecrement(&(m_pMallocSpy->m_disableCount)); + } + + virtual SIZE_T HashFunction(LPVOID &key) + { + // I couldn´t find any better and faster +#ifdef _M_IX86 +#if _MSC_VER > 1100 +#pragma warning (push) +#endif +#pragma warning (disable: 4311) + DWORD llP = (DWORD) key; +#if _MSC_VER > 1100 +#pragma warning (pop) +#endif +#else + ULONGLONG llP = (ULONGLONG) key; +#endif + return (SIZE_T) llP % sAllocEntries; + } + virtual BOOL IsKeyEmpty(LPVOID &key) + { + if (key == 0) + return TRUE; + return FALSE; + } + virtual VOID SetEmptyKey(LPVOID &key) + { + key = 0; + } + virtual VOID GetKeyAsString(LPVOID &key, CHAR *szName, SIZE_T nBufferLen) + { +#if _MSC_VER < 1400 + _snprintf_s(szName, nBufferLen, "%p", key); +#else + _snprintf_s(szName, nBufferLen, nBufferLen, "%p", key); +#endif + } + + CMallocSpy *m_pMallocSpy; + friend CMallocSpy; +}; // class COMTable + + +STDMETHODIMP CMallocSpy::QueryInterface(REFIID riid, LPVOID *ppUnk) { + HRESULT hr = S_OK; + if (IsEqualIID(riid, IID_IUnknown)) { + *ppUnk = (IUnknown *) this; + } + else if (IsEqualIID(riid, IID_IMallocSpy)) { + *ppUnk = (IMalloc *) this; + } + else { + *ppUnk = NULL; + hr = E_NOINTERFACE; + } + AddRef(); + return hr; +} +STDMETHODIMP_(ULONG) CMallocSpy::AddRef(void) { + return (ULONG) InterlockedIncrement(&m_cRef); +} +STDMETHODIMP_(ULONG) CMallocSpy::Release(void) { + LONG cRef; + cRef = InterlockedDecrement(&m_cRef); + if (cRef == 0) + { + delete this; + } + return (ULONG) cRef; +} +// IMallocSpy methods +STDMETHODIMP_(SIZE_T) CMallocSpy::PreAlloc(SIZE_T cbRequest) { + m_cbRequest = cbRequest; + return cbRequest; +} +STDMETHODIMP_(void *) CMallocSpy::PostAlloc(void *pActual) { + if (m_pComTable != NULL) + { + CONTEXT c; + GET_CURRENT_CONTEXT(c, CONTEXT_FULL); + m_pComTable->Insert(pActual, c, m_cbRequest); + } + return pActual; +} +STDMETHODIMP_(void *) CMallocSpy::PreFree(void *pRequest, BOOL fSpyed) { + if (m_pComTable != NULL) + { + m_pComTable->Remove(pRequest); + } + return pRequest; +} +STDMETHODIMP_(SIZE_T) CMallocSpy::PreRealloc(void *pRequest, SIZE_T cbRequest, + void **ppNewRequest, BOOL fSpyed) { + if (m_pComTable != NULL) + { + m_pComTable->Remove(pRequest); + } + *ppNewRequest = pRequest; // Bug fixed. Thanx to Christoph Weber + return cbRequest; +} +STDMETHODIMP_(void *) CMallocSpy::PostRealloc(void *pActual, BOOL fSpyed) { + if (m_pComTable != NULL) + { + CONTEXT c; + GET_CURRENT_CONTEXT(c, CONTEXT_FULL); + m_pComTable->Insert(pActual, c, m_cbRequest); + } + return pActual; +} + + + + +// ########################################################################## +// ########################################################################## +// ########################################################################## +// Init/Deinit functions + + +static COMTable *g_pCOMTable; +HRESULT InitLeakFinder() +{ + // _X: Disabled COM monitoring: g_pCOMTable = new COMTable(); +#ifdef _DEBUG + g_pCRTTable = new CRTTable(); +#endif + return S_OK; +} + +void DeinitLeakFinder(LeakFinderOutput *output) +{ + LeakFinderOutput *pLeakFinderOutput = output; + +#ifdef _DEBUG + g_pCRTTable->Disable(); +#endif + // _X: Disabled COM monitoring: g_pCOMTable->Disable(); + + if (pLeakFinderOutput == NULL) + pLeakFinderOutput = new LeakFinderOutput(); + + // explicite load the modules: + pLeakFinderOutput->LoadModules(); + +#ifdef _DEBUG + g_pCRTTable->ShowLeaks(*pLeakFinderOutput); + if (g_pCRTTable != NULL) + delete g_pCRTTable; + g_pCRTTable = NULL; +#endif + + /* + // _X: Disabled COM monitoring: + g_pCOMTable->ShowLeaks(*pLeakFinderOutput); + if (g_pCOMTable != NULL) + delete g_pCOMTable; + g_pCOMTable = NULL; + */ + + if (output == NULL) + delete pLeakFinderOutput; +} +void DeinitLeakFinder() +{ + DeinitLeakFinder(NULL); +} diff --git a/source/LeakFinder.h b/source/LeakFinder.h new file mode 100644 index 000000000..77f221bca --- /dev/null +++ b/source/LeakFinder.h @@ -0,0 +1,145 @@ +/********************************************************************** + * + * LEAKFINDER.H + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ + +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +HRESULT InitLeakFinder(); +void DeinitLeakFinder(); + +#ifdef __cplusplus +} +#endif + + +// The following is only available if the file is CPP +#ifdef __cplusplus + +#include "StackWalker.h" + +// Interface for output... +class LeakFinderOutput : public StackWalker +{ +public: + typedef enum LeakFinderOptions + { + // No addition info will be retrived + // (only the address is available) + LeakFinderNone = 0, + LeakFinderShowCompleteCallstack = 0x1000 + } LeakFinderOptions; + + LeakFinderOutput(int options = OptionsAll, LPCSTR szSymPath = NULL); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) + { + printf(szText); + StackWalker::OnOutput(szText); + } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) + { + if (strcmp(szFuncName, "SymGetLineFromAddr64") == 0) return; + StackWalker::OnDbgHelpErr(szFuncName, gle, addr); + } +}; + +class LeakFinderXmlOutput : public LeakFinderOutput +{ +public: + LeakFinderXmlOutput(); + virtual ~LeakFinderXmlOutput(); + LeakFinderXmlOutput(LPCTSTR szFileName); + virtual void OnLeakSearchStart(LPCSTR sszLeakFinderName); + virtual void OnLeakStartEntry(LPCSTR szKeyName, SIZE_T nDataSize); +protected: + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnOutput(LPCSTR szText) { } + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) { } + + FILE *m_fXmlFile; +}; + +// C++ interface: +void DeinitLeakFinder(LeakFinderOutput *output); + +class ZZZ_LeakFinder +{ +public: + ZZZ_LeakFinder() + { + m_pXml = NULL; +#ifdef XML_LEAK_FINDER + m_pXml = new LeakFinderXmlOutput(); +#endif + InitLeakFinder(); + } + ~ZZZ_LeakFinder() + { + DeinitLeakFinder(m_pXml); + if (m_pXml != NULL) delete m_pXml; + } +protected: + LeakFinderXmlOutput *m_pXml; +}; + +#if defined(INIT_LEAK_FINDER) +#if _MSC_VER >= 1200 +#pragma warning(push) +#endif +#pragma warning (disable:4074) + +// WARNING: If you enable this option, the code might run without the CRT being initialized or after the CRT was deinitialized!!! +// Currently the code is not designed to bypass the CRT... +//#pragma init_seg (compiler) +ZZZ_LeakFinder zzz_LeakFinder; + +#if _MSC_VER >= 1200 +#pragma warning(pop) +#else +#pragma warning(default:4074) +#endif +#endif + +#endif // __cplusplus diff --git a/source/MCSocket.h b/source/MCSocket.h deleted file mode 100644 index 30aa20bdd..000000000 --- a/source/MCSocket.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#ifndef _WIN32 - // Linux threads http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html - #include - #include - #include - #include - #include - - // TODO: We shouldn't need these! Use the OS support objects instead - #define SOCKET int - typedef void *HANDLE; - #define CRITICAL_SECTION pthread_mutex_t - #define SD_BOTH (2) - #define closesocket(x) (shutdown(x, SD_BOTH), close(x)) - #define SOCKET_ERROR SO_ERROR - #define EnterCriticalSection(x) pthread_mutex_lock(x) - #define LeaveCriticalSection(x) pthread_mutex_unlock(x) - #define InitializeCriticalSection(x) pthread_mutex_init(x, NULL) - #define DeleteCriticalSection(x) (x) -#endif - - - - - -inline bool IsSocketError( int a_ReturnedValue ) -{ - #ifdef _WIN32 - return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); - #else - return (a_ReturnedValue <= 0); - #endif -} diff --git a/source/PacketID.h b/source/PacketID.h index bc18cf9c0..374947b22 100644 --- a/source/PacketID.h +++ b/source/PacketID.h @@ -3,57 +3,57 @@ //tolua_begin enum ENUM_PACKET_ID { - E_KEEP_ALIVE = 0x00, - E_LOGIN = 0x01, - E_HANDSHAKE = 0x02, - E_CHAT = 0x03, - E_UPDATE_TIME = 0x04, - E_ENTITY_EQUIPMENT = 0x05, - E_USE_ENTITY = 0x07, - E_UPDATE_HEALTH = 0x08, - E_RESPAWN = 0x09, - E_FLYING = 0x0a, - E_PLAYERPOS = 0x0b, - E_PLAYERLOOK = 0x0c, - E_PLAYERMOVELOOK = 0x0d, - E_BLOCK_DIG = 0x0e, - E_BLOCK_PLACE = 0x0f, - E_ITEM_SWITCH = 0x10, - E_ADD_TO_INV = 0x11, //TODO: Sure this is not Use Bed?? - E_ANIMATION = 0x12, - E_PACKET_13 = 0x13, - E_NAMED_ENTITY_SPAWN= 0x14, - E_PICKUP_SPAWN = 0x15, - E_COLLECT_ITEM = 0x16, - E_ADD_VEHICLE = 0x17, - E_SPAWN_MOB = 0x18, - E_DESTROY_ENT = 0x1d, - E_ENTITY = 0x1e, - E_REL_ENT_MOVE = 0x1f, - E_ENT_LOOK = 0x20, - E_REL_ENT_MOVE_LOOK = 0x21, - E_ENT_TELEPORT = 0x22, - E_ENT_STATUS = 0x26, - E_METADATA = 0x28, - E_PRE_CHUNK = 0x32, - E_MAP_CHUNK = 0x33, - E_MULTI_BLOCK = 0x34, - E_BLOCK_CHANGE = 0x35, - E_BLOCK_ACTION = 0x36, - E_EXPLOSION = 0x3C, - E_SOUND_EFFECT = 0x3D, - E_NEW_INVALID_STATE = 0x46, - E_THUNDERBOLT = 0x47, - E_WINDOW_OPEN = 0x64, - E_WINDOW_CLOSE = 0x65, - E_WINDOW_CLICK = 0x66, - E_INVENTORY_SLOT = 0x67, - E_INVENTORY_WHOLE = 0x68, - E_INVENTORY_PROGRESS= 0x69, - E_CREATIVE_INVENTORY_ACTION = 0x6B, - E_UPDATE_SIGN = 0x82, - E_PLAYER_LIST_ITEM = 0xC9, - E_PING = 0xfe, - E_DISCONNECT = 0xff, + E_KEEP_ALIVE = 0x00, + E_LOGIN = 0x01, + E_HANDSHAKE = 0x02, + E_CHAT = 0x03, + E_UPDATE_TIME = 0x04, + E_ENTITY_EQUIPMENT = 0x05, + E_USE_ENTITY = 0x07, + E_UPDATE_HEALTH = 0x08, + E_RESPAWN = 0x09, + E_FLYING = 0x0a, + E_PLAYERPOS = 0x0b, + E_PLAYERLOOK = 0x0c, + E_PLAYERMOVELOOK = 0x0d, + E_BLOCK_DIG = 0x0e, + E_BLOCK_PLACE = 0x0f, + E_ITEM_SWITCH = 0x10, + E_ADD_TO_INV = 0x11, // TODO: Sure this is not Use Bed?? + E_ANIMATION = 0x12, + E_PACKET_13 = 0x13, + E_NAMED_ENTITY_SPAWN = 0x14, + E_PICKUP_SPAWN = 0x15, + E_COLLECT_ITEM = 0x16, + E_ADD_VEHICLE = 0x17, + E_SPAWN_MOB = 0x18, + E_DESTROY_ENT = 0x1d, + E_ENTITY = 0x1e, + E_REL_ENT_MOVE = 0x1f, + E_ENT_LOOK = 0x20, + E_REL_ENT_MOVE_LOOK = 0x21, + E_ENT_TELEPORT = 0x22, + E_ENT_STATUS = 0x26, + E_METADATA = 0x28, + E_PRE_CHUNK = 0x32, + E_MAP_CHUNK = 0x33, + E_MULTI_BLOCK = 0x34, + E_BLOCK_CHANGE = 0x35, + E_BLOCK_ACTION = 0x36, + E_EXPLOSION = 0x3C, + E_SOUND_EFFECT = 0x3D, + E_NEW_INVALID_STATE = 0x46, + E_THUNDERBOLT = 0x47, + E_WINDOW_OPEN = 0x64, + E_WINDOW_CLOSE = 0x65, + E_WINDOW_CLICK = 0x66, + E_INVENTORY_SLOT = 0x67, + E_INVENTORY_WHOLE = 0x68, + E_INVENTORY_PROGRESS = 0x69, + E_CREATIVE_INVENTORY_ACTION = 0x6B, + E_UPDATE_SIGN = 0x82, + E_PLAYER_LIST_ITEM = 0xC9, + E_PING = 0xfe, + E_DISCONNECT = 0xff, }; //tolua_end diff --git a/source/StackWalker.cpp b/source/StackWalker.cpp new file mode 100644 index 000000000..37db4a563 --- /dev/null +++ b/source/StackWalker.cpp @@ -0,0 +1,1345 @@ +/********************************************************************** + * + * StackWalker.cpp + * + * + * History: + * 2005-07-27 v1 - First public release on http://www.codeproject.com/ + * http://www.codeproject.com/threads/StackWalker.asp + * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack + * (to simplify the usage) + * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL + * (should also be enough) + * - Changed to compile correctly with the PSDK of VC7.0 + * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: + * it uses LPSTR instead of LPCSTR as first paremeter) + * - Added declarations to support VC5/6 without using 'dbghelp.h' + * - Added a 'pUserData' member to the ShowCallstack function and the + * PReadProcessMemoryRoutine declaration (to pass some user-defined data, + * which can be used in the readMemoryFunction-callback) + * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default + * - Added example for doing an exception-callstack-walking in main.cpp + * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) + * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! + * 2008-08-04 v6 - Fixed Bug: Missing LEAK-end-tag + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2502890#xx2502890xx + * Fixed Bug: Compiled with "WIN32_LEAN_AND_MEAN" + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=1824718#xx1824718xx + * Fixed Bug: Compiling with "/Wall" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2638243#xx2638243xx + * Fixed Bug: Now checking SymUseSymSrv + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1388979#xx1388979xx + * Fixed Bug: Support for recursive function calls + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1434538#xx1434538xx + * Fixed Bug: Missing FreeLibrary call in "GetModuleListTH32" + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=1326923#xx1326923xx + * Fixed Bug: SymDia is number 7, not 9! + * 2008-09-11 v7 For some (undocumented) reason, dbhelp.h is needing a packing of 8! + * Thanks to Teajay which reported the bug... + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=2718933#xx2718933xx + * 2008-11-27 v8 Debugging Tools for Windows are now stored in a different directory + * Thanks to Luiz Salamon which reported this "bug"... + * http://www.codeproject.com/KB/threads/StackWalker.aspx?msg=2822736#xx2822736xx + * 2009-04-10 v9 License slihtly corrected ( replaced) + * 2010-04-15 v10 Added support for VS2010 RTM + * 2010-05-2ß v11 Now using secure MyStrcCpy. Thanks to luke.simon: + * http://www.codeproject.com/KB/applications/leakfinder.aspx?msg=3477467#xx3477467xx + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + **********************************************************************/ +#include +#include +#include +#include +#pragma comment(lib, "version.lib") // for "VerQueryValue" +#pragma warning(disable:4826) + +#include "StackWalker.h" + + +// If VC7 and later, then use the shipped 'dbghelp.h'-file +#pragma pack(push,8) +#if _MSC_VER >= 1300 +#include +#else +// inline the important dbghelp.h-declarations... +typedef enum { + SymNone = 0, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes +} SYM_TYPE; +typedef struct _IMAGEHLP_LINE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) + PVOID Key; // internal + DWORD LineNumber; // line number in file + PCHAR FileName; // full filename + DWORD64 Address; // first instruction of line +} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; +typedef struct _IMAGEHLP_MODULE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; +typedef struct _IMAGEHLP_SYMBOL64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) + DWORD64 Address; // virtual address including dll base address + DWORD Size; // estimated size of symbol, can be zero + DWORD Flags; // info about the symbols, see the SYMF defines + DWORD MaxNameLength; // maximum size of symbol name in 'Name' + CHAR Name[1]; // symbol name (null terminated string) +} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; +typedef enum { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat +} ADDRESS_MODE; +typedef struct _tagADDRESS64 { + DWORD64 Offset; + WORD Segment; + ADDRESS_MODE Mode; +} ADDRESS64, *LPADDRESS64; +typedef struct _KDHELP64 { + DWORD64 Thread; + DWORD ThCallbackStack; + DWORD ThCallbackBStore; + DWORD NextCallback; + DWORD FramePointer; + DWORD64 KiCallUserMode; + DWORD64 KeUserCallbackDispatcher; + DWORD64 SystemRangeStart; + DWORD64 Reserved[8]; +} KDHELP64, *PKDHELP64; +typedef struct _tagSTACKFRAME64 { + ADDRESS64 AddrPC; // program counter + ADDRESS64 AddrReturn; // return address + ADDRESS64 AddrFrame; // frame pointer + ADDRESS64 AddrStack; // stack pointer + ADDRESS64 AddrBStore; // backing store pointer + PVOID FuncTableEntry; // pointer to pdata/fpo or NULL + DWORD64 Params[4]; // possible arguments to the function + BOOL Far; // WOW far call + BOOL Virtual; // is this a virtual frame? + DWORD64 Reserved[3]; + KDHELP64 KdHelp; +} STACKFRAME64, *LPSTACKFRAME64; +typedef +BOOL +(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ); +typedef +PVOID +(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( + HANDLE hProcess, + DWORD64 AddrBase + ); +typedef +DWORD64 +(__stdcall *PGET_MODULE_BASE_ROUTINE64)( + HANDLE hProcess, + DWORD64 Address + ); +typedef +DWORD64 +(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( + HANDLE hProcess, + HANDLE hThread, + LPADDRESS64 lpaddr + ); +#define SYMOPT_CASE_INSENSITIVE 0x00000001 +#define SYMOPT_UNDNAME 0x00000002 +#define SYMOPT_DEFERRED_LOADS 0x00000004 +#define SYMOPT_NO_CPP 0x00000008 +#define SYMOPT_LOAD_LINES 0x00000010 +#define SYMOPT_OMAP_FIND_NEAREST 0x00000020 +#define SYMOPT_LOAD_ANYTHING 0x00000040 +#define SYMOPT_IGNORE_CVREC 0x00000080 +#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 +#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 +#define SYMOPT_EXACT_SYMBOLS 0x00000400 +#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 +#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 +#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 +#define SYMOPT_PUBLICS_ONLY 0x00004000 +#define SYMOPT_NO_PUBLICS 0x00008000 +#define SYMOPT_AUTO_PUBLICS 0x00010000 +#define SYMOPT_NO_IMAGE_SEARCH 0x00020000 +#define SYMOPT_SECURE 0x00040000 +#define SYMOPT_DEBUG 0x80000000 +#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration +#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; +#endif // _MSC_VER < 1300 +#pragma pack(pop) + +// Some missing defines (for VC5/6): +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define strcpy_s strcpy +#define strncpy_s strncpy +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + +static void MyStrCpy(char* szDest, size_t nMaxDestSize, const char* szSrc) +{ + if (nMaxDestSize <= 0) return; + if (strlen(szSrc) < nMaxDestSize) + { + strcpy_s(szDest, nMaxDestSize, szSrc); + } + else + { + strncpy_s(szDest, nMaxDestSize, szSrc, nMaxDestSize); + szDest[nMaxDestSize-1] = 0; + } +} // MyStrCpy + +// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') +#define USED_CONTEXT_FLAGS CONTEXT_FULL + + +class StackWalkerInternal +{ +public: + StackWalkerInternal(StackWalker *parent, HANDLE hProcess) + { + m_parent = parent; + m_hDbhHelp = NULL; + pSC = NULL; + m_hProcess = hProcess; + m_szSymPath = NULL; + pSFTA = NULL; + pSGLFA = NULL; + pSGMB = NULL; + pSGMI = NULL; + pSGO = NULL; + pSGSFA = NULL; + pSI = NULL; + pSLM = NULL; + pSSO = NULL; + pSW = NULL; + pUDSN = NULL; + pSGSP = NULL; + } + ~StackWalkerInternal() + { + if (pSC != NULL) + pSC(m_hProcess); // SymCleanup + if (m_hDbhHelp != NULL) + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + m_parent = NULL; + if(m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + } + BOOL Init(LPCSTR szSymPath) + { + if (m_parent == NULL) + return FALSE; + // Dynamically load the Entry-Points for dbghelp.dll: + // First try to load the newsest one from + TCHAR szTemp[4096]; + // But before wqe do this, we first check if the ".local" file exists + if (GetModuleFileName(NULL, szTemp, 4096) > 0) + { + _tcscat_s(szTemp, _T(".local")); + if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) + { + // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" + // Ok, first try the new path according to the archtitecture: +#ifdef _M_IX86 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x86)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_X64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (x64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#elif _M_IA64 + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows (ia64)\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + // If still not found, try the old directories... + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#if defined _M_X64 || defined _M_IA64 + // Still not found? Then try to load the (old) 64-Bit version: + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } +#endif + } + } + if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one + m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") ); + if (m_hDbhHelp == NULL) + return FALSE; + pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" ); + pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" ); + + pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" ); + pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" ); + pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" ); + + pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" ); + pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" ); + pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" ); + pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" ); + pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" ); + pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" ); + pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" ); + + if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || + pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || + pSW == NULL || pUDSN == NULL || pSLM == NULL ) + { + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + pSC = NULL; + return FALSE; + } + + // SymInitialize + if (szSymPath != NULL) + m_szSymPath = _strdup(szSymPath); + if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) + this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + + DWORD symOptions = this->pSGO(); // SymGetOptions + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + //symOptions |= SYMOPT_NO_PROMPTS; + // SymSetOptions + symOptions = this->pSSO(symOptions); + + char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; + if (this->pSGSP != NULL) + { + if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) + this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + char szUserName[1024] = {0}; + DWORD dwSize = 1024; + GetUserNameA(szUserName, &dwSize); + this->m_parent->OnSymInit(buf, symOptions, szUserName); + + return TRUE; + } + + StackWalker *m_parent; + + HMODULE m_hDbhHelp; + HANDLE m_hProcess; + LPSTR m_szSymPath; + +/*typedef struct IMAGEHLP_MODULE64_V3 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + // new elements: 07-Jun-2002 + CHAR LoadedImageName[256]; // symbol file name + CHAR LoadedPdbName[256]; // pdb file name + DWORD CVSig; // Signature of the CV record in the debug directories + CHAR CVData[MAX_PATH * 3]; // Contents of the CV record + DWORD PdbSig; // Signature of PDB + GUID PdbSig70; // Signature of PDB (VC 7 and up) + DWORD PdbAge; // DBI age of pdb + BOOL PdbUnmatched; // loaded an unmatched pdb + BOOL DbgUnmatched; // loaded an unmatched dbg + BOOL LineNumbers; // we have line number information + BOOL GlobalSymbols; // we have internal symbol information + BOOL TypeInfo; // we have type information + // new elements: 17-Dec-2003 + BOOL SourceIndexed; // pdb supports source server + BOOL Publics; // contains public symbols +}; +*/ + +#pragma pack(push,8) +typedef struct IMAGEHLP_MODULE64_V2 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +}; +#pragma pack(pop) + + + // SymCleanup() + typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess ); + tSC pSC; + + // SymFunctionTableAccess64() + typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase ); + tSFTA pSFTA; + + // SymGetLineFromAddr64() + typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line ); + tSGLFA pSGLFA; + + // SymGetModuleBase64() + typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr ); + tSGMB pSGMB; + + // SymGetModuleInfo64() + typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo ); + tSGMI pSGMI; + +// // SymGetModuleInfo64() +// typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo ); +// tSGMI_V3 pSGMI_V3; + + // SymGetOptions() + typedef DWORD (__stdcall *tSGO)( VOID ); + tSGO pSGO; + + // SymGetSymFromAddr64() + typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol ); + tSGSFA pSGSFA; + + // SymInitialize() + typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess ); + tSI pSI; + + // SymLoadModule64() + typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile, + IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll ); + tSLM pSLM; + + // SymSetOptions() + typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions ); + tSSO pSSO; + + // StackWalk64() + typedef BOOL (__stdcall *tSW)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ); + tSW pSW; + + // UnDecorateSymbolName() + typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, + DWORD UndecoratedLength, DWORD Flags ); + tUDSN pUDSN; + + typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + tSGSP pSGSP; + + +private: + // **************************************** ToolHelp32 ************************ + #define MAX_MODULE_NAME32 255 + #define TH32CS_SNAPMODULE 0x00000008 + #pragma pack( push, 8 ) + typedef struct tagMODULEENTRY32 + { + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE * modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; + } MODULEENTRY32; + typedef MODULEENTRY32 * PMODULEENTRY32; + typedef MODULEENTRY32 * LPMODULEENTRY32; + #pragma pack( pop ) + + BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) + { + // CreateToolhelp32Snapshot() + typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") }; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + size_t i; + + for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + { + hToolhelp = LoadLibrary( dllname[i] ); + if (hToolhelp == NULL) + continue; + pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); + if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + break; // found the functions! + FreeLibrary(hToolhelp); + hToolhelp = NULL; + } + + if (hToolhelp == NULL) + return FALSE; + + hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); + if (hSnap == (HANDLE) -1) + { + FreeLibrary(hToolhelp); + return FALSE; + } + + keepGoing = !!pM32F( hSnap, &me ); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N( hSnap, &me ); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) + return FALSE; + return TRUE; + } // GetModuleListTH32 + + // **************************************** PSAPI ************************ + typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; + } MODULEINFO, *LPMODULEINFO; + + BOOL GetModuleListPSAPI(HANDLE hProcess) + { + // EnumProcessModules() + typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ); + // GetModuleFileNameEx() + typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleBaseName() + typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleInformation() + typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize ); + + HINSTANCE hPsapi; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; + + DWORD i; + //ModuleEntry e; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = NULL; + char *tt2 = NULL; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hPsapi = LoadLibrary( _T("psapi.dll") ); + if (hPsapi == NULL) + return FALSE; + + pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" ); + pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" ); + pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" ); + pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); + if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) + { + // we couldn´t find all functions + FreeLibrary(hPsapi); + return FALSE; + } + + hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); + tt = (char*) malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); + if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + goto cleanup; + + if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) ) + { + //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); + goto cleanup; + } + + if ( cbNeeded > TTBUFLEN ) + { + //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); + goto cleanup; + } + + for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) + { + // base address, size + pGMI(hProcess, hMods[i], &mi, sizeof mi ); + // image file name + tt[0] = 0; + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN ); + // module name + tt2[0] = 0; + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN ); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); + cnt++; + } + + cleanup: + if (hPsapi != NULL) FreeLibrary(hPsapi); + if (tt2 != NULL) free(tt2); + if (tt != NULL) free(tt); + if (hMods != NULL) free(hMods); + + return cnt != 0; + } // GetModuleListPSAPI + + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) + { + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ( (szImg == NULL) || (szMod == NULL) ) + result = ERROR_NOT_ENOUGH_MEMORY; + else + { + if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + result = GetLastError(); + } + ULONGLONG fileVersion = 0; + if ( (m_parent != NULL) && (szImg != NULL) ) + { + // try to retrive the file-version: + if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != NULL) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + fInfo = NULL; + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64_V2 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch(Module.SymType) + { + case SymNone: + szSymType = "-nosymbols-"; + break; + case SymCoff: // 1 + szSymType = "COFF"; + break; + case SymCv: // 2 + szSymType = "CV"; + break; + case SymPdb: // 3 + szSymType = "PDB"; + break; + case SymExport: // 4 + szSymType = "-exported-"; + break; + case SymDeferred: // 5 + szSymType = "-deferred-"; + break; + case SymSym: // 6 + szSymType = "SYM"; + break; + case 7: // SymDia: + szSymType = "DIA"; + break; + case 8: //SymVirtual: + szSymType = "Virtual"; + break; + } + } + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); + } + if (szImg != NULL) free(szImg); + if (szMod != NULL) free(szMod); + return result; + } +public: + BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) + { + // first try toolhelp32 + if (GetModuleListTH32(hProcess, dwProcessId)) + return true; + // then try psapi + return GetModuleListPSAPI(hProcess); + } + + + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) + { + if(this->pSGMI == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + // First try to use the larger ModuleInfo-Structure +// memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); +// pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); +// if (this->pSGMI_V3 != NULL) +// { +// if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE) +// return TRUE; +// // check if the parameter was wrong (size is bad...) +// if (GetLastError() != ERROR_INVALID_PARAMETER) +// return FALSE; +// } + // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)... + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + free(pData); + return TRUE; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } +}; + +// ############################################################# +StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = OptionsAll; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} +StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = options; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + if (szSymPath != NULL) + { + this->m_szSymPath = _strdup(szSymPath); + this->m_options |= SymBuildPath; + } + else + this->m_szSymPath = NULL; + this->m_MaxRecursionCount = 1000; +} + +StackWalker::~StackWalker() +{ + if (m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + if (this->m_sw != NULL) + delete this->m_sw; + this->m_sw = NULL; +} + +BOOL StackWalker::LoadModules() +{ + if (this->m_sw == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + if (m_modulesLoaded != FALSE) + return TRUE; + + // Build the sym-path: + char *szSymPath = NULL; + if ( (this->m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*) malloc(nSymPathLen); + if (szSymPath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (this->m_szSymPath != NULL) + { + strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const size_t nTempLen = 1024; + char szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + { + *p = 0; + break; + } + } // for (search for path separator...) + if (strlen(szTemp) > 0) + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ( (this->m_options & SymUseSymSrv) != 0) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } // if SymBuildPath + + // First Init the whole stuff... + BOOL bRet = this->m_sw->Init(szSymPath); + if (szSymPath != NULL) free(szSymPath); szSymPath = NULL; + if (bRet == FALSE) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); + if (bRet != FALSE) + m_modulesLoaded = TRUE; + return bRet; +} + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64... +// Because this class is in no case multi-threading-enabled (because of the limitations +// of dbghelp.dll) it is "safe" to use a static-variable +static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; + +BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64 *pSym = NULL; + StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + bool bLastEntryCalled = true; + int curRecursionCount = 0; + + if (m_modulesLoaded == FALSE) + this->LoadModules(); // ignore the result... + + if (this->m_sw->m_hDbhHelp == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + if (context == NULL) + { + // If no context is provided, capture the context + if (hThread == GetCurrentThread()) + { + GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS); + } + else + { + SuspendThread(hThread); + memset(&c, 0, sizeof(CONTEXT)); + c.ContextFlags = USED_CONTEXT_FLAGS; + if (GetThreadContext(hThread, &c) == FALSE) + { + ResumeThread(hThread); + return FALSE; + } + } + } + else + c = *context; + + // init STACKFRAME for first call + STACKFRAME64 s; // in/out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; +#ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; +#else +#error "Platform not supported!" +#endif + + pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) goto cleanup; // not enough memory... + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum ) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) ) + { + // INFO: "StackWalk64" does not set "GetLastError"... + this->OnDbgHelpErr("StackWalk64", 0, s.AddrPC.Offset); + break; + } + + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + if ( (this->m_MaxRecursionCount > 0) && (curRecursionCount > m_MaxRecursionCount) ) + { + this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + curRecursionCount++; + } + else + curRecursionCount = 0; + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC + // show procedure info (SymGetSymFromAddr64()) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + MyStrCpy(csEntry.name, STACKWALK_MAX_NAMELEN, pSym->Name); + // UnDecorateSymbolName() + this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); + this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method (SymGetLineFromAddr64()) + if (this->m_sw->pSGLFA != NULL ) + { // yes, we have SymGetLineFromAddr64() + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + MyStrCpy(csEntry.lineFileName, STACKWALK_MAX_NAMELEN, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + } // yes, we have SymGetLineFromAddr64() + + // show module info (SymGetModuleInfo64()) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE) + { // got module info OK + switch ( Module.SymType ) + { + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; +#if API_VERSION_NUMBER >= 9 + case SymDia: + csEntry.symTypeString = "DIA"; + break; +#endif + case 8: //SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; + } + + // TODO: Mache dies sicher...! + MyStrCpy(csEntry.moduleName, STACKWALK_MAX_NAMELEN, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + MyStrCpy(csEntry.loadedImageName, STACKWALK_MAX_NAMELEN, Module.LoadedImageName); + } // got module info OK + else + { + this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } // we seem to have a valid PC + + CallstackEntryType et = nextEntry; + if (frameNum == 0) + et = firstEntry; + bLastEntryCalled = false; + this->OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + bLastEntryCalled = true; + this->OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } // for ( frameNum ) + + cleanup: + if (pSym) free( pSym ); + + if (bLastEntryCalled == false) + this->OnCallstackEntry(lastEntry, csEntry); + + if (context == NULL) + ResumeThread(hThread); + + return TRUE; +} + +BOOL __stdcall StackWalker::myReadProcMem( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ) +{ + if (s_readMemoryFunction == NULL) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + +void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + else + { + DWORD v4 = (DWORD) fileVersion & 0xFFFF; + DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF; + DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF; + DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + OnOutput(buffer); +} + +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if (entry.name[0] == 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)"); + if (entry.undName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undName); + if (entry.undFullName[0] != 0) + MyStrCpy(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName); + if (entry.lineFileName[0] == 0) + { + MyStrCpy(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)"); + if (entry.moduleName[0] == 0) + MyStrCpy(entry.moduleName, STACKWALK_MAX_NAMELEN, "(module-name not available)"); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + buffer[STACKWALK_MAX_NAMELEN-1] = 0; + OnOutput(buffer); + } +} + +void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + OnOutput(buffer); +} + +void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + OnOutput(buffer); + // Also display the OS-version +#if _MSC_VER <= 1200 + OSVERSIONINFOA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA(&ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion); + OnOutput(buffer); + } +#else + OSVERSIONINFOEXA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + OnOutput(buffer); + } +#endif +} + +void StackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); +} diff --git a/source/StackWalker.h b/source/StackWalker.h new file mode 100644 index 000000000..212e02a9f --- /dev/null +++ b/source/StackWalker.h @@ -0,0 +1,214 @@ +/********************************************************************** + * + * StackWalker.h + * + * + * + * LICENSE (http://www.opensource.org/licenses/bsd-license.php) + * + * Copyright (c) 2005-2010, Jochen Kalmbach + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of Jochen Kalmbach nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * **********************************************************************/ +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include + +// special defines for VC5/6 (if no actual PSDK is installed): +#if _MSC_VER < 1300 +typedef unsigned __int64 DWORD64, *PDWORD64; +#if defined(_WIN64) +typedef unsigned __int64 SIZE_T, *PSIZE_T; +#else +typedef unsigned long SIZE_T, *PSIZE_T; +#endif +#endif // _MSC_VER < 1300 + +class StackWalkerInternal; // forward +class StackWalker +{ +public: + typedef enum StackWalkOptions + { + // No addition info will be retrived + // (only the address is available) + RetrieveNone = 0, + + // Try to get the symbol-name + RetrieveSymbol = 1, + + // Try to get the line for this symbol + RetrieveLine = 2, + + // Try to retrieve the module-infos + RetrieveModuleInfo = 4, + + // Also retrieve the version for the DLL/EXE + RetrieveFileVersion = 8, + + // Contains all the abouve + RetrieveVerbose = 0xF, + + // Generate a "good" symbol-search-path + SymBuildPath = 0x10, + + // Also use the public Microsoft-Symbol-Server + SymUseSymSrv = 0x20, + + // Contains all the abouve "Sym"-options + SymAll = 0x30, + + // Contains all options (default) + OptionsAll = 0x3F + } StackWalkOptions; + + StackWalker( + int options = OptionsAll, // 'int' is by design, to combine the enum-flags + LPCSTR szSymPath = NULL, + DWORD dwProcessId = GetCurrentProcessId(), + HANDLE hProcess = GetCurrentProcess() + ); + StackWalker(DWORD dwProcessId, HANDLE hProcess); + virtual ~StackWalker(); + + typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); + + BOOL LoadModules(); + + BOOL ShowCallstack( + HANDLE hThread = GetCurrentThread(), + const CONTEXT *context = NULL, + PReadProcessMemoryRoutine readMemoryFunction = NULL, + LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback + ); + +#if _MSC_VER >= 1300 +// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" +// in older compilers in order to use it... starting with VC7 we can declare it as "protected" +protected: +#endif + enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols + +protected: + // Entry for each Callstack-Entry + typedef struct CallstackEntry + { + DWORD64 offset; // if 0, we have no valid entry + CHAR name[STACKWALK_MAX_NAMELEN]; + CHAR undName[STACKWALK_MAX_NAMELEN]; + CHAR undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offsetFromSmybol; + DWORD offsetFromLine; + DWORD lineNumber; + CHAR lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + LPCSTR symTypeString; + CHAR moduleName[STACKWALK_MAX_NAMELEN]; + DWORD64 baseOfImage; + CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; + } CallstackEntry; + + typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + + virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); + virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); + virtual void OnOutput(LPCSTR szText); + + StackWalkerInternal *m_sw; + HANDLE m_hProcess; + DWORD m_dwProcessId; + BOOL m_modulesLoaded; + LPSTR m_szSymPath; + + int m_options; + int m_MaxRecursionCount; + + static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + + friend StackWalkerInternal; +}; + + +// The "ugly" assembler-implementation is needed for systems before XP +// If you have a new PSDK and you only compile for XP and later, then you can use +// the "RtlCaptureContext" +// Currently there is no define which determines the PSDK-Version... +// So we just use the compiler-version (and assumes that the PSDK is +// the one which was installed by the VS-IDE) + +// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... +// But I currently use it in x64/IA64 environments... +//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) + +#if defined(_M_IX86) +#ifdef CURRENT_THREAD_VIA_EXCEPTION +// TODO: The following is not a "good" implementation, +// because the callstack is only valid in the "__except" block... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + EXCEPTION_POINTERS *pExp = NULL; \ + __try { \ + throw 0; \ + } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ + if (pExp != NULL) \ + memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + } while(0); +#else +// The following should be enough for walking the callstack... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + __asm call x \ + __asm x: pop eax \ + __asm mov c.Eip, eax \ + __asm mov c.Ebp, ebp \ + __asm mov c.Esp, esp \ + } while(0); +#endif + +#else + +// The following is defined for x86 (XP and higher), x64 and IA64: +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + RtlCaptureContext(&c); \ +} while(0); +#endif diff --git a/source/WSSCompact.cpp b/source/WSSCompact.cpp new file mode 100644 index 000000000..b2c2d0bb9 --- /dev/null +++ b/source/WSSCompact.cpp @@ -0,0 +1,415 @@ + +// WSSCompact.cpp + +// Interfaces to the cWSSCompact class representing the "compact" storage schema (PAK-files) + +#include "Globals.h" +#include "WSSCompact.h" +#include "cWorld.h" +#include "zlib.h" +#include + + + + + +#pragma pack(push, 1) +/// The chunk header, as stored in the file: +struct cWSSCompact::sChunkHeader +{ + int m_ChunkX; + int m_ChunkZ; + int m_CompressedSize; + int m_UncompressedSize; +} ; +#pragma pack(pop) + + + + + +/// The maximum number of PAK files that are cached +const int MAX_PAK_FILES = 16; + +/// The maximum number of unsaved chunks before the cPAKFile saves them to disk +const int MAX_DIRTY_CHUNKS = 16; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact: + +cWSSCompact::~cWSSCompact() +{ + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::LoadChunk(const cChunkPtr & a_Chunk) +{ + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + // For some reason we couldn't locate the file + return false; + } + + return f->LoadChunk(a_Chunk); +} + + + + + +bool cWSSCompact::SaveChunk(const cChunkPtr & a_Chunk) +{ + cPAKFile * f = LoadPAKFile(a_Chunk); + if (f == NULL) + { + // For some reason we couldn't locate the file + return false; + } + return f->SaveChunk(a_Chunk); +} + + + + + +cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkPtr & a_Chunk) +{ + // We need to retain this weird conversion code, because some edge chunks are in the wrong PAK file + const int LayerX = (int)(floorf((float)a_Chunk->GetPosX() / 32.0f)); + const int LayerZ = (int)(floorf((float)a_Chunk->GetPosZ() / 32.0f)); + + // Is it already cached? + for (cPAKFiles::iterator itr = m_PAKFiles.begin(); itr != m_PAKFiles.end(); ++itr) + { + if (((*itr) != NULL) && ((*itr)->GetLayerX() == LayerX) && ((*itr)->GetLayerZ() == LayerZ)) + { + // Move the file to front and return it: + cPAKFile * f = *itr; + if (itr != m_PAKFiles.begin()) + { + m_PAKFiles.erase(itr); + m_PAKFiles.push_front(f); + } + return f; + } + } + + // Load it anew: + AString FileName; + Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ ); + cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ); + if (f == NULL) + { + return NULL; + } + m_PAKFiles.push_front(f); + + // If there are too many PAK files cached, delete the last one used: + if (m_PAKFiles.size() > MAX_PAK_FILES) + { + delete m_PAKFiles.back(); + m_PAKFiles.pop_back(); + } + return f; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWSSCompact::cPAKFile + +#define READ(Var) \ + if (f.Read(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) : + m_FileName(a_FileName), + m_LayerX(a_LayerX), + m_LayerZ(a_LayerZ), + m_NumDirty(0) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmRead)) + { + return; + } + + // Read headers: + char PakVersion = 0; + READ(PakVersion); + if (PakVersion != 1) + { + LOGERROR("File \"%s\" is in an unknown pak format (%d)", m_FileName.c_str(), PakVersion); + return; + } + + char ChunkVersion = 0; + READ(ChunkVersion); + if (ChunkVersion != 1) + { + LOGERROR("File \"%s\" is in an unknown chunk format (%d)", m_FileName.c_str(), ChunkVersion); + return; + } + + short NumChunks = 0; + READ(NumChunks); + + // Read chunk headers: + for (int i = 0; i < NumChunks; i++) + { + sChunkHeader * Header = new sChunkHeader; + READ(*Header); + m_ChunkHeaders.push_back(Header); + } // for i - chunk headers + + // Read chunk data: + if (f.ReadRestOfFile(m_DataContents) == -1) + { + LOGERROR("Cannot read file \"%s\" contents", m_FileName.c_str()); + return; + } +} + + + + + +cWSSCompact::cPAKFile::~cPAKFile() +{ + if (m_NumDirty > 0) + { + SynchronizeFile(); + } + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + delete *itr; + } +} + + + + + +bool cWSSCompact::cPAKFile::LoadChunk(const cChunkPtr & a_Chunk) +{ + int ChunkX = a_Chunk->GetPosX(); + int ChunkZ = a_Chunk->GetPosZ(); + sChunkHeader * Header = NULL; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + Header = *itr; + break; + } + Offset += (*itr)->m_CompressedSize; + } + if ((Header == NULL) || (Offset + Header->m_CompressedSize > (int)m_DataContents.size())) + { + // Chunk not found / data invalid + return false; + } + + return LoadChunk(a_Chunk, Offset, Header); +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunk(const cChunkPtr & a_Chunk) +{ + if (!SaveChunkToData(a_Chunk)) + { + return false; + } + if (m_NumDirty > MAX_DIRTY_CHUNKS) + { + SynchronizeFile(); + } + return true; +} + + + + + +bool cWSSCompact::cPAKFile::LoadChunk(const cChunkPtr & a_Chunk, int a_Offset, sChunkHeader * a_Header) +{ + // Decompress the data: + uLongf DestSize = a_Header->m_UncompressedSize; + std::auto_ptr BlockData(new char[ DestSize ]); + int errorcode = uncompress( (Bytef*)BlockData.get(), &DestSize, (Bytef*)m_DataContents.data() + a_Offset, a_Header->m_CompressedSize ); + if (errorcode != Z_OK) + { + LOGERROR("Error %d decompressing data for chunk [%d, %d] from file \"%s\"", + errorcode, + a_Chunk->GetPosX(), a_Chunk->GetPosZ(), + m_FileName.c_str() + ); + return false; + } + + if (a_Header->m_UncompressedSize != DestSize) + { + LOGWARNING("Uncompressed data size differs (exp %d, got %d) for chunk [%d, %d] from file \"%s\"", + a_Header->m_UncompressedSize, DestSize, + a_Chunk->GetPosX(), a_Chunk->GetPosZ(), + m_FileName.c_str() + ); + return false; + } + + a_Chunk->CopyBlockDataFrom(BlockData.get()); + a_Chunk->SetValid(); + + if (DestSize > cChunk::c_BlockDataSize ) // We gots some extra data :D + { + LOGINFO("Parsing trailing JSON"); + Json::Value root; // will contain the root value after parsing. + Json::Reader reader; + if ( !reader.parse( BlockData.get() + cChunk::c_BlockDataSize, root, false ) ) + { + LOGERROR("Failed to parse trailing JSON!"); + } + else + { + a_Chunk->LoadFromJson( root ); + } + } + + return true; +} + + + + + +void cWSSCompact::cPAKFile::EraseChunk(const cChunkPtr & a_Chunk) +{ + int ChunkX = a_Chunk->GetPosX(); + int ChunkZ = a_Chunk->GetPosZ(); + sChunkHeader * Header = NULL; + int Offset = 0; + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + if (((*itr)->m_ChunkX == ChunkX) && ((*itr)->m_ChunkZ == ChunkZ)) + { + m_DataContents.erase(Offset, (*itr)->m_CompressedSize); + delete *itr; + itr = m_ChunkHeaders.erase(itr); + return; + } + Offset += (*itr)->m_CompressedSize; + } +} + + + + + +bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkPtr & a_Chunk) +{ + // Erase any existing data for the chunk: + EraseChunk(a_Chunk); + + // Serialize the chunk: + AString Data; + Data.assign(a_Chunk->pGetBlockData(), cChunk::c_BlockDataSize); + Json::Value root; + a_Chunk->SaveToJson( root ); + if (!root.empty()) + { + AString JsonData; + Json::StyledWriter writer; + JsonData = writer.write( root ); + Data.append(JsonData); + } + + // Compress the data: + uLongf CompressedSize = compressBound(Data.size()); + std::auto_ptr Compressed(new char[CompressedSize]); + int errorcode = compress2( (Bytef*)Compressed.get(), &CompressedSize, (const Bytef*)Data.data(), Data.size(), Z_DEFAULT_COMPRESSION); + if ( errorcode != Z_OK ) + { + LOGERROR("Error %i compressing data for chunk [%d, %d]", errorcode, a_Chunk->GetPosX(), a_Chunk->GetPosZ() ); + return false; + } + + // Save the header: + sChunkHeader * Header = new sChunkHeader; + if (Header == NULL) + { + return false; + } + Header->m_CompressedSize = CompressedSize; + Header->m_ChunkX = a_Chunk->GetPosX(); + Header->m_ChunkZ = a_Chunk->GetPosZ(); + Header->m_UncompressedSize = Data.size(); + m_ChunkHeaders.push_back(Header); + + m_DataContents.append(Compressed.get(), CompressedSize); + + m_NumDirty++; + return true; +} + + + + + +#define WRITE(Var) \ + if (f.Write(&Var, sizeof(Var)) != sizeof(Var)) \ + { \ + LOGERROR("cWSSCompact: ERROR writing %s to file \"%s\" (line %d); file offset %d", #Var, m_FileName.c_str(), __LINE__, f.Tell()); \ + return; \ + } + +void cWSSCompact::cPAKFile::SynchronizeFile(void) +{ + cFile f; + if (!f.Open(m_FileName, cFile::fmWrite)) + { + LOGERROR("Cannot open PAK file \"%s\" for writing", m_FileName.c_str()); + return; + } + + char PakVersion = 1; + WRITE(PakVersion); + char ChunkVersion = 1; + WRITE(ChunkVersion); + short NumChunks = (short)m_ChunkHeaders.size(); + WRITE(NumChunks); + for (sChunkHeaders::iterator itr = m_ChunkHeaders.begin(); itr != m_ChunkHeaders.end(); ++itr) + { + WRITE(**itr); + } + if (f.Write(m_DataContents.data(), m_DataContents.size()) != m_DataContents.size()) + { + LOGERROR("cWSSCompact: ERROR writing chunk contents to file \"%s\" (line %d); file offset %d", m_FileName.c_str(), __LINE__, f.Tell()); + return; + } + m_NumDirty = 0; +} + + + + diff --git a/source/WSSCompact.h b/source/WSSCompact.h new file mode 100644 index 000000000..dc5ecfe9e --- /dev/null +++ b/source/WSSCompact.h @@ -0,0 +1,84 @@ + +// WSSCompact.h + +// Interfaces to the cWSSCompact class representing the "Compact" storage schema (PAK-files) + + + + + +#pragma once +#ifndef WSSCOMPACT_H_INCLUDED +#define WSSCOMPACT_H_INCLUDED + +#include "WorldStorage.h" + + + + + +class cWSSCompact : + public cWSSchema +{ +public: + cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {} + virtual ~cWSSCompact(); + +protected: + + struct sChunkHeader; + typedef std::vector sChunkHeaders; + + /// Implements a cache for a single PAK file; implements lazy-write in order to be able to write multiple chunks fast + class cPAKFile + { + public: + + cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ); + ~cPAKFile(); + + bool SaveChunk(const cChunkPtr & a_Chunk); + bool LoadChunk(const cChunkPtr & a_Chunk); + + int GetLayerX(void) const {return m_LayerX; } + int GetLayerZ(void) const {return m_LayerZ; } + + protected: + + AString m_FileName; + int m_LayerX; + int m_LayerZ; + + sChunkHeaders m_ChunkHeaders; + AString m_DataContents; // Data contents of the file, cached + + int m_NumDirty; // Number of chunks that were written into m_DataContents but not into the file + + bool LoadChunk(const cChunkPtr & a_Chunk, int a_Offset, sChunkHeader * a_Header); + void EraseChunk(const cChunkPtr & a_Chunk); // Erases the chunk data from m_DataContents and updates m_ChunkHeaders + bool SaveChunkToData(const cChunkPtr & a_Chunk); // Saves the chunk to m_DataContents, updates headers and m_NumDirty + void SynchronizeFile(void); // Writes m_DataContents along with the headers to file, resets m_NumDirty + } ; + + typedef std::list cPAKFiles; + + cPAKFiles m_PAKFiles; // A MRU cache of PAK files + + /// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache + cPAKFile * LoadPAKFile(const cChunkPtr & a_Chunk); + + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkPtr & a_Chunk) override; + virtual bool SaveChunk(const cChunkPtr & a_Chunk) override; + virtual const AString GetName(void) const override {return "compact"; } +} ; + + + + + +#endif // WSSCOMPACT_H_INCLUDED + + + + diff --git a/source/WorldStorage.cpp b/source/WorldStorage.cpp new file mode 100644 index 000000000..979283895 --- /dev/null +++ b/source/WorldStorage.cpp @@ -0,0 +1,256 @@ + +// WorldStorage.cpp + +// Implements the cWorldStorage class representing the chunk loading / saving thread + +// To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas() + +#include "Globals.h" +#include "WorldStorage.h" +#include "WSSCompact.h" +#include "cWorld.h" +#include "cChunkGenerator.h" + + + + + +/// Example storage schema - forgets all chunks ;) +class cWSSForgetful : + public cWSSchema +{ +public: + cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {} + +protected: + // cWSSchema overrides: + virtual bool LoadChunk(const cChunkPtr & a_Chunk) override {return false; } + virtual bool SaveChunk(const cChunkPtr & a_Chunk) override {return true; } + virtual const AString GetName(void) const override {return "forgetful"; } +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorldStorage: + +cWorldStorage::cWorldStorage(void) : + super("cWorldStorage"), + m_World(NULL), + m_SaveSchema(NULL) +{ +} + + + + + +cWorldStorage::~cWorldStorage() +{ + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + delete *itr; + } // for itr - m_Schemas[] + m_LoadQueue.clear(); + m_SaveQueue.clear(); +} + + + + + +bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName) +{ + m_World = a_World; + m_StorageSchemaName = a_StorageSchemaName; + InitSchemas(); + + return super::Start(); +} + + + + + +void cWorldStorage::WaitForFinish(void) +{ + LOG("Waiting for the world storage to finish saving"); + + // Cancel all loading requests: + cCSLock Lock(m_CSLoadQueue); + m_LoadQueue.clear(); + + // Wait for the thread to finish: + mShouldTerminate = true; + m_Event.Set(); + super::Wait(); +} + + + + + +void cWorldStorage::QueueLoadChunk(cChunkPtr & a_Chunk) +{ + // Queues the chunk for loading; if not loaded, the chunk will be generated + cCSLock Lock(m_CSLoadQueue); + m_LoadQueue.remove(a_Chunk); // Don't add twice + m_LoadQueue.push_back(a_Chunk); + m_Event.Set(); +} + + + + + +void cWorldStorage::QueueSaveChunk(cChunkPtr & a_Chunk) +{ + cCSLock Lock(m_CSSaveQueue); + m_SaveQueue.remove(a_Chunk); // Don't add twice + m_SaveQueue.push_back(a_Chunk); + m_Event.Set(); +} + + + + + +void cWorldStorage::UnqueueLoad(const cChunkPtr & a_Chunk) +{ + cCSLock Lock(m_CSLoadQueue); + m_LoadQueue.remove(a_Chunk); +} + + + + + +void cWorldStorage::UnqueueSave(const cChunkPtr & a_Chunk) +{ + cCSLock Lock(m_CSSaveQueue); + m_SaveQueue.remove(a_Chunk); +} + + + + + +void cWorldStorage::InitSchemas(void) +{ + // The first schema added is considered the default + m_Schemas.push_back(new cWSSCompact(m_World)); + m_Schemas.push_back(new cWSSForgetful(m_World)); + // Add new schemas here + + if (m_StorageSchemaName == "Default") + { + m_SaveSchema = m_Schemas.front(); + return; + } + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if ((*itr)->GetName() == m_StorageSchemaName) + { + m_SaveSchema = *itr; + return; + } + } // for itr - m_Schemas[] + + // Unknown schema selected, let the admin know: + LOGWARNING("Unknown storage schema name \"%s\". Using default. Available schemas:", m_StorageSchemaName.c_str()); + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str()); + } + m_SaveSchema = m_Schemas.front(); +} + + + + + +void cWorldStorage::Execute(void) +{ + while (!mShouldTerminate) + { + m_Event.Wait(); + + // Process both queues until they are empty again: + bool HasMore; + do + { + HasMore = false; + if (mShouldTerminate) + { + return; + } + + // Load 1 chunk: + cChunkPtr ToLoad; + { + cCSLock Lock(m_CSLoadQueue); + if (m_LoadQueue.size() > 0) + { + ToLoad = m_LoadQueue.front(); + m_LoadQueue.pop_front(); + } + HasMore = (m_LoadQueue.size() > 0); + } + if ((ToLoad != NULL) && !LoadChunk(ToLoad)) + { + // The chunk couldn't be loaded, generate it: + m_World->GetGenerator().GenerateChunk(ToLoad->GetPosX(), ToLoad->GetPosZ()); + } + + // Save 1 chunk: + cChunkPtr Save; + { + cCSLock Lock(m_CSSaveQueue); + if (m_SaveQueue.size() > 0) + { + Save = m_SaveQueue.front(); + m_SaveQueue.pop_front(); + } + HasMore = HasMore || (m_SaveQueue.size() > 0); + } + if ((Save != NULL) && (!m_SaveSchema->SaveChunk(Save))) + { + LOGWARNING("Cannot save chunk [%d, %d]", Save->GetPosX(), Save->GetPosZ()); + } + } while (HasMore); + } +} + + + + + +bool cWorldStorage::LoadChunk(const cChunkPtr & a_Chunk) +{ + if (a_Chunk->IsValid()) + { + // Already loaded (can happen, since the queue is async) + return true; + } + + if (m_SaveSchema->LoadChunk(a_Chunk)) + { + return true; + } + + for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr) + { + if ((*itr)->LoadChunk(a_Chunk)) + { + return true; + } + } + + return false; +} + + + + diff --git a/source/WorldStorage.h b/source/WorldStorage.h new file mode 100644 index 000000000..52573caf0 --- /dev/null +++ b/source/WorldStorage.h @@ -0,0 +1,94 @@ + +// WorldStorage.h + +// Interfaces to the cWorldStorage class representing the chunk loading / saving thread +// This class decides which storage schema to use for saving; it queries all available schemas for loading +// Also declares the base class for all storage schemas, cWSSchema + + + + + +#pragma once +#ifndef WORLDSTORAGE_H_INCLUDED +#define WORLDSTORAGE_H_INCLUDED + +#include "cChunk.h" +#include "cIsThread.h" + + + + + +/// Interface that all the world storage schemas need to implement +class cWSSchema ABSTRACT +{ +public: + cWSSchema(cWorld * a_World) : m_World(a_World) {} + virtual ~cWSSchema() {} // Force the descendants' destructors to be virtual + + virtual bool LoadChunk(const cChunkPtr & a_Chunk) = 0; + virtual bool SaveChunk(const cChunkPtr & a_Chunk) = 0; + virtual const AString GetName(void) const = 0; + +protected: + + cWorld * m_World; +} ; + +typedef std::list cWSSchemaList; + + + + + +class cWorldStorage : + public cIsThread +{ + typedef cIsThread super; + +public: + + cWorldStorage(void); + ~cWorldStorage(); + + void QueueLoadChunk(cChunkPtr & a_Chunk); // Queues the chunk for loading; if not loaded, the chunk will be generated + void QueueSaveChunk(cChunkPtr & a_Chunk); + + void UnqueueLoad(const cChunkPtr & a_Chunk); + void UnqueueSave(const cChunkPtr & a_Chunk); + + bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args + void WaitForFinish(void); + +protected: + + cWorld * m_World; + AString m_StorageSchemaName; + + cCriticalSection m_CSLoadQueue; + cChunkPtrList m_LoadQueue; + + cCriticalSection m_CSSaveQueue; + cChunkPtrList m_SaveQueue; + + cEvent m_Event; // Set when there's any addition to the queues + + cWSSchemaList m_Schemas; + cWSSchema * m_SaveSchema; + + void InitSchemas(void); + + virtual void Execute(void) override; + bool LoadChunk(const cChunkPtr & a_Chunk); +} ; + + + + + +#endif // WORLDSTORAGE_H_INCLUDED + + + + diff --git a/source/cAuthenticator.cpp b/source/cAuthenticator.cpp index 1f7e5b35c..9e176e0c5 100644 --- a/source/cAuthenticator.cpp +++ b/source/cAuthenticator.cpp @@ -35,6 +35,17 @@ cAuthenticator::cAuthenticator(void) : +cAuthenticator::~cAuthenticator() +{ + mShouldTerminate = true; + mQueueNonempty.Set(); + Wait(); +} + + + + + /// Read custom values from INI void cAuthenticator::ReadINI(void) { diff --git a/source/cAuthenticator.h b/source/cAuthenticator.h index 6b9e7142b..cb2a4ff1e 100644 --- a/source/cAuthenticator.h +++ b/source/cAuthenticator.h @@ -34,6 +34,7 @@ class cAuthenticator : public: cAuthenticator(void); + ~cAuthenticator(); /// (Re-)read server and address from INI: void ReadINI(void); diff --git a/source/cBlockEntity.h b/source/cBlockEntity.h index e955caff2..dc67306cd 100644 --- a/source/cBlockEntity.h +++ b/source/cBlockEntity.h @@ -1,3 +1,4 @@ + #pragma once #ifndef _WIN32 @@ -6,18 +7,26 @@ enum ENUM_BLOCK_ID; #endif -class cChunk; + + + + class cClientHandle; class cPlayer; +class cWorld; + + + + + class cBlockEntity { protected: - cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cChunk* a_Chunk) + cBlockEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : m_PosX( a_X ) , m_PosY( a_Y ) , m_PosZ( a_Z ) , m_BlockType( a_BlockType ) - , m_Chunk( a_Chunk ) {} public: virtual ~cBlockEntity() {}; @@ -26,9 +35,10 @@ public: int GetPosX() { return m_PosX; } int GetPosY() { return m_PosY; } int GetPosZ() { return m_PosZ; } - cChunk* GetChunk() { return m_Chunk; } ENUM_BLOCK_ID GetBlockType() { return m_BlockType; } + + cWorld * GetWorld(void) const {return m_World; } virtual void UsedBy( cPlayer & a_Player ) = 0; virtual void SendTo( cClientHandle* a_Client ) { (void)a_Client; } @@ -38,7 +48,11 @@ protected: int m_PosY; int m_PosZ; - cChunk* m_Chunk; - ENUM_BLOCK_ID m_BlockType; + + cWorld * m_World; }; + + + + diff --git a/source/cBlockingTCPLink.cpp b/source/cBlockingTCPLink.cpp index 525f6e939..341c71a2b 100644 --- a/source/cBlockingTCPLink.cpp +++ b/source/cBlockingTCPLink.cpp @@ -3,13 +3,16 @@ #include "cBlockingTCPLink.h" #include "packets/cPacket.h" -#include "MCSocket.h" + + + + #ifdef _WIN32 #define MSG_NOSIGNAL (0) #endif #ifdef __MACH__ -#define MSG_NOSIGNAL (0) + #define MSG_NOSIGNAL (0) #endif diff --git a/source/cBlockingTCPLink.h b/source/cBlockingTCPLink.h index 8257f25d7..ff89f7db3 100644 --- a/source/cBlockingTCPLink.h +++ b/source/cBlockingTCPLink.h @@ -1,3 +1,4 @@ + #pragma once #include "cSocket.h" @@ -21,3 +22,7 @@ protected: cSocket m_Socket; }; //tolua_export + + + + diff --git a/source/cChestEntity.cpp b/source/cChestEntity.cpp index b30800700..e96d4bbb2 100644 --- a/source/cChestEntity.cpp +++ b/source/cChestEntity.cpp @@ -23,14 +23,18 @@ class cRoot; -cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z, cChunk* a_Chunk) - : cBlockEntity( E_BLOCK_CHEST, a_X, a_Y, a_Z, a_Chunk ) +cChestEntity::cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity( E_BLOCK_CHEST, a_X, a_Y, a_Z, a_World) , m_TopChest( false ) - , m_JoinedChest( 0 ) + , m_JoinedChest( NULL ) { m_Content = new cItem[ c_ChestHeight*c_ChestWidth ]; } + + + + cChestEntity::~cChestEntity() { if( GetWindow() ) @@ -44,6 +48,10 @@ cChestEntity::~cChestEntity() } } + + + + void cChestEntity::Destroy() { // Drop items @@ -51,15 +59,21 @@ void cChestEntity::Destroy() { if( !m_Content[i].IsEmpty() ) { - cPickup* Pickup = new cPickup( m_PosX*32 + 16, m_PosY*32 + 16, m_PosZ*32 + 16, m_Content[i], 0, 1.f, 0 ); - Pickup->Initialize( GetChunk()->GetWorld() ); + cPickup * Pickup = new cPickup( m_PosX * 32 + 16, m_PosY * 32 + 16, m_PosZ * 32 + 16, m_Content[i], 0, 1.f, 0 ); + Pickup->Initialize(m_World); m_Content[i].Empty(); } } if (m_JoinedChest) + { m_JoinedChest->RemoveJoinedChest(this); + } } + + + + cItem * cChestEntity::GetSlot( int a_Slot ) { if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth ) @@ -69,6 +83,10 @@ cItem * cChestEntity::GetSlot( int a_Slot ) return 0; } + + + + void cChestEntity::SetSlot( int a_Slot, cItem & a_Item ) { if( a_Slot > -1 && a_Slot < c_ChestHeight*c_ChestWidth ) @@ -129,6 +147,10 @@ bool cChestEntity::LoadFromJson( const Json::Value& a_Value ) return true; } + + + + void cChestEntity::SaveToJson( Json::Value& a_Value ) { a_Value["x"] = m_PosX; @@ -147,6 +169,10 @@ void cChestEntity::SaveToJson( Json::Value& a_Value ) a_Value["Slots"] = AllSlots; } + + + + void cChestEntity::SendTo( cClientHandle* a_Client, cServer* a_Server ) { (void)a_Client; @@ -154,6 +180,10 @@ void cChestEntity::SendTo( cClientHandle* a_Client, cServer* a_Server ) return; } + + + + void cChestEntity::UsedBy( cPlayer & a_Player ) { LOG("Used a chest"); @@ -185,15 +215,13 @@ void cChestEntity::UsedBy( cPlayer & a_Player ) ChestOpen.m_PosZ = GetPosZ(); ChestOpen.m_Byte1 = (char)1; ChestOpen.m_Byte2 = (char)1; - cWorld::PlayerList PlayerList = cRoot::Get()->GetWorld()->GetAllPlayers(); - for( cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr ) - { - if ((*itr) && (*itr)->GetClientHandle() && !((*itr)->GetClientHandle()->IsDestroyed())) { - (*itr)->GetClientHandle()->Send( ChestOpen ); - } - } + m_World->GetChunkOfBlock(m_PosX, m_PosY, m_PosZ)->Broadcast(&ChestOpen); } + + + + cItem *cChestEntity::GetContents(bool a_OnlyThis) { if (m_JoinedChest && !a_OnlyThis) @@ -215,3 +243,7 @@ cItem *cChestEntity::GetContents(bool a_OnlyThis) else return m_Content; } + + + + diff --git a/source/cChestEntity.h b/source/cChestEntity.h index 6082b6a52..6d26d069f 100644 --- a/source/cChestEntity.h +++ b/source/cChestEntity.h @@ -1,10 +1,14 @@ + #pragma once #include "cBlockEntity.h" #include "cWindowOwner.h" -#include "FileDefine.h" #include "packets/cPacket_BlockAction.h" + + + + namespace Json { class Value; @@ -14,10 +18,17 @@ class cClientHandle; class cServer; class cItem; class cNBTData; -class cChestEntity : public cBlockEntity, public cWindowOwner + + + + + +class cChestEntity : + public cBlockEntity, + public cWindowOwner { public: - cChestEntity(int a_X, int a_Y, int a_Z, cChunk * a_Chunk); + cChestEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); virtual ~cChestEntity(); virtual void Destroy(); @@ -44,8 +55,14 @@ public: static const int c_ChestWidth = 9; static const int c_ChestHeight = 3; + private: - cItem* m_Content; - bool m_TopChest; - cChestEntity *m_JoinedChest; -}; \ No newline at end of file + + cItem * m_Content; + bool m_TopChest; + cChestEntity * m_JoinedChest; +}; + + + + diff --git a/source/cChunk.cpp b/source/cChunk.cpp index 3b0dd519c..fec06e947 100644 --- a/source/cChunk.cpp +++ b/source/cChunk.cpp @@ -27,6 +27,7 @@ #include "cWorldGenerator.h" #include "cBlockToPickup.h" #include "MersenneTwister.h" +#include "cPlayer.h" #include "packets/cPacket_DestroyEntity.h" #include "packets/cPacket_PreChunk.h" @@ -43,66 +44,27 @@ extern bool g_bWaterPhysics; -typedef std::map< int, std::string > ReferenceMap; -typedef std::list< cFurnaceEntity* > FurnaceEntityList; -typedef std::list< cClientHandle* > ClientHandleList; -typedef std::list< cBlockEntity* > BlockEntityList; -typedef std::list< cEntity* > EntityList; - - -struct cChunk::sChunkState -{ - sChunkState() - : TotalReferencesEver( 0 ) - , MinusReferences( 0 ) - , NumRefs( 0 ) - {} - - FurnaceEntityList TickBlockEntities; - std::map< unsigned int, int > ToTickBlocks; // Protected by BlockListCriticalSection - std::vector< unsigned int > PendingSendBlocks; // Protected by BlockListCriticalSection - ClientHandleList LoadedByClient; - ClientHandleList UnloadQuery; - BlockEntityList BlockEntities; // Protected by BlockListCriticalSection - EntityList Entities; - - cCriticalSection BlockListCriticalSection; - - // Reference counting - cCriticalSection ReferenceCriticalSection; - ReferenceMap References; - int MinusReferences; // References.size() - MinusReferences = Actual amount of references. This is due to removal of reference without an ID (don't know which to remove, so remove none) - int TotalReferencesEver; // For creating a unique reference ID - int NumRefs; -}; - - - - - -cChunk::cChunk(int a_X, int a_Y, int a_Z, cWorld* a_World) - : m_pState( new sChunkState ) - , m_bCalculateLighting( false ) +cChunk::cChunk(int a_X, int a_Y, int a_Z, cWorld * a_World) + : m_bCalculateLighting( false ) , m_bCalculateHeightmap( false ) , m_PosX( a_X ) , m_PosY( a_Y ) , m_PosZ( a_Z ) , m_BlockType( m_BlockData ) // Offset the pointers , m_BlockMeta( m_BlockType + c_NumBlocks ) - , m_BlockLight( m_BlockMeta + c_NumBlocks/2 ) - , m_BlockSkyLight( m_BlockLight + c_NumBlocks/2 ) + , m_BlockLight( m_BlockMeta + c_NumBlocks / 2 ) + , m_BlockSkyLight( m_BlockLight + c_NumBlocks / 2 ) , m_BlockTickNum( 0 ) , m_BlockTickX( 0 ) , m_BlockTickY( 0 ) , m_BlockTickZ( 0 ) - , m_EntitiesCriticalSection( 0 ) , m_World( a_World ) + , m_IsValid(false) { - //LOG("cChunk::cChunk(%i, %i, %i)", a_X, a_Y, a_Z); - m_EntitiesCriticalSection = new cCriticalSection(); + // LOGINFO("### new cChunk (%i, %i) at %p, thread 0x%x ###", a_X, a_Z, this, GetCurrentThreadId()); } @@ -111,79 +73,77 @@ cChunk::cChunk(int a_X, int a_Y, int a_Z, cWorld* a_World) cChunk::~cChunk() { - //LOG("~cChunk() %i %i %i", m_PosX, m_PosY, m_PosZ ); - if( !m_pState->LoadedByClient.empty() ) - { - LOGWARN("WARNING: Deleting cChunk while it contains %i clients!", m_pState->LoadedByClient.size() ); - } - - m_pState->ReferenceCriticalSection.Lock(); - if( GetReferenceCount() > 0 ) - { - LOGWARN("WARNING: Deleting cChunk while it still has %i references!", GetReferenceCount() ); - } - m_pState->ReferenceCriticalSection.Unlock(); - - m_pState->BlockListCriticalSection.Lock(); - for( std::list::iterator itr = m_pState->BlockEntities.begin(); itr != m_pState->BlockEntities.end(); ++itr) + // LOGINFO("### delete cChunk() (%i, %i) from %p, thread 0x%x ###", m_PosX, m_PosZ, this, GetCurrentThreadId() ); + + cCSLock Lock(m_CSEntities); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) { delete *itr; } - m_pState->BlockEntities.clear(); - m_pState->BlockListCriticalSection.Unlock(); + m_BlockEntities.clear(); - LockEntities(); - if( m_pState->Entities.size() > 0 ) + // Remove and destroy all entities that are not players: + cEntityList Entities; + for (cEntityList::const_iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) { - EntityList Entities = m_pState->Entities; // Copy list to a temporary list - for( EntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) + if ((*itr)->GetEntityType() != cEntity::E_PLAYER) { - if( (*itr)->GetEntityType() != cEntity::E_PLAYER ) - { - (*itr)->RemoveFromChunk( this ); - (*itr)->Destroy(); - } + Entities.push_back(*itr); } - m_pState->Entities.clear(); } - UnlockEntities(); - - if( m_EntitiesCriticalSection ) + for (cEntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) { - delete m_EntitiesCriticalSection; - m_EntitiesCriticalSection = 0; + (*itr)->RemoveFromChunk(); + (*itr)->Destroy(); } - delete m_pState; + m_Entities.clear(); } -void cChunk::Initialize() +void cChunk::SetValid(bool a_SendToClients) { - if (!LoadFromDisk()) + m_IsValid = true; + + if (!a_SendToClients) + { + return; + } + + cCSLock Lock(m_CSClients); + if (m_LoadedByClient.empty()) + { + return; + } + + // Sending the chunk here interferes with the lighting done in the tick thread and results in the "invalid compressed data" on the client + /* + cPacket_PreChunk PreChunk; + PreChunk.m_PosX = m_PosX; + PreChunk.m_PosZ = m_PosZ; + PreChunk.m_bLoad = true; + cPacket_MapChunk MapChunk(this); + Broadcast(&PreChunk); + Broadcast(&MapChunk); + + // Let all clients of this chunk know that it has been already sent to the client + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) { - // Clear memory - memset( m_BlockData, 0x00, c_BlockDataSize ); + (*itr)->ChunkJustSent(this); + } // for itr - m_LoadedByClient[] + */ +} - m_World->GetWorldGenerator()->GenerateChunk( this ); - CalculateHeightmap(); - CalculateLighting(); - CreateBlockEntities(); - // During generation, some blocks might have been set by using (Fast)SetBlock() causing this list to fill. - // This chunk has not been sent to anybody yet, so there is no need for separately sending block changes when you can send an entire chunk - cCSLock Lock(m_pState->BlockListCriticalSection); - m_pState->PendingSendBlocks.clear(); - } - else - { - //LOGINFO("Successfully loaded from disk"); - CalculateHeightmap(); - } + +bool cChunk::CanUnload(void) +{ + cCSLock Lock(m_CSClients); + return m_LoadedByClient.empty(); } @@ -201,8 +161,8 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) CalculateHeightmap(); } - cCSLock Lock(m_pState->BlockListCriticalSection); - unsigned int PendingSendBlocks = m_pState->PendingSendBlocks.size(); + cCSLock Lock(m_CSBlockLists); + unsigned int PendingSendBlocks = m_PendingSendBlocks.size(); if( PendingSendBlocks > 1 ) { cPacket_MultiBlock MultiBlock; @@ -215,7 +175,7 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) //LOG("Sending multiblock packet for %i blocks", PendingSendBlocks ); for( unsigned int i = 0; i < PendingSendBlocks; i++) { - unsigned int index = m_pState->PendingSendBlocks[i]; + unsigned int index = m_PendingSendBlocks[i]; unsigned int Y = index % 128; unsigned int Z = (index / 128) % 16; unsigned int X = (index / (128*16)); @@ -225,15 +185,15 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) MultiBlock.m_BlockTypes[i] = m_BlockType[index]; MultiBlock.m_BlockMetas[i] = GetLight( m_BlockMeta, index ); } - m_pState->PendingSendBlocks.clear(); - PendingSendBlocks = m_pState->PendingSendBlocks.size(); + m_PendingSendBlocks.clear(); + PendingSendBlocks = m_PendingSendBlocks.size(); Broadcast( MultiBlock ); } if( PendingSendBlocks > 0 ) { for( unsigned int i = 0; i < PendingSendBlocks; i++) { - unsigned int index = m_pState->PendingSendBlocks[i]; + unsigned int index = m_PendingSendBlocks[i]; int Y = index % 128; int Z = (index / 128) % 16; int X = (index / (128*16)); @@ -246,23 +206,23 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) BlockChange.m_BlockMeta = GetLight( m_BlockMeta, index ); Broadcast( BlockChange ); } - m_pState->PendingSendBlocks.clear(); + m_PendingSendBlocks.clear(); } Lock.Unlock(); - while( !m_pState->UnloadQuery.empty() ) + while ( !m_UnloadQuery.empty() ) { cPacket_PreChunk UnloadPacket; UnloadPacket.m_PosX = GetPosX(); UnloadPacket.m_PosZ = GetPosZ(); UnloadPacket.m_bLoad = false; // Unload - (*m_pState->UnloadQuery.begin())->Send( UnloadPacket ); - m_pState->UnloadQuery.remove( *m_pState->UnloadQuery.begin() ); + (*m_UnloadQuery.begin())->Send( UnloadPacket ); + m_UnloadQuery.remove( *m_UnloadQuery.begin() ); } - cCSLock Lock2(m_pState->BlockListCriticalSection); - std::map< unsigned int, int > ToTickBlocks = m_pState->ToTickBlocks; - m_pState->ToTickBlocks.clear(); + cCSLock Lock2(m_CSBlockLists); + std::map< unsigned int, int > ToTickBlocks = m_ToTickBlocks; + m_ToTickBlocks.clear(); Lock2.Unlock(); bool isRedstone = false; @@ -376,7 +336,9 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) char ID = m_BlockType[Index]; switch( ID ) { - case E_BLOCK_DIRT: + /* + // TODO: re-enable + case E_BLOCK_DIRT: { char AboveBlock = GetBlock( Index+1 ); if ( (AboveBlock == 0) && GetLight( m_BlockSkyLight, Index ) > 0xf/2 ) // Half lit //changed to not allow grass if any one hit object is on top @@ -388,9 +350,11 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) FastSetBlock( m_BlockTickX, m_BlockTickY, m_BlockTickZ, E_BLOCK_GRASS, GetLight( m_BlockMeta, Index ) ); } + break; } - break; - case E_BLOCK_GRASS: + */ + + case E_BLOCK_GRASS: { char AboveBlock = GetBlock( Index+1 ); if (!( (AboveBlock == 0) || (g_BlockOneHitDig[AboveBlock]) || (g_BlockTransparent[AboveBlock]) ) ) //changed to not allow grass if any one hit object is on top @@ -414,13 +378,12 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) } } - // Tick block entities (furnace) - std::list< cFurnaceEntity* > TickBlockEntites = m_pState->TickBlockEntities; // Dangerous stuff, better make a copy. - for( std::list< cFurnaceEntity* >::iterator itr = TickBlockEntites.begin(); itr != TickBlockEntites.end(); ++itr ) + // Tick block entities (furnaces) + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) { - if( !(*itr)->Tick( a_Dt ) ) // Remove from list + if ((*itr)->GetBlockType() == E_BLOCK_FURNACE) { - m_pState->TickBlockEntities.remove( *itr ); + ((cFurnaceEntity *)(*itr))->Tick( a_Dt ); } } } @@ -432,7 +395,9 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) char cChunk::GetHeight( int a_X, int a_Z ) { if( a_X >= 0 && a_X < 16 && a_Z >= 0 && a_Z < 16 ) + { return m_HeightMap[a_X + a_Z*16]; + } return 0; } @@ -442,41 +407,38 @@ char cChunk::GetHeight( int a_X, int a_Z ) void cChunk::CreateBlockEntities() { - m_pState->BlockListCriticalSection.Lock(); - for(int x = 0; x < 16; x++) + cCSLock Lock(m_CSBlockLists); + for (int x = 0; x < 16; x++) { - for(int z = 0; z < 16; z++) + for (int z = 0; z < 16; z++) { - for(int y = 0; y < 128; y++) + for (int y = 0; y < 128; y++) { ENUM_BLOCK_ID BlockType = (ENUM_BLOCK_ID)m_BlockData[ MakeIndex( x, y, z ) ]; - switch( BlockType ) + switch ( BlockType ) { - case E_BLOCK_CHEST: - { - m_pState->BlockEntities.push_back( new cChestEntity( x + m_PosX*16, y + m_PosY*128, z + m_PosZ*16, this ) ); - } - break; - case E_BLOCK_FURNACE: + case E_BLOCK_CHEST: { - m_pState->BlockEntities.push_back( new cFurnaceEntity( x + m_PosX*16, y + m_PosY*128, z + m_PosZ*16, this ) ); + m_BlockEntities.push_back( new cChestEntity( x + m_PosX * 16, y + m_PosY * 128, z + m_PosZ * 16, m_World) ); + break; } - break; - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: + + case E_BLOCK_FURNACE: { - m_pState->BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX*16, y + m_PosY*128, z + m_PosZ*16, this ) ); + m_BlockEntities.push_back( new cFurnaceEntity( x + m_PosX * 16, y + m_PosY * 128, z + m_PosZ * 16, m_World) ); + break; } - break; - default: + + case E_BLOCK_SIGN_POST: + case E_BLOCK_WALLSIGN: { + m_BlockEntities.push_back( new cSignEntity( BlockType, x + m_PosX * 16, y + m_PosY * 128, z + m_PosZ * 16, m_World) ); + break; } - break; - } - } - } - } - m_pState->BlockListCriticalSection.Unlock(); + } // switch (BlockType) + } // for y + } // for z + } // for x } @@ -486,21 +448,21 @@ void cChunk::CreateBlockEntities() void cChunk::CalculateHeightmap() { m_bCalculateHeightmap = false; - for(int x = 0; x < 16; x++) + for (int x = 0; x < 16; x++) { - for(int z = 0; z < 16; z++) + for (int z = 0; z < 16; z++) { - for(int y = 127; y > -1; y--) + for (int y = 127; y > -1; y--) { int index = MakeIndex( x, y, z ); - if(m_BlockData[index] != E_BLOCK_AIR) + if (m_BlockData[index] != E_BLOCK_AIR) { - m_HeightMap[x + z*16] = (char)y; + m_HeightMap[x + z * 16] = (char)y; break; } - } - } - } + } // for y + } // for z + } // for x } @@ -510,7 +472,7 @@ void cChunk::CalculateHeightmap() void cChunk::CalculateLighting() { // Calculate sunlight - memset(m_BlockSkyLight, 0xff, c_NumBlocks/2 ); // Set all to fully lit, so everything above HeightMap is lit + memset(m_BlockSkyLight, 0xff, c_NumBlocks / 2 ); // Set all to fully lit, so everything above HeightMap is lit for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) @@ -577,15 +539,23 @@ void cChunk::SpreadLight(char* a_LightBuffer) bool bCalcLeft, bCalcRight, bCalcFront, bCalcBack; bCalcLeft = bCalcRight = bCalcFront = bCalcBack = false; + // Spread to neighbour chunks X-axis - ptr_cChunk LeftChunk = m_World->GetChunkUnreliable( m_PosX-1, m_PosY, m_PosZ ); - ptr_cChunk RightChunk = m_World->GetChunkUnreliable( m_PosX+1, m_PosY, m_PosZ ); - char* LeftSky = 0, *RightSky = 0; - if(LeftChunk) LeftSky = (a_LightBuffer==m_BlockSkyLight)?LeftChunk->pGetSkyLight():LeftChunk->pGetLight(); - if(RightChunk) RightSky = (a_LightBuffer==m_BlockSkyLight)?RightChunk->pGetSkyLight():RightChunk->pGetLight(); - for(int z = 0; z < 16; z++) for(int y = 0; y < 128; y++) + cChunkPtr LeftChunk = m_World->GetChunkNoGen( m_PosX - 1, m_PosY, m_PosZ ); + cChunkPtr RightChunk = m_World->GetChunkNoGen( m_PosX + 1, m_PosY, m_PosZ ); + char * LeftSky = NULL, *RightSky = NULL; + if (LeftChunk->IsValid()) { - if( LeftChunk ) + LeftSky = (a_LightBuffer == m_BlockSkyLight) ? LeftChunk->pGetSkyLight() : LeftChunk->pGetLight(); + } + if (RightChunk->IsValid()) + { + RightSky = (a_LightBuffer == m_BlockSkyLight) ? RightChunk->pGetSkyLight() : RightChunk->pGetLight(); + } + + for (int z = 0; z < 16; z++) for(int y = 0; y < 128; y++) + { + if (LeftSky != NULL) { int index = y + (z * 128) + (0 * 128 * 16); if( g_BlockSpreadLightFalloff[ m_BlockData[index] ] > 0 ) @@ -599,7 +569,7 @@ void cChunk::SpreadLight(char* a_LightBuffer) } } } - if( RightChunk ) + if (RightSky != NULL) { int index = y + (z * 128) + (15 * 128 * 16); if( g_BlockSpreadLightFalloff[ m_BlockData[index] ] > 0 ) @@ -616,14 +586,20 @@ void cChunk::SpreadLight(char* a_LightBuffer) } // Spread to neighbour chunks Z-axis - ptr_cChunk FrontChunk = m_World->GetChunkUnreliable( m_PosX, m_PosY, m_PosZ-1 ); - ptr_cChunk BackChunk = m_World->GetChunkUnreliable( m_PosX, m_PosY, m_PosZ+1 ); - char* FrontSky = 0, *BackSky = 0; - if(FrontChunk) FrontSky = (a_LightBuffer==m_BlockSkyLight)?FrontChunk->pGetSkyLight():FrontChunk->pGetLight(); - if(BackChunk) BackSky = (a_LightBuffer==m_BlockSkyLight)?BackChunk->pGetSkyLight():BackChunk->pGetLight(); + cChunkPtr FrontChunk = m_World->GetChunkNoGen( m_PosX, m_PosY, m_PosZ - 1 ); + cChunkPtr BackChunk = m_World->GetChunkNoGen( m_PosX, m_PosY, m_PosZ + 1 ); + char * FrontSky = NULL, * BackSky = NULL; + if (FrontChunk->IsValid()) + { + FrontSky = (a_LightBuffer == m_BlockSkyLight) ? FrontChunk->pGetSkyLight() : FrontChunk->pGetLight(); + } + if (BackChunk->IsValid()) + { + BackSky = (a_LightBuffer == m_BlockSkyLight) ? BackChunk->pGetSkyLight() : BackChunk->pGetLight(); + } for(int x = 0; x < 16; x++) for(int y = 0; y < 128; y++) { - if( FrontChunk ) + if (FrontSky != NULL) { int index = y + (0 * 128) + (x * 128 * 16); if( g_BlockSpreadLightFalloff[ m_BlockData[index] ] > 0 ) @@ -637,14 +613,14 @@ void cChunk::SpreadLight(char* a_LightBuffer) } } } - if( BackChunk ) + if (BackSky != NULL) { int index = y + (15 * 128) + (x * 128 * 16); if( g_BlockSpreadLightFalloff[ m_BlockData[index] ] > 0 ) { char CurrentLight = GetLight( a_LightBuffer, x, y, 15 ); char BackLight = GetLight( BackSky, x, y, 0 ); - if( BackLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockData[index] ] ) + if ( BackLight < CurrentLight-g_BlockSpreadLightFalloff[ m_BlockData[index] ] ) { SetLight( BackSky, x, y, 0, MAX(0, CurrentLight-g_BlockSpreadLightFalloff[ m_BlockData[index] ]) ); bCalcBack = true; @@ -665,8 +641,8 @@ void cChunk::SpreadLight(char* a_LightBuffer) void cChunk::AsyncUnload( cClientHandle* a_Client ) { - m_pState->UnloadQuery.remove( a_Client ); // Make sure this client is only in the list once - m_pState->UnloadQuery.push_back( a_Client ); + m_UnloadQuery.remove( a_Client ); // Make sure this client is only in the list once + m_UnloadQuery.push_back( a_Client ); } @@ -682,12 +658,11 @@ void cChunk::Send( cClientHandle* a_Client ) a_Client->Send( PreChunk ); a_Client->Send( cPacket_MapChunk( this ) ); - m_pState->BlockListCriticalSection.Lock(); - for( BlockEntityList::iterator itr = m_pState->BlockEntities.begin(); itr != m_pState->BlockEntities.end(); ++itr ) + cCSLock Lock(m_CSBlockLists); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr ) { (*itr)->SendTo( a_Client ); } - m_pState->BlockListCriticalSection.Unlock(); } @@ -701,6 +676,8 @@ void cChunk::SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_Block return; // Clip } + assert(IsValid()); // Is this chunk loaded / generated? + int index = a_Y + (a_Z * 128) + (a_X * 128 * 16); char OldBlockMeta = GetLight( m_BlockMeta, index ); char OldBlockType = m_BlockType[index]; @@ -713,17 +690,16 @@ void cChunk::SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_Block return; } - //LOG("Old: %i %i New: %i %i", OldBlockType, OldBlockMeta, a_BlockType, a_BlockMeta ); - cCSLock Lock(m_pState->BlockListCriticalSection); - m_pState->PendingSendBlocks.push_back( index ); + cCSLock Lock(m_CSBlockLists); + m_PendingSendBlocks.push_back( index ); - m_pState->ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X+1, a_Y, a_Z ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X-1, a_Y, a_Z ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X, a_Y+1, a_Z ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X, a_Y-1, a_Z ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z+1 ) ]++; - m_pState->ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z-1 ) ]++; + m_ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z ) ]++; + m_ToTickBlocks[ MakeIndex( a_X+1, a_Y, a_Z ) ]++; + m_ToTickBlocks[ MakeIndex( a_X-1, a_Y, a_Z ) ]++; + m_ToTickBlocks[ MakeIndex( a_X, a_Y+1, a_Z ) ]++; + m_ToTickBlocks[ MakeIndex( a_X, a_Y-1, a_Z ) ]++; + m_ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z+1 ) ]++; + m_ToTickBlocks[ MakeIndex( a_X, a_Y, a_Z-1 ) ]++; cBlockEntity* BlockEntity = GetBlockEntity( a_X + m_PosX*16, a_Y+m_PosY*128, a_Z+m_PosZ*16 ); if( BlockEntity ) @@ -736,18 +712,18 @@ void cChunk::SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_Block { case E_BLOCK_CHEST: { - AddBlockEntity( new cChestEntity( a_X + m_PosX*16, a_Y + m_PosY*128, a_Z + m_PosZ*16, this ) ); + AddBlockEntity( new cChestEntity( a_X + m_PosX * 16, a_Y + m_PosY * 128, a_Z + m_PosZ * 16, m_World) ); break; } case E_BLOCK_FURNACE: { - AddBlockEntity( new cFurnaceEntity( a_X + m_PosX*16, a_Y + m_PosY*128, a_Z + m_PosZ*16, this ) ); + AddBlockEntity( new cFurnaceEntity( a_X + m_PosX * 16, a_Y + m_PosY * 128, a_Z + m_PosZ * 16, m_World) ); break; } case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: { - AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, a_X + m_PosX*16, a_Y + m_PosY*128, a_Z + m_PosZ*16, this ) ); + AddBlockEntity( new cSignEntity( (ENUM_BLOCK_ID)a_BlockType, a_X + m_PosX * 16, a_Y + m_PosY * 128, a_Z + m_PosZ * 16, m_World) ); break; } } // switch (a_BlockType) @@ -761,17 +737,24 @@ void cChunk::FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_B { if(a_X < 0 || a_X >= 16 || a_Y < 0 || a_Y >= 128 || a_Z < 0 || a_Z >= 16) { - //printf(">>>>>>>>>>>>>>>> CLIPPED SETBLOCK %i %i %i\n", a_X, a_Y, a_Z ); return; // Clip } + assert(IsValid()); + const int index = a_Y + (a_Z * 128) + (a_X * 128 * 16); const char OldBlock = m_BlockType[index]; + if (OldBlock == a_BlockType) + { + return; + } m_BlockType[index] = a_BlockType; - m_pState->BlockListCriticalSection.Lock(); - m_pState->PendingSendBlocks.push_back( index ); - m_pState->BlockListCriticalSection.Unlock(); + { + cCSLock Lock(m_CSBlockLists); + m_PendingSendBlocks.push_back( index ); + } + SetLight( m_BlockMeta, index, a_BlockMeta ); // ONLY recalculate lighting if it's necessary! @@ -794,15 +777,14 @@ void cChunk::SendBlockTo( int a_X, int a_Y, int a_Z, cClientHandle* a_Client ) { if( a_Client == 0 ) { - m_pState->BlockListCriticalSection.Lock(); - m_pState->PendingSendBlocks.push_back( MakeIndex( a_X, a_Y, a_Z ) ); - m_pState->BlockListCriticalSection.Unlock(); + cCSLock Lock(m_CSBlockLists); + m_PendingSendBlocks.push_back( MakeIndex( a_X, a_Y, a_Z ) ); return; } - for( std::list< cClientHandle* >::iterator itr = m_pState->LoadedByClient.begin(); itr != m_pState->LoadedByClient.end(); ++itr ) + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) { - if( *itr == a_Client ) + if ( *itr == a_Client ) { unsigned int index = MakeIndex( a_X, a_Y, a_Z ); cPacket_BlockChange BlockChange; @@ -823,9 +805,85 @@ void cChunk::SendBlockTo( int a_X, int a_Y, int a_Z, cClientHandle* a_Client ) void cChunk::AddBlockEntity( cBlockEntity* a_BlockEntity ) { - m_pState->BlockListCriticalSection.Lock(); - m_pState->BlockEntities.push_back( a_BlockEntity ); - m_pState->BlockListCriticalSection.Unlock(); + cCSLock Lock(m_CSBlockLists); + m_BlockEntities.push_back( a_BlockEntity ); +} + + + + + +cBlockEntity * cChunk::GetBlockEntity(int a_X, int a_Y, int a_Z) +{ + // Assumes that the m_CSBlockList is already locked, we're being called from SetBlock() + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_X) && + ((*itr)->GetPosY() == a_Y) && + ((*itr)->GetPosZ() == a_Z) + ) + { + return *itr; + } + } // for itr - m_BlockEntities[] + + return NULL; +} + + + + + +void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) +{ + cCSLock Lock(m_CSEntities); + + double PosX = a_Player->GetPosX(); + double PosY = a_Player->GetPosY(); + double PosZ = a_Player->GetPosZ(); + + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr) + { + if ( (*itr)->GetEntityType() != cEntity::E_PICKUP ) + { + continue; // Only pickups + } + float DiffX = (float)((*itr)->GetPosX() - PosX ); + float DiffY = (float)((*itr)->GetPosY() - PosY ); + float DiffZ = (float)((*itr)->GetPosZ() - PosZ ); + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if (SqrDist < 1.5f * 1.5f) // 1.5 block + { + (reinterpret_cast(*itr))->CollectedBy( a_Player ); + } + } +} + + + + + +void cChunk::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 + cCSLock Lock(m_CSEntities); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ( + ((*itr)->GetPosX() == a_PosX) && + ((*itr)->GetPosY() == a_PosY) && + ((*itr)->GetPosZ() == a_PosZ) && + ( + ((*itr)->GetBlockType() == E_BLOCK_WALLSIGN) || + ((*itr)->GetBlockType() == E_BLOCK_SIGN_POST) + ) + ) + { + (reinterpret_cast(*itr))->SetLines(a_Line1, a_Line2, a_Line3, a_Line4); + (*itr)->SendTo(NULL); + } + } // for itr - m_BlockEntities[] } @@ -834,9 +892,8 @@ void cChunk::AddBlockEntity( cBlockEntity* a_BlockEntity ) void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) { - m_pState->BlockListCriticalSection.Lock(); - m_pState->BlockEntities.remove( a_BlockEntity ); - m_pState->BlockListCriticalSection.Unlock(); + cCSLock Lock(m_CSBlockLists); + m_BlockEntities.remove( a_BlockEntity ); } @@ -845,16 +902,18 @@ void cChunk::RemoveBlockEntity( cBlockEntity* a_BlockEntity ) void cChunk::AddClient( cClientHandle* a_Client ) { - m_pState->LoadedByClient.remove( a_Client ); - m_pState->LoadedByClient.push_back( a_Client ); + { + cCSLock Lock(m_CSClients); + m_LoadedByClient.remove( a_Client ); + m_LoadedByClient.push_back( a_Client ); + } - LockEntities(); - for( EntityList::iterator itr = m_pState->Entities.begin(); itr != m_pState->Entities.end(); ++itr ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) { - LOG("%i %i %i Spawning on %s", m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str() ); + LOG("Entity at [%i %i %i] spawning for player \"%s\"", m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str() ); (*itr)->SpawnOn( a_Client ); } - UnlockEntities(); } @@ -863,18 +922,20 @@ void cChunk::AddClient( cClientHandle* a_Client ) void cChunk::RemoveClient( cClientHandle* a_Client ) { - m_pState->LoadedByClient.remove( a_Client ); + { + cCSLock Lock(m_CSClients); + m_LoadedByClient.remove( a_Client ); + } - if( !a_Client->IsDestroyed() ) + if ( !a_Client->IsDestroyed() ) { - LockEntities(); - for( EntityList::iterator itr = m_pState->Entities.begin(); itr != m_pState->Entities.end(); ++itr ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr ) { - LOG("%i %i %i Destroying on %s", m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str() ); + LOG("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() ); cPacket_DestroyEntity DestroyEntity( *itr ); a_Client->Send( DestroyEntity ); } - UnlockEntities(); } } @@ -882,53 +943,52 @@ void cChunk::RemoveClient( cClientHandle* a_Client ) -void cChunk::AddEntity( cEntity & a_Entity ) +bool cChunk::HasClient( cClientHandle* a_Client ) { - LockEntities(); - m_pState->Entities.push_back( &a_Entity ); - UnlockEntities(); + cCSLock Lock(m_CSClients); + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + if ((*itr) == a_Client) + { + return true; + } + } + return false; } -bool cChunk::RemoveEntity( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ ) +bool cChunk::HasAnyClient(void) { - LockEntities(); - unsigned int SizeBefore = m_pState->Entities.size(); - m_pState->Entities.remove( &a_Entity ); - if( SizeBefore == m_pState->Entities.size() ) - { - LOG("WARNING: Entity was not in chunk %i %i %i", m_PosX, m_PosY, m_PosZ ); - if( !a_CalledFrom ) - { - UnlockEntities(); - return m_World->RemoveEntityFromChunk( a_Entity, this ); - } - UnlockEntities(); - return false; - } - UnlockEntities(); - return true; + cCSLock Lock(m_CSClients); + return !m_LoadedByClient.empty(); } -void cChunk::LockEntities() +void cChunk::AddEntity( cEntity * a_Entity ) { - m_EntitiesCriticalSection->Lock(); + cCSLock Lock(m_CSEntities); + m_Entities.push_back( a_Entity ); } -void cChunk::UnlockEntities() +void cChunk::RemoveEntity(cEntity * a_Entity) { - m_EntitiesCriticalSection->Unlock(); + size_t SizeBefore, SizeAfter; + { + cCSLock Lock(m_CSEntities); + SizeBefore = m_Entities.size(); + m_Entities.remove(a_Entity); + SizeAfter = m_Entities.size(); + } } @@ -937,7 +997,7 @@ void cChunk::UnlockEntities() char cChunk::GetBlock( int a_X, int a_Y, int a_Z ) { - if(a_X < 0 || a_X >= 16 || a_Y < 0 || a_Y >= 128 || a_Z < 0 || a_Z >= 16) return 0; // Clip + if ((a_X < 0) || (a_X >= 16) || (a_Y < 0) || (a_Y >= 128) || (a_Z < 0) || (a_Z >= 16)) return 0; // Clip int index = a_Y + (a_Z * 128) + (a_X * 128 * 16); return m_BlockType[index]; @@ -957,28 +1017,6 @@ char cChunk::GetBlock( int a_BlockIdx ) -cBlockEntity* cChunk::GetBlockEntity( int a_X, int a_Y, int a_Z ) -{ - m_pState->BlockListCriticalSection.Lock(); - for( std::list::iterator itr = m_pState->BlockEntities.begin(); itr != m_pState->BlockEntities.end(); ++itr) - { - if( (*itr)->GetPosX() == a_X && - (*itr)->GetPosY() == a_Y && - (*itr)->GetPosZ() == a_Z ) - { - cBlockEntity* BlockEnt = *itr; - m_pState->BlockListCriticalSection.Unlock(); - return BlockEnt; - } - } - m_pState->BlockListCriticalSection.Unlock(); - return 0; -} - - - - - /// Loads the chunk from the old-format disk file, erases the file afterwards. Returns true if successful bool cChunk::LoadFromDisk() { @@ -998,7 +1036,7 @@ bool cChunk::LoadFromDisk() } // Now load Block Entities - cCSLock Lock(m_pState->BlockListCriticalSection); + cCSLock Lock(m_CSEntities); ENUM_BLOCK_ID BlockType; while (f.Read(&BlockType, sizeof(ENUM_BLOCK_ID)) == sizeof(ENUM_BLOCK_ID)) @@ -1007,42 +1045,41 @@ bool cChunk::LoadFromDisk() { case E_BLOCK_CHEST: { - cChestEntity * ChestEntity = new cChestEntity( 0, 0, 0, this ); + cChestEntity * ChestEntity = new cChestEntity( 0, 0, 0, m_World ); if (!ChestEntity->LoadFromFile(f)) { LOGERROR("ERROR READING CHEST FROM FILE %s", SourceFile.c_str()); delete ChestEntity; return false; } - m_pState->BlockEntities.push_back( ChestEntity ); + m_BlockEntities.push_back( ChestEntity ); break; } case E_BLOCK_FURNACE: { - cFurnaceEntity* FurnaceEntity = new cFurnaceEntity( 0, 0, 0, this ); + cFurnaceEntity* FurnaceEntity = new cFurnaceEntity( 0, 0, 0, m_World ); if (!FurnaceEntity->LoadFromFile(f)) { LOGERROR("ERROR READING FURNACE FROM FILE %s", SourceFile.c_str()); delete FurnaceEntity; return false; } - m_pState->BlockEntities.push_back( FurnaceEntity ); - m_pState->TickBlockEntities.push_back( FurnaceEntity ); // They need tickin' + m_BlockEntities.push_back( FurnaceEntity ); break; } case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: { - cSignEntity * SignEntity = new cSignEntity(BlockType, 0, 0, 0, this ); + cSignEntity * SignEntity = new cSignEntity(BlockType, 0, 0, 0, m_World ); if (!SignEntity->LoadFromFile( f ) ) { LOGERROR("ERROR READING SIGN FROM FILE %s", SourceFile.c_str()); delete SignEntity; return false; } - m_pState->BlockEntities.push_back( SignEntity ); + m_BlockEntities.push_back( SignEntity ); break; } @@ -1072,9 +1109,10 @@ bool cChunk::LoadFromDisk() -void cChunk::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude /* = 0 */ ) const +void cChunk::Broadcast( const cPacket * a_Packet, cClientHandle* a_Exclude) { - for( std::list< cClientHandle* >::const_iterator itr = m_pState->LoadedByClient.begin(); itr != m_pState->LoadedByClient.end(); ++itr ) + cCSLock Lock(m_CSClients); + for (cClientHandleList::const_iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) { if (*itr == a_Exclude) { @@ -1088,58 +1126,69 @@ void cChunk::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude /* = +void cChunk::CopyBlockDataFrom(const char * a_NewBlockData) +{ + // Copies all blockdata, recalculates heightmap (used by chunk loaders) + memcpy(m_BlockData, a_NewBlockData, sizeof(m_BlockData)); + CalculateHeightmap(); +} + + + + + void cChunk::LoadFromJson( const Json::Value & a_Value ) { - cCSLock Lock(m_pState->BlockListCriticalSection); + cCSLock Lock(m_CSEntities); // Load chests Json::Value AllChests = a_Value.get("Chests", Json::nullValue); if (!AllChests.empty()) { - for( Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) + for ( Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) { Json::Value & Chest = *itr; - cChestEntity* ChestEntity = new cChestEntity(0,0,0, this); - if( !ChestEntity->LoadFromJson( Chest ) ) + cChestEntity* ChestEntity = new cChestEntity(0, 0, 0, m_World); + if ( !ChestEntity->LoadFromJson( Chest ) ) { LOGERROR("ERROR READING CHEST FROM JSON!" ); delete ChestEntity; } - else m_pState->BlockEntities.push_back( ChestEntity ); + else m_BlockEntities.push_back( ChestEntity ); } } // Load furnaces Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue); - if( !AllFurnaces.empty() ) + if ( !AllFurnaces.empty() ) { - for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr ) + for ( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr ) { Json::Value & Furnace = *itr; - cFurnaceEntity* FurnaceEntity = new cFurnaceEntity(0,0,0, this); - if( !FurnaceEntity->LoadFromJson( Furnace ) ) + cFurnaceEntity* FurnaceEntity = new cFurnaceEntity(0, 0, 0, m_World); + if ( !FurnaceEntity->LoadFromJson( Furnace ) ) { LOGERROR("ERROR READING FURNACE FROM JSON!" ); delete FurnaceEntity; } - else m_pState->BlockEntities.push_back( FurnaceEntity ); + else m_BlockEntities.push_back( FurnaceEntity ); } } // Load signs Json::Value AllSigns = a_Value.get("Signs", Json::nullValue); - if( !AllSigns.empty() ) + if ( !AllSigns.empty() ) { - for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr ) + for ( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr ) { Json::Value & Sign = *itr; - cSignEntity* SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, this); - if( !SignEntity->LoadFromJson( Sign ) ) + cSignEntity* SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0, 0, 0, m_World); + if ( !SignEntity->LoadFromJson( Sign ) ) { LOGERROR("ERROR READING SIGN FROM JSON!" ); delete SignEntity; } - else m_pState->BlockEntities.push_back( SignEntity ); + else m_BlockEntities.push_back( SignEntity ); } } } @@ -1153,11 +1202,11 @@ void cChunk::SaveToJson( Json::Value & a_Value ) Json::Value AllChests; Json::Value AllFurnaces; Json::Value AllSigns; - cCSLock Lock(m_pState->BlockListCriticalSection); - for (std::list::iterator itr = m_pState->BlockEntities.begin(); itr != m_pState->BlockEntities.end(); ++itr) + cCSLock Lock(m_CSEntities); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) { - cBlockEntity* BlockEntity = *itr; - switch( BlockEntity->GetBlockType() ) + cBlockEntity * BlockEntity = *itr; + switch ( BlockEntity->GetBlockType() ) { case E_BLOCK_CHEST: { @@ -1196,48 +1245,17 @@ void cChunk::SaveToJson( Json::Value & a_Value ) } // for itr - BlockEntities[] if( !AllChests.empty() ) + { a_Value["Chests"] = AllChests; + } if( !AllFurnaces.empty() ) + { a_Value["Furnaces"] = AllFurnaces; + } if( !AllSigns.empty() ) + { a_Value["Signs"] = AllSigns; -} - - - - - -EntityList & cChunk::GetEntities() -{ - return m_pState->Entities; -} - - - - - -const ClientHandleList & cChunk::GetClients() -{ - return m_pState->LoadedByClient; -} - - - - - -void cChunk::AddTickBlockEntity( cFurnaceEntity* a_Entity ) -{ - m_pState->TickBlockEntities.remove( a_Entity ); - m_pState->TickBlockEntities.push_back( a_Entity ); -} - - - - - -void cChunk::RemoveTickBlockEntity( cFurnaceEntity* a_Entity ) -{ - m_pState->TickBlockEntities.remove( a_Entity ); + } } @@ -1255,42 +1273,6 @@ void cChunk::PositionToWorldPosition(int a_ChunkX, int a_ChunkY, int a_ChunkZ, i -void cChunk::AddReference() -{ - cCSLock Lock(m_pState->ReferenceCriticalSection); - m_pState->NumRefs++; -} - - - - - -void cChunk::RemoveReference() -{ - cCSLock Lock(m_pState->ReferenceCriticalSection); - m_pState->NumRefs--; - assert (m_pState->NumRefs >= 0); - if (m_pState->NumRefs < 0) - { - LOGWARN("WARNING: cChunk: Tried to remove reference, but the chunk is not referenced!"); - } -} - - - - - -int cChunk::GetReferenceCount() -{ - cCSLock Lock(m_pState->ReferenceCriticalSection); - int Refs = m_pState->NumRefs; - return Refs; -} - - - - - #if !C_CHUNK_USE_INLINE # include "cChunk.inc" #endif diff --git a/source/cChunk.h b/source/cChunk.h index d18f09160..0e1dccfef 100644 --- a/source/cChunk.h +++ b/source/cChunk.h @@ -1,15 +1,25 @@ #pragma once +#include "cEntity.h" + + + + + #define C_CHUNK_USE_INLINE 1 // Do not touch #if C_CHUNK_USE_INLINE -# define __C_CHUNK_INLINE__ inline + #define __C_CHUNK_INLINE__ inline #else -# define __C_CHUNK_INLINE__ + #define __C_CHUNK_INLINE__ #endif + + + + namespace Json { class Value; @@ -23,10 +33,14 @@ class cWorld; class cFurnaceEntity; class cPacket; class cBlockEntity; -class cEntity; class cClientHandle; class cServer; class MTRand; +class cPlayer; + +typedef std::list cFurnaceEntityList; +typedef std::list cClientHandleList; +typedef std::list cBlockEntityList; @@ -39,6 +53,9 @@ public: ~cChunk(); void Initialize(); + bool IsValid(void) const {return m_IsValid; } // Returns true if the chunk is valid (loaded / generated) + void SetValid(bool a_SendToClients = true); // Also wakes up all clients attached to this chunk to let them finish logging in + bool CanUnload(void); void Tick(float a_Dt, MTRand & a_TickRandom); @@ -51,42 +68,49 @@ public: void AsyncUnload( cClientHandle* a_Client ); void SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); - void FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); + void FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc. char GetBlock( int a_X, int a_Y, int a_Z ); char GetBlock( int a_BlockIdx ); - cBlockEntity* GetBlockEntity( int a_X, int a_Y, int a_Z ); - void RemoveBlockEntity( cBlockEntity* a_BlockEntity ); - void AddBlockEntity( cBlockEntity* a_BlockEntity ); + + 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 char GetHeight( int a_X, int a_Z ); void SendBlockTo( int a_X, int a_Y, int a_Z, cClientHandle* a_Client ); - void AddClient( cClientHandle* a_Client ); + void AddClient ( cClientHandle* a_Client ); void RemoveClient( cClientHandle* a_Client ); + bool HasClient ( cClientHandle* a_Client ); + bool HasAnyClient(void); // Returns true if theres any client in the chunk; false otherwise - std::list< cEntity* > & GetEntities();// { return m_Entities; } - void AddEntity( cEntity & a_Entity ); - bool RemoveEntity( cEntity & a_Entity, cChunk* a_CalledFrom = 0 ); - void LockEntities(); - void UnlockEntities(); + void AddEntity( cEntity * a_Entity ); + void RemoveEntity( cEntity * a_Entity); - const std::list< cClientHandle* > & GetClients();// { return m_LoadedByClient; } + // TODO: This interface is dangerous + OBSOLETE const std::list< cClientHandle* > & GetClients();// { return m_LoadedByClient; } inline void RecalculateLighting() { m_bCalculateLighting = true; } // Recalculate lighting next tick inline void RecalculateHeightmap() { m_bCalculateHeightmap = true; } // Recalculate heightmap next tick void SpreadLight(char* a_LightBuffer); + void CalculateLighting(); // Recalculate right now + void CalculateHeightmap(); bool LoadFromDisk(); // Broadcasts to all clients that have loaded this chunk - void Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude = 0 ) const; + void Broadcast( const cPacket & a_Packet, cClientHandle * a_Exclude = NULL) {Broadcast(&a_Packet, a_Exclude); } + void Broadcast( const cPacket * a_Packet, cClientHandle * a_Exclude = NULL); char* pGetBlockData() { return m_BlockData; } char* pGetType() { return m_BlockType; } char* pGetMeta() { return m_BlockMeta; } char* pGetLight() { return m_BlockLight; } char* pGetSkyLight() { return m_BlockSkyLight; } + + void CopyBlockDataFrom(const char * a_NewBlockData); // Copies all blockdata, recalculates heightmap (used by chunk loaders) + void LoadFromJson( const Json::Value & a_Value ); + void SaveToJson( Json::Value & a_Value ); char GetLight(char* a_Buffer, int a_BlockIdx); char GetLight(char* a_Buffer, int x, int y, int z); @@ -95,9 +119,6 @@ public: void PositionToWorldPosition(int a_ChunkX, int a_ChunkY, int a_ChunkZ, int & a_X, int & a_Y, int & a_Z); - void AddTickBlockEntity( cFurnaceEntity* a_Entity ); - void RemoveTickBlockEntity( cFurnaceEntity* a_Entity ); - inline static unsigned int MakeIndex(int x, int y, int z ) { if( x < 16 && x > -1 && y < 128 && y > -1 && z < 16 && z > -1 ) @@ -112,28 +133,28 @@ public: void AddReference(); void RemoveReference(); int GetReferenceCount(); + private: - struct sChunkState; - sChunkState* m_pState; - friend class cChunkMap; // So it has access to buffers and shit - void LoadFromJson( const Json::Value & a_Value ); - void SaveToJson( Json::Value & a_Value ); + bool m_IsValid; // True if the chunk is loaded / generated + + cCriticalSection m_CSBlockLists; + std::map< unsigned int, int > m_ToTickBlocks; + std::vector< unsigned int > m_PendingSendBlocks; + + cCriticalSection m_CSClients; + cClientHandleList m_LoadedByClient; + cClientHandleList m_UnloadQuery; - void CalculateLighting(); // Recalculate right now - void CalculateHeightmap(); - void SpreadLightOfBlock(char* a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); - void SpreadLightOfBlockX(char* a_LightBuffer, int a_X, int a_Y, int a_Z); - void SpreadLightOfBlockY(char* a_LightBuffer, int a_X, int a_Y, int a_Z); - void SpreadLightOfBlockZ(char* a_LightBuffer, int a_X, int a_Y, int a_Z); - - void CreateBlockEntities(); + cCriticalSection m_CSEntities; + cEntityList m_Entities; + cBlockEntityList m_BlockEntities; bool m_bCalculateLighting; bool m_bCalculateHeightmap; int m_PosX, m_PosY, m_PosZ; - cWorld* m_World; + cWorld * m_World; char m_BlockData[c_BlockDataSize]; // Chunk data ready to be compressed and sent char *m_BlockType; // Pointers to an element in m_BlockData @@ -141,19 +162,55 @@ private: char *m_BlockLight; // += NumBlocks/2 char *m_BlockSkyLight; // += NumBlocks/2 - unsigned char m_HeightMap[16*16]; + unsigned char m_HeightMap[16 * 16]; unsigned int m_BlockTickNum; unsigned int m_BlockTickX, m_BlockTickY, m_BlockTickZ; - cCriticalSection* m_EntitiesCriticalSection; + void RemoveBlockEntity( cBlockEntity* a_BlockEntity ); + void AddBlockEntity( cBlockEntity* a_BlockEntity ); + cBlockEntity * GetBlockEntity( int a_X, int a_Y, int a_Z ); + + void SpreadLightOfBlock(char* a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff); + void SpreadLightOfBlockX(char* a_LightBuffer, int a_X, int a_Y, int a_Z); + void SpreadLightOfBlockY(char* a_LightBuffer, int a_X, int a_Y, int a_Z); + void SpreadLightOfBlockZ(char* a_LightBuffer, int a_X, int a_Y, int a_Z); + + void CreateBlockEntities(); }; +typedef std::tr1::shared_ptr cChunkPtr; + +typedef std::list cChunkPtrList; + + + + + +class cChunkCoords +{ +public: + int m_ChunkX; + int m_ChunkZ; + + cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {} + + bool operator == (const cChunkCoords & a_Other) + { + return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ)); + } +} ; + +typedef std::list cChunkCoordsList; #if C_CHUNK_USE_INLINE -# include "cChunk.inl.h" -#endif \ No newline at end of file + #include "cChunk.inl.h" +#endif + + + + diff --git a/source/cChunkGenerator.cpp b/source/cChunkGenerator.cpp index 78853a26b..8755ea325 100644 --- a/source/cChunkGenerator.cpp +++ b/source/cChunkGenerator.cpp @@ -2,11 +2,13 @@ #include "Globals.h" #include "cChunkGenerator.h" -#include "cChunkMap.h" -#include "cChunk.h" #include "cWorld.h" +#include "cWorldGenerator.h" +#include "cWorldGenerator_Test.h" + + + -#include "cMCLogger.h" typedef std::pair ChunkCoord; typedef std::list< ChunkCoord > ChunkCoordList; @@ -16,144 +18,128 @@ typedef std::list< ChunkCoord > ChunkCoordList; /// If the generation queue size exceeds this number, a warning will be output -#define QUEUE_WARNING_LIMIT 1000 +const int QUEUE_WARNING_LIMIT = 1000; + +/// If the generation queue size exceeds this number, chunks with no clients will be skipped +const int QUEUE_SKIP_LIMIT = 50; -struct cChunkGenerator::sChunkGeneratorState +cChunkGenerator::cChunkGenerator(void) + : super("cChunkGenerator") + , m_World(NULL) + , m_pWorldGenerator(NULL) { - cCriticalSection m_CriticalSection; // For protecting the variables in this struct +} + - ChunkCoordList GenerateQueue; - ChunkCoord CurrentlyGeneratingCoords; - cChunk* pCurrentlyGenerating; - bool bCurrentlyGenerating; - cSemaphore m_Semaphore; - cThread * pThread; - bool bStop; - sChunkGeneratorState(void) - : m_Semaphore(1, 0) - , pThread( 0 ) - , bStop( false ) - , bCurrentlyGenerating( false ) - , pCurrentlyGenerating( false ) - {} -}; +cChunkGenerator::~cChunkGenerator() +{ + Stop(); +} -cChunkGenerator::cChunkGenerator( cChunkMap* a_pChunkMap ) - : m_pState( new sChunkGeneratorState ) - , m_pChunkMap( a_pChunkMap ) +bool cChunkGenerator::Start(cWorld * a_World, const AString & a_WorldGeneratorName) { - m_pState->pThread = new cThread( GenerateThread, this, "cChunkGenerator::GenerateThread" ); - m_pState->pThread->Start( true ); + m_World = a_World; + + if (a_WorldGeneratorName.compare("Test") == 0 ) + { + m_pWorldGenerator = new cWorldGenerator_Test(); + } + else // Default + { + m_pWorldGenerator = new cWorldGenerator(); + } + + return super::Start(); } -cChunkGenerator::~cChunkGenerator() +void cChunkGenerator::Stop(void) { - m_pState->bStop = true; - - m_pState->m_Semaphore.Signal(); // Signal so thread can continue and exit - delete m_pState->pThread; - - delete m_pState; + mShouldTerminate = true; + m_Event.Set(); + Wait(); + + delete m_pWorldGenerator; + m_pWorldGenerator = NULL; } -void cChunkGenerator::GenerateChunk( int a_X, int a_Z ) +void cChunkGenerator::GenerateChunk(int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(&m_pState->m_CriticalSection); - - if (m_pState->bCurrentlyGenerating) + cCSLock Lock(m_CS); + + // Check if it is already in the queue: + for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) { - if ((m_pState->CurrentlyGeneratingCoords.first == a_X) && (m_pState->CurrentlyGeneratingCoords.second == a_Z)) + if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) { - return; // Already generating this chunk, so ignore + // Already in the queue, bail out + return; } - } - - m_pState->GenerateQueue.remove( ChunkCoord(a_X, a_Z) ); - if (m_pState->GenerateQueue.size() >= QUEUE_WARNING_LIMIT) + } // for itr - m_Queue[] + + // Add to queue, issue a warning if too many: + if (m_Queue.size() >= QUEUE_WARNING_LIMIT) { - LOGWARN("WARNING: Adding chunk (%i, %i) to generation queue; Queue is too big! (%i)", a_X, a_Z, m_pState->GenerateQueue.size() ); + LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%i)", a_ChunkX, a_ChunkZ, m_Queue.size()); } - m_pState->GenerateQueue.push_back( ChunkCoord(a_X, a_Z) ); - - Lock.Unlock(); + m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); - m_pState->m_Semaphore.Signal(); + m_Event.Set(); } -void cChunkGenerator::GenerateThread( void* a_Params ) +void cChunkGenerator::Execute(void) { - // Cache some values for easy access (they are all references/pointers) - cChunkGenerator * self = (cChunkGenerator*)a_Params; - sChunkGeneratorState * m_pState = self->m_pState; - ChunkCoordList & GenerateQueue = m_pState->GenerateQueue; - cChunkMap & ChunkMap = *self->m_pChunkMap; - cCriticalSection * CriticalSection = &m_pState->m_CriticalSection; - cSemaphore & Semaphore = m_pState->m_Semaphore; - - while (!m_pState->bStop) + while (!mShouldTerminate) { - cCSLock Lock(CriticalSection); - if (GenerateQueue.size() == 0) + cCSLock Lock(m_CS); + while (m_Queue.size() == 0) { cCSUnlock Unlock(Lock); - Semaphore.Wait(); + m_Event.Wait(); + if (mShouldTerminate) + { + return; + } } - if (m_pState->bStop) break; - ChunkCoord coord = *GenerateQueue.begin(); // Get next coord from queue - GenerateQueue.erase( GenerateQueue.begin() ); // Remove coordinate from queue - m_pState->bCurrentlyGenerating = true; - m_pState->CurrentlyGeneratingCoords = coord; + cChunkCoords coords = m_Queue.front(); // Get next coord from queue + m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue + bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); Lock.Unlock(); // Unlock ASAP - ChunkMap.GetWorld()->LockChunks(); - if( ChunkMap.GetChunk( coord.first, 0, coord.second ) ) // Make sure it has not been loaded in the meantime. Don't want to generate the same chunk twice - { // This is possible when forcing the server to generate a chunk in the main thread - ChunkMap.GetWorld()->UnlockChunks(); + cChunkPtr Chunk = m_World->GetChunk(coords.m_ChunkX, 0, coords.m_ChunkZ); + if ((Chunk != NULL) && (Chunk->IsValid() || (SkipEnabled && !Chunk->HasAnyClient()))) + { + // Already generated / overload-skip, ignore request continue; } - ChunkMap.GetWorld()->UnlockChunks(); - - LOGINFO("cChunkGenerator generating chunk %i %i", coord.first, coord.second ); - cChunk* Chunk = new cChunk( coord.first, 0, coord.second, ChunkMap.GetWorld() ); - - Lock.Lock(); - m_pState->pCurrentlyGenerating = Chunk; - Lock.Unlock(); - Chunk->Initialize(); // Generate the chunk - - ChunkMap.GetWorld()->LockChunks(); - ChunkMap.AddChunk( Chunk ); - ChunkMap.GetWorld()->UnlockChunks(); - - Lock.Lock(); - m_pState->bCurrentlyGenerating = false; - m_pState->pCurrentlyGenerating = 0; - Lock.Unlock(); + LOG("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + m_pWorldGenerator->GenerateChunk(Chunk); + + Chunk->SetValid(); } // while (!bStop) } diff --git a/source/cChunkGenerator.h b/source/cChunkGenerator.h index bdba4da98..5de056572 100644 --- a/source/cChunkGenerator.h +++ b/source/cChunkGenerator.h @@ -1,20 +1,62 @@ + +// cChunkGenerator.h + +// 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 (if the cChunkPtr is thread-safe) +// If the generator queue is overloaded, the generator skips chunks with no clients in them + + + + + #pragma once -class cChunk; -class cChunkMap; -class cChunkGenerator +#include "cIsThread.h" +#include "cChunk.h" + + + + + +class cWorld; +class cWorldGenerator; + + + + + +class cChunkGenerator : + cIsThread { + typedef cIsThread super; + public: - cChunkGenerator( cChunkMap* a_pChunkMap ); + + cChunkGenerator (void); ~cChunkGenerator(); - void GenerateChunk( int a_X, int a_Z ); + bool Start(cWorld * a_World, const AString & a_WorldGeneratorName); + void Stop(void); + + void GenerateChunk(int a_ChunkX, int a_ChunkZ); // Queues the chunk for generation; removes duplicate requests private: - static void GenerateThread( void* a_Params ); - cChunkMap* m_pChunkMap; + // cIsThread override: + virtual void Execute(void) override; + + cWorld * m_World; + cWorldGenerator * m_pWorldGenerator; + + cCriticalSection m_CS; + cChunkCoordsList m_Queue; + cEvent m_Event; // Set when an item is added to the queue or the thread should terminate +}; + + + - struct sChunkGeneratorState; - sChunkGeneratorState* m_pState; -}; \ No newline at end of file diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp index 2d3aabd7b..b864b9792 100644 --- a/source/cChunkMap.cpp +++ b/source/cChunkMap.cpp @@ -6,7 +6,6 @@ #include "cWorld.h" #include "cRoot.h" #include "cMakeDir.h" -#include // floorf #ifndef _WIN32 #include // abs @@ -18,27 +17,6 @@ #define USE_MEMCPY -#define LAYER_SIZE (32) - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChunkMap::cChunkLayer: - -cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z ) -{ - const int LocalX = a_X - m_X * LAYER_SIZE; - const int LocalZ = a_Z - m_Z * LAYER_SIZE; - //LOG("LocalX:%i LocalZ:%i", LocalX, LocalZ ); - if ((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1)) - { - return &m_Chunks[ LocalX + LocalZ * LAYER_SIZE ]; - } - return 0; -} - @@ -46,10 +24,8 @@ cChunkMap::cChunkData* cChunkMap::cChunkLayer::GetChunk( int a_X, int a_Z ) //////////////////////////////////////////////////////////////////////////////// // cChunkMap: -cChunkMap::cChunkMap(cWorld* a_World ) - : m_Layers( 0 ) - , m_NumLayers( 0 ) - , m_World( a_World ) +cChunkMap::cChunkMap(cWorld * a_World ) + : m_World( a_World ) { } @@ -59,173 +35,54 @@ cChunkMap::cChunkMap(cWorld* a_World ) cChunkMap::~cChunkMap() { - // TODO: delete layers -} - - - - - -bool cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) -{ - cChunkLayer* NewLayers = 0; - if( m_NumLayers > 1 ) - NewLayers = new cChunkLayer[m_NumLayers-1]; - - int idx = 0; - bool bExcludedLayer = false; - for( int i = 0; i < m_NumLayers; ++i ) - { - if( &m_Layers[i] != a_Layer ) - { - if( idx < m_NumLayers-1 ) - { - NewLayers[ idx ] = m_Layers[i]; - idx++; - } - } - else - bExcludedLayer = true; - } - - if( !bExcludedLayer ) - { - LOGWARN("Could not remove layer, because layer was not found %i %i", a_Layer->m_X, a_Layer->m_Z); - delete [] NewLayers; - return false; - } - - if( m_Layers ) delete [] m_Layers; - m_Layers = NewLayers; - m_NumLayers--; - return true; -} - - - - - -cChunkMap::cChunkLayer* cChunkMap::AddLayer( const cChunkLayer & a_Layer ) -{ - cChunkLayer* TempLayers = new cChunkLayer[m_NumLayers+1]; - if( m_NumLayers > 0 ) + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - memcpy( TempLayers, m_Layers, sizeof( cChunkLayer ) * m_NumLayers ); - delete [] m_Layers; - } - m_Layers = TempLayers; - - m_Layers[m_NumLayers] = a_Layer; - cChunkLayer* NewLayer = &m_Layers[m_NumLayers]; - m_NumLayers++; - - return NewLayer; + delete *itr; + } // for itr - m_Layers[] } -void cChunkMap::AddChunk( cChunk* a_Chunk ) +void cChunkMap::RemoveLayer( cChunkLayer* a_Layer ) { - const int LayerX = (int)(floorf((float)a_Chunk->GetPosX() / (float)(LAYER_SIZE))); - const int LayerZ = (int)(floorf((float)a_Chunk->GetPosZ() / (float)(LAYER_SIZE))); - cChunkLayer* FoundLayer = GetLayer( LayerX, LayerZ ); - if( !FoundLayer ) - { - cChunkLayer NewLayer( LAYER_SIZE*LAYER_SIZE ); - NewLayer.m_X = LayerX; - NewLayer.m_Z = LayerZ; - FoundLayer = AddLayer( NewLayer ); - LOG("Created new layer [%i %i] (total layers %i)", LayerX, LayerZ, m_NumLayers ); - } - - //Get local coordinates in layer - const int LocalX = a_Chunk->GetPosX() - LayerX * LAYER_SIZE; - const int LocalZ = a_Chunk->GetPosZ() - LayerZ * LAYER_SIZE; - if( FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk ) - { - LOGWARN("WARNING: Added chunk to layer while it was already loaded!"); - } - if( FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_Compressed ) - { - LOGWARN("WARNING: Added chunk to layer while a compressed version exists!"); - } - FoundLayer->m_Chunks[ LocalX + LocalZ * LAYER_SIZE ].m_LiveChunk = a_Chunk; - FoundLayer->m_NumChunksLoaded++; + cCSLock Lock(m_CSLayers); + m_Layers.remove(a_Layer); } -void cChunkMap::RemoveChunk( cChunk* a_Chunk ) +cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ) { - cChunkLayer* Layer = GetLayerForChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ); - if( Layer ) + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - cChunkData* Data = Layer->GetChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() ); - if( Data->m_LiveChunk ) + if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ)) { - CompressChunk( Data ); - Data->m_LiveChunk = 0; // Set live chunk to 0 + return *itr; } - Layer->m_NumChunksLoaded--; } -} - - - - - -void cChunkMap::CompressChunk( cChunkData* a_ChunkData ) -{ - if( a_ChunkData->m_LiveChunk ) + + // Not found, create new: + cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this); + if (Layer == NULL) { - // Delete already present compressed data - if( a_ChunkData->m_Compressed ) delete [] a_ChunkData->m_Compressed; - - // Get Json data - Json::Value root; - std::string JsonData = ""; - a_ChunkData->m_LiveChunk->SaveToJson( root ); - if( !root.empty() ) - { - Json::StyledWriter writer; // TODO FIXME: change to FastWriter ? :D - JsonData = writer.write( root ); - } - - unsigned int TotalSize = cChunk::c_BlockDataSize + JsonData.size(); - uLongf CompressedSize = compressBound( TotalSize ); - a_ChunkData->m_Compressed = new char[CompressedSize]; - char* DataSource = a_ChunkData->m_LiveChunk->pGetBlockData(); - if( JsonData.size() > 0 ) - { - // Move stuff around, so data is aligned in memory - DataSource = new char[TotalSize]; - memcpy( DataSource, a_ChunkData->m_LiveChunk->pGetBlockData(), cChunk::c_BlockDataSize ); - memcpy( DataSource + cChunk::c_BlockDataSize, JsonData.c_str(), JsonData.size() ); - } - - int errorcode = compress2( (Bytef*)a_ChunkData->m_Compressed, &CompressedSize, (const Bytef*)DataSource, TotalSize, Z_DEFAULT_COMPRESSION); - if( errorcode != Z_OK ) - { - LOGERROR("Error compressing data (%i)", errorcode ); - } - - a_ChunkData->m_CompressedSize = CompressedSize; - a_ChunkData->m_UncompressedSize = TotalSize; - - if( DataSource != a_ChunkData->m_LiveChunk->pGetBlockData() ) - delete [] DataSource; + LOGERROR("cChunkMap: Cannot create new layer, server out of memory?"); + return NULL; } + m_Layers.push_back(Layer); + return Layer; } -cChunkMap::cChunkLayer* cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) +cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ ) { const int LayerX = (int)(floorf((float)a_ChunkX / (float)(LAYER_SIZE))); const int LayerZ = (int)(floorf((float)a_ChunkZ / (float)(LAYER_SIZE))); @@ -236,108 +93,43 @@ cChunkMap::cChunkLayer* cChunkMap::GetLayerForChunk( int a_ChunkX, int a_ChunkZ -cChunkMap::cChunkLayer* cChunkMap::GetLayer( int a_LayerX, int a_LayerZ ) +cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) { - // Find layer in memory - for( int i = 0; i < m_NumLayers; ++i ) + cCSLock Lock(m_CSLayers); + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); + if (Layer == NULL) { - if( m_Layers[i].m_X == a_LayerX && m_Layers[i].m_Z == a_LayerZ ) - { - return &m_Layers[i]; - } + // An error must have occurred, since layers are automatically created if they don't exist + return cChunkPtr(); } - - // Find layer on disk - cChunkLayer* Layer = LoadLayer( a_LayerX, a_LayerZ ); - if( !Layer ) return 0; - cChunkLayer* NewLayer = AddLayer( *Layer ); - delete Layer; - return NewLayer; + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ); + if (!(Chunk->IsValid())) + { + m_World->GetStorage().QueueLoadChunk(Chunk); + } + return Chunk; } -cChunk* cChunkMap::GetChunk( int a_X, int a_Y, int a_Z ) +cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) { - cChunkLayer* Layer = GetLayerForChunk( a_X, a_Z ); + cCSLock Lock(m_CSLayers); + cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ ); if (Layer == NULL) { - return NULL; + // An error must have occurred, since layers are automatically created if they don't exist + return cChunkPtr(); } - cChunkData* Data = Layer->GetChunk( a_X, a_Z ); - if (Data->m_LiveChunk != NULL) - { - // Already loaded and alive - return Data->m_LiveChunk; - } - - // Do we at least have the compressed chunk? - if (Data->m_Compressed == NULL) - { - return NULL; - } - - // The chunk has been cached (loaded from file, but not decompressed): - uLongf DestSize = Data->m_UncompressedSize; - char* BlockData = new char[ DestSize ]; - int errorcode = uncompress( (Bytef*)BlockData, &DestSize, (Bytef*)Data->m_Compressed, Data->m_CompressedSize ); - if( Data->m_UncompressedSize != DestSize ) - { - LOGWARN("Lulwtf, expected uncompressed size differs!"); - delete [] BlockData; - } - else if( errorcode != Z_OK ) - { - LOGERROR("ERROR: Decompressing chunk data! %i", errorcode ); - switch( errorcode ) - { - case Z_MEM_ERROR: - LOGERROR("Not enough memory"); - break; - case Z_BUF_ERROR: - LOGERROR("Not enough room in output buffer"); - break; - case Z_DATA_ERROR: - LOGERROR("Input data corrupted or incomplete"); - break; - default: - break; - }; - - delete [] BlockData; - } - else - { - cChunk* Chunk = new cChunk(a_X, a_Y, a_Z, m_World); - memcpy( Chunk->m_BlockData, BlockData, cChunk::c_BlockDataSize ); - Chunk->CalculateHeightmap(); - Data->m_LiveChunk = Chunk; - Layer->m_NumChunksLoaded++; - - if( DestSize > cChunk::c_BlockDataSize ) // We gots some extra data :D - { - LOGINFO("Parsing trailing JSON"); - Json::Value root; // will contains the root value after parsing. - Json::Reader reader; - if( !reader.parse( BlockData + cChunk::c_BlockDataSize, root, false ) ) - { - LOGERROR("Failed to parse trailing JSON!"); - } - else - { - Chunk->LoadFromJson( root ); - } - } - - delete [] BlockData; - delete [] Data->m_Compressed; Data->m_Compressed = 0; Data->m_CompressedSize = 0; - return Chunk; - } - return NULL; + cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ); + + // TODO: Load, but do not generate, if not valid + + return Chunk; } @@ -346,17 +138,11 @@ cChunk* cChunkMap::GetChunk( int a_X, int a_Y, int a_Z ) void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom ) { - for( int lay = 0; lay < m_NumLayers; ++lay ) + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) - { - cChunk* Chunk = m_Layers[lay].m_Chunks[i].m_LiveChunk; - if ( Chunk != NULL) - { - Chunk->Tick( a_Dt, a_TickRandom ); - } - } - } // for lay - m_Layers[] + (*itr)->Tick(a_Dt, a_TickRandom); + } // for itr - m_Layers } @@ -365,255 +151,125 @@ void cChunkMap::Tick( float a_Dt, MTRand & a_TickRandom ) void cChunkMap::UnloadUnusedChunks() { - cWorld* World = m_World; - for( int l = 0; l < m_NumLayers; ++l ) + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - cChunkLayer & Layer = m_Layers[l]; - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) - { - cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk; - if( Chunk && Chunk->GetClients().size() == 0 && Chunk->GetReferenceCount() <= 0 ) - { - //Chunk->SaveToDisk(); - World->RemoveSpread( ptr_cChunk( Chunk ) ); - RemoveChunk( Chunk ); - delete Chunk; - } - } - - // Unload layers - if( Layer.m_NumChunksLoaded == 0 ) - { - SaveLayer( &Layer ); - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) // Free all chunk data for layer - { - delete [] Layer.m_Chunks[i].m_Compressed; - delete Layer.m_Chunks[i].m_LiveChunk; - } - if( RemoveLayer( &Layer ) ) l--; - } - else if( Layer.m_NumChunksLoaded < 0 ) - { - LOGERROR("WTF! Chunks loaded in layer is %i !!", Layer.m_NumChunksLoaded ); - } - } + (*itr)->UnloadUnusedChunks(); + } // for itr - m_Layers } -bool cChunkMap::RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ ) +void cChunkMap::SaveAllChunks(void) { - for( int i = 0; i < m_NumLayers; ++i ) + cCSLock Lock(m_CSLayers); + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - cChunkLayer & Layer = m_Layers[i]; - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) - { - cChunk* Chunk = Layer.m_Chunks[i].m_LiveChunk; - if( Chunk != a_CalledFrom ) - { - if( Chunk && Chunk->RemoveEntity( a_Entity, a_CalledFrom ) ) - return true; - } - } - } - - LOG("WARNING: Entity was not found in any chunk!"); - return false; + (*itr)->Save(); + } // for itr - m_Layers[] } -void cChunkMap::SaveAllChunks() +//////////////////////////////////////////////////////////////////////////////// +// cChunkMap::cChunkLayer: + +cChunkMap::cChunkLayer::cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent) + : m_LayerX( a_LayerX ) + , m_LayerZ( a_LayerZ ) + , m_Parent( a_Parent ) + , m_NumChunksLoaded( 0 ) { - for( int i = 0; i < m_NumLayers; ++i ) - { - SaveLayer( &m_Layers[i] ); - } } -/******************************** - * Saving and loading - **/ - -void cChunkMap::SaveLayer( cChunkLayer* a_Layer ) +cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkZ ) { - std::string WorldName = m_World->GetName(); - cMakeDir::MakeDir( WorldName.c_str() ); - - AString SourceFile; - Printf(SourceFile, "%s/X%i_Z%i.pak", WorldName.c_str(), a_Layer->m_X, a_Layer->m_Z ); - - cFile f; - if (!f.Open(SourceFile, cFile::fmWrite)) + // Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + + const int LocalX = a_ChunkX - m_LayerX * LAYER_SIZE; + const int LocalZ = a_ChunkZ - m_LayerZ * LAYER_SIZE; + + + if (!((LocalX < LAYER_SIZE) && (LocalZ < LAYER_SIZE) && (LocalX > -1) && (LocalZ > -1))) { - LOGERROR("ERROR: Could not write to file %s", SourceFile.c_str()); - return; + assert(!"Asking a cChunkLayer for a chunk that doesn't belong to it!"); + return cChunkPtr(); } - - //--------------- - // Header - char PakVersion = 1; - char ChunkVersion = 1; - f.Write(&PakVersion, sizeof(PakVersion)); // pak version - f.Write(&ChunkVersion, sizeof(ChunkVersion)); // chunk version - - // Count number of chunks in layer - short NumChunks = 0; - for( int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i ) + + int Index = LocalX + LocalZ * LAYER_SIZE; + if (m_Chunks[Index].get() == NULL) { - if( a_Layer->m_Chunks[i].m_Compressed || a_Layer->m_Chunks[i].m_LiveChunk ) - { - NumChunks++; - } + m_Chunks[Index].reset(new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent->GetWorld())); } + return m_Chunks[Index]; +} - f.Write(&NumChunks, sizeof(NumChunks)); - LOG("Num Chunks in layer [%d, %d]: %i", a_Layer->m_X, a_Layer->m_Z, NumChunks); - // Chunk headers - for (int z = 0; z < LAYER_SIZE; ++z) - { - for (int x = 0; x < LAYER_SIZE; ++x) - { - cChunkData & Data = a_Layer->m_Chunks[x + z * LAYER_SIZE]; - CompressChunk(&Data); - if (Data.m_Compressed != NULL) - { - int ChunkX = a_Layer->m_X * LAYER_SIZE + x; - int ChunkZ = a_Layer->m_Z * LAYER_SIZE + z; - unsigned int Size = Data.m_CompressedSize; // Needs to be size of compressed data - unsigned int USize = Data.m_UncompressedSize; // Uncompressed size - f.Write(&ChunkX, sizeof(ChunkX)); - f.Write(&ChunkZ, sizeof(ChunkZ)); - f.Write(&Size, sizeof(Size)); - f.Write(&USize, sizeof(USize)); - } - } // for x - a_Layer->mChunks[x] - } // for z - a_Layer->m_Chunks[z] - - // Chunk data - for (int i = 0; i < LAYER_SIZE*LAYER_SIZE; ++i) + + + +void cChunkMap::cChunkLayer::Tick(float a_Dt, MTRand & a_TickRand) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) { - char * Compressed = a_Layer->m_Chunks[i].m_Compressed; - if (Compressed != NULL) + if ((m_Chunks[i] != NULL) && (m_Chunks[i]->IsValid())) { - f.Write(Compressed, a_Layer->m_Chunks[i].m_CompressedSize); - if (a_Layer->m_Chunks[i].m_LiveChunk != NULL) // If there's a live chunk we have no need for compressed data - { - delete [] a_Layer->m_Chunks[i].m_Compressed; - a_Layer->m_Chunks[i].m_Compressed = 0; - a_Layer->m_Chunks[i].m_CompressedSize = 0; - } + m_Chunks[i]->Tick(a_Dt, a_TickRand); } - } // for i - a_Layer->m_Chunks[] + } // for i - m_Chunks[] } -#define READ(File, Var) \ - if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \ - { \ - LOGERROR("ERROR READING %s FROM FILE %s (line %d)", #Var, SourceFile.c_str(), __LINE__); \ - return NULL; \ - } - -cChunkMap::cChunkLayer* cChunkMap::LoadLayer(int a_LayerX, int a_LayerZ ) +void cChunkMap::cChunkLayer::Save(void) { - std::string WorldName = m_World->GetName(); - - AString SourceFile; - Printf(SourceFile, "%s/X%i_Z%i.pak", WorldName.c_str(), a_LayerX, a_LayerZ); - - cFile f(SourceFile, cFile::fmRead); - if (!f.IsOpen()) + cWorld * World = m_Parent->GetWorld(); + for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i) { - return NULL; - } - - char PakVersion = 0; - char ChunkVersion = 0; + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid()) + { + World->GetStorage().QueueSaveChunk(m_Chunks[i]); + } + } // for i - m_Chunks[] +} + - READ(f, PakVersion); - if (PakVersion != 1) - { - LOGERROR("WRONG PAK VERSION in file \"%s\"!", SourceFile.c_str()); - return NULL; - } - - READ(f, ChunkVersion); - if (ChunkVersion != 1 ) - { - LOGERROR("WRONG CHUNK VERSION in file \"%s\"!", SourceFile.c_str()); - return NULL; - } - short NumChunks = 0; - READ(f, NumChunks); - - LOG("Num chunks in file \"%s\": %i", SourceFile.c_str(), NumChunks); - std::auto_ptr Layer(new cChunkLayer(LAYER_SIZE * LAYER_SIZE)); // The auto_ptr deletes the Layer if we exit with an error - Layer->m_X = a_LayerX; - Layer->m_Z = a_LayerZ; - - cChunkData * OrderedData[LAYER_SIZE * LAYER_SIZE]; // So we can loop over the chunks in the order they were loaded - - // Loop over all chunk headers - for( short i = 0; i < NumChunks; ++i ) - { - int ChunkX = 0; - int ChunkZ = 0; - READ(f, ChunkX); - READ(f, ChunkZ); - cChunkData* Data = Layer->GetChunk( ChunkX, ChunkZ ); - - if (Data == NULL) - { - LOGERROR("Chunk with wrong coordinates [%i, %i] in pak file \"%s\"!", ChunkX, ChunkZ, SourceFile.c_str()); - return NULL; - } - else - { - READ(f, Data->m_CompressedSize); - READ(f, Data->m_UncompressedSize); - } - OrderedData[i] = Data; - } - // Loop over chunks again, in the order they were loaded, and load their compressed data - for( short i = 0; i < NumChunks; ++i ) +void cChunkMap::cChunkLayer::UnloadUnusedChunks(void) +{ + for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++) { - cChunkData* Data = OrderedData[i]; - Data->m_Compressed = new char[ Data->m_CompressedSize ]; - if (f.Read(Data->m_Compressed, Data->m_CompressedSize) != Data->m_CompressedSize) + if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload())) { - LOGERROR("ERROR reading compressed data for chunk #%i from file \"%s\"", i, SourceFile.c_str()); - return NULL; + // TODO: Save the chunk if it was changed + m_Chunks[i].reset(); } - } - return Layer.release(); + } // for i - m_Chunks[] } -int cChunkMap::GetNumChunks() +int cChunkMap::GetNumChunks(void) { + cCSLock Lock(m_CSLayers); int NumChunks = 0; - for( int i = 0; i < m_NumLayers; ++i ) + for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr) { - NumChunks += m_Layers[i].m_NumChunksLoaded; + NumChunks += (*itr)->GetNumChunksLoaded(); } return NumChunks; } diff --git a/source/cChunkMap.h b/source/cChunkMap.h index dfccf8213..e43c095d0 100644 --- a/source/cChunkMap.h +++ b/source/cChunkMap.h @@ -1,91 +1,84 @@ +// cChunkMap.h + +// Interfaces to the cChunkMap class representing the chunk storage for a single world + #pragma once +#include "cChunk.h" + class cWorld; class cEntity; -class cChunk; class MTRand; - class cChunkMap { public: + + static const int LAYER_SIZE = 32; + cChunkMap(cWorld* a_World ); ~cChunkMap(); - void AddChunk( cChunk* a_Chunk ); - - cChunk* GetChunk( int a_X, int a_Y, int a_Z ); - void RemoveChunk( cChunk* a_Chunk ); + cChunkPtr GetChunk ( int a_X, int a_Y, int a_Z ); // Also queues the chunk for loading / generating if not valid + cChunkPtr GetChunkNoGen( int a_X, int a_Y, int a_Z ); // Also queues the chunk for loading if not valid; doesn't generate void Tick( float a_Dt, MTRand & a_TickRand ); void UnloadUnusedChunks(); - bool RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom = 0 ); void SaveAllChunks(); cWorld* GetWorld() { return m_World; } - int GetNumChunks(); + int GetNumChunks(void); private: - class cChunkData - { - public: - cChunkData() - : m_Compressed( 0 ) - , m_LiveChunk( 0 ) - , m_CompressedSize( 0 ) - , m_UncompressedSize( 0 ) - {} - char* m_Compressed; - unsigned int m_CompressedSize; - unsigned int m_UncompressedSize; - cChunk* m_LiveChunk; - }; - class cChunkLayer { public: - cChunkLayer() - : m_Chunks( 0 ) - , m_X( 0 ) - , m_Z( 0 ) - , m_NumChunksLoaded( 0 ) - {} - cChunkLayer( int a_NumChunks ) - : m_Chunks( new cChunkData[a_NumChunks] ) - , m_X( 0 ) - , m_Z( 0 ) - , m_NumChunksLoaded( 0 ) - {} - cChunkData * GetChunk( int a_X, int a_Z ); + cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent); + + /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check + cChunkPtr GetChunk( int a_ChunkX, int a_ChunkZ ); + + int GetX(void) const {return m_LayerX; } + int GetZ(void) const {return m_LayerZ; } + int GetNumChunksLoaded(void) const {return m_NumChunksLoaded; } - cChunkData * m_Chunks; - int m_X, m_Z; + void Save(void); + void UnloadUnusedChunks(void); + + void Tick( float a_Dt, MTRand & a_TickRand ); + + protected: + + cChunkPtr m_Chunks[LAYER_SIZE * LAYER_SIZE]; + int m_LayerX; + int m_LayerZ; + cChunkMap * m_Parent; int m_NumChunksLoaded; }; + + typedef std::list cChunkLayerList; + // TODO: Use smart pointers for cChunkLayerList as well, so that ticking and saving needn't lock the entire layerlist + // This however means that cChunkLayer needs to interlock its m_Chunks[] - void SaveLayer( cChunkLayer* a_Layer ); - cChunkLayer* LoadLayer( int a_LayerX, int a_LayerZ ); - cChunkLayer* GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); - cChunkLayer* GetLayer( int a_LayerX, int a_LayerZ ); - cChunkLayer* AddLayer( const cChunkLayer & a_Layer ); - bool RemoveLayer( cChunkLayer* a_Layer ); - void CompressChunk( cChunkData* a_ChunkData ); + cChunkLayer * GetLayerForChunk( int a_ChunkX, int a_ChunkZ ); // Creates the layer if it doesn't already exist + cChunkLayer * GetLayer( int a_LayerX, int a_LayerZ ); // Creates the layer if it doesn't already exist + void RemoveLayer( cChunkLayer* a_Layer ); - int m_NumLayers; - cChunkLayer* m_Layers; + cCriticalSection m_CSLayers; + cChunkLayerList m_Layers; - cWorld* m_World; + cWorld * m_World; }; diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp index 1019f26c6..92dd93d73 100644 --- a/source/cClientHandle.cpp +++ b/source/cClientHandle.cpp @@ -4,7 +4,6 @@ #include "cClientHandle.h" #include "cServer.h" #include "cWorld.h" -#include "cChunk.h" #include "cPickup.h" #include "cPluginManager.h" #include "cPlayer.h" @@ -24,7 +23,6 @@ #include "cBlockToPickup.h" #include "cMonster.h" #include "cChatColor.h" -#include "cThread.h" #include "cSocket.h" #include "cTimer.h" @@ -67,6 +65,10 @@ #include "packets/cPacket_UpdateSign.h" #include "packets/cPacket_Ping.h" #include "packets/cPacket_PlayerListItem.h" +#include "packets/cPacket_NamedEntitySpawn.h" + + + #define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\ @@ -79,13 +81,6 @@ -// fwd: cServer.cpp: -extern std::string GetWSAError(); - - - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cClientHandle: @@ -98,11 +93,11 @@ cClientHandle::cClientHandle(const cSocket & a_Socket) , m_Player(NULL) , m_bKicking(false) , m_TimeLastPacket(cWorld::GetTime()) - , m_bLoggedIn(false) , m_bKeepThreadGoing(true) - , m_bSendLoginResponse(false) , m_Ping(1000) - , m_bPositionConfirmed(false) + , m_State(csConnected) + , m_LastStreamedChunkX(0x7fffffff) // bogus chunk coords to force streaming upon login + , m_LastStreamedChunkZ(0x7fffffff) { cTimer t1; m_LastPingTime = t1.GetNowTime(); @@ -137,8 +132,6 @@ cClientHandle::cClientHandle(const cSocket & a_Socket) m_PacketMap[E_RESPAWN] = new cPacket_Respawn; m_PacketMap[E_PING] = new cPacket_Ping; - memset(m_LoadedChunks, 0x00, sizeof(m_LoadedChunks)); - ////////////////////////////////////////////////////////////////////////// m_pSendThread = new cThread(SendThread, this, "cClientHandle::SendThread"); m_pSendThread->Start (true); @@ -155,27 +148,27 @@ cClientHandle::~cClientHandle() { LOG("Deleting client \"%s\"", GetUsername().c_str()); - for(unsigned int i = 0; i < VIEWDISTANCE*VIEWDISTANCE; i++) - { - if (m_LoadedChunks[i]) m_LoadedChunks[i]->RemoveClient(this); - } + // Remove from cSocketThreads, just in case + cRoot::Get()->GetServer()->ClientDestroying(this); + + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); - cWorld::PlayerList PlayerList = cRoot::Get()->GetWorld()->GetAllPlayers(); - for(cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) + if (m_Player != NULL) { - if ((*itr) && (*itr)->GetClientHandle() && !GetUsername().empty()) + cWorld * World = m_Player->GetWorld(); + if (!m_Username.empty() && (World != NULL)) { - std::string NameColor = (m_Player ? m_Player->GetColor() : ""); + // Send the Offline PlayerList packet: + AString NameColor = (m_Player ? m_Player->GetColor() : ""); cPacket_PlayerListItem PlayerList(NameColor + GetUsername(), false, (short)9999); - (*itr)->GetClientHandle()->Send(PlayerList); + World->Broadcast(PlayerList, this); + + // Send the Chat packet: + cPacket_Chat Left(m_Username + " left the game!"); + World->Broadcast(Left, this); } } - - if (m_Username.size() > 0) - { - cPacket_Chat Left(m_Username + " left the game!"); - cRoot::Get()->GetServer()->Broadcast(Left, this); - } // First stop sending thread m_bKeepThreadGoing = false; @@ -191,16 +184,6 @@ cClientHandle::~cClientHandle() m_Semaphore.Signal(); delete m_pSendThread; - while (!m_PendingNrmSendPackets.empty()) - { - delete *m_PendingNrmSendPackets.begin(); - m_PendingNrmSendPackets.erase(m_PendingNrmSendPackets.begin()); - } - while (!m_PendingLowSendPackets.empty()) - { - delete *m_PendingLowSendPackets.begin(); - m_PendingLowSendPackets.erase(m_PendingLowSendPackets.begin()); - } if (m_Player != NULL) { m_Player->SetClientHandle(NULL); @@ -211,6 +194,18 @@ cClientHandle::~cClientHandle() { delete m_PacketMap[i]; } + + { + cCSLock Lock(m_SendCriticalSection); + for (PacketList::iterator itr = m_PendingNrmSendPackets.begin(); itr != m_PendingNrmSendPackets.end(); ++itr) + { + delete *itr; + } + for (PacketList::iterator itr = m_PendingLowSendPackets.begin(); itr != m_PendingLowSendPackets.end(); ++itr) + { + delete *itr; + } + } LOG("ClientHandle at %p destroyed", this); } @@ -222,6 +217,12 @@ cClientHandle::~cClientHandle() void cClientHandle::Destroy() { m_bDestroyed = true; + + if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) + { + RemoveFromAllChunks(); + } + if (m_Socket.IsValid()) { m_Socket.CloseSocket(); @@ -237,7 +238,10 @@ void cClientHandle::Destroy() void cClientHandle::Kick(const AString & a_Reason) { - LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), a_Reason.c_str()); + if (m_State >= csAuthenticating) // Don't log pings + { + LOG("Kicking user \"%s\" for \"%s\"", m_Username.c_str(), a_Reason.c_str()); + } Send(cPacket_Disconnect(a_Reason)); m_bKicking = true; } @@ -248,7 +252,56 @@ void cClientHandle::Kick(const AString & a_Reason) void cClientHandle::Authenticate(void) { - m_bSendLoginResponse = true; + // Spawn player (only serversided, so data is loaded) + m_Player = new cPlayer(this, GetUsername()); + + cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); + if (World == NULL) + { + World = cRoot::Get()->GetDefaultWorld(); + } + + m_Player->LoginSetGameMode (World->GetGameMode()); //set player's gamemode to server's gamemode at login. TODO: set to last player's gamemode at logout + + m_Player->SetIP (m_Socket.GetIPString()); + + cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::E_PLUGIN_PLAYER_SPAWN, 1, m_Player); + + // Return a server login packet + cPacket_Login LoginResponse; + LoginResponse.m_ProtocolVersion = m_Player->GetUniqueID(); + //LoginResponse.m_Username = ""; + LoginResponse.m_ServerMode = m_Player->GetGameMode(); // set gamemode from player. + LoginResponse.m_MapSeed = cRoot::Get()->GetWorld()->GetWorldSeed(); + LoginResponse.m_Dimension = 0; + LoginResponse.m_MaxPlayers = (unsigned char)cRoot::Get()->GetWorld()->GetMaxPlayers(); + LoginResponse.m_Difficulty = 2; + Send(LoginResponse); + + // Send Weather if raining: + if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) + { + cPacket_NewInvalidState RainPacket; + RainPacket.m_Reason = 1; //begin rain + Send(RainPacket); + } + + // Send time + Send(cPacket_TimeUpdate(World->GetWorldTime())); + + // Send inventory + m_Player->GetInventory().SendWholeInventory(this); + + // Send health + cPacket_UpdateHealth Health; + Health.m_Health = (short)m_Player->GetHealth(); + Health.m_Food = m_Player->GetFood(); + Health.m_Saturation = m_Player->GetFoodSaturation(); + Send(Health); + + m_Player->Initialize(World); + m_State = csDownloadingWorld; + StreamChunks(); } @@ -257,92 +310,110 @@ void cClientHandle::Authenticate(void) void cClientHandle::StreamChunks(void) { - if (!m_bLoggedIn) + if (m_State < csDownloadingWorld) { return; } assert(m_Player != NULL); - int ChunkPosX = (int)floor(m_Player->GetPosX() / 16); - int ChunkPosZ = (int)floor(m_Player->GetPosZ() / 16); - + int ChunkPosX = FAST_FLOOR_DIV(m_Player->GetPosX(), 16); + int ChunkPosZ = FAST_FLOOR_DIV(m_Player->GetPosZ(), 16); + if ((ChunkPosX == m_LastStreamedChunkX) && (ChunkPosZ == m_LastStreamedChunkZ)) + { + // Already streamed for this position + return; + } + m_LastStreamedChunkX = ChunkPosX; + m_LastStreamedChunkZ = ChunkPosZ; + + // DEBUG: + LOGINFO("Streaming chunks centered on [%d, %d]", ChunkPosX, ChunkPosZ); + cWorld * World = m_Player->GetWorld(); + assert(World != NULL); - cChunk * NeededChunks[VIEWDISTANCE * VIEWDISTANCE] = { 0 }; - const int MaxDist = VIEWDISTANCE + GENERATEDISTANCE * 2; - for (int x = 0; x < MaxDist; x++) + // Remove all loaded chunks that are no longer in range: { - for (int z = 0; z < MaxDist; z++) + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) { - int RelX = x - (MaxDist - 1) / 2; - int RelZ = z - (MaxDist - 1) / 2; - cChunk * Chunk = World->GetChunk(ChunkPosX + RelX, 0, ChunkPosZ + RelZ); // Touch all chunks in wide range, so they get generated - if ( - (x >= GENERATEDISTANCE) && - (x < VIEWDISTANCE + GENERATEDISTANCE) && - (z >= GENERATEDISTANCE) && - (z < VIEWDISTANCE + GENERATEDISTANCE) - ) // but player only needs chunks in view distance + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > VIEWDISTANCE) || (RelX < -VIEWDISTANCE) || (RelZ > VIEWDISTANCE) || (RelZ < -VIEWDISTANCE)) { - NeededChunks[(x - GENERATEDISTANCE) + (z - GENERATEDISTANCE) * VIEWDISTANCE] = Chunk; + World->GetChunk((*itr).m_ChunkX, 0, (*itr).m_ChunkZ)->RemoveClient(this); + itr = m_LoadedChunks.erase(itr); } - } - } - - cChunk * MissingChunks[VIEWDISTANCE * VIEWDISTANCE]; - memset(MissingChunks, 0, sizeof(MissingChunks)); - unsigned int MissIndex = 0; - for(int i = 0; i < VIEWDISTANCE * VIEWDISTANCE; i++) // Handshake loop - touch each chunk once - { - if (NeededChunks[i] == 0) continue; // Chunk is not yet loaded, so ignore - // This can cause MissIndex to be 0 and thus chunks will not be unloaded while they are actually out of range, - // which might actually be a good thing, otherwise it would keep trying to unload chunks until the new chunks are fully loaded - bool bChunkMissing = true; - for(int j = 0; j < VIEWDISTANCE*VIEWDISTANCE; j++) - { - if (m_LoadedChunks[j] == NeededChunks[i]) + else { - bChunkMissing = false; - break; + ++itr; } - } - if (bChunkMissing) + } // for itr - m_LoadedChunks[] + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) { - MissingChunks[MissIndex] = NeededChunks[i]; - MissIndex++; + int RelX = (*itr).m_ChunkX - ChunkPosX; + int RelZ = (*itr).m_ChunkZ - ChunkPosZ; + if ((RelX > VIEWDISTANCE) || (RelX < -VIEWDISTANCE) || (RelZ > VIEWDISTANCE) || (RelZ < -VIEWDISTANCE)) + { + itr = m_ChunksToSend.erase(itr); + } + else + { + ++itr; + } } } - - if (MissIndex > 0) + + // Add all chunks that are in range and not yet in m_LoadedChunks: + // Queue these smartly - from the center out to the edge + for (int d = 0; d <= VIEWDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest { - // Chunks are gonna be streamed in, so chunks probably also need to be streamed out <- optimization - for(int x = 0; x < VIEWDISTANCE; x++) + // For each distance add chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) { - for(int z = 0; z < VIEWDISTANCE; z++) - { - cChunk* Chunk = m_LoadedChunks[x + z*VIEWDISTANCE]; - if (Chunk != NULL) - { - if ((Chunk->GetPosX() < ChunkPosX - (VIEWDISTANCE - 1) / 2) - || (Chunk->GetPosX() > ChunkPosX + (VIEWDISTANCE - 1) / 2) - || (Chunk->GetPosZ() < ChunkPosZ - (VIEWDISTANCE - 1) / 2) - || (Chunk->GetPosZ() > ChunkPosZ + (VIEWDISTANCE - 1) / 2) - ) - { - Chunk->RemoveClient(this); - Chunk->AsyncUnload(this); // TODO - I think it's possible to unload the chunk immediately instead of next tick - // I forgot why I made it happen next tick + StreamChunk(ChunkPosX + d, ChunkPosZ + i); + StreamChunk(ChunkPosX - d, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + StreamChunk(ChunkPosX + i, ChunkPosZ + d); + StreamChunk(ChunkPosX + i, ChunkPosZ - d); + } // for i + } // for d + + // Touch chunks GENERATEDISTANCE ahead to let them generate: + for (int d = VIEWDISTANCE + 1; d <= VIEWDISTANCE + GENERATEDISTANCE; ++d) // cycle through (square) distance, from nearest to furthest + { + // For each distance touch chunks in a hollow square centered around current position: + for (int i = -d; i <= d; ++i) + { + World->GetChunk(ChunkPosX + d, 0, ChunkPosZ + i); + World->GetChunk(ChunkPosX - d, 0, ChunkPosZ + i); + } // for i + for (int i = -d + 1; i < d; ++i) + { + World->GetChunk(ChunkPosX + i, 0, ChunkPosZ + d); + World->GetChunk(ChunkPosX + i, 0, ChunkPosZ - d); + } // for i + } // for d +} - m_LoadedChunks[x + z * VIEWDISTANCE] = NULL; - } - } - } - } - StreamChunksSmart(MissingChunks, MissIndex); - memcpy(m_LoadedChunks, NeededChunks, sizeof(NeededChunks)); + +void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ) +{ + cWorld * World = m_Player->GetWorld(); + assert(World != NULL); + + cChunkPtr Chunk = World->GetChunk(a_ChunkX, 0, a_ChunkZ); + if (!Chunk->HasClient(this)) + { + Chunk->AddClient(this); + cCSLock Lock(m_CSChunkLists); + m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ)); } } @@ -350,61 +421,41 @@ void cClientHandle::StreamChunks(void) -// Sends chunks to the player from the player position outward -void cClientHandle::StreamChunksSmart(cChunk** a_Chunks, unsigned int a_NumChunks) +// Removes the client from all chunks. Used when switching worlds or destroying the player +void cClientHandle::RemoveFromAllChunks() { - int X = (int)floor(m_Player->GetPosX() / 16); - int Y = (int)floor(m_Player->GetPosY() / 128); - int Z = (int)floor(m_Player->GetPosZ() / 16); - - bool bAllDone = false; - while(!bAllDone) + cCSLock Lock(m_CSChunkLists); + cWorld * World = m_Player->GetWorld(); + if (World != NULL) { - bAllDone = true; - int ClosestIdx = -1; - unsigned int ClosestSqrDist = (unsigned int)-1; // wraps around, becomes biggest number possible - for(unsigned int i = 0; i < a_NumChunks; ++i) + for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end(); ++itr) { - if (a_Chunks[i]) - { - bAllDone = false; - int DistX = a_Chunks[i]->GetPosX()-X; - int DistY = a_Chunks[i]->GetPosY()-Y; - int DistZ = a_Chunks[i]->GetPosZ()-Z; - unsigned int SqrDist = (DistX*DistX)+(DistY*DistY)+(DistZ*DistZ); - if (SqrDist < ClosestSqrDist) - { - ClosestSqrDist = SqrDist; - ClosestIdx = i; - } - } - } - if (ClosestIdx > -1) - { - a_Chunks[ClosestIdx]->Send(this); - a_Chunks[ClosestIdx]->AddClient(this); - //LOGINFO("CCC: Sending chunk %i %i", a_Chunks[ClosestIdx]->GetPosX(), a_Chunks[ClosestIdx]->GetPosZ()); - a_Chunks[ClosestIdx] = 0; + World->GetChunk(itr->m_ChunkX, 0, itr->m_ChunkZ)->RemoveClient(this); } } + m_LoadedChunks.clear(); + m_ChunksToSend.clear(); } -// This removes the client from all chunks. Used when switching worlds -void cClientHandle::RemoveFromAllChunks() +void cClientHandle::ChunkJustSent(cChunk * a_ChunkCompleted) { - for(int i = 0; i < VIEWDISTANCE*VIEWDISTANCE; i++) + cCSLock Lock(m_CSChunkLists); + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr) { - if (m_LoadedChunks[i]) + if (((*itr).m_ChunkX == a_ChunkCompleted->GetPosX()) && ((*itr).m_ChunkZ == a_ChunkCompleted->GetPosZ())) { - m_LoadedChunks[i]->RemoveClient(this); - m_LoadedChunks[i]->AsyncUnload(this); - m_LoadedChunks[i] = 0; + m_ChunksToSend.erase(itr); + if ((m_State == csDownloadingWorld) && (m_ChunksToSend.empty())) + { + CheckIfWorldDownloaded(); + } + return; } - } + } // for itr - m_ChunksToSend[] } @@ -418,63 +469,115 @@ void cClientHandle::HandlePacket(cPacket * a_Packet) // cPacket* CopiedPacket = a_Packet->Clone(); // a_Packet = CopiedPacket; - //LOG("Packet: 0x%02x", a_Packet->m_PacketID); + // LOG("Recv packet 0x%02x from client \"%s\" (\"%s\")", a_Packet->m_PacketID, m_Socket.GetIPString().c_str(), m_Username.c_str()); if (m_bKicking) { return; } - if (!m_bLoggedIn) + switch (m_State) { - switch (a_Packet->m_PacketID) + case csConnected: { - case E_NEW_INVALID_STATE: // New/Invalid State packet received. I'm guessing the client only sends it when there's a problem with the bed? + switch (a_Packet->m_PacketID) { - LOGINFO("Got New Invalid State packet"); - break; + case E_NEW_INVALID_STATE: // New/Invalid State packet received. I'm guessing the client only sends it when there's a problem with the bed? + { + LOGINFO("Got New Invalid State packet"); + break; + } + case E_PING: HandlePing (); break; + case E_HANDSHAKE: HandleHandshake(reinterpret_cast(a_Packet)); break; + case E_LOGIN: HandleLogin (reinterpret_cast (a_Packet)); break; + + // Ignored packets: + case E_PLAYERLOOK: + case E_PLAYERMOVELOOK: + case E_PLAYERPOS: + case E_KEEP_ALIVE: break; + default: HandleUnexpectedPacket(a_Packet); break; + } // switch (PacketType) + break; + } // case csConnected + + case csAuthenticating: + { + // Waiting for external authentication, no packets are handled + switch (a_Packet->m_PacketID) + { + // Ignored packets: + case E_KEEP_ALIVE: + case E_PLAYERLOOK: + case E_PLAYERMOVELOOK: + case E_PLAYERPOS: break; + + default: HandleUnexpectedPacket(a_Packet); break; } + break; + } - case E_PING: HandlePing(); break; - case E_HANDSHAKE: HandleHandshake (reinterpret_cast (a_Packet)); break; - case E_LOGIN: HandleLogin (reinterpret_cast (a_Packet)); break; - case E_PLAYERMOVELOOK: HandleMoveLookLogin(reinterpret_cast(a_Packet)); break; - case E_KEEP_ALIVE: break; - default: HandleDefaultLogin (a_Packet); break; - } // switch (Packet type) - } - else if (!m_bPositionConfirmed) // m_bLoggedIn is true - { - switch (a_Packet->m_PacketID) + case csDownloadingWorld: { - case E_PLAYERMOVELOOK: HandleMoveLookConfirm(reinterpret_cast(a_Packet)); break; - // No default handling, ignore everything else - } // switch (Packet type) - } // if (! position confirmed) + // Waiting for chunks to stream to client, no packets are handled + switch (a_Packet->m_PacketID) + { + // Ignored packets: + case E_KEEP_ALIVE: + case E_PLAYERLOOK: + case E_PLAYERMOVELOOK: + case E_PLAYERPOS: break; + + default: HandleUnexpectedPacket(a_Packet); break; + } + break; + } + + case csConfirmingPos: + { + switch (a_Packet->m_PacketID) + { + // Ignored packets: + case E_KEEP_ALIVE: + case E_PLAYERLOOK: + case E_PLAYERPOS: break; - if (m_bPositionConfirmed) - { - switch (a_Packet->m_PacketID) + case E_PLAYERMOVELOOK: HandleMoveLookConfirm(reinterpret_cast(a_Packet)); break; + + default: + { + HandleUnexpectedPacket(a_Packet); + break; + } + } // switch (PacketType) + break; + } // case csConfirmingPos + + case csPlaying: { - case E_CREATIVE_INVENTORY_ACTION: HandleCreativeInventory(reinterpret_cast(a_Packet)); break; - case E_PLAYERPOS: HandlePlayerPos (reinterpret_cast (a_Packet)); break; - case E_BLOCK_DIG: HandleBlockDig (reinterpret_cast (a_Packet)); break; - case E_BLOCK_PLACE: HandleBlockPlace (reinterpret_cast (a_Packet)); break; - case E_PICKUP_SPAWN: HandlePickupSpawn (reinterpret_cast (a_Packet)); break; - case E_CHAT: HandleChat (reinterpret_cast (a_Packet)); break; - case E_PLAYERLOOK: HandlePlayerLook (reinterpret_cast (a_Packet)); break; - case E_PLAYERMOVELOOK: HandlePlayerMoveLook (reinterpret_cast (a_Packet)); break; - case E_ANIMATION: HandleAnimation (reinterpret_cast (a_Packet)); break; - case E_ITEM_SWITCH: HandleItemSwitch (reinterpret_cast (a_Packet)); break; - case E_WINDOW_CLOSE: HandleWindowClose (reinterpret_cast (a_Packet)); break; - case E_WINDOW_CLICK: HandleWindowClick (reinterpret_cast (a_Packet)); break; - case E_UPDATE_SIGN: HandleUpdateSign (reinterpret_cast (a_Packet)); break; - case E_USE_ENTITY: HandleUseEntity (reinterpret_cast (a_Packet)); break; - case E_RESPAWN: HandleRespawn(); - case E_DISCONNECT: HandleDisconnect (reinterpret_cast (a_Packet)); break; - case E_KEEP_ALIVE: HandleKeepAlive (reinterpret_cast (a_Packet)); break; - } // switch (Packet type) - } // if (normal game) + switch (a_Packet->m_PacketID) + { + case E_CREATIVE_INVENTORY_ACTION: HandleCreativeInventory(reinterpret_cast(a_Packet)); break; + case E_PLAYERPOS: HandlePlayerPos (reinterpret_cast (a_Packet)); break; + case E_BLOCK_DIG: HandleBlockDig (reinterpret_cast (a_Packet)); break; + case E_BLOCK_PLACE: HandleBlockPlace (reinterpret_cast (a_Packet)); break; + case E_PICKUP_SPAWN: HandlePickupSpawn (reinterpret_cast (a_Packet)); break; + case E_CHAT: HandleChat (reinterpret_cast (a_Packet)); break; + case E_PLAYERLOOK: HandlePlayerLook (reinterpret_cast (a_Packet)); break; + case E_PLAYERMOVELOOK: HandlePlayerMoveLook (reinterpret_cast (a_Packet)); break; + case E_ANIMATION: HandleAnimation (reinterpret_cast (a_Packet)); break; + case E_ITEM_SWITCH: HandleItemSwitch (reinterpret_cast (a_Packet)); break; + case E_WINDOW_CLOSE: HandleWindowClose (reinterpret_cast (a_Packet)); break; + case E_WINDOW_CLICK: HandleWindowClick (reinterpret_cast (a_Packet)); break; + case E_UPDATE_SIGN: HandleUpdateSign (reinterpret_cast (a_Packet)); break; + case E_USE_ENTITY: HandleUseEntity (reinterpret_cast (a_Packet)); break; + case E_RESPAWN: HandleRespawn(); break; + case E_DISCONNECT: HandleDisconnect (reinterpret_cast (a_Packet)); break; + case E_KEEP_ALIVE: HandleKeepAlive (reinterpret_cast (a_Packet)); break; + } // switch (Packet type) + break; + } // case csPlaying + } // switch (m_State) } @@ -484,7 +587,6 @@ void cClientHandle::HandlePacket(cPacket * a_Packet) void cClientHandle::HandlePing(void) { // Somebody tries to retrieve information about the server - LOGINFO("Got ping from \"%s\"", m_Socket.GetIPString().c_str()); AString Reply; Printf(Reply, "%s%s%i%s%i", cRoot::Get()->GetWorld()->GetDescription().c_str(), @@ -554,6 +656,7 @@ void cClientHandle::HandleLogin(cPacket_Login * a_Packet) } // Schedule for authentication; until then, let them wait (but do not block) + m_State = csAuthenticating; cRoot::Get()->GetAuthenticator().Authenticate(m_Username, cRoot::Get()->GetServer()->GetServerID()); } @@ -561,48 +664,11 @@ void cClientHandle::HandleLogin(cPacket_Login * a_Packet) -void cClientHandle::HandleMoveLookLogin(cPacket_PlayerMoveLook * a_Packet) -{ - // After this is received we're safe to send anything - if (m_Player == NULL) - { - LOGWARNING("Received PlayerMoveLook packet for NULL player from client \"%s\", kicking.", m_Socket.GetIPString().c_str()); - Kick("Hacked client"); // Don't tell them why we don't want them - return; - } - if (!cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::E_PLUGIN_PLAYER_JOIN, 1, m_Player)) - { - // Broadcast that this player has joined the game! Yay~ - cPacket_Chat Joined(m_Username + " joined the game!"); - cRoot::Get()->GetServer()->Broadcast(Joined, this); - } - - // Now initialize player (adds to entity list etc.) - cWorld* PlayerWorld = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); - if (!PlayerWorld) - { - PlayerWorld = cRoot::Get()->GetDefaultWorld(); - } - m_Player->Initialize(PlayerWorld); - - // Then we can start doing more stuffs! :D - m_bLoggedIn = true; - LOG("Player \"%s\" completely logged in", m_Username.c_str()); - StreamChunks(); - - // Send position - m_ConfirmPosition = m_Player->GetPosition(); - Send(cPacket_PlayerMoveLook(m_Player)); -} - - - - - -void cClientHandle::HandleDefaultLogin(cPacket * a_Packet) +void cClientHandle::HandleUnexpectedPacket(cPacket * a_Packet) { LOGWARNING( - "Invalid packet in login state: 0x%02x from client \"%s\", username \"%s\"", + "Invalid packet in state %d: 0x%02x from client \"%s\", username \"%s\"", + m_State, a_Packet->m_PacketID, m_Socket.GetIPString().c_str(), m_Username.c_str() @@ -627,11 +693,13 @@ void cClientHandle::HandleMoveLookConfirm(cPacket_PlayerMoveLook * a_Packet) { LOGINFO("Exact position confirmed by client!"); } - m_bPositionConfirmed = true; + m_State = csPlaying; } else { - LOGWARNING("Player \"%s\" sent a weird position confirmation %.2 blocks away, waiting for another confirmation", m_Username.c_str(), Dist); + LOGWARNING("Player \"%s\" sent a weird position confirmation %.2f blocks away, retrying", m_Username.c_str(), Dist); + m_ConfirmPosition = m_Player->GetPosition(); + Send(cPacket_PlayerMoveLook(m_Player)); } } @@ -1448,14 +1516,12 @@ void cClientHandle::HandleWindowClick(cPacket_WindowClick * a_Packet) void cClientHandle::HandleUpdateSign(cPacket_UpdateSign * a_Packet) { cWorld * World = m_Player->GetWorld(); - cChunk * Chunk = World->GetChunkOfBlock(a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ); - cBlockEntity * BlockEntity = Chunk->GetBlockEntity(a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ); - if ((BlockEntity != NULL) && ((BlockEntity->GetBlockType() == E_BLOCK_SIGN_POST) || (BlockEntity->GetBlockType() == E_BLOCK_WALLSIGN))) + cChunkPtr Chunk = World->GetChunkOfBlock(a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ); + if ((Chunk == NULL) || !Chunk->IsValid()) { - cSignEntity * Sign = reinterpret_cast(BlockEntity); - Sign->SetLines(a_Packet->m_Line1, a_Packet->m_Line2, a_Packet->m_Line3, a_Packet->m_Line4); - Sign->SendTo(NULL); // Broadcast to all players in chunk + return; } + Chunk->UpdateSign(a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ, a_Packet->m_Line1, a_Packet->m_Line2, a_Packet->m_Line3, a_Packet->m_Line4); } @@ -1554,6 +1620,7 @@ void cClientHandle::Tick(float a_Dt) cPacket_Disconnect DC("Nooooo!! You timed out! D: Come back!"); m_Socket.Send(&DC); + // TODO: Cannot sleep in the tick thread! cSleep::MilliSleep(1000); // Give packet some time to be received Destroy(); @@ -1563,63 +1630,37 @@ void cClientHandle::Tick(float a_Dt) // Send ping packet if (m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()) { - // TODO: why a random ping ID, can't we just use normal ascending numbers? - MTRand r1; - m_PingID = r1.randInt(); + m_PingID++; cPacket_KeepAlive Ping(m_PingID); m_PingStartTime = t1.GetNowTime(); Send(Ping); m_LastPingTime = m_PingStartTime; } - - if (m_bSendLoginResponse) + + if (m_State >= csDownloadingWorld) { - m_bSendLoginResponse = false; - - // Spawn player (only serversided, so data is loaded) - m_Player = new cPlayer(this, GetUsername()); // !!DO NOT INITIALIZE!! <- is done after receiving MoveLook Packet - - cWorld* World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName()); - if (!World) World = cRoot::Get()->GetDefaultWorld(); - World->LockEntities(); - m_Player->LoginSetGameMode (World->GetGameMode()); //set player's gamemode to server's gamemode at login. TODO: set to last player's gamemode at logout - - m_Player->SetIP (m_Socket.GetIPString()); - - cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::E_PLUGIN_PLAYER_SPAWN, 1, m_Player); - - // Return a server login packet - cPacket_Login LoginResponse; - LoginResponse.m_ProtocolVersion = m_Player->GetUniqueID(); - //LoginResponse.m_Username = ""; - LoginResponse.m_ServerMode = m_Player->GetGameMode(); //set gamemode from player. - LoginResponse.m_MapSeed = cRoot::Get()->GetWorld()->GetWorldSeed(); - LoginResponse.m_Dimension = 0; - LoginResponse.m_MaxPlayers = (unsigned char)cRoot::Get()->GetWorld()->GetMaxPlayers(); - LoginResponse.m_Difficulty = 2; - Send(LoginResponse); - - // Send Weather if raining: - if ((World->GetWeather() == 1) || (World->GetWeather() == 2)) { - cPacket_NewInvalidState RainPacket; - RainPacket.m_Reason = 1; //begin rain - Send(RainPacket); - } - - // Send time - Send(cPacket_TimeUpdate(World->GetWorldTime())); - - // Send inventory - m_Player->GetInventory().SendWholeInventory(this); - - // Send health - cPacket_UpdateHealth Health; - Health.m_Health = (short)m_Player->GetHealth(); - Health.m_Food = m_Player->GetFood(); - Health.m_Saturation = m_Player->GetFoodSaturation(); - Send(Health); - - World->UnlockEntities(); + cWorld * World = m_Player->GetWorld(); + cCSLock Lock(m_CSChunkLists); + int NumSent = 0; + for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();) + { + cChunkPtr Chunk = World->GetChunk(itr->m_ChunkX, 0, itr->m_ChunkZ); + if (!Chunk->IsValid()) + { + ++itr; + continue; + } + // The chunk has become valid, send it and remove it from the list: + Chunk->Send(this); + itr = m_ChunksToSend.erase(itr); + NumSent++; + if (NumSent > 10) + { + // Only send up to 10 chunks per tick, otherwise we'd choke the tick thread + break; + } + CheckIfWorldDownloaded(); + } // for itr - m_ChunksToSend[] } } @@ -1627,15 +1668,46 @@ void cClientHandle::Tick(float a_Dt) -void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* = E_PRIORITY_NORMAL */) +void cClientHandle::Send(const cPacket * a_Packet, ENUM_PRIORITY a_Priority /* = E_PRIORITY_NORMAL */) { if (m_bKicking) return; // Don't add more packets if player is getting kicked anyway + // If it is the packet spawning myself for myself, drop it silently: + if (a_Packet->m_PacketID == E_NAMED_ENTITY_SPAWN) + { + if (((cPacket_NamedEntitySpawn *)a_Packet)->m_UniqueID == m_Player->GetUniqueID()) + { + return; + } + } + + // Filter out packets that don't belong to a csDownloadingWorld state: + if (m_State == csDownloadingWorld) + { + switch (a_Packet->m_PacketID) + { + case E_PLAYERMOVELOOK: + case E_KEEP_ALIVE: + case E_PRE_CHUNK: + { + // Allow + break; + } + case E_MAP_CHUNK: + { + CheckIfWorldDownloaded(); + break; + } + + default: return; + } + } + bool bSignalSemaphore = true; cCSLock Lock(m_SendCriticalSection); if (a_Priority == E_PRIORITY_NORMAL) { - if (a_Packet.m_PacketID == E_REL_ENT_MOVE_LOOK) + if (a_Packet->m_PacketID == E_REL_ENT_MOVE_LOOK) { PacketList & Packets = m_PendingNrmSendPackets; for (PacketList::iterator itr = Packets.begin(); itr != Packets.end(); ++itr) @@ -1645,11 +1717,10 @@ void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* = { case E_REL_ENT_MOVE_LOOK: { - const cPacket_RelativeEntityMoveLook* ThisPacketData = reinterpret_cast< const cPacket_RelativeEntityMoveLook* >(&a_Packet); + const cPacket_RelativeEntityMoveLook* ThisPacketData = reinterpret_cast< const cPacket_RelativeEntityMoveLook* >(a_Packet); cPacket_RelativeEntityMoveLook* PacketData = reinterpret_cast< cPacket_RelativeEntityMoveLook* >(*itr); if (ThisPacketData->m_UniqueID == PacketData->m_UniqueID) { - //LOGINFO("Optimized by removing double packet"); Packets.erase(itr); bBreak = true; bSignalSemaphore = false; // Because 1 packet is removed, semaphore count is the same @@ -1665,11 +1736,11 @@ void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* = } } // for itr - Packets[] } // if (E_REL_ENT_MOVE_LOOK - m_PendingNrmSendPackets.push_back(a_Packet.Clone()); + m_PendingNrmSendPackets.push_back(a_Packet->Clone()); } else if (a_Priority == E_PRIORITY_LOW) { - m_PendingLowSendPackets.push_back(a_Packet.Clone()); + m_PendingLowSendPackets.push_back(a_Packet->Clone()); } Lock.Unlock(); if (bSignalSemaphore) @@ -1682,6 +1753,44 @@ void cClientHandle::Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority /* = +void cClientHandle::CheckIfWorldDownloaded(void) +{ + if (m_State != csDownloadingWorld) + { + return; + } + cCSLock Lock(m_CSChunkLists); + if (m_ChunksToSend.empty()) + { + SendConfirmPosition(); + } +} + + + + + +void cClientHandle::SendConfirmPosition(void) +{ + LOG("Spawning player \"%s\"", m_Username.c_str()); + + m_State = csConfirmingPos; + + if (!cRoot::Get()->GetPluginManager()->CallHook(cPluginManager::E_PLUGIN_PLAYER_JOIN, 1, m_Player)) + { + // Broadcast that this player has joined the game! Yay~ + cPacket_Chat Joined(m_Username + " joined the game!"); + cRoot::Get()->GetServer()->Broadcast(Joined, this); + } + + m_ConfirmPosition = m_Player->GetPosition(); + Send(cPacket_PlayerMoveLook(m_Player)); +} + + + + + void cClientHandle::SendThread(void *lpParam) { cClientHandle* self = (cClientHandle*)lpParam; @@ -1737,6 +1846,9 @@ void cClientHandle::SendThread(void *lpParam) { break; } + + // LOG("Sending packet 0x%02x to \"%s\" (\"%s\")", Packet->m_PacketID, self->m_Socket.GetIPString().c_str(), self->m_Username.c_str()); + bool bSuccess = self->m_Socket.Send(Packet); if (!bSuccess) diff --git a/source/cClientHandle.h b/source/cClientHandle.h index 48a51c2df..f45cf5b7d 100644 --- a/source/cClientHandle.h +++ b/source/cClientHandle.h @@ -14,6 +14,7 @@ #include "packets/cPacket.h" #include "Vector3d.h" #include "cSocketThreads.h" +#include "cChunk.h" #include "packets/cPacket_KeepAlive.h" #include "packets/cPacket_PlayerPosition.h" @@ -46,8 +47,6 @@ -// class Game; -class cChunk; class cPlayer; class cRedstone; @@ -70,9 +69,6 @@ public: cClientHandle(const cSocket & a_Socket); ~cClientHandle(); - static const int VIEWDISTANCE = 17; // MUST be odd number or CRASH! - static const int GENERATEDISTANCE = 2; // Server generates this many chunks AHEAD of player sight. - const cSocket & GetSocket(void) const {return m_Socket; } cSocket & GetSocket(void) {return m_Socket; } @@ -81,21 +77,22 @@ public: void Kick(const AString & a_Reason); //tolua_export void Authenticate(void); // Called by cAuthenticator when the user passes authentication - void StreamChunks(); - void StreamChunksSmart( cChunk** a_Chunks, unsigned int a_NumChunks ); - void RemoveFromAllChunks(); + void StreamChunks(void); + + // Removes the client from all chunks. Used when switching worlds or destroying the player + void RemoveFromAllChunks(void); + + void ChunkJustSent(cChunk * a_ChunkSent); // Called by cChunk when it is loaded / generated and sent to all clients registered in it - inline void SetLoggedIn( bool a_bLoggedIn ) { m_bLoggedIn = a_bLoggedIn; } - inline bool IsLoggedIn() { return m_bLoggedIn; } + inline bool IsLoggedIn(void) const { return m_State >= csAuthenticating; } void Tick(float a_Dt); bool IsDestroyed() { return m_bDestroyed; } void Destroy(); - cChunk* m_LoadedChunks[VIEWDISTANCE*VIEWDISTANCE]; - - void Send( const cPacket & a_Packet, ENUM_PRIORITY a_Priority = E_PRIORITY_NORMAL ); + void Send(const cPacket & a_Packet, ENUM_PRIORITY a_Priority = E_PRIORITY_NORMAL) { Send(&a_Packet, a_Priority); } + void Send(const cPacket * a_Packet, ENUM_PRIORITY a_Priority = E_PRIORITY_NORMAL); static void SendThread( void *lpParam ); @@ -105,6 +102,9 @@ public: private: + static const int VIEWDISTANCE = 4; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) + static const int GENERATEDISTANCE = 1; // Server generates this many chunks AHEAD of player sight. + int m_ProtocolVersion; AString m_Username; AString m_Password; @@ -114,13 +114,16 @@ private: PacketList m_PendingNrmSendPackets; PacketList m_PendingLowSendPackets; + cCriticalSection m_CSChunkLists; + cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to + cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) + cThread * m_pSendThread; cSocket m_Socket; cCriticalSection m_CriticalSection; cCriticalSection m_SendCriticalSection; - // cCriticalSection m_SocketCriticalSection; cSemaphore m_Semaphore; Vector3d m_ConfirmPosition; @@ -130,34 +133,46 @@ private: bool m_bDestroyed; cPlayer * m_Player; bool m_bKicking; + + // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk + int m_LastStreamedChunkX; + int m_LastStreamedChunkZ; float m_TimeLastPacket; - + short m_Ping; int m_PingID; long long m_PingStartTime; long long m_LastPingTime; static const unsigned short PING_TIME_MS = 1000; //minecraft sends 1 per 20 ticks (1 second or every 1000 ms) - bool m_bLoggedIn; - bool m_bPositionConfirmed; - bool m_bSendLoginResponse; + enum eState + { + csConnected, // The client has just connected, waiting for their handshake / login + csAuthenticating, // The client has logged in, waiting for external authentication + csDownloadingWorld, // The client is waiting for chunks, we're waiting for the loader to provide and send them + csConfirmingPos, // The client has been sent the position packet, waiting for them to repeat the position back + csPlaying, // Normal gameplay + + // TODO: Add Kicking and Destroyed here as well + } ; + + eState m_State; bool m_bKeepThreadGoing; void HandlePacket(cPacket * a_Packet); - // Packets handled while !m_bLoggedIn: - void HandlePing (void); - void HandleHandshake (cPacket_Handshake * a_Packet); - void HandleLogin (cPacket_Login * a_Packet); - void HandleMoveLookLogin(cPacket_PlayerMoveLook * a_Packet); // While !m_bLoggedIn - void HandleDefaultLogin (cPacket * a_Packet); // the default case + // Packets handled in csConnected: + void HandlePing (void); + void HandleHandshake (cPacket_Handshake * a_Packet); + void HandleLogin (cPacket_Login * a_Packet); + void HandleUnexpectedPacket(cPacket * a_Packet); // the default case -> kick - // Packets handled while !m_bPositionConfirmed: + // Packets handled while in csConfirmingPos: void HandleMoveLookConfirm(cPacket_PlayerMoveLook * a_Packet); // While !m_bPositionConfirmed - // Packets handled while m_bPositionConfirmed (normal gameplay): + // Packets handled while in csPlaying: void HandleCreativeInventory(cPacket_CreativeInventoryAction * a_Packet); void HandlePlayerPos (cPacket_PlayerPosition * a_Packet); void HandleBlockDig (cPacket_BlockDig * a_Packet); @@ -179,7 +194,14 @@ private: /// Returns true if the rate block interactions is within a reasonable limit (bot protection) bool CheckBlockInteractionsRate(void); - void SendLoginResponse(); + /// Checks whether all loaded chunks have been sent to the client; if so, sends the position to confirm + void CheckIfWorldDownloaded(void); + + /// Sends the PlayerMoveLook packet that the client needs to reply to for the game to start + void SendConfirmPosition(void); + + /// Adds a single chunk to be streamed to the client; used by StreamChunks() + void StreamChunk(int a_ChunkX, int a_ChunkZ); // cSocketThreads::cCallback overrides: virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client diff --git a/source/cCriticalSection.cpp b/source/cCriticalSection.cpp index d7498aa5f..db182c299 100644 --- a/source/cCriticalSection.cpp +++ b/source/cCriticalSection.cpp @@ -11,8 +11,7 @@ cCriticalSection::cCriticalSection() { #ifdef _WIN32 - m_CriticalSectionPtr = new CRITICAL_SECTION; - InitializeCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr ); + InitializeCriticalSection( &m_CriticalSection ); #else m_Attributes = new pthread_mutexattr_t; pthread_mutexattr_init((pthread_mutexattr_t*)m_Attributes); @@ -33,8 +32,7 @@ cCriticalSection::cCriticalSection() cCriticalSection::~cCriticalSection() { #ifdef _WIN32 - DeleteCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr ); - delete (CRITICAL_SECTION*)m_CriticalSectionPtr; + DeleteCriticalSection( &m_CriticalSection ); #else if( pthread_mutex_destroy( (pthread_mutex_t*)m_CriticalSectionPtr ) != 0 ) { @@ -53,7 +51,7 @@ cCriticalSection::~cCriticalSection() void cCriticalSection::Lock() { #ifdef _WIN32 - EnterCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr ); + EnterCriticalSection( &m_CriticalSection ); #else pthread_mutex_lock( (pthread_mutex_t*)m_CriticalSectionPtr ); #endif @@ -66,7 +64,7 @@ void cCriticalSection::Lock() void cCriticalSection::Unlock() { #ifdef _WIN32 - LeaveCriticalSection( (CRITICAL_SECTION*)m_CriticalSectionPtr ); + LeaveCriticalSection( &m_CriticalSection ); #else pthread_mutex_unlock( (pthread_mutex_t*)m_CriticalSectionPtr ); #endif @@ -81,9 +79,7 @@ void cCriticalSection::Unlock() cCSLock::cCSLock(cCriticalSection * a_CS) : m_CS(a_CS) - #ifdef _DEBUG , m_IsLocked(false) - #endif { Lock(); } @@ -94,9 +90,7 @@ cCSLock::cCSLock(cCriticalSection * a_CS) cCSLock::cCSLock(cCriticalSection & a_CS) : m_CS(&a_CS) - #ifdef _DEBUG , m_IsLocked(false) - #endif { Lock(); } @@ -107,12 +101,10 @@ cCSLock::cCSLock(cCriticalSection & a_CS) cCSLock::~cCSLock() { - #ifdef _DEBUG if (!m_IsLocked) { return; } - #endif // _DEBUG Unlock(); } @@ -122,11 +114,8 @@ cCSLock::~cCSLock() void cCSLock::Lock(void) { - #ifdef _DEBUG assert(!m_IsLocked); m_IsLocked = true; - #endif // _DEBUG - m_CS->Lock(); } @@ -136,11 +125,8 @@ void cCSLock::Lock(void) void cCSLock::Unlock(void) { - #ifdef _DEBUG assert(m_IsLocked); m_IsLocked = false; - #endif // _DEBUG - m_CS->Unlock(); } diff --git a/source/cCriticalSection.h b/source/cCriticalSection.h index 86c196255..8faa04765 100644 --- a/source/cCriticalSection.h +++ b/source/cCriticalSection.h @@ -1,18 +1,27 @@ + #pragma once + + + + class cCriticalSection { public: - cCriticalSection(); + cCriticalSection(void); ~cCriticalSection(); - void Lock(); - void Unlock(); + void Lock(void); + void Unlock(void); + private: - void* m_CriticalSectionPtr; // Pointer to a CRITICAL_SECTION object -#ifndef _WIN32 - void* m_Attributes; -#endif + + #ifdef _WIN32 + CRITICAL_SECTION m_CriticalSection; + #else // _WIN32 + void* m_CriticalSectionPtr; // Pointer to a CRITICAL_SECTION object + void* m_Attributes; + #endif // else _WIN32 }; @@ -23,10 +32,10 @@ class cCSLock { cCriticalSection * m_CS; - #ifdef _DEBUG // Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe + // In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held, + // therefore we need to check this value whether we are locked or not. bool m_IsLocked; - #endif // _DEBUG public: cCSLock(cCriticalSection * a_CS); diff --git a/source/cEntity.cpp b/source/cEntity.cpp index cdb6ea211..8b24d247e 100644 --- a/source/cEntity.cpp +++ b/source/cEntity.cpp @@ -14,15 +14,24 @@ #include "packets/cPacket_DestroyEntity.h" + + + + int cEntity::m_EntityCount = 0; +cCriticalSection cEntity::m_CSCount; + + + + cEntity::cEntity(const double & a_X, const double & a_Y, const double & a_Z) - : m_UniqueID( 0 ) + : m_UniqueID( 0 ) , m_Referencers( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCERS ) ) , m_References( new cReferenceManager( cReferenceManager::RFMNGR_REFERENCES ) ) - , m_ChunkX( 0 ) - , m_ChunkY( 0 ) - , m_ChunkZ( 0 ) + , m_ChunkX( 0 ) + , m_ChunkY( 0 ) + , m_ChunkZ( 0 ) , m_Pos( new Vector3d( a_X, a_Y, a_Z ) ) , m_bDirtyPosition( true ) , m_Rot( new Vector3f() ) @@ -32,12 +41,24 @@ cEntity::cEntity(const double & a_X, const double & a_Y, const double & a_Z) , m_World( 0 ) , m_bRemovedFromChunk( false ) { + cCSLock Lock(m_CSCount); m_EntityCount++; m_UniqueID = m_EntityCount; } + + + + cEntity::~cEntity() { + LOG("Deleting entity %d at pos {%.2f, %.2f} ~ [%d, %d]; ptr %p", + m_UniqueID, + m_Pos->x, m_Pos->z, + (int)(m_Pos->x / 16), (int)(m_Pos->z / 16), + this + ); + if( !m_bDestroyed || !m_bRemovedFromChunk ) { LOGERROR("ERROR: Entity deallocated without being destroyed %i or unlinked %i", m_bDestroyed, m_bRemovedFromChunk ); @@ -48,6 +69,10 @@ cEntity::~cEntity() delete m_Rot; } + + + + void cEntity::Initialize( cWorld* a_World ) { m_World = a_World; @@ -56,111 +81,124 @@ void cEntity::Initialize( cWorld* a_World ) MoveToCorrectChunk(true); } + + + + void cEntity::WrapRotation() { - while(m_Rot->x > 180.f) m_Rot->x-=360.f; // Wrap it - while(m_Rot->x < -180.f) m_Rot->x+=360.f; - while(m_Rot->y > 180.f) m_Rot->y-=360.f; - while(m_Rot->y < -180.f) m_Rot->y+=360.f; + while (m_Rot->x > 180.f) m_Rot->x-=360.f; // Wrap it + while (m_Rot->x < -180.f) m_Rot->x+=360.f; + while (m_Rot->y > 180.f) m_Rot->y-=360.f; + while (m_Rot->y < -180.f) m_Rot->y+=360.f; } + + + + void cEntity::MoveToCorrectChunk(bool a_bIgnoreOldChunk) { - if( !m_World ) return; // Entity needs a world to move to a chunk + assert(m_World != NULL); // Entity needs a world to move to a chunk + if( !m_World ) return; int ChunkX = 0, ChunkY = 0, ChunkZ = 0; cWorld::BlockToChunk( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z, ChunkX, ChunkY, ChunkZ ); - if(a_bIgnoreOldChunk || m_ChunkX != ChunkX || m_ChunkY != ChunkY || m_ChunkZ != ChunkZ) + if (!a_bIgnoreOldChunk && (m_ChunkX == ChunkX) && (m_ChunkY == ChunkY) && (m_ChunkZ == ChunkZ)) { - LOG("From %i %i To %i %i", m_ChunkX, m_ChunkZ, ChunkX, ChunkZ ); - cChunk* Chunk = 0; - if(!a_bIgnoreOldChunk) - Chunk = m_World->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - - typedef std::list< cClientHandle* > ClientList; - ClientList BeforeClients; - if( Chunk ) - { - Chunk->RemoveEntity( *this ); - BeforeClients = Chunk->GetClients(); - } - m_ChunkX = ChunkX; m_ChunkY = ChunkY; m_ChunkZ = ChunkZ; - cChunk* NewChunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - ClientList AfterClients; - if( NewChunk ) - { - NewChunk->AddEntity( *this ); - AfterClients = NewChunk->GetClients(); - } - - - /******************** - * I reaalllyyyy don't like this piece of code, but it's needed I guess (maybe there's a way to optimize this) - **/ - // Now compare clients before with after - for( ClientList::iterator itr = BeforeClients.begin(); itr != BeforeClients.end(); ++itr ) - { - bool bFound = false; - for( ClientList::iterator itr2 = AfterClients.begin(); itr2 != AfterClients.end(); ++itr2 ) - { - if( *itr2 == *itr ) - { - bFound = true; - break; - } - } - if( !bFound ) // Client was in old chunk, but not new, so destroy on that client - { - cPacket_DestroyEntity DestroyEntity( this ); - (*itr)->Send( DestroyEntity ); - } - } + return; + } + + if (!a_bIgnoreOldChunk) + { + cChunkPtr OldChunk = m_World->GetChunk(m_ChunkX, m_ChunkY, m_ChunkZ); + OldChunk->RemoveEntity(this); + cPacket_DestroyEntity DestroyEntity( this ); + OldChunk->Broadcast(DestroyEntity); + } - // Now compare clients after with before - for( ClientList::iterator itr = AfterClients.begin(); itr != AfterClients.end(); ++itr ) + m_ChunkX = ChunkX; + m_ChunkY = ChunkY; + m_ChunkZ = ChunkZ; + cChunkPtr NewChunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( NewChunk != NULL ) + { + NewChunk->AddEntity( this ); + std::auto_ptr SpawnPacket(GetSpawnPacket()); + if (SpawnPacket.get() != NULL) { - bool bFound = false; - for( ClientList::iterator itr2 = BeforeClients.begin(); itr2 != BeforeClients.end(); ++itr2 ) - { - if( *itr2 == *itr ) - { - bFound = true; - break; - } - } - if( !bFound ) // Client is in the new chunk, but not in old, so spawn on the client - { - SpawnOn( *itr ); - } + NewChunk->Broadcast(SpawnPacket.get()); } } } + + + + void cEntity::Destroy() { if( !m_bDestroyed ) { m_bDestroyed = true; if( !m_bRemovedFromChunk ) - RemoveFromChunk(0); + { + RemoveFromChunk(); + } } } -void cEntity::RemoveFromChunk( cChunk* a_Chunk ) + + + + +void cEntity::RemoveFromChunk(void) { - if( m_World ) + if ( m_World == NULL ) { - cChunk* Chunk = ( a_Chunk ? a_Chunk : (cChunk*)m_World->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ) ); - if( Chunk ) + return; + } + + cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( Chunk != NULL ) + { + cPacket_DestroyEntity DestroyEntity( this ); + Chunk->Broadcast( DestroyEntity ); + Chunk->RemoveEntity( this ); + m_bRemovedFromChunk = true; + } +} + + + + + +void cEntity::SpawnOn(cClientHandle * a_Client) +{ + std::auto_ptr SpawnPacket(GetSpawnPacket()); + if (SpawnPacket.get() == NULL) + { + return; + } + + if (a_Client == NULL) + { + cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( Chunk != NULL ) { - cPacket_DestroyEntity DestroyEntity( this ); - Chunk->Broadcast( DestroyEntity ); - Chunk->RemoveEntity( *this ); - m_bRemovedFromChunk = true; + Chunk->Broadcast(SpawnPacket.get()); } } + else + { + a_Client->Send(SpawnPacket.get()); + } } + + + + CLASS_DEF_GETCLASS( cEntity ); bool cEntity::IsA( const char* a_EntityType ) { @@ -169,6 +207,10 @@ bool cEntity::IsA( const char* a_EntityType ) return false; } + + + + ////////////////////////////////////////////////////////////////////////// // Set orientations void cEntity::SetRot( const Vector3f & a_Rot ) @@ -177,45 +219,39 @@ void cEntity::SetRot( const Vector3f & a_Rot ) m_bDirtyOrientation = true; } + + + + void cEntity::SetRotation( float a_Rotation ) { m_Rot->x = a_Rotation; m_bDirtyOrientation = true; } + + + + void cEntity::SetPitch( float a_Pitch ) { m_Rot->y = a_Pitch; m_bDirtyOrientation = true; } + + + + void cEntity::SetRoll( float a_Roll ) { m_Rot->z = a_Roll; m_bDirtyOrientation = true; } -////////////////////////////////////////////////////////////////////////// -// Get orientations -const Vector3f & cEntity::GetRot() -{ - return *m_Rot; -} -float cEntity::GetRotation() -{ - return m_Rot->x; -} -float cEntity::GetPitch() -{ - return m_Rot->y; -} -float cEntity::GetRoll() -{ - return m_Rot->z; -} ////////////////////////////////////////////////////////////////////////// // Get look vector (this is NOT a rotation!) @@ -228,6 +264,10 @@ Vector3f cEntity::GetLookVector() return Look; } + + + + ////////////////////////////////////////////////////////////////////////// // Set position void cEntity::SetPosition( const Vector3d & a_Pos ) @@ -237,6 +277,10 @@ void cEntity::SetPosition( const Vector3d & a_Pos ) m_bDirtyPosition = true; } + + + + void cEntity::SetPosition( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) { m_Pos->Set( a_PosX, a_PosY, a_PosZ ); @@ -244,6 +288,10 @@ void cEntity::SetPosition( const double & a_PosX, const double & a_PosY, const d m_bDirtyPosition = true; } + + + + void cEntity::SetPosX( const double & a_PosX ) { m_Pos->x = a_PosX; @@ -251,6 +299,10 @@ void cEntity::SetPosX( const double & a_PosX ) m_bDirtyPosition = true; } + + + + void cEntity::SetPosY( const double & a_PosY ) { m_Pos->y = a_PosY; @@ -258,6 +310,10 @@ void cEntity::SetPosY( const double & a_PosY ) m_bDirtyPosition = true; } + + + + void cEntity::SetPosZ( const double & a_PosZ ) { m_Pos->z = a_PosZ; @@ -265,27 +321,9 @@ void cEntity::SetPosZ( const double & a_PosZ ) m_bDirtyPosition = true; } -////////////////////////////////////////////////////////////////////////// -// Get position -const Vector3d & cEntity::GetPosition() -{ - return *m_Pos; -} -const double & cEntity::GetPosX() -{ - return m_Pos->x; -} -const double & cEntity::GetPosY() -{ - return m_Pos->y; -} -const double & cEntity::GetPosZ() -{ - return m_Pos->z; -} ////////////////////////////////////////////////////////////////////////// // Reference stuffs @@ -295,12 +333,24 @@ void cEntity::AddReference( cEntity*& a_EntityPtr ) a_EntityPtr->ReferencedBy( a_EntityPtr ); } + + + + void cEntity::ReferencedBy( cEntity*& a_EntityPtr ) { m_Referencers->AddReference( a_EntityPtr ); } + + + + void cEntity::Dereference( cEntity*& a_EntityPtr ) { m_Referencers->Dereference( a_EntityPtr ); } + + + + diff --git a/source/cEntity.h b/source/cEntity.h index 80fc33cf1..b406e759f 100644 --- a/source/cEntity.h +++ b/source/cEntity.h @@ -1,6 +1,15 @@ + #pragma once -#include "MemoryLeak.h" + + + +#include "Vector3d.h" +#include "Vector3f.h" + + + + #define CLASS_PROT_ISA() virtual bool IsA( const char* a_EntityType ); #define CLASS_PROT_GETCLASS() virtual const char* GetClass(); @@ -27,12 +36,19 @@ CLASS_DEF_ISA( classname, superclass ) \ CLASS_DEF_GETCLASS( classname ) -class cChunk; + + + + class cWorld; class cReferenceManager; -class Vector3d; -class Vector3f; class cClientHandle; +class cPacket; + + + + + class cEntity //tolua_export { //tolua_export public: //tolua_export @@ -52,16 +68,16 @@ public: //tolua_export virtual bool IsA( const char* a_EntityType ); //tolua_export virtual const char* GetClass(); //tolua_export - cWorld* GetWorld() { return m_World; } //tolua_export + cWorld * GetWorld(void) const { return m_World; } //tolua_export - const Vector3d & GetPosition(); //tolua_export - const double & GetPosX(); //tolua_export - const double & GetPosY(); //tolua_export - const double & GetPosZ(); //tolua_export - const Vector3f & GetRot(); //tolua_export - float GetRotation(); //tolua_export - float GetPitch(); //tolua_export - float GetRoll(); //tolua_export + const Vector3d & GetPosition(void) const {return *m_Pos; } //tolua_export + const double & GetPosX (void) const {return m_Pos->x; } //tolua_export + const double & GetPosY (void) const {return m_Pos->y; } //tolua_export + const double & GetPosZ (void) const {return m_Pos->z; } //tolua_export + const Vector3f & GetRot (void) const {return *m_Rot; } //tolua_export + float GetRotation(void) const {return m_Rot->x; } //tolua_export + float GetPitch (void) const {return m_Rot->y; } //tolua_export + float GetRoll (void) const {return m_Rot->z; } //tolua_export Vector3f GetLookVector(); //tolua_export void SetPosX( const double & a_PosX ); //tolua_export @@ -74,18 +90,21 @@ public: //tolua_export void SetPitch( float a_Pitch ); //tolua_export void SetRoll( float a_Roll ); //tolua_export - inline int GetUniqueID() { return m_UniqueID; } //tolua_export - inline bool IsDestroyed() { return m_bDestroyed; } //tolua_export + inline int GetUniqueID(void) const { return m_UniqueID; } //tolua_export + inline bool IsDestroyed(void) const { return m_bDestroyed; } //tolua_export void Destroy(); //tolua_export - void RemoveFromChunk( cChunk* a_Chunk ); // for internal use in cChunk + void RemoveFromChunk(void); // for internal use in cChunk virtual void Tick(float a_Dt) = 0; //tolua_export - virtual void SpawnOn( cClientHandle* a_Target ) = 0; //tolua_export + virtual cPacket * GetSpawnPacket(void) const {assert(!"GetSpawnedPacket unimplemented!"); return NULL; }; // _X: Needs to be implemented due to Lua bindings + void SpawnOn (cClientHandle * a_Client); // tolua_export + void WrapRotation(); protected: + void SetWorld( cWorld* a_World ) { m_World = a_World; } void MoveToCorrectChunk(bool a_bIgnoreOldChunk = false); @@ -94,7 +113,9 @@ protected: void ReferencedBy( cEntity*& a_EntityPtr ); void Dereference( cEntity*& a_EntityPtr ); + static cCriticalSection m_CSCount; static int m_EntityCount; + int m_UniqueID; cReferenceManager* m_Referencers; @@ -111,6 +132,12 @@ protected: bool m_bRemovedFromChunk; ENUM_ENTITY_TYPE m_EntityType; -private: + cWorld* m_World; }; //tolua_export + +typedef std::list cEntityList; + + + + diff --git a/source/cFile.cpp b/source/cFile.cpp index 2026f1fe2..019b125ee 100644 --- a/source/cFile.cpp +++ b/source/cFile.cpp @@ -154,7 +154,8 @@ int cFile::Write(const void * iBuffer, int iNumBytes) return -1; } - return fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count + int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count + return res; } @@ -230,3 +231,23 @@ int cFile::GetSize(void) const + +int cFile::ReadRestOfFile(AString & a_Contents) +{ + assert(IsOpen()); + + if (!IsOpen()) + { + return -1; + } + + int DataSize = GetSize() - Tell(); + + // HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly + a_Contents.assign(DataSize, '\0'); + return Read((void *)a_Contents.data(), DataSize); +} + + + + diff --git a/source/cFile.h b/source/cFile.h index 674ce93a2..390847eaf 100644 --- a/source/cFile.h +++ b/source/cFile.h @@ -51,7 +51,7 @@ public: { fmRead, // Read-only. If the file doesn't exist, object will not be valid fmWrite, // Write-only. If the file already exists, it will be overwritten - fmReadWrite, // Read/write. If the file already exists, it will be left intact + fmReadWrite, // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning } ; /// Simple constructor - creates an unopened file object, use Open() to open / create a real file @@ -83,6 +83,9 @@ public: /// Returns the size of file, in bytes, or -1 for failure; asserts if not open int GetSize(void) const; + /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error + int ReadRestOfFile(AString & a_Contents); + private: #ifdef USE_STDIO_FILE FILE * m_File; diff --git a/source/cFurnaceEntity.cpp b/source/cFurnaceEntity.cpp index e07a1685d..d5bd192c5 100644 --- a/source/cFurnaceEntity.cpp +++ b/source/cFurnaceEntity.cpp @@ -18,8 +18,12 @@ #include -cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cChunk* a_Chunk) - : cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_Chunk ) + + + + +cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World ) , m_Items( new cItem[3] ) , m_CookingItem( 0 ) , m_CookTime( 0 ) @@ -29,6 +33,10 @@ cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cChunk* a_Chunk) { } + + + + cFurnaceEntity::~cFurnaceEntity() { // Tell window its owner is destroyed @@ -44,6 +52,10 @@ cFurnaceEntity::~cFurnaceEntity() } } + + + + void cFurnaceEntity::Destroy() { // Drop items @@ -51,16 +63,17 @@ void cFurnaceEntity::Destroy() { if( !m_Items[i].IsEmpty() ) { - cPickup* Pickup = new cPickup( m_PosX*32 + 16, m_PosY*32 + 16, m_PosZ*32 + 16, m_Items[i], 0, 1.f, 0 ); - Pickup->Initialize( m_Chunk->GetWorld() ); + cPickup* Pickup = new cPickup( m_PosX * 32 + 16, m_PosY * 32 + 16, m_PosZ * 32 + 16, m_Items[i], 0, 1.f, 0 ); + Pickup->Initialize(m_World); m_Items[i].Empty(); } } - - // Remove from tick list - GetChunk()->RemoveTickBlockEntity( this ); } + + + + void cFurnaceEntity::UsedBy( cPlayer & a_Player ) { LOG("Used a furnace"); @@ -84,6 +97,10 @@ void cFurnaceEntity::UsedBy( cPlayer & a_Player ) } } + + + + bool cFurnaceEntity::Tick( float a_Dt ) { //LOG("Time left: %0.1f Time burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime ); @@ -173,6 +190,10 @@ bool cFurnaceEntity::Tick( float a_Dt ) return ((m_CookingItem != 0) || (m_TimeBurned < m_BurnTime)) && m_BurnTime > 0.f; // Keep on ticking, if there's more to cook, or if it's cooking } + + + + bool cFurnaceEntity::StartCooking() { cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe(); @@ -200,7 +221,6 @@ bool cFurnaceEntity::StartCooking() m_TimeCooked = 0.f; m_CookTime = R->CookTime; } - GetChunk()->AddTickBlockEntity( this ); return true; } } @@ -208,13 +228,14 @@ bool cFurnaceEntity::StartCooking() return false; } + + + + void cFurnaceEntity::ResetCookTimer() { - if( m_CookingItem ) - { - delete m_CookingItem; - m_CookingItem = 0; - } + delete m_CookingItem; + m_CookingItem = NULL; m_TimeCooked = 0.f; m_CookTime = 0.f; } @@ -290,18 +311,21 @@ bool cFurnaceEntity::LoadFromJson( const Json::Value& a_Value ) if( !Item.IsEmpty() ) { m_CookingItem = new cItem( Item ); - GetChunk()->AddTickBlockEntity( this ); } } - m_CookTime = (float)a_Value.get("CookTime", 0).asDouble(); + m_CookTime = (float)a_Value.get("CookTime", 0).asDouble(); m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble(); - m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble(); + m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble(); m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble(); return true; } + + + + void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) { a_Value["x"] = m_PosX; @@ -329,4 +353,8 @@ void cFurnaceEntity::SaveToJson( Json::Value& a_Value ) a_Value["TimeCooked"] = m_TimeCooked; a_Value["BurnTime"] = m_BurnTime; a_Value["TimeBurned"] = m_TimeBurned; -} \ No newline at end of file +} + + + + diff --git a/source/cFurnaceEntity.h b/source/cFurnaceEntity.h index 99209acf8..19b518cda 100644 --- a/source/cFurnaceEntity.h +++ b/source/cFurnaceEntity.h @@ -1,23 +1,33 @@ + #pragma once #include "cBlockEntity.h" #include "cWindowOwner.h" #include "FileDefine.h" + + + + namespace Json { class Value; } -class cChunk; class cClientHandle; class cServer; class cItem; class cNBTData; -class cFurnaceEntity : public cBlockEntity, public cWindowOwner + + + + +class cFurnaceEntity : + public cBlockEntity, + public cWindowOwner { public: - cFurnaceEntity(int a_X, int a_Y, int a_Z, cChunk* a_Chunk); + cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World); virtual ~cFurnaceEntity(); virtual void Destroy(); @@ -32,12 +42,18 @@ public: bool StartCooking(); void ResetCookTimer(); + private: - cItem* m_Items; - cItem* m_CookingItem; - float m_CookTime; - float m_TimeCooked; - - float m_BurnTime; - float m_TimeBurned; -}; \ No newline at end of file + + cItem * m_Items; + cItem * m_CookingItem; + float m_CookTime; + float m_TimeCooked; + + float m_BurnTime; + float m_TimeBurned; +}; + + + + diff --git a/source/cGroup.h b/source/cGroup.h index 303df85fa..a097ea94d 100644 --- a/source/cGroup.h +++ b/source/cGroup.h @@ -25,7 +25,7 @@ public: //tolua_export typedef std::map< std::string, bool > CommandMap; const CommandMap & GetCommands() const { return m_Commands; } - std::string GetColor() const { return m_Color; } //tolua_export + const AString & GetColor() const { return m_Color; } //tolua_export typedef std::list< cGroup* > GroupList; const GroupList & GetInherits() const { return m_Inherits; } diff --git a/source/cIsThread.cpp b/source/cIsThread.cpp index 4aa25b534..3f4442f7f 100644 --- a/source/cIsThread.cpp +++ b/source/cIsThread.cpp @@ -67,6 +67,7 @@ cIsThread::cIsThread(const AString & iThreadName) : cIsThread::~cIsThread() { + mShouldTerminate = true; Wait(); } @@ -81,7 +82,7 @@ bool cIsThread::Start(void) // Create the thread suspended, so that the mHandle variable is valid in the thread procedure DWORD ThreadID = 0; - HANDLE mHandle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); + mHandle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID); if (mHandle == NULL) { LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", mThreadName.c_str(), GetLastError()); @@ -122,10 +123,12 @@ bool cIsThread::Wait(void) { return true; } - LOG("Waiting for thread \"%s\" to terminate.", mThreadName.c_str()); + // Cannot log, logger may already be stopped: + // LOG("Waiting for thread \"%s\" to terminate.", mThreadName.c_str()); int res = WaitForSingleObject(mHandle, INFINITE); mHandle = NULL; - LOG("Thread \"%s\" %s terminated, GLE = %d", mThreadName.c_str(), (res == WAIT_OBJECT_0) ? "" : "not", GetLastError()); + // Cannot log, logger may already be stopped: + // LOG("Thread \"%s\" %s terminated, GLE = %d", mThreadName.c_str(), (res == WAIT_OBJECT_0) ? "" : "not", GetLastError()); return (res == WAIT_OBJECT_0); #else // _WIN32 diff --git a/source/cIsThread.h b/source/cIsThread.h index f46d7f065..fda789d3a 100644 --- a/source/cIsThread.h +++ b/source/cIsThread.h @@ -39,7 +39,6 @@ public: private: AString mThreadName; - cEvent mEvent; // This event is set when the thread begins executing #ifdef _WIN32 diff --git a/source/cItem.h b/source/cItem.h index c1b62495e..ba88a1940 100644 --- a/source/cItem.h +++ b/source/cItem.h @@ -24,11 +24,11 @@ public: m_ItemCount = 0; m_ItemHealth = 0; } //tolua_export - bool IsEmpty() //tolua_export + bool IsEmpty(void) const //tolua_export { //tolua_export return (m_ItemID <= 0 || m_ItemCount <= 0); } //tolua_export - bool Equals( cItem & a_Item ) //tolua_export + bool Equals( cItem & a_Item ) const //tolua_export { //tolua_export return ( (m_ItemID == a_Item.m_ItemID) && (m_ItemHealth == a_Item.m_ItemHealth) ); } //tolua_export diff --git a/source/cMonster.cpp b/source/cMonster.cpp index 03e50c4e0..8ad97a0b6 100644 --- a/source/cMonster.cpp +++ b/source/cMonster.cpp @@ -79,29 +79,28 @@ bool cMonster::IsA( const char* a_EntityType ) return cPawn::IsA( a_EntityType ); } -void cMonster::SpawnOn( cClientHandle* a_Target ) + + + + +cPacket * cMonster::GetSpawnPacket(void) const { - LOG("Spawn monster on client"); - cPacket_SpawnMob Spawn; - Spawn.m_UniqueID = GetUniqueID(); - Spawn.m_Type = m_MobType; - *Spawn.m_Pos = Vector3i((*m_Pos)*32); - Spawn.m_Yaw = 0; - Spawn.m_Pitch = 0; - Spawn.m_MetaDataSize = 1; - Spawn.m_MetaData = new char[Spawn.m_MetaDataSize]; - Spawn.m_MetaData[0] = 0x7f; // not on fire/crouching/riding - if( a_Target == 0 ) - { - cChunk* Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - Chunk->Broadcast( Spawn ); - } - else - { - a_Target->Send( Spawn ); - } + cPacket_SpawnMob * Spawn = new cPacket_SpawnMob; + Spawn->m_UniqueID = GetUniqueID(); + Spawn->m_Type = m_MobType; + *Spawn->m_Pos = Vector3i((*m_Pos) * 32); + Spawn->m_Yaw = 0; + Spawn->m_Pitch = 0; + Spawn->m_MetaDataSize = 1; + Spawn->m_MetaData = new char[Spawn->m_MetaDataSize]; + Spawn->m_MetaData[0] = 0x7f; // not on fire/crouching/riding + return Spawn; } + + + + void cMonster::MoveToPosition( const Vector3f & a_Position ) { m_bMovingToDestination = true; @@ -118,6 +117,10 @@ bool cMonster::ReachedDestination() return false; } + + + + void cMonster::Tick(float a_Dt) { cPawn::Tick(a_Dt); @@ -201,10 +204,17 @@ void cMonster::Tick(float a_Dt) } + + + + void cMonster::ReplicateMovement() { - cChunk* InChunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( !InChunk ) return; + cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( !InChunk->IsValid() ) + { + return; + } if(m_bDirtyOrientation && !m_bDirtyPosition) { @@ -259,6 +269,10 @@ void cMonster::ReplicateMovement() MoveToCorrectChunk(); } + + + + void cMonster::HandlePhysics(float a_Dt) { if( m_bOnGround ) // check if it's still on the ground @@ -464,9 +478,13 @@ void cMonster::Attack(float a_Dt) { } -//----Debug -void cMonster::ListMonsters() { + + +#if 0 +// TODO: Implement this debug function inside cWorld instead - the world owns the entities +void cMonster::ListMonsters() +{ cWorld::EntityList Entities = cRoot::Get()->GetWorld()->GetEntities(); cRoot::Get()->GetWorld()->LockEntities(); @@ -478,67 +496,80 @@ void cMonster::ListMonsters() { } cRoot::Get()->GetWorld()->UnlockEntities(); } +#endif + + + + //Checks for Players close by and if they are visible return the closest -cPlayer *cMonster::FindClosestPlayer() +cPlayer * cMonster::FindClosestPlayer(void) { - cTracer LineOfSight(cRoot::Get()->GetWorld()); - cWorld::PlayerList Players = cRoot::Get()->GetWorld()->GetAllPlayers(); + return m_World->FindClosestPlayer(m_Pos, m_SightDistance); +} + - float ClosestDistance = m_SightDistance + 1.f; //Something that is higher than the max :D - cPlayer* ClosestPlayer = 0; - for( cWorld::PlayerList::iterator itr = Players.begin(); itr != Players.end(); ++itr) - { - Vector3f Pos = (*itr)->GetPosition(); - float Distance = (Pos - *(m_Pos)).Length(); - if(Distance <= m_SightDistance) - { - if(!LineOfSight.Trace(*(m_Pos),(Pos - *(m_Pos)),(int)(Pos - *(m_Pos)).Length())) - { - if(Distance < ClosestDistance) - { - ClosestDistance = Distance; - ClosestPlayer = *itr; - } - } - } - } - return ClosestPlayer; -} void cMonster::GetMonsterConfig(const char* pm_name) { cRoot::Get()->GetMonsterConfig()->Get()->AssignAttributes(this, pm_name); } + + + + void cMonster::SetAttackRate(int ar) { m_AttackRate = (float)ar; } + + + + void cMonster::SetAttackRange(float ar) { m_AttackRange = ar; } -void cMonster::SetAttackDamage(float ad) { + + + + + +void cMonster::SetAttackDamage(float ad) +{ m_AttackDamage = ad; } -void cMonster::SetSightDistance(float sd) { + + + + + +void cMonster::SetSightDistance(float sd) +{ m_SightDistance = sd; } + + + void cMonster::DropItem(ENUM_ITEM_ID a_Item, unsigned int a_Count) { - if(a_Count > 0) + if (a_Count > 0) { - cPickup* Pickup = new cPickup( (int)(m_Pos->x*32), (int)(m_Pos->y*32), (int)(m_Pos->z*32), cItem( a_Item, (char) a_Count ) ); + cPickup * Pickup = new cPickup( (int)(m_Pos->x * 32), (int)(m_Pos->y * 32), (int)(m_Pos->z * 32), cItem( a_Item, (char) a_Count ) ); Pickup->Initialize( GetWorld() ); } } + + + + void cMonster::RandomDropItem(ENUM_ITEM_ID a_Item, unsigned int a_Min, unsigned int a_Max) { MTRand r1; diff --git a/source/cMonster.h b/source/cMonster.h index faf54b04f..25644257c 100644 --- a/source/cMonster.h +++ b/source/cMonster.h @@ -16,9 +16,10 @@ public: virtual bool IsA( const char* a_EntityType ); - virtual void SpawnOn( cClientHandle* a_Target ); + virtual cPacket * GetSpawnPacket(void) const override; - virtual void Tick(float a_Dt); + virtual void Tick(float a_Dt) override; + virtual void HandlePhysics(float a_Dt); virtual void ReplicateMovement(); diff --git a/source/cPawn.cpp b/source/cPawn.cpp index fa79fdcb9..0e169e676 100644 --- a/source/cPawn.cpp +++ b/source/cPawn.cpp @@ -17,8 +17,16 @@ #include "packets/cPacket_EntityStatus.h" #include "packets/cPacket_Metadata.h" + + + + CLASS_DEFINITION( cPawn, cEntity ) + + + + cPawn::cPawn() : cEntity( 0, 0, 0 ) , m_LastPosX( 0.0 ) @@ -34,16 +42,28 @@ cPawn::cPawn() SetMaxFoodLevel(125); } + + + + cPawn::~cPawn() { } + + + + void cPawn::Heal( int a_Health ) { (void)a_Health; } + + + + void cPawn::TakeDamage( int a_Damage, cEntity* a_Instigator ) { TakeDamageInfo TDI; @@ -51,8 +71,6 @@ void cPawn::TakeDamage( int a_Damage, cEntity* a_Instigator ) TDI.Instigator = a_Instigator; cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_TAKE_DAMAGE, 2, this, &TDI ); - - if( TDI.Damage == 0 ) return; if( m_Health <= 0 ) return; // Can't take damage if already dead @@ -62,14 +80,19 @@ void cPawn::TakeDamage( int a_Damage, cEntity* a_Instigator ) cPacket_EntityStatus Status; Status.m_UniqueID = GetUniqueID(); Status.m_Status = cPacket_EntityStatus::STATUS_TAKEDAMAGE; - cChunk* Chunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( Chunk ) - Chunk->Broadcast( Status ); + cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + Chunk->Broadcast( Status ); - if( m_Health <= 0 ) + if (m_Health <= 0) + { KilledBy( TDI.Instigator ); + } } + + + + void cPawn::KilledBy( cEntity* a_Killer ) { m_Health = 0; @@ -82,16 +105,23 @@ void cPawn::KilledBy( cEntity* a_Killer ) cPacket_EntityStatus Status; Status.m_UniqueID = GetUniqueID(); Status.m_Status = cPacket_EntityStatus::STATUS_DIE; - cChunk* Chunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( Chunk ) - Chunk->Broadcast( Status ); // Die + cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + Chunk->Broadcast( Status ); // Die } + + + + void cPawn::TeleportTo( cEntity* a_Entity ) { TeleportTo( a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ() ); } + + + + void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) { SetPosition( a_PosX, a_PosY, a_PosZ ); @@ -99,31 +129,41 @@ void cPawn::TeleportTo( const double & a_PosX, const double & a_PosY, const doub cPacket_TeleportEntity TeleportEntity( this ); cRoot::Get()->GetServer()->Broadcast( TeleportEntity ); - } + + + + void cPawn::Tick(float a_Dt) { CheckMetaDataBurn(); //Check to see if pawn should burn based on block they are on - if(GetMetaData() == BURNING) + if (GetMetaData() == BURNING) + { InStateBurning(a_Dt); - + } } + + + + void cPawn::SetMetaData(MetaData a_MetaData) { - cChunk* InChunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); + cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - if(InChunk) - { //Broadcast new status to clients in the chunk - m_MetaData = a_MetaData; - cPacket_Metadata md(a_MetaData, GetUniqueID()); - InChunk->Broadcast(md); - } + //Broadcast new status to clients in the chunk + m_MetaData = a_MetaData; + cPacket_Metadata md(a_MetaData, GetUniqueID()); + InChunk->Broadcast(md); } + + + + //----Change Entity MetaData void cPawn::CheckMetaDataBurn() { @@ -131,49 +171,67 @@ void cPawn::CheckMetaDataBurn() char BlockAbove = GetWorld()->GetBlock((int) m_Pos->x, (int) m_Pos->y + 1, (int) m_Pos->z); char BlockBelow = GetWorld()->GetBlock((int) m_Pos->x, (int) m_Pos->y - 1, (int) m_Pos->z); - if(GetMetaData() == BURNING - && (IsBlockWater(Block) - || IsBlockWater(BlockAbove) - || IsBlockWater(BlockBelow))) + if ( + (GetMetaData() == BURNING) && + (IsBlockWater(Block) || IsBlockWater(BlockAbove) || IsBlockWater(BlockBelow)) + ) { SetMetaData(NORMAL); - }else if(m_bBurnable && GetMetaData() != BURNING - && (IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE - || IsBlockLava(BlockBelow) || BlockBelow == E_BLOCK_FIRE)) { + } + else if ( + m_bBurnable && + (GetMetaData() != BURNING) && + ( + IsBlockLava(Block) || (Block == E_BLOCK_FIRE) || + IsBlockLava(BlockAbove) || (BlockAbove == E_BLOCK_FIRE) || + IsBlockLava(BlockBelow) || (BlockBelow == E_BLOCK_FIRE) + ) + ) + { SetMetaData(BURNING); } } + + + + //What to do if On fire void cPawn::InStateBurning(float a_Dt) { m_FireDamageInterval += a_Dt; char Block = GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y, (int)m_Pos->z ); char BlockAbove = GetWorld()->GetBlock( (int)m_Pos->x, (int)m_Pos->y + 1, (int)m_Pos->z ); - if(m_FireDamageInterval > 800) { + if (m_FireDamageInterval > 800) + { m_FireDamageInterval = 0; TakeDamage(1, this); m_BurnPeriod++; - if(IsBlockLava(Block) || Block == E_BLOCK_FIRE - || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE) { + if (IsBlockLava(Block) || Block == E_BLOCK_FIRE + || IsBlockLava(BlockAbove) || BlockAbove == E_BLOCK_FIRE) + { m_BurnPeriod = 0; TakeDamage(6, this); - }else{ + } + else + { TakeDamage(1, this); } - if(m_BurnPeriod > 7) { + if (m_BurnPeriod > 7) + { SetMetaData(NORMAL); m_BurnPeriod = 0; - } } - } + + + + void cPawn::SetMaxHealth(short a_MaxHealth) { this->m_MaxHealth = a_MaxHealth; @@ -182,6 +240,10 @@ void cPawn::SetMaxHealth(short a_MaxHealth) m_Health = a_MaxHealth; } + + + + void cPawn::SetMaxFoodLevel(short a_MaxFoodLevel) { m_MaxFoodLevel = a_MaxFoodLevel; @@ -189,3 +251,7 @@ void cPawn::SetMaxFoodLevel(short a_MaxFoodLevel) //Reset food level m_FoodLevel = a_MaxFoodLevel; } + + + + diff --git a/source/cPawn.h b/source/cPawn.h index a5156cec3..b35350392 100644 --- a/source/cPawn.h +++ b/source/cPawn.h @@ -1,6 +1,11 @@ + #pragma once + #include "cEntity.h" -#include "math.h" + + + + struct TakeDamageInfo //tolua_export { //tolua_export @@ -8,6 +13,10 @@ struct TakeDamageInfo //tolua_export cEntity* Instigator; //tolua_export }; //tolua_export + + + + class cPawn : public cEntity //tolua_export { //tolua_export public: @@ -19,7 +28,7 @@ public: virtual void TeleportTo( cEntity* a_Entity ); //tolua_export virtual void TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ); //tolua_export - virtual void Tick(float a_Dt); + virtual void Tick(float a_Dt) override; void Heal( int a_Health ); //tolua_export virtual void TakeDamage( int a_Damage, cEntity* a_Instigator ); //tolua_export @@ -39,8 +48,8 @@ public: virtual short GetMaxHealth() { return m_MaxHealth; } //virtual void SetMaxFood(short a_MaxFood); - virtual short GetMaxFood() { return m_MaxFoodLevel/6; } - virtual short GetFood() { return m_FoodLevel/6; } + virtual short GetMaxFood() { return m_MaxFoodLevel / 6; } + virtual short GetFood() { return m_FoodLevel / 6; } //virtual void SetMaxFoodSaturation(float a_MaxFoodSaturation); virtual float GetMaxFoodSaturation() { return fmod(m_MaxFoodLevel, 6.f); } @@ -50,6 +59,7 @@ public: virtual short GetMaxFoodLevel() { return m_MaxFoodLevel; } protected: + short m_Health; short m_FoodLevel; short m_MaxHealth; @@ -57,7 +67,7 @@ protected: bool m_bBurnable; - MetaData m_MetaData; + MetaData m_MetaData; double m_LastPosX, m_LastPosY, m_LastPosZ; float m_TimeLastTeleportPacket; @@ -66,3 +76,7 @@ protected: float m_BurnPeriod; }; //tolua_export + + + + diff --git a/source/cPickup.cpp b/source/cPickup.cpp index 120fe17aa..b84e07314 100644 --- a/source/cPickup.cpp +++ b/source/cPickup.cpp @@ -54,23 +54,22 @@ cPickup::cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX //LOG("New pickup: ID(%i) Amount(%i) Health(%i)", m_Item.m_ItemID, m_Item.m_ItemCount, m_Item.m_ItemHealth ); // Spawn it on clients - cPacket_PickupSpawn PickupSpawn; - PickupSpawn.m_UniqueID = m_UniqueID; - PickupSpawn.m_Item = (short)m_Item->m_ItemID; - PickupSpawn.m_Count = m_Item->m_ItemCount; - PickupSpawn.m_Health = m_Item->m_ItemHealth; - PickupSpawn.m_PosX = a_X; - PickupSpawn.m_PosY = a_Y; - PickupSpawn.m_PosZ = a_Z; - PickupSpawn.m_Rotation = (char)(m_Speed->x * 8); - PickupSpawn.m_Pitch = (char)(m_Speed->y * 8); - PickupSpawn.m_Roll = (char)(m_Speed->z * 8); - if(PickupSpawn.m_Item != E_ITEM_EMPTY) - cRoot::Get()->GetServer()->Broadcast( PickupSpawn ); + if (!a_Item.IsEmpty()) + { + std::auto_ptr PickupSpawn(GetSpawnPacket()); + if (PickupSpawn.get() != NULL) + { + cRoot::Get()->GetServer()->Broadcast( PickupSpawn.get() ); + } + } m_EntityType = E_PICKUP; } + + + + cPickup::cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket) : cEntity( ((double)a_PickupSpawnPacket->m_PosX)/32, ((double)a_PickupSpawnPacket->m_PosY)/32, ((double)a_PickupSpawnPacket->m_PosZ)/32 ) , m_Speed( new Vector3f() ) @@ -93,29 +92,43 @@ cPickup::cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket) m_Speed->z = (float)(a_PickupSpawnPacket->m_Roll) / 8; // Spawn it on clients - if(a_PickupSpawnPacket->m_Item != E_ITEM_EMPTY) + if (a_PickupSpawnPacket->m_Item != E_ITEM_EMPTY) + { cRoot::Get()->GetServer()->Broadcast( *a_PickupSpawnPacket ); + } m_EntityType = E_PICKUP; } -void cPickup::SpawnOn( cClientHandle* a_Target ) + + + + +cPacket * cPickup::GetSpawnPacket(void) const { - cPacket_PickupSpawn PickupSpawn; - PickupSpawn.m_UniqueID = m_UniqueID; - PickupSpawn.m_Item = (short)m_Item->m_ItemID; - PickupSpawn.m_Count = m_Item->m_ItemCount; - PickupSpawn.m_Health = m_Item->m_ItemHealth; - PickupSpawn.m_PosX = (int)(m_Pos->x * 32); - PickupSpawn.m_PosY = (int)(m_Pos->y * 32); - PickupSpawn.m_PosZ = (int)(m_Pos->z * 32); - PickupSpawn.m_Rotation = (char)(m_Speed->x * 8); - PickupSpawn.m_Pitch = (char)(m_Speed->y * 8); - PickupSpawn.m_Roll = (char)(m_Speed->z * 8); - if(PickupSpawn.m_Item != E_ITEM_EMPTY) - a_Target->Send( PickupSpawn ); + if (m_Item->IsEmpty()) + { + return NULL; + } + + cPacket_PickupSpawn * PickupSpawn = new cPacket_PickupSpawn; + PickupSpawn->m_UniqueID = m_UniqueID; + PickupSpawn->m_Item = (short)m_Item->m_ItemID; + PickupSpawn->m_Count = m_Item->m_ItemCount; + PickupSpawn->m_Health = m_Item->m_ItemHealth; + PickupSpawn->m_PosX = (int) (m_Pos->x * 32); + PickupSpawn->m_PosY = (int) (m_Pos->y * 32); + PickupSpawn->m_PosZ = (int) (m_Pos->z * 32); + PickupSpawn->m_Rotation = (char)(m_Speed->x * 8); + PickupSpawn->m_Pitch = (char)(m_Speed->y * 8); + PickupSpawn->m_Roll = (char)(m_Speed->z * 8); + return PickupSpawn; } + + + + void cPickup::Tick(float a_Dt) { m_Timer += a_Dt; @@ -142,28 +155,28 @@ void cPickup::Tick(float a_Dt) } if(!m_bCollected) + { HandlePhysics( a_Dt ); + } if( !m_bReplicated || m_bDirtyPosition ) { MoveToCorrectChunk(); m_bReplicated = true; m_bDirtyPosition = false; - cChunk* Chunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( Chunk ) - { - cPacket_TeleportEntity TeleportEntity( this ); - Chunk->Broadcast( TeleportEntity ); - } + cPacket_TeleportEntity TeleportEntity( this ); + GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ )->Broadcast( &TeleportEntity ); } - - //printf("YSpeed: %f, OnGround: %i\n", m_SpeedY, m_bOnGround ); } + + + + void cPickup::HandlePhysics(float a_Dt) { m_ResultingSpeed->Set(0.f, 0.f, 0.f); - cWorld* World = GetWorld(); + cWorld * World = GetWorld(); if( m_bOnGround ) // check if it's still on the ground { @@ -273,14 +286,14 @@ void cPickup::HandlePhysics(float a_Dt) *m_Pos += *m_ResultingSpeed * a_Dt; } } - - //Usable for debugging //SetPosition(m_Pos->x, m_Pos->y, m_Pos->z); - - } + + + + bool cPickup::CollectedBy( cPlayer* a_Dest ) { if(m_bCollected) return false; // It's already collected! @@ -303,3 +316,7 @@ bool cPickup::CollectedBy( cPlayer* a_Dest ) return false; } + + + + diff --git a/source/cPickup.h b/source/cPickup.h index fead89447..22af39367 100644 --- a/source/cPickup.h +++ b/source/cPickup.h @@ -1,38 +1,53 @@ + #pragma once #include "cEntity.h" + + class cPacket_PickupSpawn; class cPlayer; class cItem; + + + + + class cPickup : public cEntity //tolua_export { //tolua_export public: CLASS_PROTOTYPE(); cPickup(int a_X, int a_Y, int a_Z, const cItem & a_Item, float a_SpeedX = 0.f, float a_SpeedY = 0.f, float a_SpeedZ = 0.f); //tolua_export - cPickup(cPacket_PickupSpawn* a_PickupSpawnPacket); //tolua_export + cPickup(cPacket_PickupSpawn * a_PickupSpawnPacket); //tolua_export ~cPickup(); //tolua_export - cItem* GetItem() { return m_Item; } //tolua_export + cItem * GetItem() { return m_Item; } //tolua_export - void SpawnOn( cClientHandle* a_Target ); + virtual cPacket * GetSpawnPacket(void) const override; + virtual bool CollectedBy( cPlayer* a_Dest ); //tolua_export void Tick(float a_Dt); void HandlePhysics(float a_Dt); + private: - Vector3f* m_Speed; - Vector3f* m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) - Vector3f* m_WaterSpeed; - bool m_bOnGround; - bool m_bReplicated; + Vector3f * m_Speed; + Vector3f * m_ResultingSpeed; //Can be used to modify the resulting speed for the current tick ;) + + Vector3f * m_WaterSpeed; + bool m_bOnGround; + bool m_bReplicated; float m_Timer; cItem* m_Item; bool m_bCollected; -};//tolua_export \ No newline at end of file +}; //tolua_export + + + + diff --git a/source/cPiston.cpp b/source/cPiston.cpp index 7fb503a3a..cc6d48ca9 100644 --- a/source/cPiston.cpp +++ b/source/cPiston.cpp @@ -20,8 +20,6 @@ extern bool g_BlockPistonBreakable[]; case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\ case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; } -#define FAST_FLOOR( x ) ( (x) < 0 ? ((int)x)-1 : ((int)x) ) - @@ -49,6 +47,10 @@ unsigned short cPiston::FirstPassthroughBlock( int pistonX, int pistonY, int pis return 9001; } + + + + void cPiston::ExtendPiston( int pistx, int pisty, int pistz ) { char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); @@ -87,7 +89,7 @@ void cPiston::ExtendPiston( int pistx, int pisty, int pistz ) { Action.m_Byte2 = pistonMeta; - cChunk* Chunk = m_World->GetChunk( FAST_FLOOR(pistx/16), FAST_FLOOR(pisty/16), FAST_FLOOR(pistz/16) ); + cChunkPtr Chunk = m_World->GetChunkOfBlock(pistx, pisty, pistz); Chunk->Broadcast( Action ); m_World->FastSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta | 8 ); @@ -109,33 +111,53 @@ void cPiston::ExtendPiston( int pistx, int pisty, int pistz ) { } -void cPiston::RetractPiston( int pistx, int pisty, int pistz ) { + + + + +void cPiston::RetractPiston( int pistx, int pisty, int pistz ) +{ char pistonBlock = m_World->GetBlock( pistx, pisty, pistz ); char pistonMeta = m_World->GetBlockMeta( pistx, pisty, pistz ); - if (pistonMeta > 6) {// only retract if piston is not already retracted - //send blockaction packet - cPacket_BlockAction Action; - Action.m_PosX = (int)pistx; - Action.m_PosY = (short)pisty; - Action.m_PosZ = (int)pistz; - Action.m_Byte1 = 1; - Action.m_Byte2 = pistonMeta & ~(8); - cChunk* Chunk = m_World->GetChunk( FAST_FLOOR(pistx/16), FAST_FLOOR(pisty/16), FAST_FLOOR(pistz/16) ); - Chunk->Broadcast( Action ); - m_World->FastSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8) ); - - AddDir( pistx, pisty, pistz, pistonMeta & 7, 1 ) - if( m_World->GetBlock( pistx, pisty, pistz ) == E_BLOCK_PISTON_EXTENSION ) { - if( pistonBlock == E_BLOCK_STICKY_PISTON ) { - int tempx = pistx, tempy = pisty, tempz = pistz; - AddDir( tempx, tempy, tempz, pistonMeta&7, 1 ) - char tempblock = m_World->GetBlock( tempx, tempy, tempz ); - if(tempblock == E_BLOCK_OBSIDIAN || tempblock == E_BLOCK_BEDROCK || tempblock == E_BLOCK_PISTON_EXTENSION) {return;} - m_World->SetBlock( pistx, pisty, pistz, tempblock, m_World->GetBlockMeta( tempx, tempy, tempz ) ); - m_World->SetBlock( tempx, tempy, tempz, E_BLOCK_AIR, 0 ); - }else{ - m_World->SetBlock( pistx, pisty, pistz, E_BLOCK_AIR, 0 ); + if (pistonMeta <= 6) // only retract if piston is not already retracted + { + return; + } + + //send blockaction packet + cPacket_BlockAction Action; + Action.m_PosX = (int)pistx; + Action.m_PosY = (short)pisty; + Action.m_PosZ = (int)pistz; + Action.m_Byte1 = 1; + Action.m_Byte2 = pistonMeta & ~(8); + cChunkPtr Chunk = m_World->GetChunkOfBlock(pistx, pisty, pistz); + Chunk->Broadcast( Action ); + m_World->FastSetBlock( pistx, pisty, pistz, pistonBlock, pistonMeta & ~(8) ); + + AddDir( pistx, pisty, pistz, pistonMeta & 7, 1 ) + if ( m_World->GetBlock( pistx, pisty, pistz ) == E_BLOCK_PISTON_EXTENSION ) + { + if ( pistonBlock == E_BLOCK_STICKY_PISTON ) + { + int tempx = pistx, tempy = pisty, tempz = pistz; + AddDir( tempx, tempy, tempz, pistonMeta & 7, 1 ) + char tempblock = m_World->GetBlock( tempx, tempy, tempz ); + if ( + (tempblock == E_BLOCK_OBSIDIAN) || + (tempblock == E_BLOCK_BEDROCK) || + (tempblock == E_BLOCK_PISTON_EXTENSION) + ) + { + // These cannot be moved by the sticky piston, bail out + return; } + m_World->SetBlock( pistx, pisty, pistz, tempblock, m_World->GetBlockMeta( tempx, tempy, tempz ) ); + m_World->SetBlock( tempx, tempy, tempz, E_BLOCK_AIR, 0 ); + } + else + { + m_World->SetBlock( pistx, pisty, pistz, E_BLOCK_AIR, 0 ); } } } \ No newline at end of file diff --git a/source/cPiston.h b/source/cPiston.h index afb5780fb..54111f9d6 100644 --- a/source/cPiston.h +++ b/source/cPiston.h @@ -20,8 +20,8 @@ public: static char RotationPitchToMetaData( float a_Rotation, float a_Pitch ) { - std::printf("pre:a_Rotation %f \n",a_Rotation); - std::printf("a_Pitch %f \n",a_Pitch); + LOG("pre:a_Rotation %f \n",a_Rotation); + LOG("a_Pitch %f \n",a_Pitch); if (a_Pitch >= 50.f ){ return 0x1; @@ -34,13 +34,13 @@ public: if( a_Rotation > 360.f ) a_Rotation -= 360.f; if( a_Rotation >= 0.f && a_Rotation < 90.f ) - { std::printf("1111\n");return 0x4;} + { LOG("1111\n");return 0x4;} else if( a_Rotation >= 180 && a_Rotation < 270 ) - { std::printf("2222\n");return 0x5;} + { LOG("2222\n");return 0x5;} else if( a_Rotation >= 90 && a_Rotation < 180 ) - { std::printf("3333\n");return 0x2;} + { LOG("3333\n");return 0x2;} else - { std::printf("4444\n");return 0x3;} + { LOG("4444\n");return 0x3;} } } diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp index fa833ff47..1086e862a 100644 --- a/source/cPlayer.cpp +++ b/source/cPlayer.cpp @@ -126,39 +126,39 @@ cPlayer::~cPlayer(void) delete m_CreativeInventory; } delete m_pState; - GetWorld()->RemovePlayer( this ); // TODO - Remove from correct world? Or get rid of this? + m_World->RemovePlayer( this ); } -void cPlayer::SpawnOn( cClientHandle* a_Target ) + + +cPacket * cPlayer::GetSpawnPacket(void) const { - if( a_Target == m_ClientHandle || !m_bVisible ) return; - LOG("cPlayer::SpawnOn -> Sending %s to %s", m_pState->PlayerName.c_str(), (a_Target) ? a_Target->GetUsername().c_str() : "Everybody" ); - cPacket_NamedEntitySpawn SpawnPacket; - SpawnPacket.m_UniqueID = m_UniqueID; - SpawnPacket.m_PlayerName = m_pState->PlayerName; - SpawnPacket.m_PosX = (int)(m_Pos->x * 32); - SpawnPacket.m_PosY = (int)(m_Pos->y * 32); - SpawnPacket.m_PosZ = (int)(m_Pos->z * 32); - SpawnPacket.m_Rotation = (char)((m_Rot->x/360.f)*256); - SpawnPacket.m_Pitch = (char)((m_Rot->y/360.f)*256); - SpawnPacket.m_CurrentItem = (short)m_Inventory->GetEquippedItem().m_ItemID; - if( a_Target == 0 ) - { - cChunk* Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - Chunk->Broadcast( SpawnPacket, m_ClientHandle ); - } - else + if (!m_bVisible ) { - a_Target->Send( SpawnPacket ); + return NULL; } + + cPacket_NamedEntitySpawn * SpawnPacket = new cPacket_NamedEntitySpawn; + SpawnPacket->m_UniqueID = m_UniqueID; + SpawnPacket->m_PlayerName = m_pState->PlayerName; + SpawnPacket->m_PosX = (int)(m_Pos->x * 32); + SpawnPacket->m_PosY = (int)(m_Pos->y * 32); + SpawnPacket->m_PosZ = (int)(m_Pos->z * 32); + SpawnPacket->m_Rotation = (char)((m_Rot->x / 360.f) * 256); + SpawnPacket->m_Pitch = (char)((m_Rot->y / 360.f) * 256); + SpawnPacket->m_CurrentItem = (short)m_Inventory->GetEquippedItem().m_ItemID; + return SpawnPacket; } + + + + void cPlayer::Tick(float a_Dt) { - cChunk* InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - if ( !InChunk ) return; + cChunkPtr InChunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); cPawn::Tick(a_Dt); @@ -168,16 +168,18 @@ void cPlayer::Tick(float a_Dt) InChunk->Broadcast( EntityLook, m_ClientHandle ); m_bDirtyOrientation = false; } - else if(m_bDirtyPosition ) + else if (m_bDirtyPosition ) { cRoot::Get()->GetPluginManager()->CallHook( cPluginManager::E_PLUGIN_PLAYER_MOVE, 1, this ); float DiffX = (float)(GetPosX() - m_LastPosX ); float DiffY = (float)(GetPosY() - m_LastPosY ); float DiffZ = (float)(GetPosZ() - m_LastPosZ ); - float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ; - if( SqrDist > 4*4 // 4 blocks is max Relative Move - || cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds + float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ; + if ( + (SqrDist > 4 * 4) || // 4 blocks is max Relative Move + (cWorld::GetTime() - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds + ) { //LOG("Teleported %f", sqrtf(SqrDist) ); cPacket_TeleportEntity TeleportEntity( this ); @@ -216,49 +218,23 @@ void cPlayer::Tick(float a_Dt) if( m_Health > 0 ) // make sure player is alive { - if( cWorld::GetTime() - m_TimeLastPickupCheck > 0.5f ) // Each 0.5 second, check for pickups - { - m_TimeLastPickupCheck = cWorld::GetTime(); - // and also check if near a pickup - // TODO: Don't only check in current chunks, but also close chunks (chunks within range) - cChunk* Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); - Chunk->LockEntities(); - cWorld::EntityList Entities = Chunk->GetEntities(); - for( cWorld::EntityList::iterator itr = Entities.begin(); itr != Entities.end();++itr) - { - if( (*itr)->GetEntityType() != cEntity::E_PICKUP ) continue; // Only pickups - float DiffX = (float)((*itr)->GetPosX() - GetPosX() ); - float DiffY = (float)((*itr)->GetPosY() - GetPosY() ); - float DiffZ = (float)((*itr)->GetPosZ() - GetPosZ() ); - float SqrDist = DiffX*DiffX + DiffY*DiffY + DiffZ*DiffZ; - if(SqrDist < 1.5f*1.5f) // 1.5 block - { - cPickup* Pickup = reinterpret_cast(*itr); - Pickup->CollectedBy( this ); - } - } - Chunk->UnlockEntities(); - } + // TODO: Don't only check in current chunks, but also close chunks (chunks within range) + GetWorld()->GetChunk(m_ChunkX, m_ChunkY, m_ChunkZ)->CollectPickupsByPlayer(this); } cTimer t1; // Send Player List (Once per m_LastPlayerListTime/1000 ms) if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) { - cWorld::PlayerList PlayerList = cRoot::Get()->GetWorld()->GetAllPlayers(); - for( cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) - { - if ((*itr) && (*itr)->GetClientHandle() && !((*itr)->GetClientHandle()->IsDestroyed())) - { - cPacket_PlayerListItem PlayerListItem(GetColor() + m_pState->PlayerName, true, GetClientHandle()->GetPing()); - (*itr)->GetClientHandle()->Send( PlayerListItem ); - } - } + m_World->SendPlayerList(this); m_LastPlayerListTime = t1.GetNowTime(); } - } + + + + void cPlayer::SetTouchGround( bool a_bTouchGround ) { m_bTouchGround = a_bTouchGround; @@ -439,30 +415,36 @@ void cPlayer::CloseWindow(char a_WindowType) ChestClose.m_PosZ = block->GetPosZ(); ChestClose.m_Byte1 = 1; ChestClose.m_Byte2 = 0; - cWorld::PlayerList PlayerList = cRoot::Get()->GetWorld()->GetAllPlayers(); - for( cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr ) - { - if ((*itr) && (*itr)->GetClientHandle() && !((*itr)->GetClientHandle()->IsDestroyed())) { - (*itr)->GetClientHandle()->Send( ChestClose ); - } - } + m_World->Broadcast(ChestClose); } m_CurrentWindow->Close( *this ); } - m_CurrentWindow = 0; + m_CurrentWindow = NULL; } + + + + void cPlayer::SetLastBlockActionTime() { m_LastBlockActionTime = cRoot::Get()->GetWorld()->GetTime(); } + + + + void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) { m_LastBlockActionCnt = a_LastBlockActionCnt; } + + + + void cPlayer::SetGameMode( int a_GameMode ) { if ( (a_GameMode < 2) && (a_GameMode >= 0) ) @@ -487,21 +469,37 @@ void cPlayer::SetGameMode( int a_GameMode ) } } + + + + void cPlayer::LoginSetGameMode( int a_GameMode ) { m_GameMode = a_GameMode; } + + + + void cPlayer::SetIP( std::string a_IP ) { - m_IP = a_IP; + m_IP = a_IP; } + + + + void cPlayer::SendMessage( const char* a_Message ) { m_ClientHandle->Send( cPacket_Chat( a_Message ) ); } + + + + void cPlayer::TeleportTo( const double & a_PosX, const double & a_PosY, const double & a_PosZ ) { SetPosition( a_PosX, a_PosY, a_PosZ ); @@ -515,31 +513,43 @@ void cPlayer::TeleportTo( const double & a_PosX, const double & a_PosY, const do m_ClientHandle->Send( PlayerPosition ); } + + + + void cPlayer::MoveTo( const Vector3d & a_NewPos ) { // TODO: should do some checks to see if player is not moving through terrain SetPosition( a_NewPos ); } + + + + void cPlayer::SetVisible( bool a_bVisible ) { - if( a_bVisible == true && m_bVisible == false ) // Make visible + if (a_bVisible && !m_bVisible) // Make visible { m_bVisible = true; - SpawnOn( 0 ); // Spawn on everybody + SpawnOn( NULL ); // Spawn on everybody } - if( a_bVisible == false && m_bVisible == true ) + if (!a_bVisible && m_bVisible) { m_bVisible = false; cPacket_DestroyEntity DestroyEntity( this ); - cChunk* Chunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( Chunk != NULL ) { Chunk->Broadcast( DestroyEntity ); // Destroy on all clients } } } + + + + void cPlayer::AddToGroup( const char* a_GroupName ) { cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); @@ -628,6 +638,10 @@ void cPlayer::ResolvePermissions() } } + + + + void cPlayer::ResolveGroups() { // Clear resolved groups first @@ -666,17 +680,29 @@ void cPlayer::ResolveGroups() } } -std::string cPlayer::GetColor() + + + + +AString cPlayer::GetColor(void) const { - if( m_Color != '-' ) + if ( m_Color != '-' ) + { return cChatColor::MakeColor( m_Color ); + } - if( m_pState->Groups.size() < 1 ) + if ( m_pState->Groups.size() < 1 ) + { return cChatColor::White; + } return (*m_pState->Groups.begin())->GetColor(); } + + + + void cPlayer::TossItem( bool a_bDraggingItem, int a_Amount /* = 1 */ ) { if( a_bDraggingItem ) @@ -714,18 +740,22 @@ void cPlayer::TossItem( bool a_bDraggingItem, int a_Amount /* = 1 */ ) } } + + + + bool cPlayer::MoveToWorld( const char* a_WorldName ) { - cWorld* World = cRoot::Get()->GetWorld( a_WorldName ); - if( World ) + cWorld * World = cRoot::Get()->GetWorld( a_WorldName ); + if ( World ) { /* Remove all links to the old world */ - GetWorld()->RemovePlayer( this ); - GetClientHandle()->RemoveFromAllChunks(); - cChunk* Chunk = GetWorld()->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ); - if( Chunk ) + m_World->RemovePlayer( this ); + m_ClientHandle->RemoveFromAllChunks(); + cChunkPtr Chunk = m_World->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ ); + if ( Chunk != NULL ) { - Chunk->RemoveEntity( *this ); + Chunk->RemoveEntity( this ); Chunk->Broadcast( cPacket_DestroyEntity( this ) ); // Remove player entity from all clients in old world } @@ -741,6 +771,10 @@ bool cPlayer::MoveToWorld( const char* a_WorldName ) return false; } + + + + void cPlayer::LoadPermissionsFromDisk() { m_pState->Groups.clear(); diff --git a/source/cPlayer.h b/source/cPlayer.h index 6f8cbfca6..105a4924c 100644 --- a/source/cPlayer.h +++ b/source/cPlayer.h @@ -1,3 +1,4 @@ + #pragma once #include "cPawn.h" @@ -14,7 +15,10 @@ class cInventory; class cClientHandle; -class cPlayer : public cPawn //tolua_export + + + +class cPlayer : public cPawn //tolua_export { //tolua_export public: CLASS_PROTOTYPE(); @@ -24,8 +28,8 @@ public: virtual void Initialize( cWorld* a_World ); //tolua_export - virtual void SpawnOn( cClientHandle* a_Target ); - virtual void Tick(float a_Dt); + virtual cPacket * GetSpawnPacket(void) const override; + virtual void Tick(float a_Dt) override; void SetTouchGround( bool a_bTouchGround ); inline void SetStance( const double & a_Stance ) { m_Stance = a_Stance; } @@ -71,7 +75,7 @@ public: StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS << bool IsInGroup( const char* a_Group ); //tolua_export - std::string GetColor(); //tolua_export + AString GetColor(void) const; //tolua_export void TossItem( bool a_bDraggingItem, int a_Amount = 1 ); //tolua_export diff --git a/source/cPluginManager.h b/source/cPluginManager.h index ebe974b11..b42db94d6 100644 --- a/source/cPluginManager.h +++ b/source/cPluginManager.h @@ -1,7 +1,5 @@ -#pragma once -#include -#include // TODO - use const char* +#pragma once struct lua_State; class cLuaCommandBinder; diff --git a/source/cRoot.h b/source/cRoot.h index be5480e9e..a3c3f143b 100644 --- a/source/cRoot.h +++ b/source/cRoot.h @@ -12,7 +12,6 @@ class cThread; class cMonsterConfig; -class cMCLogger; class cGroupManager; class cRecipeChecker; class cFurnaceRecipe; @@ -21,6 +20,10 @@ class cPluginManager; class cServer; class cWorld; + + + + class cRoot //tolua_export { //tolua_export public: @@ -32,7 +35,7 @@ public: void Start(); cServer* GetServer() { return m_Server; } //tolua_export - cWorld* GetWorld(); //tolua_export + OBSOLETE cWorld* GetWorld(); //tolua_export cWorld* GetDefaultWorld(); //tolua_export cWorld* GetWorld( const char* a_WorldName ); //tolua_export cMonsterConfig *GetMonsterConfig() { return m_MonsterConfig;} diff --git a/source/cServer.cpp b/source/cServer.cpp index e3fdb2f86..0836900e3 100644 --- a/source/cServer.cpp +++ b/source/cServer.cpp @@ -241,7 +241,7 @@ cServer::~cServer() // TODO - Need to modify this or something, so it broadcasts to all worlds? And move this to cWorld? -void cServer::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude /* = 0 */ ) +void cServer::Broadcast( const cPacket * a_Packet, cClientHandle* a_Exclude /* = 0 */ ) { for( ClientList::iterator itr = m_pState->Clients.begin(); itr != m_pState->Clients.end(); ++itr) { @@ -277,6 +277,7 @@ void cServer::StartListenClient() if (!m_SocketThreads.AddClient(&(NewHandle->GetSocket()), NewHandle)) { // For some reason SocketThreads have rejected the handle, clean it up + LOGERROR("Client \"%s\" cannot be handled, server probably unstable", SClient.GetIPString().c_str()); SClient.CloseSocket(); delete NewHandle; return; @@ -423,7 +424,7 @@ bool cServer::Command( cClientHandle & a_Client, const char* a_Cmd ) -void cServer::ServerCommand( const char* a_Cmd ) +void cServer::ServerCommand( const char * a_Cmd ) { AString Command( a_Cmd ); AStringVector split = StringSplit( Command, " " ); @@ -454,30 +455,29 @@ void cServer::ServerCommand( const char* a_Cmd ) } if( split[0].compare( "list" ) == 0 ) { - cWorld::EntityList Entities = cRoot::Get()->GetWorld()->GetEntities(); - std::string PlayerString; - int NumPlayers = 0; - cRoot::Get()->GetWorld()->LockEntities(); - for( cWorld::EntityList::iterator itr = Entities.begin(); itr != Entities.end(); ++itr) + class cPlayerLogger : public cPlayerListCallback { - if( (*itr)->GetEntityType() != cEntity::E_PLAYER ) continue; - PlayerString.push_back(' '); - PlayerString += ((cPlayer*)*itr)->GetName(); - NumPlayers++; - } - cRoot::Get()->GetWorld()->UnlockEntities(); - printf( "Players (%i):%s\n", NumPlayers, PlayerString.c_str() ); + virtual bool Item(cPlayer * a_Player) override + { + LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetSocket().GetIPString()); + return false; + } + } Logger; + cRoot::Get()->GetWorld()->ForEachPlayer(&Logger); return; } if( split[0].compare( "numchunks" ) == 0 ) { - printf("Num loaded chunks: %i\n", cRoot::Get()->GetWorld()->GetNumChunks() ); + printf("Num loaded chunks: %i\n", cRoot::Get()->GetTotalChunkCount() ); return; } - if(split[0].compare("monsters") == 0 ){ - cMonster::ListMonsters(); + + if(split[0].compare("monsters") == 0 ) + { + // TODO: cWorld::ListMonsters(); return; } + if(split.size() > 1) { if( split[0].compare( "say" ) == 0 ) diff --git a/source/cServer.h b/source/cServer.h index b56047487..7e42cb1e7 100644 --- a/source/cServer.h +++ b/source/cServer.h @@ -35,9 +35,9 @@ public: //tolua_export int GetPort() { return m_iServerPort; } bool IsConnected(){return m_bIsConnected;} // returns connection status void StartListenClient(); // Listen to client - int RecClient(cClientHandle *sRecSocket); // receive message for a particular socket - void Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude = 0 ); + void Broadcast(const cPacket & a_Packet, cClientHandle* a_Exclude = NULL) { Broadcast(&a_Packet, a_Exclude); } + void Broadcast(const cPacket * a_Packet, cClientHandle* a_Exclude = NULL); bool Tick(float a_Dt); diff --git a/source/cSignEntity.cpp b/source/cSignEntity.cpp index 2f57b3867..463e1072a 100644 --- a/source/cSignEntity.cpp +++ b/source/cSignEntity.cpp @@ -17,8 +17,8 @@ -cSignEntity::cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cChunk* a_Chunk) - : cBlockEntity(a_BlockType, a_X, a_Y, a_Z, a_Chunk) +cSignEntity::cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) + : cBlockEntity(a_BlockType, a_X, a_Y, a_Z, a_World) { } @@ -44,7 +44,7 @@ void cSignEntity::UsedBy( cPlayer & a_Player ) -void cSignEntity::SetLines( const std::string & a_Line1, const std::string & a_Line2, const std::string & a_Line3, const std::string & a_Line4 ) +void cSignEntity::SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ) { m_Line[0] = a_Line1; m_Line[1] = a_Line2; @@ -56,7 +56,7 @@ void cSignEntity::SetLines( const std::string & a_Line1, const std::string & a_L -void cSignEntity::SetLine( int a_Index, std::string a_Line ) +void cSignEntity::SetLine( int a_Index, const AString & a_Line ) { if( a_Index < 4 && a_Index > -1 ) { @@ -68,7 +68,7 @@ void cSignEntity::SetLine( int a_Index, std::string a_Line ) -std::string cSignEntity::GetLine( int a_Index ) +AString cSignEntity::GetLine( int a_Index ) const { if( a_Index < 4 && a_Index > -1 ) { @@ -92,13 +92,13 @@ void cSignEntity::SendTo( cClientHandle* a_Client ) Sign.m_Line3 = m_Line[2]; Sign.m_Line4 = m_Line[3]; - if( a_Client ) + if ( a_Client != NULL ) { a_Client->Send( Sign ); } else // broadcast of a_Client == 0 { - GetChunk()->Broadcast( Sign ); + m_World->GetChunkOfBlock(m_PosX, m_PosY, m_PosZ)->Broadcast( Sign ); } } diff --git a/source/cSignEntity.h b/source/cSignEntity.h index 31413c8c3..0c9e40710 100644 --- a/source/cSignEntity.h +++ b/source/cSignEntity.h @@ -1,7 +1,7 @@ + #pragma once #include "cBlockEntity.h" -#include "FileDefine.h" @@ -16,11 +16,11 @@ namespace Json -class cWorld; -class cSignEntity : public cBlockEntity +class cSignEntity : + public cBlockEntity { public: - cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cChunk* a_Chunk); + cSignEntity(ENUM_BLOCK_ID a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World); virtual ~cSignEntity(); bool LoadFromFile(cFile & a_File); // deprecated format @@ -28,15 +28,17 @@ public: bool LoadFromJson( const Json::Value& a_Value ); void SaveToJson( Json::Value& a_Value ); - void SetLines( const std::string & a_Line1, const std::string & a_Line2, const std::string & a_Line3, const std::string & a_Line4 ); - void SetLine( int a_Index, std::string a_Line ); + void SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 ); + void SetLine( int a_Index, const AString & a_Line ); - std::string GetLine( int a_Index ); + AString GetLine( int a_Index ) const; virtual void UsedBy( cPlayer & a_Player ); virtual void SendTo( cClientHandle* a_Client ); + private: - std::string m_Line[4]; + + AString m_Line[4]; }; diff --git a/source/cSimulatorManager.cpp b/source/cSimulatorManager.cpp index 2ff44c640..0663e5fe6 100644 --- a/source/cSimulatorManager.cpp +++ b/source/cSimulatorManager.cpp @@ -12,33 +12,56 @@ cSimulatorManager::cSimulatorManager() } + + + + cSimulatorManager::~cSimulatorManager() { + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + { + delete *itr; + } // for itr - m_Simulators[] } + + + + void cSimulatorManager::Simulate( float a_Dt ) { m_Ticks++; - for( std::vector *>::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) { - if(m_Ticks % (*itr)->second == 0) (*itr)->first->Simulate(a_Dt); } } + + + + void cSimulatorManager::WakeUp(int a_X, int a_Y, int a_Z) { - for( std::vector *>::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) + for (cSimulators::iterator itr = m_Simulators.begin(); itr != m_Simulators.end(); ++itr ) { (*itr)->first->WakeUp(a_X, a_Y, a_Z); } } + + + + void cSimulatorManager::RegisterSimulator(cSimulator *a_Simulator, short a_Rate) { //TODO needs some checking std::pair *Pair = new std::pair(a_Simulator, a_Rate); m_Simulators.push_back(Pair); -} \ No newline at end of file +} + + + + diff --git a/source/cSimulatorManager.h b/source/cSimulatorManager.h index de59b8536..1168e5af8 100644 --- a/source/cSimulatorManager.h +++ b/source/cSimulatorManager.h @@ -1,6 +1,18 @@ + +// cSimulatorManager.h + + + + #pragma once + + + + #include "cSimulator.h" -#include + + + class cSimulatorManager @@ -12,9 +24,16 @@ public: void Simulate( float a_Dt ); void WakeUp(int a_X, int a_Y, int a_Z); - void RegisterSimulator(cSimulator *a_Simulator, short a_Rate); + void RegisterSimulator(cSimulator * a_Simulator, short a_Rate); // Takes ownership of the simulator object! protected: - std::vector *> m_Simulators; - long long m_Ticks; -}; \ No newline at end of file + + typedef std::vector *> cSimulators; + + cSimulators m_Simulators; + long long m_Ticks; +}; + + + + diff --git a/source/cSocket.cpp b/source/cSocket.cpp index 00f10154b..245342fab 100644 --- a/source/cSocket.cpp +++ b/source/cSocket.cpp @@ -267,6 +267,34 @@ int cSocket::Connect(SockAddr_In & a_Address) +int cSocket::Connect(const AString & a_HostNameOrAddr, unsigned short a_Port) +{ + // First try IP Address string to hostent conversion, because it's faster + unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); + hostent * hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); + if (hp == NULL) + { + // It is not an IP Address string, but rather a regular hostname, resolve: + hp = gethostbyname(a_HostNameOrAddr.c_str()); + if (hp == NULL) + { + LOGWARN("cTCPLink: Could not resolve hostname \"%s\"", a_HostNameOrAddr.c_str()); + CloseSocket(); + return false; + } + } + + sockaddr_in server; + server.sin_addr.s_addr = *((unsigned long*)hp->h_addr); + server.sin_family = AF_INET; + server.sin_port = htons( (unsigned short)a_Port ); + return connect(m_Socket, (sockaddr *)&server, sizeof(server)); +} + + + + + int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags) { return recv(m_Socket, a_Buffer, a_Length, a_Flags); diff --git a/source/cSocket.h b/source/cSocket.h index 81749048b..21a7cf3c2 100644 --- a/source/cSocket.h +++ b/source/cSocket.h @@ -1,3 +1,4 @@ + #pragma once @@ -70,6 +71,7 @@ public: int Listen( int a_Backlog ); cSocket Accept(); int Connect(SockAddr_In & a_Address); // Returns 0 on success, !0 on failure + int Connect(const AString & a_HostNameOrAddr, unsigned short a_Port); // Returns 0 on success, !0 on failure int Receive( char* a_Buffer, unsigned int a_Length, unsigned int a_Flags ); int Send (const char * a_Buffer, unsigned int a_Length); int Send (const cPacket * a_Packet); // Sends the packet, doesn't handle partial sends diff --git a/source/cSocketThreads.cpp b/source/cSocketThreads.cpp index 796316878..9d9258499 100644 --- a/source/cSocketThreads.cpp +++ b/source/cSocketThreads.cpp @@ -8,7 +8,7 @@ #include "Globals.h" #include "cSocketThreads.h" #include "cClientHandle.h" -#include "packets/cPacket_RelativeEntityMoveLook.h" +// #include "packets/cPacket_RelativeEntityMoveLook.h" @@ -19,6 +19,7 @@ cSocketThreads::cSocketThreads(void) { + LOG("cSocketThreads startup"); } @@ -60,6 +61,7 @@ bool cSocketThreads::AddClient(cSocket * a_Socket, cCallback * a_Client) if (!Thread->Start()) { // There was an error launching the thread (but it was already logged along with the reason) + LOGERROR("A new cSocketThread failed to start"); delete Thread; return false; } @@ -141,6 +143,17 @@ cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) : +cSocketThreads::cSocketThread::~cSocketThread() +{ + mShouldTerminate = true; + m_ControlSocket1.CloseSocket(); + m_ControlSocket2.CloseSocket(); +} + + + + + void cSocketThreads::cSocketThread::AddClient(cSocket * a_Socket, cCallback * a_Client) { assert(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding @@ -310,6 +323,7 @@ bool cSocketThreads::cSocketThread::Start(void) // Start the thread if (!super::Start()) { + LOGERROR("Cannot start new cSocketThread"); m_ControlSocket2.CloseSocket(); return false; } @@ -383,8 +397,6 @@ void cSocketThreads::cSocketThread::Execute(void) RemoveClosedSockets(); } // while (!mShouldTerminate) - - LOG("cSocketThread %p is terminating", this); } diff --git a/source/cSocketThreads.h b/source/cSocketThreads.h index cbf73a27e..a32808aeb 100644 --- a/source/cSocketThreads.h +++ b/source/cSocketThreads.h @@ -87,6 +87,7 @@ private: public: cSocketThread(cSocketThreads * a_Parent); + ~cSocketThread(); // All these methods assume parent's m_CS is locked bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; } diff --git a/source/cTCPLink.cpp b/source/cTCPLink.cpp index 19f04f4ee..0ade65d55 100644 --- a/source/cTCPLink.cpp +++ b/source/cTCPLink.cpp @@ -3,7 +3,6 @@ #include "cTCPLink.h" #include "cSocket.h" -#include "MCSocket.h" @@ -45,58 +44,24 @@ void cTCPLink::CloseSocket() } } -bool cTCPLink::Connect( const char* a_Address, unsigned int a_Port ) +bool cTCPLink::Connect( const AString & a_Address, unsigned int a_Port ) { if( m_Socket ) { LOGWARN("WARNING: cTCPLink Connect() called while still connected. ALWAYS disconnect before re-connecting!"); } - struct hostent *hp; - unsigned int addr; - struct sockaddr_in server; - -#ifdef _WIN32 - WSADATA wsaData; - int wsaret=WSAStartup(/*0x101*/ MAKEWORD(2, 2),&wsaData); - - if(wsaret!=0) - { - LOGERROR("cTCPLink: WSAStartup returned error"); - return false; - } -#endif - - m_Socket=socket(AF_INET,SOCK_STREAM,0); + m_Socket = cSocket::CreateSocket(); if( !m_Socket.IsValid() ) { - LOGERROR("cTCPLink: Invalid socket"); - m_Socket = 0; + LOGERROR("cTCPLink: Failed to create socket"); return false; } - - addr=inet_addr( a_Address ); - hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET); - if(hp==NULL) - { - //LOGWARN("cTCPLink: gethostbyaddr returned NULL"); - hp = gethostbyname( a_Address ); - if( hp == NULL ) - { - LOGWARN("cTCPLink: Could not resolve %s", a_Address); - CloseSocket(); - return false; - } - } - - server.sin_addr.s_addr=*((unsigned long*)hp->h_addr); - server.sin_family=AF_INET; - server.sin_port=htons( (unsigned short)a_Port ); - if( connect( m_Socket, (struct sockaddr*)&server, sizeof(server) ) ) + if (m_Socket.Connect(a_Address, a_Port) != 0) { - LOGWARN("cTCPLink: No response from server (%i)", errno); - CloseSocket(); + LOGWARN("cTCPLink: Cannot connect to server \"%s\" (%s)", m_Socket.GetLastErrorString().c_str()); + m_Socket.CloseSocket(); return false; } @@ -105,41 +70,60 @@ bool cTCPLink::Connect( const char* a_Address, unsigned int a_Port ) return true; } -int cTCPLink::Send( char* a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) + + + + +int cTCPLink::Send(const char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) { - //LOG("TCPLink::Send()"); - if( !m_Socket ) + (void)a_Flags; + if (!m_Socket.IsValid()) { LOGWARN("cTCPLink: Trying to send data without a valid connection!"); return -1; } - return send( m_Socket, a_Data, a_Size, a_Flags | MSG_NOSIGNAL ); + return m_Socket.Send(a_Data, a_Size); } -int cTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ ) + + + + +int cTCPLink::SendMessage(const char * a_Message, int a_Flags /* = 0 */ ) { - //LOG("TCPLink::SendMessage()"); - if( !m_Socket ) + (void)a_Flags; + if (!m_Socket.IsValid()) { LOGWARN("cTCPLink: Trying to send message without a valid connection!"); return -1; } - return send( m_Socket, a_Message, strlen(a_Message), a_Flags | MSG_NOSIGNAL ); + return m_Socket.Send(a_Message, strlen(a_Message)); } + + + + void cTCPLink::ReceiveThread( void* a_Param) { cTCPLink* self = (cTCPLink*)a_Param; - SOCKET Socket = self->m_Socket; + cSocket Socket = self->m_Socket; int Received = 0; do { char Data[256]; - Received = recv(Socket, Data, 256, 0); - self->ReceivedData( Data, (Received>0?Received:-1) ); + Received = Socket.Receive(Data, sizeof(Data), 0); + self->ReceivedData( Data, ((Received > 0) ? Received : -1) ); } while ( Received > 0 ); LOGINFO("cTCPLink Disconnected (%i)", Received ); - if( Socket == self->m_Socket ) self->m_StopEvent->Set(); + if (Socket == self->m_Socket) + { + self->m_StopEvent->Set(); + } } + + + + diff --git a/source/cTCPLink.h b/source/cTCPLink.h index ac5d86584..ea38f9b7b 100644 --- a/source/cTCPLink.h +++ b/source/cTCPLink.h @@ -8,9 +8,9 @@ public: //tolua_export cTCPLink(); //tolua_export ~cTCPLink(); //tolua_export - bool Connect( const char* a_Address, unsigned int a_Port ); //tolua_export - int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); //tolua_export - int SendMessage( const char* a_Message, int a_Flags = 0 ); //tolua_export + bool Connect (const AString & a_Address, unsigned int a_Port ); //tolua_export + int Send (const char * a_Data, unsigned int a_Size, int a_Flags = 0 ); //tolua_export + int SendMessage(const char * a_Message, int a_Flags = 0 ); //tolua_export void CloseSocket(); //tolua_export protected: //tolua_export virtual void ReceivedData( char a_Data[256], int a_Size ) = 0; //tolua_export diff --git a/source/cWebAdmin.cpp b/source/cWebAdmin.cpp index b33d28e16..bd762736f 100644 --- a/source/cWebAdmin.cpp +++ b/source/cWebAdmin.cpp @@ -27,7 +27,28 @@ -cWebAdmin * WebAdmin = 0; +/// Helper class - appends all player names together in a HTML list +class cPlayerAccum : + public cPlayerListCallback +{ + virtual bool Item(cPlayer * a_Player) override + { + m_Contents.append("
  • "); + m_Contents.append(a_Player->GetName()); + m_Contents.append("
  • "); + return false; + } + +public: + + AString m_Contents; +} ; + + + + + +cWebAdmin * WebAdmin = NULL; @@ -191,12 +212,10 @@ void cWebAdmin::Request_Handler(webserver::http_request* r) Content += ""; Content += "

    Players:

      "; - cWorld* World = cRoot::Get()->GetWorld(); // TODO - Create a list of worlds and players - cWorld::PlayerList PlayerList = World->GetAllPlayers(); - for( cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr ) - { - Content += std::string("
    • ") + std::string( (*itr)->GetName() ) + "
    • "; - } + cPlayerAccum PlayerAccum; + cWorld * World = cRoot::Get()->GetWorld(); // TODO - Create a list of worlds and players + World->ForEachPlayer(&PlayerAccum); + Content.append(PlayerAccum.m_Contents); Content += "

    "; } diff --git a/source/cWorld.cpp b/source/cWorld.cpp index 00dcbd80e..c007070c6 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -40,14 +40,13 @@ #include "cChunkGenerator.h" #include "MersenneTwister.h" #include "cWorldGenerator_Test.h" +#include "cTracer.h" #include "packets/cPacket_TimeUpdate.h" #include "packets/cPacket_NewInvalidState.h" #include "packets/cPacket_Thunderbolt.h" -#include "ptr_cChunk.h" - #include "Vector3d.h" #include @@ -62,6 +61,13 @@ +/// Up to this many m_SpreadQueue elements are handled each world tick +const int MAX_LIGHTING_SPREAD_PER_TICK = 10; + + + + + float cWorld::m_Time = 0.f; char g_BlockLightValue[128]; @@ -70,6 +76,10 @@ bool g_BlockTransparent[128]; bool g_BlockOneHitDig[128]; bool g_BlockPistonBreakable[128]; + + + + #define RECI_RAND_MAX (1.f/RAND_MAX) inline float fRadRand( float a_Radius ) { @@ -77,36 +87,9 @@ inline float fRadRand( float a_Radius ) return ((float)r1.rand() * RECI_RAND_MAX)*a_Radius - a_Radius*0.5f; } -struct sSetBlockData -{ - sSetBlockData( int a_X, int a_Y, int a_Z, char a_BlockID, char a_BlockMeta ) - : x( a_X ) - , y( a_Y ) - , z( a_Z ) - , BlockID( a_BlockID ) - , BlockMeta( a_BlockMeta ) - {} - int x, y, z; - char BlockID, BlockMeta; -}; - -typedef std::list< sSetBlockData > FastSetBlockList; - -struct cWorld::sWorldState -{ - cWorld::EntityList RemoveEntityQueue; - cWorld::EntityList AllEntities; - cWorld::ClientList Clients; - cWorld::PlayerList Players; - cWorld::ChunkList SpreadQueue; - FastSetBlockList FastSetBlockQueue; - cChunkGenerator* pChunkGenerator; - - std::string WorldName; -}; cWorld* cWorld::GetWorld() { @@ -120,15 +103,19 @@ cWorld* cWorld::GetWorld() cWorld::~cWorld() { - LockEntities(); - while( m_pState->AllEntities.begin() != m_pState->AllEntities.end() ) { - cEntity* Entity = *m_pState->AllEntities.begin(); - m_pState->AllEntities.remove( Entity ); - if( !Entity->IsDestroyed() ) Entity->Destroy(); - RemoveEntity( Entity ); + cCSLock Lock(m_CSEntities); + while( m_AllEntities.begin() != m_AllEntities.end() ) + { + cEntity* Entity = *m_AllEntities.begin(); + m_AllEntities.remove( Entity ); + if ( !Entity->IsDestroyed() ) + { + Entity->Destroy(); + } + delete Entity; + } } - UnlockEntities(); delete m_SimulatorManager; delete m_SandSimulator; @@ -136,33 +123,28 @@ cWorld::~cWorld() delete m_LavaSimulator; delete m_FireSimulator; - UnloadUnusedChunks(); - delete m_pState->pChunkGenerator; - delete m_ChunkMap; + m_Generator.Stop(); - delete m_ClientHandleCriticalSection; m_ClientHandleCriticalSection = 0; - delete m_EntitiesCriticalSection; m_EntitiesCriticalSection = 0; - delete m_ChunksCriticalSection; m_ChunksCriticalSection = 0; - delete m_pState; + UnloadUnusedChunks(); + + m_Storage.WaitForFinish(); - delete m_WorldGenerator; + delete m_ChunkMap; } -cWorld::cWorld( const char* a_WorldName ) - : m_pState( new sWorldState ) - , m_SpawnMonsterTime( 0.f ) +cWorld::cWorld( const AString & a_WorldName ) + : m_SpawnMonsterTime( 0.f ) , m_RSList ( 0 ) , m_Weather ( 0 ) - , m_WorldGenerator( 0 ) { - LOG("cWorld::cWorld(%s)", a_WorldName); - m_pState->WorldName = a_WorldName; + LOG("cWorld::cWorld(%s)", a_WorldName.c_str()); + m_WorldName = a_WorldName; - cMakeDir::MakeDir(m_pState->WorldName.c_str()); + cMakeDir::MakeDir(m_WorldName.c_str()); MTRand r1; m_SpawnX = (double)((r1.randInt()%1000)-500); @@ -171,9 +153,10 @@ cWorld::cWorld( const char* a_WorldName ) m_WorldSeed = r1.randInt(); m_GameMode = 0; - std::string WorldGeneratorName; + AString GeneratorName; + AString StorageSchema; - cIniFile IniFile( m_pState->WorldName + "/world.ini"); + cIniFile IniFile( m_WorldName + "/world.ini"); if( IniFile.ReadFile() ) { m_SpawnX = IniFile.GetValueF("SpawnPosition", "X", m_SpawnX ); @@ -181,7 +164,8 @@ cWorld::cWorld( const char* a_WorldName ) m_SpawnZ = IniFile.GetValueF("SpawnPosition", "Z", m_SpawnZ ); m_WorldSeed = IniFile.GetValueI("Seed", "Seed", m_WorldSeed ); m_GameMode = IniFile.GetValueI("GameMode", "GameMode", m_GameMode ); - WorldGeneratorName = IniFile.GetValue("Generator", "GeneratorName", "Default"); + GeneratorName = IniFile.GetValue("Generator", "GeneratorName", "Default"); + StorageSchema = IniFile.GetValue("Storage", "Schema", "Default"); } else { @@ -198,11 +182,8 @@ cWorld::cWorld( const char* a_WorldName ) } LOGINFO("Seed: %i", m_WorldSeed ); - if( WorldGeneratorName.compare("Test") == 0 ) - m_WorldGenerator = new cWorldGenerator_Test(); - else // Default - m_WorldGenerator = new cWorldGenerator(); - + m_Storage.Start(this, StorageSchema); + m_Generator.Start(this, GeneratorName); cIniFile GenSettings("terrain.ini"); if( GenSettings.ReadFile() ) { @@ -238,16 +219,12 @@ cWorld::cWorld( const char* a_WorldName ) } m_ChunkMap = new cChunkMap(this ); - m_pState->pChunkGenerator = new cChunkGenerator( m_ChunkMap ); m_Time = 0; m_WorldTimeFraction = 0.f; m_WorldTime = 0; m_LastSave = 0; m_LastUnload = 0; - m_ClientHandleCriticalSection = new cCriticalSection(); - m_EntitiesCriticalSection = new cCriticalSection(); - m_ChunksCriticalSection = new cCriticalSection(); //Simulators: m_WaterSimulator = new cWaterSimulator( this ); @@ -380,7 +357,12 @@ void cWorld::SetWeather( int Weather ) } } -void cWorld::CastThunderbolt ( int X, int Y, int Z ) { + + + + +void cWorld::CastThunderbolt ( int X, int Y, int Z ) +{ cPacket_Thunderbolt ThunderboltPacket; ThunderboltPacket.m_xLBPos = X; ThunderboltPacket.m_yLBPos = Y; @@ -396,16 +378,18 @@ void cWorld::InitializeSpawn() { int ChunkX = 0, ChunkY = 0, ChunkZ = 0; BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ ); - int ViewDist = cClientHandle::VIEWDISTANCE; - LOG("Loading spawn area"); - for(int x = 0; x < ViewDist; x++) + int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is + 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++) + for (int z = 0; z < ViewDist; z++) { - GetChunk( x + ChunkX-(ViewDist-1)/2, 0, z + ChunkZ-(ViewDist-1)/2 ); + GetChunk( x + ChunkX-(ViewDist - 1) / 2, 0, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader } - LOG("Loaded %0.2f", ((float)x / (float)ViewDist)*100 ); + LOG("Queued %0.2f %% of spawn area", ((float)x / (float)ViewDist) * 100 ); } + + // TODO: Wait for the generator to finish generating these chunks } @@ -415,188 +399,123 @@ void cWorld::InitializeSpawn() void cWorld::Tick(float a_Dt) { int randWeather = 0; - m_Time+=a_Dt/1000.f; + m_Time += a_Dt / 1000.f; CurrentTick++; bool bSendTime = false; - m_WorldTimeFraction+=a_Dt/1000.f; - while( m_WorldTimeFraction > 1.f ) + m_WorldTimeFraction += a_Dt / 1000.f; + while ( m_WorldTimeFraction > 1.f ) { - m_WorldTimeFraction-=1.f; - m_WorldTime+=20; - m_WorldTime %= 24000; // 24000 units in a day + m_WorldTimeFraction -= 1.f; + m_WorldTime += 20; bSendTime = true; } - if( bSendTime ) Broadcast( cPacket_TimeUpdate( (m_WorldTime) ) ); + m_WorldTime %= 24000; // 24000 units in a day + if ( bSendTime ) + { + Broadcast( cPacket_TimeUpdate( (m_WorldTime) ) ); + } - LockEntities(); - for( cWorld::EntityList::iterator itr = GetEntities().begin(); itr != GetEntities().end();) { - if( (*itr)->IsDestroyed() ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end();) { - LOG("Destroy that entity! %i", (*itr)->GetUniqueID() ); - cEntity* RemoveMe = *itr; - itr = m_pState->AllEntities.erase( itr ); - m_pState->RemoveEntityQueue.push_back( RemoveMe ); - continue; + if ((*itr)->IsDestroyed()) + { + LOG("Destroying entity #%i", (*itr)->GetUniqueID()); + cEntity * RemoveMe = *itr; + itr = m_AllEntities.erase( itr ); + m_RemoveEntityQueue.push_back( RemoveMe ); + continue; + } + (*itr)->Tick(a_Dt); + itr++; } - (*itr)->Tick(a_Dt); - itr++; } - UnlockEntities(); - - LockChunks(); - - int TimesSpreaded = 0; - while( !m_pState->SpreadQueue.empty() && TimesSpreaded < 50 ) // Spread a max of 50 times each tick, otherwise server will hang { - ptr_cChunk& Chunk = *m_pState->SpreadQueue.begin(); - //LOG("Spreading: %p", Chunk ); - Chunk->SpreadLight( Chunk->pGetSkyLight() ); - Chunk->SpreadLight( Chunk->pGetLight() ); - m_pState->SpreadQueue.remove( Chunk ); - TimesSpreaded++; - } - if( TimesSpreaded >= 50 ) - { - LOGWARN("Lots of lighting to do! At least %i chunks left!", m_pState->SpreadQueue.size() ); + cCSLock Lock(m_CSLighting); + if (m_SpreadQueue.size() >= 50 ) + { + LOGWARN("cWorld: Lots of lighting to do! Still %i chunks left!", m_SpreadQueue.size() ); + } + int TimesSpreaded = 0; + while ( !m_SpreadQueue.empty() && TimesSpreaded < MAX_LIGHTING_SPREAD_PER_TICK ) // Do not choke the tick thread + { + cChunkPtr & Chunk = *m_SpreadQueue.begin(); + //LOG("Spreading: %p", Chunk ); + Chunk->SpreadLight( Chunk->pGetSkyLight() ); + Chunk->SpreadLight( Chunk->pGetLight() ); + m_SpreadQueue.pop_front(); + TimesSpreaded++; + } } m_ChunkMap->Tick(a_Dt, m_TickRand); GetSimulatorManager()->Simulate(a_Dt); - UnlockChunks(); - TickWeather(a_Dt); // Asynchronously set blocks - FastSetBlockList FastSetBlockQueueCopy = m_pState->FastSetBlockQueue; - m_pState->FastSetBlockQueue.clear(); - for( FastSetBlockList::iterator itr = FastSetBlockQueueCopy.begin(); itr != FastSetBlockQueueCopy.end(); ++itr ) + FastSetBlockList FastSetBlockQueueCopy; + { + cCSLock Lock(m_CSFastSetBlock); + FastSetBlockQueueCopy = m_FastSetBlockQueue; + m_FastSetBlockQueue.clear(); + } + for ( FastSetBlockList::iterator itr = FastSetBlockQueueCopy.begin(); itr != FastSetBlockQueueCopy.end(); ++itr ) { sSetBlockData & SetBlockData = *itr; FastSetBlock( SetBlockData.x, SetBlockData.y, SetBlockData.z, SetBlockData.BlockID, SetBlockData.BlockMeta ); // If unable to set block, it's added to FastSetBlockQueue again } - if( FastSetBlockQueueCopy.size() != m_pState->FastSetBlockQueue.size() ) - LOG(" Before: %i, after %i" , FastSetBlockQueueCopy.size(), m_pState->FastSetBlockQueue.size() ); - if( m_Time - m_LastSave > 60*5 ) // Save each 5 minutes + if( m_Time - m_LastSave > 60 * 5 ) // Save each 5 minutes { SaveAllChunks(); } - if( m_Time - m_LastUnload > 10 ) // Unload each minute + if( m_Time - m_LastUnload > 10 ) // Unload every 10 seconds { UnloadUnusedChunks(); } - while( !m_pState->RemoveEntityQueue.empty() ) + // Delete entities queued for removal: + for (cEntityList::iterator itr = m_RemoveEntityQueue.begin(); itr != m_RemoveEntityQueue.end(); ++itr) { - RemoveEntity( *m_pState->RemoveEntityQueue.begin() ); - } - - if( m_bAnimals && ( m_Time - m_SpawnMonsterTime > m_SpawnMonsterRate ) ) // 10 seconds - { - m_SpawnMonsterTime = m_Time; - if( m_pState->Players.size() > 0 ) - { - cMonster *Monster = 0; - - //srand ( time(NULL) ); // Only seed random ONCE! Is already done in the cWorld constructor - int dayRand = m_TickRand.randInt() % 6; //added mob code - int nightRand = m_TickRand.randInt() % 10; //added mob code - - int RandomPlayerIdx = m_TickRand.randInt() & m_pState->Players.size(); - PlayerList::iterator itr = m_pState->Players.begin(); - for( int i = 1; i < RandomPlayerIdx; i++ ) - itr++; - - cPlayer* Player = *itr; - Vector3d SpawnPos = Player->GetPosition(); - SpawnPos += Vector3d( (double)(m_TickRand.randInt()%64)-32, (double)(m_TickRand.randInt()%64)-32, (double)(m_TickRand.randInt()%64)-32 ); - char Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z ); - - if(m_WorldTime >= 12000 + 1000) { - if (nightRand == 0) //random percent to spawn for night - Monster = new cSpider(); - else if (nightRand == 1) - Monster = new cZombie(); - else if (nightRand == 2) - Monster = new cEnderman(); - else if (nightRand == 3) - Monster = new cCreeper(); - else if (nightRand == 4) - Monster = new cCavespider(); - else if (nightRand == 5) - Monster = new cGhast(); - else if (nightRand == 6) - Monster = new cZombiepigman(); - else if (nightRand == 7) - Monster = new cSlime(); - else if (nightRand == 8) - Monster = new cSilverfish(); - else if (nightRand == 9) - Monster = new cSkeleton(); - //end random percent to spawn for night - } else { - if (dayRand == 0) //random percent to spawn for day - Monster = new cChicken(); - else if (dayRand == 1) - Monster = new cCow(); - else if (dayRand == 2) - Monster = new cPig(); - else if (dayRand == 3) - Monster = new cSheep(); - else if (dayRand == 4) - Monster = new cSquid(); - else if (dayRand == 5) - Monster = new cWolf(); - //end random percent to spawn for day - } - - if( Monster ) - { - Monster->Initialize( this ); - Monster->TeleportTo( SpawnPos.x, (double)(Height)+2, SpawnPos.z ); - Monster->SpawnOn( 0 ); - } - } + delete *itr; } + m_RemoveEntityQueue.clear(); + TickSpawnMobs(a_Dt); std::vector m_RSList_copy(m_RSList); - //copy(m_RSList.begin(), m_RSList.end(), m_RSList_copy.begin()); - m_RSList.erase(m_RSList.begin(),m_RSList.end()); - int tempX; // FIXME - Keep the scope in mind, these variables are not used in this scope at all, move them down into the for loop - int tempY; - int tempZ; - int state; + + m_RSList.clear(); std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) for(cii=m_RSList_copy.begin(); cii!=m_RSList_copy.end();) { - tempX = *cii;cii++; - tempY = *cii;cii++; - tempZ = *cii;cii++; - state = *cii;cii++; + int tempX = *cii;cii++; + int tempY = *cii;cii++; + int tempZ = *cii;cii++; + int state = *cii;cii++; - //printf ("%i, %i, %i, %i\n",tempX,tempY,tempZ,state) ; - if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) { + if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) + { FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta( tempX, tempY, tempZ ) ); cRedstone Redstone(this); Redstone.ChangeRedstone( tempX, tempY, tempZ, true ); - } else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) { + } + else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) + { FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta( tempX, tempY, tempZ ) ); cRedstone Redstone(this); Redstone.ChangeRedstone( tempX, tempY, tempZ, false ); } - } m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); - } @@ -605,7 +524,6 @@ void cWorld::Tick(float a_Dt) void cWorld::TickWeather(float a_Dt) { - ////////////////Weather/////////////////////// if ( GetWeather() == 0 ) // if sunny { if( CurrentTick % 19 == 0 ) //every 20 ticks random weather @@ -655,6 +573,90 @@ void cWorld::TickWeather(float a_Dt) +void cWorld::TickSpawnMobs(float a_Dt) +{ + if (!m_bAnimals || (m_Time - m_SpawnMonsterTime <= m_SpawnMonsterRate)) + { + return; + } + + m_SpawnMonsterTime = m_Time; + Vector3d SpawnPos; + { + cCSLock Lock(m_CSPlayers); + if ( m_Players.size() <= 0) + { + return; + } + int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); + cPlayerList::iterator itr = m_Players.begin(); + for( int i = 1; i < RandomPlayerIdx; i++ ) + { + itr++; + } + SpawnPos = (*itr)->GetPosition(); + } + + cMonster * Monster = NULL; + int dayRand = m_TickRand.randInt() % 6; + int nightRand = m_TickRand.randInt() % 10; + + SpawnPos += Vector3d( (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32 ); + char Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z ); + + if (m_WorldTime >= 12000 + 1000) + { + if (nightRand == 0) //random percent to spawn for night + Monster = new cSpider(); + else if (nightRand == 1) + Monster = new cZombie(); + else if (nightRand == 2) + Monster = new cEnderman(); + else if (nightRand == 3) + Monster = new cCreeper(); + else if (nightRand == 4) + Monster = new cCavespider(); + else if (nightRand == 5) + Monster = new cGhast(); + else if (nightRand == 6) + Monster = new cZombiepigman(); + else if (nightRand == 7) + Monster = new cSlime(); + else if (nightRand == 8) + Monster = new cSilverfish(); + else if (nightRand == 9) + Monster = new cSkeleton(); + //end random percent to spawn for night + } + else + { + if (dayRand == 0) //random percent to spawn for day + Monster = new cChicken(); + else if (dayRand == 1) + Monster = new cCow(); + else if (dayRand == 2) + Monster = new cPig(); + else if (dayRand == 3) + Monster = new cSheep(); + else if (dayRand == 4) + Monster = new cSquid(); + else if (dayRand == 5) + Monster = new cWolf(); + //end random percent to spawn for day + } + + if( Monster ) + { + Monster->Initialize( this ); + Monster->TeleportTo( SpawnPos.x, (double)(Height) + 2, SpawnPos.z ); + Monster->SpawnOn(0); + } +} + + + + + void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) { // new tree code, looks much better @@ -714,234 +716,234 @@ void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) // end new tree code } -void cWorld::UnloadUnusedChunks() -{ - m_LastUnload = m_Time; - - LockChunks(); - LOGINFO("Unloading unused chunks"); - m_ChunkMap->UnloadUnusedChunks(); - UnlockChunks(); -} -cChunk* cWorld::GetChunkReliable( int a_X, int a_Y, int a_Z ) // TODO - FIXME - WARNING - This can cause a duplicate chunk to be generated!! -{ - cChunk* Chunk = GetChunkUnreliable( a_X, a_Y, a_Z ); - if( Chunk ) - { - return Chunk; - } - // Found nothing, create a chunk - Chunk = new cChunk( a_X, a_Y, a_Z, this ); - if(Chunk) - { - LOGWARN("Created new chunk! %i %i", a_X, a_Z); - LockChunks(); - m_ChunkMap->AddChunk( Chunk ); - UnlockChunks(); - Chunk->Initialize(); - return Chunk; - } - // This should never happen since it's reliable, but yeah - return 0; -} -cChunk* cWorld::GetChunk( int a_X, int a_Y, int a_Z ) +void cWorld::UnloadUnusedChunks() { - // Get chunk from memory - cChunk* Chunk = GetChunkUnreliable( a_X, a_Y, a_Z ); - if( Chunk ) return Chunk; - -#if 1 // Current thread chunk generation + m_LastUnload = m_Time; + m_ChunkMap->UnloadUnusedChunks(); +} - // Found nothing, create a chunk - Chunk = new cChunk( a_X, a_Y, a_Z, this ); - if(Chunk) - { - LOGWARN("Created new chunk! %i %i", a_X, a_Z); - LockChunks(); - m_ChunkMap->AddChunk( Chunk ); - UnlockChunks(); - Chunk->Initialize(); - return Chunk; - } - return 0; -#else // Async thread generation - // Generate new chunk asynchronously - m_pState->pChunkGenerator->GenerateChunk( a_X, a_Z ); - // Could not find chunk, it's being generated, so return 0 - return 0; -#endif -} -ptr_cChunk cWorld::GetChunkUnreliable( int a_X, int a_Y, int a_Z ) -{ - LockChunks(); - ptr_cChunk Chunk( m_ChunkMap->GetChunk( a_X, a_Y, a_Z ) ); - UnlockChunks(); - return Chunk; -} -cChunk* cWorld::GetChunkOfBlock( int a_X, int a_Y, int a_Z ) +cChunkPtr cWorld::GetChunkOfBlock( int a_X, int a_Y, int a_Z ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); return GetChunk( ChunkX, ChunkY, ChunkZ ); } + + + + void cWorld::SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ) { int ChunkX, ChunkY, ChunkZ, X = a_X, Y = a_Y, Z = a_Z; AbsoluteToRelative( X, Y, Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) { Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); this->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); } + // The chunk is not yet initialized, so it's probably far away from all players, no need to store this Meta change } + + + + void cWorld::FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ) { int ChunkX, ChunkY, ChunkZ, X = a_X, Y = a_Y, Z = a_Z; AbsoluteToRelative( X, Y, Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunkNoGen( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) { Chunk->FastSetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); return; } // Unable to set block right now, try again later - m_pState->FastSetBlockQueue.push_back( sSetBlockData( a_X, a_Y, a_Z, a_BlockType, a_BlockMeta ) ); + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.push_back( sSetBlockData( a_X, a_Y, a_Z, a_BlockType, a_BlockMeta ) ); } -char cWorld::GetBlock( int a_X, int a_Y, int a_Z ) + + + + +char cWorld::GetBlock(int a_X, int a_Y, int a_Z) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetBlock(a_X, a_Y, a_Z); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) + { + return Chunk->GetBlock(a_X, a_Y, a_Z); + } return 0; } + + + + char cWorld::GetBlockMeta( int a_X, int a_Y, int a_Z ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) + { + return Chunk->GetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z ); + } return 0; } + + + + void cWorld::SetBlockMeta( int a_X, int a_Y, int a_Z, char a_MetaData ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) { Chunk->SetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z, a_MetaData ); - Chunk->SendBlockTo( a_X, a_Y, a_Z, 0 ); + Chunk->SendBlockTo( a_X, a_Y, a_Z, NULL ); } + // The chunk is not yet initialized, so it's probably far away from all players, no need to store this Meta change } + + + + bool cWorld::DigBlock( int a_X, int a_Y, int a_Z, cItem & a_PickupItem ) { int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ ); - cChunk* DestChunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if(DestChunk) + cChunkPtr DestChunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (DestChunk->IsValid()) { DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); - if( !a_PickupItem.IsEmpty() ) + if ( !a_PickupItem.IsEmpty() ) { - cPickup* Pickup = new cPickup( a_X*32 + 16 + (int)fRadRand(16.f), a_Y*32 + 16 + (int)fRadRand(16.f), a_Z*32 + 16 + (int)fRadRand(16.f), a_PickupItem ); + cPickup * Pickup = new cPickup( a_X * 32 + 16 + (int)fRadRand(16.f), a_Y * 32 + 16 + (int)fRadRand(16.f), a_Z * 32 + 16 + (int)fRadRand(16.f), a_PickupItem ); Pickup->Initialize( this ); } } - return true; } -void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer* a_Player ) + + + + +void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer * a_Player ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - Chunk->SendBlockTo( a_X, a_Y, a_Z, a_Player->GetClientHandle() ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) + { + Chunk->SendBlockTo( a_X, a_Y, a_Z, a_Player->GetClientHandle() ); + } } -cBlockEntity* cWorld::GetBlockEntity( int a_X, int a_Y, int a_Z ) + + + + +// TODO: This interface is dangerous! +cBlockEntity * cWorld::GetBlockEntity( int a_X, int a_Y, int a_Z ) { int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( !Chunk ) return 0; - - return Chunk->GetBlockEntity( a_X, a_Y, a_Z ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) + { + // TODO: return Chunk->GetBlockEntity( a_X, a_Y, a_Z ); + } + return NULL; } + + + + char cWorld::GetHeight( int a_X, int a_Z ) { int PosX = a_X, PosY = 0, PosZ = a_Z, ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetHeight( PosX, PosZ ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid()) + { + return Chunk->GetHeight( PosX, PosZ ); + } return 0; } -const double & cWorld::GetSpawnY() + + + + +const double & cWorld::GetSpawnY(void) { m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height return m_SpawnY; } -void cWorld::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude /* = 0 */ ) + + + +void cWorld::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude) { - for( PlayerList::iterator itr = m_pState->Players.begin(); itr != m_pState->Players.end(); ++itr) + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { - if( (*itr)->GetClientHandle() == a_Exclude || !(*itr)->GetClientHandle()->IsLoggedIn() ) continue; + if (((*itr)->GetClientHandle() == a_Exclude) || !(*itr)->GetClientHandle()->IsLoggedIn() ) + { + continue; + } (*itr)->GetClientHandle()->Send( a_Packet ); } } -std::string cWorld::GetDescription() -{ - return this->m_Description; -} -unsigned int cWorld::GetMaxPlayers() -{ - return this->m_MaxPlayers; -} + + void cWorld::SetMaxPlayers(int iMax) { - this->m_MaxPlayers = MAX_PLAYERS; + m_MaxPlayers = MAX_PLAYERS; if (iMax > 0 && iMax < MAX_PLAYERS) { - this->m_MaxPlayers = iMax; + m_MaxPlayers = iMax; } } @@ -951,8 +953,9 @@ void cWorld::SetMaxPlayers(int iMax) void cWorld::AddPlayer( cPlayer* a_Player ) { - m_pState->Players.remove( a_Player ); - m_pState->Players.push_back( a_Player ); + cCSLock Lock(m_CSPlayers); + m_Players.remove( a_Player ); // Make sure the player is registered only once + m_Players.push_back( a_Player ); } @@ -961,7 +964,26 @@ void cWorld::AddPlayer( cPlayer* a_Player ) void cWorld::RemovePlayer( cPlayer* a_Player ) { - m_pState->Players.remove( a_Player ); + cCSLock Lock(m_CSPlayers); + m_Players.remove( a_Player ); +} + + + + + +bool cWorld::ForEachPlayer(cPlayerListCallback * a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (a_Callback->Item(*itr)) + { + return false; + } + } // for itr - m_Players[] + return true; } @@ -970,11 +992,12 @@ void cWorld::RemovePlayer( cPlayer* a_Player ) void cWorld::GetAllPlayers( lua_State* L ) { - lua_createtable(L, m_pState->Players.size(), 0); + lua_createtable(L, m_Players.size(), 0); int newTable = lua_gettop(L); int index = 1; - PlayerList::const_iterator iter = m_pState->Players.begin(); - while(iter != m_pState->Players.end()) { + cPlayerList::const_iterator iter = m_Players.begin(); + while(iter != m_Players.end()) + { tolua_pushusertype( L, (*iter), "cPlayer" ); lua_rawseti(L, newTable, index); ++iter; @@ -986,6 +1009,7 @@ void cWorld::GetAllPlayers( lua_State* L ) +// TODO: This interface is dangerous! cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) { cPlayer* BestMatch = 0; @@ -994,12 +1018,13 @@ cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) bool bPerfectMatch = false; unsigned int NameLength = strlen( a_PlayerName ); - for( PlayerList::iterator itr = m_pState->Players.begin(); itr != m_pState->Players.end(); itr++ ) + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); itr++ ) { std::string Name = (*itr)->GetName(); if( NameLength > Name.length() ) continue; // Definitely not a match - for(unsigned int i = 0; i < NameLength; i++) + for (unsigned int i = 0; i < NameLength; i++) { char c1 = (char)toupper( a_PlayerName[i] ); char c2 = (char)toupper( Name[i] ); @@ -1029,65 +1054,91 @@ cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) break; } } - if( NumMatches == 1 ) + if ( NumMatches == 1 ) + { return BestMatch; + } - // More than one matches, so it's undefined. Return 0 instead - return 0; + // More than one matches, so it's undefined. Return NULL instead + return NULL; } -cEntity* cWorld::GetEntity( int a_UniqueID ) +cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) { - for( EntityList::iterator itr = m_pState->AllEntities.begin(); itr != m_pState->AllEntities.end(); ++itr ) + cTracer LineOfSight(this); + + float ClosestDistance = a_SightLimit; + cPlayer* ClosestPlayer = NULL; + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { - if( (*itr)->GetUniqueID() == a_UniqueID ) - return *itr; + Vector3f Pos = (*itr)->GetPosition(); + float Distance = (Pos - a_Pos).Length(); + + if (Distance <= a_SightLimit) + { + if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + if (Distance < ClosestDistance) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } + } + } } - return 0; + return ClosestPlayer; } -// void cWorld::RemoveClient( cClientHandle* a_Client ) -// { -// m_pState->m_Clients.remove( a_Client ); -// if( a_Client ) -// { -// delete a_Client; -// a_Client = 0; -// } -// } +void cWorld::SendPlayerList(cPlayer * a_DestPlayer) +{ + // Sends the playerlist to a_DestPlayer + cCSLock Lock(m_CSPlayers); + for ( cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (((*itr)->GetClientHandle() != NULL) && !((*itr)->GetClientHandle()->IsDestroyed())) + { + cPacket_PlayerListItem PlayerListItem((*itr)->GetColor() + (*itr)->GetName(), true, (*itr)->GetClientHandle()->GetPing()); + a_DestPlayer->GetClientHandle()->Send( PlayerListItem ); + } + } +} -void cWorld::RemoveEntity( cEntity* a_Entity ) +// TODO: This interface is dangerous! +cEntity * cWorld::GetEntity( int a_UniqueID ) { - m_pState->RemoveEntityQueue.remove( a_Entity ); - if( a_Entity ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end(); ++itr ) { - delete a_Entity; - a_Entity = 0; + if( (*itr)->GetUniqueID() == a_UniqueID ) + { + return *itr; + } } + return NULL; } -bool cWorld::RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ ) +void cWorld::RemoveEntityFromChunk(cEntity * a_Entity) { - LockChunks(); - bool retVal = m_ChunkMap->RemoveEntityFromChunk( a_Entity, a_CalledFrom ); - UnlockChunks(); - return retVal; + cChunkPtr Chunk = GetChunkOfBlock((int)(a_Entity->GetPosX()), (int)(a_Entity->GetPosY()), (int)(a_Entity->GetPosZ())); + Chunk->RemoveEntity(a_Entity); } @@ -1098,9 +1149,7 @@ void cWorld::SaveAllChunks() { LOG("Saving all chunks..."); m_LastSave = m_Time; - LockChunks(); m_ChunkMap->SaveAllChunks(); - UnlockChunks(); LOG("Done saving chunks"); } @@ -1108,79 +1157,21 @@ void cWorld::SaveAllChunks() -void cWorld::LockClientHandle() -{ - m_ClientHandleCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockClientHandle() -{ - m_ClientHandleCriticalSection->Unlock(); -} - - - - - -void cWorld::LockEntities() -{ - m_EntitiesCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockEntities() -{ - m_EntitiesCriticalSection->Unlock(); -} - - - - - -void cWorld::LockChunks() -{ - m_ChunksCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockChunks() +void cWorld::ReSpreadLighting( const cChunkPtr & a_Chunk ) { - m_ChunksCriticalSection->Unlock(); + cCSLock Lock(m_CSLighting); + m_SpreadQueue.remove( a_Chunk ); + m_SpreadQueue.push_back( a_Chunk ); } -void cWorld::ReSpreadLighting( const ptr_cChunk& a_Chunk ) +void cWorld::RemoveSpread( const cChunkPtr & a_Chunk ) { - LockChunks(); - m_pState->SpreadQueue.remove( a_Chunk ); - m_pState->SpreadQueue.push_back( a_Chunk ); - //#define STRINGIZE(x) #x - //a_Chunk->AddReference( __FILE__ ": " STRINGIZE(__LINE__) ); - UnlockChunks(); -} - - - - - -void cWorld::RemoveSpread( const ptr_cChunk& a_Chunk ) -{ - LockChunks(); - m_pState->SpreadQueue.remove( a_Chunk ); - UnlockChunks(); + cCSLock Lock(m_CSLighting); + m_SpreadQueue.remove( a_Chunk ); } @@ -1192,38 +1183,21 @@ void cWorld::RemoveSpread( const ptr_cChunk& a_Chunk ) /************************************************************************/ // void cWorld::AddClient( cClientHandle* a_Client ) // { -// m_pState->m_Clients.push_back( a_Client ); +// m_m_Clients.push_back( a_Client ); // } // cWorld::ClientList & cWorld::GetClients() // { -// return m_pState->m_Clients; +// return m_m_Clients; // } -cWorld::EntityList & cWorld::GetEntities() -{ - return m_pState->AllEntities; -} - - - - - void cWorld::AddEntity( cEntity* a_Entity ) { - m_pState->AllEntities.push_back( a_Entity ); -} - - - - - -cWorld::PlayerList & cWorld::GetAllPlayers() -{ - return m_pState->Players; + cCSLock Lock(m_CSEntities); + m_AllEntities.push_back( a_Entity ); } @@ -1232,38 +1206,17 @@ cWorld::PlayerList & cWorld::GetAllPlayers() unsigned int cWorld::GetNumPlayers() { - return m_pState->Players.size(); -} - - - - - -void cWorld::AddToRemoveEntityQueue( cEntity & a_Entity ) -{ - m_pState->AllEntities.remove( &a_Entity); - m_pState->RemoveEntityQueue.push_back( &a_Entity ); -} - - - - - -const char* cWorld::GetName() -{ - return m_pState->WorldName.c_str(); + cCSLock Lock(m_CSPlayers); + return m_Players.size(); } -int cWorld::GetNumChunks(void) +int cWorld::GetNumChunks(void) const { - LockChunks(); - int NumChunks = m_ChunkMap->GetNumChunks(); - UnlockChunks(); - return NumChunks; + return m_ChunkMap->GetNumChunks(); } diff --git a/source/cWorld.h b/source/cWorld.h index e81d3e1df..b895a8a9b 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -2,16 +2,18 @@ #pragma once #ifndef _WIN32 -#include "BlockID.h" + #include "BlockID.h" #else -enum ENUM_ITEM_ID; + enum ENUM_ITEM_ID; #endif #define MAX_PLAYERS 65535 #include "cSimulatorManager.h" -#include "ptr_cChunk.h" #include "MersenneTwister.h" +#include "cChunkMap.h" +#include "WorldStorage.h" +#include "cChunkGenerator.h" @@ -23,25 +25,23 @@ class cFireSimulator; class cWaterSimulator; class cLavaSimulator; class cSandSimulator; -class cChunkMap; class cItem; class cPlayer; class cClientHandle; -class cChunk; class cEntity; class cBlockEntity; -class cWorldGenerator; +class cWorldGenerator; // The generator that actually generates the chunks for a single world +class cChunkGenerator; // The thread responsible for generating chunks +typedef std::list< cPlayer * > cPlayerList; +typedef cListCallback cPlayerListCallback; + + + class cWorld //tolua_export { //tolua_export public: - typedef std::list< cClientHandle* > ClientList; - typedef std::list< cEntity* > EntityList; - typedef std::list< ptr_cChunk > ChunkList; - typedef std::list< cPlayer* > PlayerList; - std::vector m_RSList; - static cWorld* GetWorld(); //tolua_export @@ -50,16 +50,15 @@ public: { return m_Time; } - long long GetWorldTime() { return m_WorldTime; } //tolua_export + long long GetWorldTime(void) const { return m_WorldTime; } //tolua_export - int GetGameMode() { return m_GameMode; } //return gamemode for world + int GetGameMode(void) const { return m_GameMode; } //return gamemode for world void SetWorldTime(long long a_WorldTime) { m_WorldTime = a_WorldTime; } //tolua_export - cChunk* GetChunk( int a_X, int a_Y, int a_Z ); - cChunk* GetChunkReliable( int a_X, int a_Y, int a_Z ); - ptr_cChunk GetChunkUnreliable( int a_X, int a_Y, int a_Z ); - cChunk* GetChunkOfBlock( int a_X, int a_Y, int a_Z ); + cChunkPtr GetChunk ( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) {return m_ChunkMap->GetChunk (a_ChunkX, a_ChunkY, a_ChunkZ); } + cChunkPtr GetChunkNoGen ( int a_ChunkX, int a_ChunkY, int a_ChunkZ ) {return m_ChunkMap->GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ); } + cChunkPtr GetChunkOfBlock( int a_X, int a_Y, int a_Z ); char GetHeight( int a_X, int a_Z ); //tolua_export //void AddClient( cClientHandle* a_Client ); @@ -69,25 +68,38 @@ public: void Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude = 0 ); // MOTD - std::string GetDescription(); + const AString & GetDescription(void) const {return m_Description; } // Max Players - unsigned int GetMaxPlayers(); + unsigned int GetMaxPlayers(void) const {return m_MaxPlayers; } void SetMaxPlayers(int iMax); void AddPlayer( cPlayer* a_Player ); void RemovePlayer( cPlayer* a_Player ); - PlayerList & GetAllPlayers(); + bool ForEachPlayer(cPlayerListCallback * a_Callback); // Calls the callback for each player in the list + + // TODO: This interface is dangerous! + cPlayerList & GetAllPlayers() {return m_Players; } + typedef struct lua_State lua_State; void GetAllPlayers( lua_State* L ); // >> EXPORTED IN MANUALBINDINGS << unsigned int GetNumPlayers(); //tolua_export - cPlayer* GetPlayer( const char* a_PlayerName ); //tolua_export + + // TODO: This interface is dangerous + cPlayer * GetPlayer( const char * a_PlayerName ); //tolua_export + + cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit); + + void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player void AddEntity( cEntity* a_Entity ); - bool RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom = 0 ); - EntityList & GetEntities(); + void RemoveEntityFromChunk( cEntity * a_Entity); + + // TODO: This interface is dangerous! + cEntityList & GetEntities(void) {return m_AllEntities; } - cEntity* GetEntity( int a_UniqueID ); //tolua_export + // TODO: This interface is dangerous! + cEntity * GetEntity( int a_UniqueID ); //tolua_export void SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); //tolua_export void FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ); //tolua_export @@ -105,13 +117,13 @@ public: inline cWaterSimulator *GetWaterSimulator() { return m_WaterSimulator; } inline cLavaSimulator *GetLavaSimulator() { return m_LavaSimulator; } - - cBlockEntity* GetBlockEntity( int a_X, int a_Y, int a_Z ); //tolua_export + // TODO: This interface is dangerous! + cBlockEntity * GetBlockEntity( int a_X, int a_Y, int a_Z ); //tolua_export void GrowTree( int a_X, int a_Y, int a_Z ); //tolua_export - unsigned int GetWorldSeed() { return m_WorldSeed; } //tolua_export - const char* GetName(); //tolua_export + unsigned int GetWorldSeed(void) const { return m_WorldSeed; } //tolua_export + const AString & GetName(void) const {return m_WorldName; } //tolua_export inline static void AbsoluteToRelative( int & a_X, int & a_Y, int & a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) { @@ -126,6 +138,7 @@ public: //a_Y = a_Y - a_ChunkY*16; a_Z = a_Z - a_ChunkZ*16; } + inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) { (void)a_Y; // not unused anymore @@ -137,21 +150,12 @@ public: } void SaveAllChunks(); //tolua_export - int GetNumChunks(); //tolua_export + int GetNumChunks() const; //tolua_export void Tick(float a_Dt); - void LockClientHandle(); - void UnlockClientHandle(); - - void LockEntities(); - void UnlockEntities(); - - void LockChunks(); - void UnlockChunks(); - - void ReSpreadLighting( const ptr_cChunk& a_Chunk ); - void RemoveSpread( const ptr_cChunk& a_Chunk ); + void ReSpreadLighting(const cChunkPtr & a_Chunk ); + void RemoveSpread(const cChunkPtr & a_Chunk ); void InitializeSpawn(); @@ -159,18 +163,28 @@ public: void SetWeather ( int ); //tolua_export int GetWeather() { return m_Weather; }; //tolua_export - cWorldGenerator* GetWorldGenerator() { return m_WorldGenerator; } + cChunkGenerator & GetGenerator(void) { return m_Generator; } + cWorldStorage & GetStorage (void) { return m_Storage; } private: friend class cRoot; - cWorld( const char* a_WorldName ); - ~cWorld(); + struct sSetBlockData + { + sSetBlockData( int a_X, int a_Y, int a_Z, char a_BlockID, char a_BlockMeta ) + : x( a_X ) + , y( a_Y ) + , z( a_Z ) + , BlockID( a_BlockID ) + , BlockMeta( a_BlockMeta ) + {} + int x, y, z; + char BlockID, BlockMeta; + }; + + typedef std::list< sSetBlockData > FastSetBlockList; - struct sWorldState; - sWorldState* m_pState; - // 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; @@ -186,36 +200,59 @@ private: int m_GameMode; float m_WorldTimeFraction; // When this > 1.f m_WorldTime is incremented by 20 - cSimulatorManager *m_SimulatorManager; - cSandSimulator *m_SandSimulator; - cWaterSimulator* m_WaterSimulator; - cLavaSimulator* m_LavaSimulator; - cFireSimulator* m_FireSimulator; + // The cRedstone class simulates redstone and needs access to m_RSList + friend class cRedstone; + std::vector m_RSList; + + cSimulatorManager * m_SimulatorManager; + cSandSimulator * m_SandSimulator; + cWaterSimulator * m_WaterSimulator; + cLavaSimulator * m_LavaSimulator; + cFireSimulator * m_FireSimulator; - cCriticalSection* m_ClientHandleCriticalSection; - cCriticalSection* m_EntitiesCriticalSection; - cCriticalSection* m_ChunksCriticalSection; + cCriticalSection m_CSClients; + cCriticalSection m_CSEntities; + cCriticalSection m_CSPlayers; - cWorldGenerator* m_WorldGenerator; + cWorldStorage m_Storage; + + AString m_Description; - std::string m_Description; unsigned int m_MaxPlayers; - cChunkMap* m_ChunkMap; + cChunkMap * m_ChunkMap; bool m_bAnimals; float m_SpawnMonsterTime; float m_SpawnMonsterRate; unsigned int m_WorldSeed; + int m_Weather; - void TickWeather(float a_Dt); // Handles weather each tick + cEntityList m_RemoveEntityQueue; + cEntityList m_AllEntities; + cClientHandleList m_Clients; + cPlayerList m_Players; - void AddToRemoveEntityQueue( cEntity & a_Entity ); - void RemoveEntity( cEntity* a_Entity ); - void UnloadUnusedChunks(); + cCriticalSection m_CSLighting; + cChunkPtrList m_SpreadQueue; + cCriticalSection m_CSFastSetBlock; + FastSetBlockList m_FastSetBlockQueue; + + cChunkGenerator m_Generator; + + AString m_WorldName; + + cWorld(const AString & a_WorldName); + ~cWorld(); + + void TickWeather(float a_Dt); // Handles weather each tick + void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick + + void RemoveEntity( cEntity * a_Entity ); + void UnloadUnusedChunks(); }; //tolua_export diff --git a/source/cWorldGenerator.cpp b/source/cWorldGenerator.cpp index c54be8899..5d087014b 100644 --- a/source/cWorldGenerator.cpp +++ b/source/cWorldGenerator.cpp @@ -6,7 +6,6 @@ #include "cWorld.h" #include "cChunk.h" #include "cGenSettings.h" -#include "MersenneTwister.h" #include "BlockID.h" #include "Vector3i.h" @@ -19,17 +18,33 @@ cWorldGenerator::cWorldGenerator() { } + + + + cWorldGenerator::~cWorldGenerator() { } -void cWorldGenerator::GenerateChunk( cChunk* a_Chunk ) + + + +void cWorldGenerator::GenerateChunk( cChunkPtr a_Chunk ) { + assert(!a_Chunk->IsValid()); + + memset(a_Chunk->pGetBlockData(), 0, cChunk::c_BlockDataSize); GenerateTerrain( a_Chunk ); GenerateFoliage( a_Chunk ); + a_Chunk->CalculateHeightmap(); + a_Chunk->CalculateLighting(); } + + + + static float GetNoise( float x, float y, cNoise & a_Noise ) { float oct1 = a_Noise.CubicNoise2D( x*cGenSettings::HeightFreq1, y*cGenSettings::HeightFreq1 )*cGenSettings::HeightAmp1; @@ -44,6 +59,10 @@ static float GetNoise( float x, float y, cNoise & a_Noise ) return (oct1 + oct2 + oct3) * flatness + height; } + + + + #define PI_2 (1.57079633f) static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) { @@ -56,6 +75,10 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise ) return oct1; } + + + + static float GetOreNoise( float x, float y, float z, cNoise & a_Noise ) { float oct1 = a_Noise.CubicNoise3D( x*0.1f, y*0.1f, z*0.1f ); @@ -69,7 +92,11 @@ static float GetOreNoise( float x, float y, float z, cNoise & a_Noise ) return oct1; } -void cWorldGenerator::GenerateTerrain( cChunk* a_Chunk ) + + + + +void cWorldGenerator::GenerateTerrain( cChunkPtr a_Chunk ) { Vector3i ChunkPos( a_Chunk->GetPosX(), a_Chunk->GetPosY(), a_Chunk->GetPosZ() ); char* BlockType = a_Chunk->pGetType(); @@ -154,7 +181,9 @@ void cWorldGenerator::GenerateTerrain( cChunk* a_Chunk ) -void cWorldGenerator::GenerateFoliage( cChunk* a_Chunk ) + + +void cWorldGenerator::GenerateFoliage( cChunkPtr a_Chunk ) { const ENUM_BLOCK_ID GrassID = E_BLOCK_GRASS; const ENUM_BLOCK_ID DirtID = E_BLOCK_DIRT; @@ -213,7 +242,6 @@ void cWorldGenerator::GenerateFoliage( cChunk* a_Chunk ) BlockType[ index ] = (char)GrassID; } - MTRand r1; // Plant sum trees { int xx = x + PosX*16; @@ -253,3 +281,7 @@ void cWorldGenerator::GenerateFoliage( cChunk* a_Chunk ) } } } + + + + diff --git a/source/cWorldGenerator.h b/source/cWorldGenerator.h index 01a086612..933105d24 100644 --- a/source/cWorldGenerator.h +++ b/source/cWorldGenerator.h @@ -1,17 +1,34 @@ + #pragma once -class cChunk; + + + + +#include "cChunk.h" +#include "MersenneTwister.h" + + + + + class cWorldGenerator { public: cWorldGenerator(); ~cWorldGenerator(); - virtual void GenerateChunk( cChunk* a_Chunk ); + virtual void GenerateChunk( cChunkPtr a_Chunk ); protected: - virtual void GenerateTerrain( cChunk* a_Chunk ); - virtual void GenerateFoliage( cChunk* a_Chunk ); + MTRand r1; + + virtual void GenerateTerrain( cChunkPtr a_Chunk ); + virtual void GenerateFoliage( cChunkPtr a_Chunk ); + +}; + + + -}; \ No newline at end of file diff --git a/source/cWorldGenerator_Test.cpp b/source/cWorldGenerator_Test.cpp index a484e43ec..08e649fb9 100644 --- a/source/cWorldGenerator_Test.cpp +++ b/source/cWorldGenerator_Test.cpp @@ -9,7 +9,7 @@ -void cWorldGenerator_Test::GenerateTerrain( cChunk* a_Chunk ) +void cWorldGenerator_Test::GenerateTerrain( cChunkPtr a_Chunk ) { char* BlockType = a_Chunk->pGetType(); @@ -26,7 +26,7 @@ void cWorldGenerator_Test::GenerateTerrain( cChunk* a_Chunk ) } } -void cWorldGenerator_Test::GenerateFoliage( cChunk* a_Chunk ) +void cWorldGenerator_Test::GenerateFoliage( cChunkPtr a_Chunk ) { (void)a_Chunk; } \ No newline at end of file diff --git a/source/cWorldGenerator_Test.h b/source/cWorldGenerator_Test.h index cb08cf97c..0dd9d5b1c 100644 --- a/source/cWorldGenerator_Test.h +++ b/source/cWorldGenerator_Test.h @@ -1,10 +1,20 @@ + #pragma once #include "cWorldGenerator.h" -class cWorldGenerator_Test : public cWorldGenerator + + + + +class cWorldGenerator_Test : + public cWorldGenerator { protected: - virtual void GenerateTerrain( cChunk* a_Chunk ); - virtual void GenerateFoliage( cChunk* a_Chunk ); -}; \ No newline at end of file + virtual void GenerateTerrain( cChunkPtr a_Chunk ) override; + virtual void GenerateFoliage( cChunkPtr a_Chunk ) override; +}; + + + + diff --git a/source/main.cpp b/source/main.cpp index 37b3be5ac..835d7ffc7 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -17,6 +17,22 @@ +/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window +#define ENABLE_LEAK_FINDER + + + + + +#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + #define XML_LEAK_FINDER + #include "LeakFinder.h" +#endif + + + + + void ShowCrashReport(int) { @@ -27,17 +43,31 @@ void ShowCrashReport(int) exit(-1); } + + + + int main( int argc, char **argv ) { (void)argc; (void)argv; -#ifdef _DEBUG + + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + InitLeakFinder(); + #endif + + #ifdef _DEBUG _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); -#endif - -#ifndef _DEBUG + + // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output) + // Only useful when the leak is in the same sequence all the time + // _CrtSetBreakAlloc(85950); + + #endif + + #ifndef _DEBUG std::signal(SIGSEGV, ShowCrashReport); -#endif + #endif try { @@ -53,12 +83,17 @@ int main( int argc, char **argv ) LOGERROR("Unknown exception!"); } -#if USE_SQUIRREL + #if USE_SQUIRREL SquirrelVM::Shutdown(); -#endif + #endif -#ifdef _DEBUG - _CrtDumpMemoryLeaks(); -#endif + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + DeinitLeakFinder(); + #endif + return 0; } + + + + diff --git a/source/packets/cPacket_MapChunk.cpp b/source/packets/cPacket_MapChunk.cpp index a9448f050..522842d32 100644 --- a/source/packets/cPacket_MapChunk.cpp +++ b/source/packets/cPacket_MapChunk.cpp @@ -19,8 +19,9 @@ cPacket_MapChunk::~cPacket_MapChunk() -cPacket_MapChunk::cPacket_MapChunk(cChunk* a_Chunk) +cPacket_MapChunk::cPacket_MapChunk(cChunk * a_Chunk) { + assert(a_Chunk->IsValid()); m_PacketID = E_MAP_CHUNK; m_PosX = a_Chunk->GetPosX() * 16; // It has to be block coordinates diff --git a/source/packets/cPacket_MapChunk.h b/source/packets/cPacket_MapChunk.h index b4994f12b..2cefd8b59 100644 --- a/source/packets/cPacket_MapChunk.h +++ b/source/packets/cPacket_MapChunk.h @@ -8,7 +8,13 @@ class cChunk; -class cPacket_MapChunk : public cPacket + + + + + +class cPacket_MapChunk : + public cPacket { public: cPacket_MapChunk() @@ -21,7 +27,7 @@ public: , m_CompressedSize( 0 ) , m_CompressedData( 0 ) { m_PacketID = E_MAP_CHUNK; m_CompressedData = 0; } - cPacket_MapChunk(cChunk* a_Chunk); + cPacket_MapChunk( const cPacket_MapChunk & a_Copy ); ~cPacket_MapChunk(); virtual cPacket* Clone() const { return new cPacket_MapChunk(*this); } @@ -38,6 +44,12 @@ public: static const unsigned int c_Size = 1 + 4 + 2 + 4 + 1 + 1 + 1 + 4; char * m_CompressedData; + +protected: + friend class cChunk; + + cPacket_MapChunk(cChunk * a_Chunk); // Called only from within cChunk, therefore it CAN receive a direct pointer + }; diff --git a/source/packets/cPacket_Metadata.cpp b/source/packets/cPacket_Metadata.cpp index 9d2d61704..595801b54 100644 --- a/source/packets/cPacket_Metadata.cpp +++ b/source/packets/cPacket_Metadata.cpp @@ -35,6 +35,20 @@ cPacket_Metadata::cPacket_Metadata() +cPacket_Metadata::cPacket_Metadata(const cPacket_Metadata & a_Other) + : m_EMetaData( a_Other.m_EMetaData ) + , m_UniqueID( a_Other.m_UniqueID ) + , m_Type( a_Other.m_Type ) + , m_MetaData( NULL ) +{ + m_PacketID = E_METADATA; + FormPacket(); +} + + + + + cPacket_Metadata::~cPacket_Metadata() { delete [] m_MetaData; diff --git a/source/packets/cPacket_Metadata.h b/source/packets/cPacket_Metadata.h index 4900bad0a..24818e379 100644 --- a/source/packets/cPacket_Metadata.h +++ b/source/packets/cPacket_Metadata.h @@ -13,6 +13,7 @@ class cPacket_Metadata : public cPacket public: cPacket_Metadata(int s, int id); cPacket_Metadata(); + cPacket_Metadata(const cPacket_Metadata & a_Other); ~cPacket_Metadata(); virtual void Serialize(AString & a_Data) const override; diff --git a/source/ptr_cChunk.h b/source/ptr_cChunk.h deleted file mode 100644 index c3556839c..000000000 --- a/source/ptr_cChunk.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "cChunk.h" - -class ptr_cChunk -{ -public: - ptr_cChunk( cChunk* a_Ptr ) - : m_Ptr( a_Ptr ) - { - if( m_Ptr ) m_Ptr->AddReference(); - } - - ptr_cChunk( const ptr_cChunk& a_Clone ) - : m_Ptr( a_Clone.m_Ptr ) - { - if( m_Ptr ) m_Ptr->AddReference(); - } - - ~ptr_cChunk() - { - if( m_Ptr ) m_Ptr->RemoveReference(); - } - - cChunk* operator-> () - { - return m_Ptr; - } - - cChunk& operator* () { return *m_Ptr; } - bool operator!() { return !m_Ptr; } - bool operator==( const ptr_cChunk& a_Other ) { return m_Ptr == a_Other.m_Ptr; } - operator bool() { return m_Ptr != 0; } - operator cChunk*() { return m_Ptr; } -private: - cChunk* m_Ptr; -}; \ No newline at end of file -- cgit v1.2.3