summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--VC2008/JsonCpp.vcproj8
-rw-r--r--VC2008/Lua.vcproj8
-rw-r--r--VC2008/MCServer.vcproj126
-rw-r--r--VC2008/ToLua.vcproj8
-rw-r--r--VC2008/WebServer.vcproj4
-rw-r--r--VC2008/zlib.vcproj8
-rw-r--r--VC2010/MCServer.vcxproj18
-rw-r--r--VC2010/MCServer.vcxproj.filters8
-rw-r--r--WebServer/Globals.h5
-rw-r--r--WebServer/Socket.h15
-rw-r--r--source/Bindings.cpp182
-rw-r--r--source/Bindings.h2
-rw-r--r--source/Globals.h21
-rw-r--r--source/LeakFinder.cpp1040
-rw-r--r--source/LeakFinder.h145
-rw-r--r--source/MCSocket.h35
-rw-r--r--source/PacketID.h104
-rw-r--r--source/StackWalker.cpp1345
-rw-r--r--source/StackWalker.h214
-rw-r--r--source/WSSCompact.cpp415
-rw-r--r--source/WSSCompact.h84
-rw-r--r--source/WorldStorage.cpp256
-rw-r--r--source/WorldStorage.h94
-rw-r--r--source/cAuthenticator.cpp11
-rw-r--r--source/cAuthenticator.h1
-rw-r--r--source/cBlockEntity.h26
-rw-r--r--source/cBlockingTCPLink.cpp7
-rw-r--r--source/cBlockingTCPLink.h5
-rw-r--r--source/cChestEntity.cpp56
-rw-r--r--source/cChestEntity.h31
-rw-r--r--source/cChunk.cpp704
-rw-r--r--source/cChunk.h129
-rw-r--r--source/cChunkGenerator.cpp162
-rw-r--r--source/cChunkGenerator.h62
-rw-r--r--source/cChunkMap.cpp568
-rw-r--r--source/cChunkMap.h89
-rw-r--r--source/cClientHandle.cpp700
-rw-r--r--source/cClientHandle.h76
-rw-r--r--source/cCriticalSection.cpp22
-rw-r--r--source/cCriticalSection.h27
-rw-r--r--source/cEntity.cpp276
-rw-r--r--source/cEntity.h63
-rw-r--r--source/cFile.cpp23
-rw-r--r--source/cFile.h5
-rw-r--r--source/cFurnaceEntity.cpp62
-rw-r--r--source/cFurnaceEntity.h38
-rw-r--r--source/cGroup.h2
-rw-r--r--source/cIsThread.cpp9
-rw-r--r--source/cIsThread.h1
-rw-r--r--source/cItem.h4
-rw-r--r--source/cMonster.cpp133
-rw-r--r--source/cMonster.h5
-rw-r--r--source/cPawn.cpp134
-rw-r--r--source/cPawn.h24
-rw-r--r--source/cPickup.cpp99
-rw-r--r--source/cPickup.h33
-rw-r--r--source/cPiston.cpp76
-rw-r--r--source/cPiston.h12
-rw-r--r--source/cPlayer.cpp200
-rw-r--r--source/cPlayer.h12
-rw-r--r--source/cPluginManager.h4
-rw-r--r--source/cRoot.h7
-rw-r--r--source/cServer.cpp34
-rw-r--r--source/cServer.h4
-rw-r--r--source/cSignEntity.cpp14
-rw-r--r--source/cSignEntity.h18
-rw-r--r--source/cSimulatorManager.cpp31
-rw-r--r--source/cSimulatorManager.h29
-rw-r--r--source/cSocket.cpp28
-rw-r--r--source/cSocket.h2
-rw-r--r--source/cSocketThreads.cpp18
-rw-r--r--source/cSocketThreads.h1
-rw-r--r--source/cTCPLink.cpp90
-rw-r--r--source/cTCPLink.h6
-rw-r--r--source/cWebAdmin.cpp33
-rw-r--r--source/cWorld.cpp855
-rw-r--r--source/cWorld.h161
-rw-r--r--source/cWorldGenerator.cpp42
-rw-r--r--source/cWorldGenerator.h27
-rw-r--r--source/cWorldGenerator_Test.cpp4
-rw-r--r--source/cWorldGenerator_Test.h18
-rw-r--r--source/main.cpp55
-rw-r--r--source/packets/cPacket_MapChunk.cpp3
-rw-r--r--source/packets/cPacket_MapChunk.h16
-rw-r--r--source/packets/cPacket_Metadata.cpp14
-rw-r--r--source/packets/cPacket_Metadata.h1
-rw-r--r--source/ptr_cChunk.h37
87 files changed, 6848 insertions, 2736 deletions
diff --git a/VC2008/JsonCpp.vcproj b/VC2008/JsonCpp.vcproj
index 5c53e1dbf..b17db9b5f 100644
--- a/VC2008/JsonCpp.vcproj
+++ b/VC2008/JsonCpp.vcproj
@@ -18,8 +18,8 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\JsonCpp"
+ IntermediateDirectory="$(ConfigurationName)\JsonCpp"
ConfigurationType="4"
CharacterSet="1"
>
@@ -80,8 +80,8 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\JsonCpp"
+ IntermediateDirectory="$(ConfigurationName)\JsonCpp"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
diff --git a/VC2008/Lua.vcproj b/VC2008/Lua.vcproj
index 355c6a620..23bf9fdc7 100644
--- a/VC2008/Lua.vcproj
+++ b/VC2008/Lua.vcproj
@@ -18,8 +18,8 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\Lua"
+ IntermediateDirectory="$(ConfigurationName)\Lua"
ConfigurationType="4"
CharacterSet="1"
>
@@ -79,8 +79,8 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\Lua"
+ IntermediateDirectory="$(ConfigurationName)\Lua"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index 4dc9f2aa6..151c589fd 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -206,14 +206,6 @@
>
</File>
<File
- RelativePath="..\source\cChestEntity.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cChestEntity.h"
- >
- </File>
- <File
RelativePath="..\source\cChunk.cpp"
>
</File>
@@ -380,6 +372,14 @@
>
</File>
<File
+ RelativePath="..\source\cPiston.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\cPiston.h"
+ >
+ </File>
+ <File
RelativePath="..\source\cPlugin.cpp"
>
</File>
@@ -512,10 +512,6 @@
>
</File>
<File
- RelativePath="..\source\FileDefine.h"
- >
- </File>
- <File
RelativePath="..\source\Globals.cpp"
>
<FileConfiguration
@@ -540,6 +536,31 @@
>
</File>
<File
+ RelativePath="..\source\LeakFinder.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\source\LeakFinder.h"
+ >
+ </File>
+ <File
RelativePath="..\source\LuaFunctions.h"
>
</File>
@@ -556,10 +577,6 @@
>
</File>
<File
- RelativePath="..\source\MCSocket.h"
- >
- </File>
- <File
RelativePath="..\source\MemoryLeak.h"
>
</File>
@@ -568,11 +585,28 @@
>
</File>
<File
- RelativePath="..\source\PacketID.h"
+ RelativePath="..\source\StackWalker.cpp"
>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
</File>
<File
- RelativePath="..\source\ptr_cChunk.h"
+ RelativePath="..\source\StackWalker.h"
>
</File>
<File
@@ -1022,6 +1056,10 @@
RelativePath="..\source\packets\cPacket_WindowOpen.h"
>
</File>
+ <File
+ RelativePath="..\source\PacketID.h"
+ >
+ </File>
</Filter>
<Filter
Name="Mobs"
@@ -1203,6 +1241,14 @@
>
</File>
<File
+ RelativePath="..\source\cChestEntity.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\cChestEntity.h"
+ >
+ </File>
+ <File
RelativePath="..\source\cDoors.h"
>
</File>
@@ -1251,14 +1297,6 @@
>
</File>
<File
- RelativePath="..\source\cPiston.cpp"
- >
- </File>
- <File
- RelativePath="..\source\cPiston.h"
- >
- </File>
- <File
RelativePath="..\source\cPlayer.cpp"
>
</File>
@@ -1489,6 +1527,22 @@
<File
RelativePath="..\source\Bindings.cpp"
>
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
</File>
<File
RelativePath="..\source\Bindings.h"
@@ -1579,6 +1633,26 @@
>
</File>
</Filter>
+ <Filter
+ Name="World storage"
+ >
+ <File
+ RelativePath="..\source\WorldStorage.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\WorldStorage.h"
+ >
+ </File>
+ <File
+ RelativePath="..\source\WSSCompact.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\WSSCompact.h"
+ >
+ </File>
+ </Filter>
</Filter>
<File
RelativePath="..\makefile"
diff --git a/VC2008/ToLua.vcproj b/VC2008/ToLua.vcproj
index 1c89aaee9..5d44fe8a4 100644
--- a/VC2008/ToLua.vcproj
+++ b/VC2008/ToLua.vcproj
@@ -18,8 +18,8 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\ToLua"
+ IntermediateDirectory="$(ConfigurationName)\ToLua"
ConfigurationType="4"
CharacterSet="1"
>
@@ -80,8 +80,8 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\ToLua"
+ IntermediateDirectory="$(ConfigurationName)\ToLua"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
diff --git a/VC2008/WebServer.vcproj b/VC2008/WebServer.vcproj
index f86f4ba8b..b136c9dee 100644
--- a/VC2008/WebServer.vcproj
+++ b/VC2008/WebServer.vcproj
@@ -18,7 +18,7 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\WebServer"
IntermediateDirectory="$(ConfigurationName)\webserver"
ConfigurationType="4"
CharacterSet="1"
@@ -80,7 +80,7 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\WebServer"
IntermediateDirectory="$(ConfigurationName)\webserver"
ConfigurationType="4"
CharacterSet="1"
diff --git a/VC2008/zlib.vcproj b/VC2008/zlib.vcproj
index de7d05def..7d885b222 100644
--- a/VC2008/zlib.vcproj
+++ b/VC2008/zlib.vcproj
@@ -18,8 +18,8 @@
<Configurations>
<Configuration
Name="Debug|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\zlib"
+ IntermediateDirectory="$(ConfigurationName)\zlib"
ConfigurationType="4"
CharacterSet="1"
>
@@ -79,8 +79,8 @@
</Configuration>
<Configuration
Name="Release|Win32"
- OutputDirectory="$(SolutionDir)$(ConfigurationName)"
- IntermediateDirectory="$(ConfigurationName)"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)\zlib"
+ IntermediateDirectory="$(ConfigurationName)\zlib"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj
index 2144ca2c3..61f6237b7 100644
--- a/VC2010/MCServer.vcxproj
+++ b/VC2010/MCServer.vcxproj
@@ -394,6 +394,12 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="..\source\LeakFinder.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ </ClCompile>
<ClCompile Include="..\Source\ManualBindings.cpp" />
<ClCompile Include="..\source\Matrix4f.cpp" />
<ClCompile Include="..\source\md5\md5.cpp" />
@@ -465,10 +471,18 @@
<ClCompile Include="..\source\packets\cPacket_WindowClose.cpp" />
<ClCompile Include="..\source\packets\cPacket_WindowOpen.cpp" />
<ClCompile Include="..\source\SquirrelBindings.cpp" />
+ <ClCompile Include="..\source\StackWalker.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="..\source\StringUtils.cpp" />
<ClCompile Include="..\source\Vector3d.cpp" />
<ClCompile Include="..\source\Vector3f.cpp" />
<ClCompile Include="..\source\Vector3i.cpp" />
+ <ClCompile Include="..\source\WorldStorage.cpp" />
+ <ClCompile Include="..\source\WSSCompact.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\Source\BlockID.h" />
@@ -558,6 +572,7 @@
<ClInclude Include="..\source\Endianness.h" />
<ClInclude Include="..\Source\FileDefine.h" />
<ClInclude Include="..\source\Globals.h" />
+ <ClInclude Include="..\source\LeakFinder.h" />
<ClInclude Include="..\Source\LuaFunctions.h" />
<ClInclude Include="..\Source\ManualBindings.h" />
<ClInclude Include="..\source\Matrix4f.h" />
@@ -637,10 +652,13 @@
<ClInclude Include="..\source\packets\cPacket_WindowOpen.h" />
<ClInclude Include="..\source\ptr_cChunk.h" />
<ClInclude Include="..\source\SquirrelBindings.h" />
+ <ClInclude Include="..\source\StackWalker.h" />
<ClInclude Include="..\source\StringUtils.h" />
<ClInclude Include="..\source\Vector3d.h" />
<ClInclude Include="..\source\Vector3f.h" />
<ClInclude Include="..\source\Vector3i.h" />
+ <ClInclude Include="..\source\WorldStorage.h" />
+ <ClInclude Include="..\source\WSSCompact.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters
index 1a8e72fdc..437a8de53 100644
--- a/VC2010/MCServer.vcxproj.filters
+++ b/VC2010/MCServer.vcxproj.filters
@@ -912,6 +912,10 @@
<ClCompile Include="..\source\StringUtils.cpp" />
<ClCompile Include="..\source\cIsThread.cpp" />
<ClCompile Include="..\source\cSocketThreads.cpp" />
+ <ClCompile Include="..\source\LeakFinder.cpp" />
+ <ClCompile Include="..\source\StackWalker.cpp" />
+ <ClCompile Include="..\source\WorldStorage.cpp" />
+ <ClCompile Include="..\source\WSSCompact.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\source\cServer.h">
@@ -1405,6 +1409,10 @@
<ClInclude Include="..\source\StringUtils.h" />
<ClInclude Include="..\source\cIsThread.h" />
<ClInclude Include="..\source\cSocketThreads.h" />
+ <ClInclude Include="..\source\WSSCompact.h" />
+ <ClInclude Include="..\source\LeakFinder.h" />
+ <ClInclude Include="..\source\StackWalker.h" />
+ <ClInclude Include="..\source\WorldStorage.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\source\AllToLua.pkg">
diff --git a/WebServer/Globals.h b/WebServer/Globals.h
index a9cf6d790..405de1075 100644
--- a/WebServer/Globals.h
+++ b/WebServer/Globals.h
@@ -17,6 +17,10 @@
#include <sys/types.h>
#include <sys/stat.h> // for mkdir
#include <sys/time.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
@@ -28,6 +32,7 @@
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
+ #include <tr1/memory>
#endif
diff --git a/WebServer/Socket.h b/WebServer/Socket.h
index 339dae188..e38df69ea 100644
--- a/WebServer/Socket.h
+++ b/WebServer/Socket.h
@@ -32,14 +32,19 @@
#ifndef SOCKET_H
#define SOCKET_H
-#include "../source/MCSocket.h"
-// #ifdef _WIN32
-// #include <winsock2.h>
-// #endif
-#include <string>
+
+#ifndef _WIN32
+ typedef int SOCKET;
+ #define SOCKET_ERROR (-1)
+ #define closesocket close
+#endif // !_WIN32
+
+
+
+
enum TypeSocket {BlockingSocket, NonBlockingSocket};
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 <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
@@ -40,6 +42,7 @@
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
+ #include <tr1/memory>
#endif
@@ -49,6 +52,7 @@
// CRT stuff:
#include <assert.h>
#include <stdio.h>
+#include <math.h>
@@ -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 <typename Type> 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 <windows.h>
+#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN"
+#include <tchar.h>
+#include <crtdbg.h>
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+
+#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<strlen(szText); i++)
+ {
+ switch(szText[i])
+ {
+ case '&':
+ szRet.append("&amp;");
+ break;
+ case '<':
+ szRet.append("&lt;");
+ break;
+ case '>':
+ szRet.append("&gt;");
+ break;
+ case '"':
+ szRet.append("&quot;");
+ break;
+ case '\'':
+ szRet.append("&apos;");
+ 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, "<MEMREPORT date=\"%.2d/%.2d/%.4d\" time=\"%.2d:%.2d:%.2d\">\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, "</MEMREPORT>\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, " <LEAK requestID=\"%s\" size=\"%d\">\n", SimpleXMLEncode(szKeyName).c_str(), nDataSize);
+ }
+}
+void LeakFinderXmlOutput::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
+{
+ if (m_fXmlFile != NULL)
+ {
+ if (eType != lastEntry)
+ {
+ fprintf(m_fXmlFile, " <STACKENTRY decl=\"%s\" decl_offset=\"%+ld\" ", SimpleXMLEncode(entry.undName).c_str(), entry.offsetFromSmybol);
+ fprintf(m_fXmlFile, "srcfile=\"%s\" line=\"%d\" line_offset=\"%+ld\" ", SimpleXMLEncode(entry.lineFileName).c_str(), entry.lineNumber, entry.offsetFromLine);
+ fprintf(m_fXmlFile, "module=\"%s\" base=\"%08lx\" ", SimpleXMLEncode(entry.moduleName).c_str(), entry.baseOfImage);
+ fprintf(m_fXmlFile, "/>\n");
+ }
+ else
+ {
+ fprintf(m_fXmlFile, " </LEAK>\n");
+ }
+ }
+}
+
+// ##########################################################################
+// ##########################################################################
+// ##########################################################################
+// Base class for storing contexts in a hashtable
+template <typename HASHTABLE_KEY> 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 <typename HASHTABLE_KEY> class ContextHashtableBase
+
+
+// ##########################################################################
+// ##########################################################################
+// ##########################################################################
+// Specialization for CRT-Leaks:
+// VC5 has excluded all types in release-builds
+#ifdef _DEBUG
+
+// The follwoing is copied from dbgint.h:
+// <CRT_INTERNALS>
+/*
+* 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<LONG>
+{
+public:
+ CRTTable() : ContextHashtableBase<LONG>(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)
+// </CRT_INTERNALS>
+
+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<LPVOID>
+{
+public:
+ COMTable() : ContextHashtableBase<LPVOID>(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 <windows.h>
+
+#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 <netdb.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <unistd.h>
-
- // 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 (<ORGANIZATION> 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 <windows.h>
+#include <tchar.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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 <dbghelp.h>
+#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 <windows.h>
+
+// 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 <json/json.h>
+
+
+
+
+
+#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<char> 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<char> 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<sChunkHeader *> 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<cPAKFile *> 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<cWSSchema *> 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<cBlockEntity*>::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<cPickup *>(*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<cSignEntity *>(*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<cBlockEntity*>::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<cBlockEntity*>::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<cFurnaceEntity *> cFurnaceEntityList;
+typedef std::list<cClientHandle *> cClientHandleList;
+typedef std::list<cBlockEntity *> 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<cChunk> cChunkPtr;
+
+typedef std::list<cChunkPtr> 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<cChunkCoords> 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<int, int> 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 <math.h> // floorf
#ifndef _WIN32
#include <cstdlib> // 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<cChunkLayer> 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<cChunkLayer *> 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<cPacket_Handshake *>(a_Packet)); break;
+ case E_LOGIN: HandleLogin (reinterpret_cast<cPacket_Login *> (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<cPacket_Handshake *> (a_Packet)); break;
- case E_LOGIN: HandleLogin (reinterpret_cast<cPacket_Login *> (a_Packet)); break;
- case E_PLAYERMOVELOOK: HandleMoveLookLogin(reinterpret_cast<cPacket_PlayerMoveLook *>(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<cPacket_PlayerMoveLook *>(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<cPacket_PlayerMoveLook *>(a_Packet)); break;
+
+ default:
+ {
+ HandleUnexpectedPacket(a_Packet);
+ break;
+ }
+ } // switch (PacketType)
+ break;
+ } // case csConfirmingPos
+
+ case csPlaying:
{
- case E_CREATIVE_INVENTORY_ACTION: HandleCreativeInventory(reinterpret_cast<cPacket_CreativeInventoryAction *>(a_Packet)); break;
- case E_PLAYERPOS: HandlePlayerPos (reinterpret_cast<cPacket_PlayerPosition *> (a_Packet)); break;
- case E_BLOCK_DIG: HandleBlockDig (reinterpret_cast<cPacket_BlockDig *> (a_Packet)); break;
- case E_BLOCK_PLACE: HandleBlockPlace (reinterpret_cast<cPacket_BlockPlace *> (a_Packet)); break;
- case E_PICKUP_SPAWN: HandlePickupSpawn (reinterpret_cast<cPacket_PickupSpawn *> (a_Packet)); break;
- case E_CHAT: HandleChat (reinterpret_cast<cPacket_Chat *> (a_Packet)); break;
- case E_PLAYERLOOK: HandlePlayerLook (reinterpret_cast<cPacket_PlayerLook *> (a_Packet)); break;
- case E_PLAYERMOVELOOK: HandlePlayerMoveLook (reinterpret_cast<cPacket_PlayerMoveLook *> (a_Packet)); break;
- case E_ANIMATION: HandleAnimation (reinterpret_cast<cPacket_ArmAnim *> (a_Packet)); break;
- case E_ITEM_SWITCH: HandleItemSwitch (reinterpret_cast<cPacket_ItemSwitch *> (a_Packet)); break;
- case E_WINDOW_CLOSE: HandleWindowClose (reinterpret_cast<cPacket_WindowClose *> (a_Packet)); break;
- case E_WINDOW_CLICK: HandleWindowClick (reinterpret_cast<cPacket_WindowClick *> (a_Packet)); break;
- case E_UPDATE_SIGN: HandleUpdateSign (reinterpret_cast<cPacket_UpdateSign *> (a_Packet)); break;
- case E_USE_ENTITY: HandleUseEntity (reinterpret_cast<cPacket_UseEntity *> (a_Packet)); break;
- case E_RESPAWN: HandleRespawn();
- case E_DISCONNECT: HandleDisconnect (reinterpret_cast<cPacket_Disconnect *> (a_Packet)); break;
- case E_KEEP_ALIVE: HandleKeepAlive (reinterpret_cast<cPacket_KeepAlive *> (a_Packet)); break;
- } // switch (Packet type)
- } // if (normal game)
+ switch (a_Packet->m_PacketID)
+ {
+ case E_CREATIVE_INVENTORY_ACTION: HandleCreativeInventory(reinterpret_cast<cPacket_CreativeInventoryAction *>(a_Packet)); break;
+ case E_PLAYERPOS: HandlePlayerPos (reinterpret_cast<cPacket_PlayerPosition *> (a_Packet)); break;
+ case E_BLOCK_DIG: HandleBlockDig (reinterpret_cast<cPacket_BlockDig *> (a_Packet)); break;
+ case E_BLOCK_PLACE: HandleBlockPlace (reinterpret_cast<cPacket_BlockPlace *> (a_Packet)); break;
+ case E_PICKUP_SPAWN: HandlePickupSpawn (reinterpret_cast<cPacket_PickupSpawn *> (a_Packet)); break;
+ case E_CHAT: HandleChat (reinterpret_cast<cPacket_Chat *> (a_Packet)); break;
+ case E_PLAYERLOOK: HandlePlayerLook (reinterpret_cast<cPacket_PlayerLook *> (a_Packet)); break;
+ case E_PLAYERMOVELOOK: HandlePlayerMoveLook (reinterpret_cast<cPacket_PlayerMoveLook *> (a_Packet)); break;
+ case E_ANIMATION: HandleAnimation (reinterpret_cast<cPacket_ArmAnim *> (a_Packet)); break;
+ case E_ITEM_SWITCH: HandleItemSwitch (reinterpret_cast<cPacket_ItemSwitch *> (a_Packet)); break;
+ case E_WINDOW_CLOSE: HandleWindowClose (reinterpret_cast<cPacket_WindowClose *> (a_Packet)); break;
+ case E_WINDOW_CLICK: HandleWindowClick (reinterpret_cast<cPacket_WindowClick *> (a_Packet)); break;
+ case E_UPDATE_SIGN: HandleUpdateSign (reinterpret_cast<cPacket_UpdateSign *> (a_Packet)); break;
+ case E_USE_ENTITY: HandleUseEntity (reinterpret_cast<cPacket_UseEntity *> (a_Packet)); break;
+ case E_RESPAWN: HandleRespawn(); break;
+ case E_DISCONNECT: HandleDisconnect (reinterpret_cast<cPacket_Disconnect *> (a_Packet)); break;
+ case E_KEEP_ALIVE: HandleKeepAlive (reinterpret_cast<cPacket_KeepAlive *> (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<cSignEntity *>(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<cPacket> 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<cPacket> 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<cEntity *> 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 <json/json.h>
-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<cPacket> 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<cPickup*>(*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 <list>
-#include <string> // 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 <std::pair<cSimulator *, short> *>::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 <std::pair<cSimulator *, short> *>::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<cSimulator *, short> *Pair = new std::pair<cSimulator *, short>(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 <vector>
+
+
+
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 <std::pair<cSimulator *, short> *> m_Simulators;
- long long m_Ticks;
-}; \ No newline at end of file
+
+ typedef std::vector <std::pair<cSimulator *, short> *> 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("<li>");
+ m_Contents.append(a_Player->GetName());
+ m_Contents.append("</li>");
+ return false;
+ }
+
+public:
+
+ AString m_Contents;
+} ;
+
+
+
+
+
+cWebAdmin * WebAdmin = NULL;
@@ -191,12 +212,10 @@ void cWebAdmin::Request_Handler(webserver::http_request* r)
Content += "</ul>";
Content += "<h4>Players:</h4><ul>";
- 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("<li>") + std::string( (*itr)->GetName() ) + "</li>";
- }
+ cPlayerAccum PlayerAccum;
+ cWorld * World = cRoot::Get()->GetWorld(); // TODO - Create a list of worlds and players
+ World->ForEachPlayer(&PlayerAccum);
+ Content.append(PlayerAccum.m_Contents);
Content += "</ul><br>";
}
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 <time.h>
@@ -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<int> 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<int>::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<cPlayer> 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<int> 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<int> 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