1
0

Rewritten most of the code for multithreading; still not 100%, but getting there. If this commit proves to be too problematic, we can always undo it.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@251 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com 2012-02-13 21:47:03 +00:00
parent 0a46c065bf
commit 4f17362aeb
87 changed files with 6915 additions and 2803 deletions

View File

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

View File

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

View File

@ -205,14 +205,6 @@
RelativePath="..\source\cChatColor.h"
>
</File>
<File
RelativePath="..\source\cChestEntity.cpp"
>
</File>
<File
RelativePath="..\source\cChestEntity.h"
>
</File>
<File
RelativePath="..\source\cChunk.cpp"
>
@ -379,6 +371,14 @@
RelativePath="..\source\cNoise.inc"
>
</File>
<File
RelativePath="..\source\cPiston.cpp"
>
</File>
<File
RelativePath="..\source\cPiston.h"
>
</File>
<File
RelativePath="..\source\cPlugin.cpp"
>
@ -511,10 +511,6 @@
RelativePath="..\source\Endianness.h"
>
</File>
<File
RelativePath="..\source\FileDefine.h"
>
</File>
<File
RelativePath="..\source\Globals.cpp"
>
@ -539,6 +535,31 @@
RelativePath="..\source\Globals.h"
>
</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"
>
@ -555,10 +576,6 @@
RelativePath="..\source\Matrix4f.h"
>
</File>
<File
RelativePath="..\source\MCSocket.h"
>
</File>
<File
RelativePath="..\source\MemoryLeak.h"
>
@ -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"
@ -1202,6 +1240,14 @@
RelativePath="..\source\cBlockToPickup.h"
>
</File>
<File
RelativePath="..\source\cChestEntity.cpp"
>
</File>
<File
RelativePath="..\source\cChestEntity.h"
>
</File>
<File
RelativePath="..\source\cDoors.h"
>
@ -1250,14 +1296,6 @@
RelativePath="..\source\cPickup.h"
>
</File>
<File
RelativePath="..\source\cPiston.cpp"
>
</File>
<File
RelativePath="..\source\cPiston.h"
>
</File>
<File
RelativePath="..\source\cPlayer.cpp"
>
@ -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"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

1040
source/LeakFinder.cpp Normal file

File diff suppressed because it is too large Load Diff

145
source/LeakFinder.h Normal file
View File

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

View File

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

View File

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

1345
source/StackWalker.cpp Normal file

File diff suppressed because it is too large Load Diff

214
source/StackWalker.h Normal file
View File

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

415
source/WSSCompact.cpp Normal file
View File

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

84
source/WSSCompact.h Normal file
View File

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

256
source/WorldStorage.cpp Normal file
View File

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

94
source/WorldStorage.h Normal file
View File

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

View File

@ -35,6 +35,17 @@ cAuthenticator::cAuthenticator(void) :
cAuthenticator::~cAuthenticator()
{
mShouldTerminate = true;
mQueueNonempty.Set();
Wait();
}
/// Read custom values from INI
void cAuthenticator::ReadINI(void)
{

View File

@ -34,6 +34,7 @@ class cAuthenticator :
public:
cAuthenticator(void);
~cAuthenticator();
/// (Re-)read server and address from INI:
void ReadINI(void);

View File

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

View File

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

View File

@ -1,3 +1,4 @@
#pragma once
#include "cSocket.h"
@ -21,3 +22,7 @@ protected:
cSocket m_Socket;
}; //tolua_export

View File

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

View File

@ -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;
};
cItem * m_Content;
bool m_TopChest;
cChestEntity * m_JoinedChest;
};

File diff suppressed because it is too large Load Diff

View File

@ -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
#include "cChunk.inl.h"
#endif

View File

@ -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,45 +18,20 @@ 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( cChunkMap* a_pChunkMap )
: m_pState( new sChunkGeneratorState )
, m_pChunkMap( a_pChunkMap )
{
m_pState->pThread = new cThread( GenerateThread, this, "cChunkGenerator::GenerateThread" );
m_pState->pThread->Start( true );
}
@ -63,97 +40,106 @@ cChunkGenerator::cChunkGenerator( cChunkMap* a_pChunkMap )
cChunkGenerator::~cChunkGenerator()
{
m_pState->bStop = true;
m_pState->m_Semaphore.Signal(); // Signal so thread can continue and exit
delete m_pState->pThread;
delete m_pState;
Stop();
}
void cChunkGenerator::GenerateChunk( int a_X, int a_Z )
bool cChunkGenerator::Start(cWorld * a_World, const AString & a_WorldGeneratorName)
{
cCSLock Lock(&m_pState->m_CriticalSection);
if (m_pState->bCurrentlyGenerating)
{
if ((m_pState->CurrentlyGeneratingCoords.first == a_X) && (m_pState->CurrentlyGeneratingCoords.second == a_Z))
{
return; // Already generating this chunk, so ignore
}
}
m_pState->GenerateQueue.remove( ChunkCoord(a_X, a_Z) );
if (m_pState->GenerateQueue.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() );
}
m_pState->GenerateQueue.push_back( ChunkCoord(a_X, a_Z) );
Lock.Unlock();
m_World = a_World;
m_pState->m_Semaphore.Signal();
if (a_WorldGeneratorName.compare("Test") == 0 )
{
m_pWorldGenerator = new cWorldGenerator_Test();
}
else // Default
{
m_pWorldGenerator = new cWorldGenerator();
}
return super::Start();
}
void cChunkGenerator::GenerateThread( void* a_Params )
void cChunkGenerator::Stop(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;
mShouldTerminate = true;
m_Event.Set();
Wait();
delete m_pWorldGenerator;
m_pWorldGenerator = NULL;
}
while (!m_pState->bStop)
void cChunkGenerator::GenerateChunk(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CS);
// Check if it is already in the queue:
for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
cCSLock Lock(CriticalSection);
if (GenerateQueue.size() == 0)
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already in the queue, bail out
return;
}
} // 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_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
m_Event.Set();
}
void cChunkGenerator::Execute(void)
{
while (!mShouldTerminate)
{
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)
}

View File

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

View File

@ -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
cCSLock Lock(m_CSLayers);
for (cChunkLayerList::iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
{
delete *itr;
} // for itr - m_Layers[]
}
bool cChunkMap::RemoveLayer( cChunkLayer* a_Layer )
void cChunkMap::RemoveLayer( cChunkLayer* a_Layer )
{
cChunkLayer* NewLayers = 0;
if( m_NumLayers > 1 )
NewLayers = new cChunkLayer[m_NumLayers-1];
cCSLock Lock(m_CSLayers);
m_Layers.remove(a_Layer);
}
int idx = 0;
bool bExcludedLayer = false;
for( int i = 0; i < m_NumLayers; ++i )
cChunkMap::cChunkLayer * cChunkMap::GetLayer(int a_LayerX, int a_LayerZ)
{
cCSLock Lock(m_CSLayers);
for (cChunkLayerList::const_iterator itr = m_Layers.begin(); itr != m_Layers.end(); ++itr)
{
if( &m_Layers[i] != a_Layer )
if (((*itr)->GetX() == a_LayerX) && ((*itr)->GetZ() == a_LayerZ))
{
if( idx < m_NumLayers-1 )
{
NewLayers[ idx ] = m_Layers[i];
idx++;
}
return *itr;
}
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 )
{
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;
// Not found, create new:
cChunkLayer * Layer = new cChunkLayer(a_LayerX, a_LayerZ, this);
if (Layer == NULL)
{
LOGERROR("cChunkMap: Cannot create new layer, server out of memory?");
return NULL;
}
m_Layers.push_back(Layer);
return Layer;
}
void cChunkMap::AddChunk( cChunk* a_Chunk )
{
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++;
}
void cChunkMap::RemoveChunk( cChunk* a_Chunk )
{
cChunkLayer* Layer = GetLayerForChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() );
if( Layer )
{
cChunkData* Data = Layer->GetChunk( a_Chunk->GetPosX(), a_Chunk->GetPosZ() );
if( Data->m_LiveChunk )
{
CompressChunk( Data );
Data->m_LiveChunk = 0; // Set live chunk to 0
}
Layer->m_NumChunksLoaded--;
}
}
void cChunkMap::CompressChunk( cChunkData* a_ChunkData )
{
if( a_ChunkData->m_LiveChunk )
{
// 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;
}
}
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()));
}
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)
{
char * Compressed = a_Layer->m_Chunks[i].m_Compressed;
if (Compressed != NULL)
{
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;
}
}
} // for i - a_Layer->m_Chunks[]
return m_Chunks[Index];
}
#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::Tick(float a_Dt, MTRand & a_TickRand)
{
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())
for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{
return NULL;
}
char PakVersion = 0;
char ChunkVersion = 0;
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)
if ((m_Chunks[i] != NULL) && (m_Chunks[i]->IsValid()))
{
LOGERROR("Chunk with wrong coordinates [%i, %i] in pak file \"%s\"!", ChunkX, ChunkZ, SourceFile.c_str());
return NULL;
m_Chunks[i]->Tick(a_Dt, a_TickRand);
}
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 )
{
cChunkData* Data = OrderedData[i];
Data->m_Compressed = new char[ Data->m_CompressedSize ];
if (f.Read(Data->m_Compressed, Data->m_CompressedSize) != Data->m_CompressedSize)
{
LOGERROR("ERROR reading compressed data for chunk #%i from file \"%s\"", i, SourceFile.c_str());
return NULL;
}
}
return Layer.release();
} // for i - m_Chunks[]
}
int cChunkMap::GetNumChunks()
void cChunkMap::cChunkLayer::Save(void)
{
cWorld * World = m_Parent->GetWorld();
for (int i = 0; i < ARRAYCOUNT(m_Chunks); ++i)
{
if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid())
{
World->GetStorage().QueueSaveChunk(m_Chunks[i]);
}
} // for i - m_Chunks[]
}
void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
{
for (int i = 0; i < ARRAYCOUNT(m_Chunks); i++)
{
if ((m_Chunks[i] != NULL) && (m_Chunks[i]->CanUnload()))
{
// TODO: Save the chunk if it was changed
m_Chunks[i].reset();
}
} // for i - m_Chunks[]
}
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;
}

View File

@ -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 );
cChunkData * m_Chunks;
int m_X, m_Z;
int GetX(void) const {return m_LayerX; }
int GetZ(void) const {return m_LayerZ; }
int GetNumChunksLoaded(void) const {return m_NumChunksLoaded; }
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;
};

View File

@ -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,26 +148,26 @@ 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);
}
cWorld::PlayerList PlayerList = cRoot::Get()->GetWorld()->GetAllPlayers();
for(cWorld::PlayerList::iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
{
if ((*itr) && (*itr)->GetClientHandle() && !GetUsername().empty())
{
std::string NameColor = (m_Player ? m_Player->GetColor() : "");
cPacket_PlayerListItem PlayerList(NameColor + GetUsername(), false, (short)9999);
(*itr)->GetClientHandle()->Send(PlayerList);
}
}
// Remove from cSocketThreads, just in case
cRoot::Get()->GetServer()->ClientDestroying(this);
if (m_Username.size() > 0)
m_LoadedChunks.clear();
m_ChunksToSend.clear();
if (m_Player != NULL)
{
cPacket_Chat Left(m_Username + " left the game!");
cRoot::Get()->GetServer()->Broadcast(Left, this);
cWorld * World = m_Player->GetWorld();
if (!m_Username.empty() && (World != NULL))
{
// Send the Offline PlayerList packet:
AString NameColor = (m_Player ? m_Player->GetColor() : "");
cPacket_PlayerListItem PlayerList(NameColor + GetUsername(), false, (short)9999);
World->Broadcast(PlayerList, this);
// Send the Chat packet:
cPacket_Chat Left(m_Username + " left the game!");
World->Broadcast(Left, this);
}
}
// First stop sending thread
@ -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,135 +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);
}
else
{
++itr;
}
} // for itr - m_LoadedChunks[]
for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
{
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;
}
}
}
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
// 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
{
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++)
// For each distance add chunks in a hollow square centered around current position:
for (int i = -d; i <= d; ++i)
{
if (m_LoadedChunks[j] == NeededChunks[i])
{
bChunkMissing = false;
break;
}
}
if (bChunkMissing)
StreamChunk(ChunkPosX + d, ChunkPosZ + i);
StreamChunk(ChunkPosX - d, ChunkPosZ + i);
} // for i
for (int i = -d + 1; i < d; ++i)
{
MissingChunks[MissIndex] = NeededChunks[i];
MissIndex++;
}
}
if (MissIndex > 0)
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
{
// 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 touch 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
m_LoadedChunks[x + z * VIEWDISTANCE] = NULL;
}
}
}
}
StreamChunksSmart(MissingChunks, MissIndex);
memcpy(m_LoadedChunks, NeededChunks, sizeof(NeededChunks));
}
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
}
// Sends chunks to the player from the player position outward
void cClientHandle::StreamChunksSmart(cChunk** a_Chunks, unsigned int a_NumChunks)
void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ)
{
int X = (int)floor(m_Player->GetPosX() / 16);
int Y = (int)floor(m_Player->GetPosY() / 128);
int Z = (int)floor(m_Player->GetPosZ() / 16);
cWorld * World = m_Player->GetWorld();
assert(World != NULL);
bool bAllDone = false;
while(!bAllDone)
cChunkPtr Chunk = World->GetChunk(a_ChunkX, 0, a_ChunkZ);
if (!Chunk->HasClient(this))
{
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)
{
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;
}
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));
}
}
@ -393,18 +421,41 @@ void cClientHandle::StreamChunksSmart(cChunk** a_Chunks, unsigned int a_NumChunk
// This removes the client from all chunks. Used when switching worlds
// Removes the client from all chunks. Used when switching worlds or destroying the player
void cClientHandle::RemoveFromAllChunks()
{
for(int i = 0; i < VIEWDISTANCE*VIEWDISTANCE; i++)
cCSLock Lock(m_CSChunkLists);
cWorld * World = m_Player->GetWorld();
if (World != NULL)
{
if (m_LoadedChunks[i])
for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end(); ++itr)
{
m_LoadedChunks[i]->RemoveClient(this);
m_LoadedChunks[i]->AsyncUnload(this);
m_LoadedChunks[i] = 0;
World->GetChunk(itr->m_ChunkX, 0, itr->m_ChunkZ)->RemoveClient(this);
}
}
m_LoadedChunks.clear();
m_ChunksToSend.clear();
}
void cClientHandle::ChunkJustSent(cChunk * a_ChunkCompleted)
{
cCSLock Lock(m_CSChunkLists);
for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr)
{
if (((*itr).m_ChunkX == a_ChunkCompleted->GetPosX()) && ((*itr).m_ChunkZ == a_ChunkCompleted->GetPosZ()))
{
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 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 csAuthenticating:
{
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 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 csDownloadingWorld:
{
// 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)

View File

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

View File

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

View File

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

View File

@ -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 );
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);
}
typedef std::list< cClientHandle* > ClientList;
ClientList BeforeClients;
if( Chunk )
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)
{
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 );
}
}
// Now compare clients after with before
for( ClientList::iterator itr = AfterClients.begin(); itr != AfterClients.end(); ++itr )
{
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);
}
}
void cEntity::RemoveFromChunk( cChunk* a_Chunk )
{
if( m_World )
{
cChunk* Chunk = ( a_Chunk ? a_Chunk : (cChunk*)m_World->GetChunkUnreliable( m_ChunkX, m_ChunkY, m_ChunkZ ) );
if( Chunk )
{
cPacket_DestroyEntity DestroyEntity( this );
Chunk->Broadcast( DestroyEntity );
Chunk->RemoveEntity( *this );
m_bRemovedFromChunk = true;
RemoveFromChunk();
}
}
}
void cEntity::RemoveFromChunk(void)
{
if ( m_World == NULL )
{
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 )
{
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 );
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
};
cItem * m_Items;
cItem * m_CookingItem;
float m_CookTime;
float m_TimeCooked;
float m_BurnTime;
float m_TimeBurned;
};

View File

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

View File

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

View File

@ -39,7 +39,6 @@ public:
private:
AString mThreadName;
cEvent mEvent; // This event is set when the thread begins executing
#ifdef _WIN32

View File

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

View File

@ -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();
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;
return m_World->FindClosestPlayer(m_Pos, m_SightDistance);
}
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 )
if (!m_bVisible )
{
cChunk* Chunk = GetWorld()->GetChunk( m_ChunkX, m_ChunkY, m_ChunkZ );
Chunk->Broadcast( SpawnPacket, m_ClientHandle );
}
else
{
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()
{
if( m_Color != '-' )
return cChatColor::MakeColor( m_Color );
if( m_pState->Groups.size() < 1 )
AString cPlayer::GetColor(void) const
{
if ( m_Color != '-' )
{
return cChatColor::MakeColor( m_Color );
}
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();

View File

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

View File

@ -1,7 +1,5 @@
#pragma once
#include <list>
#include <string> // TODO - use const char*
#pragma once
struct lua_State;
class cLuaCommandBinder;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
};
typedef std::vector <std::pair<cSimulator *, short> *> cSimulators;
cSimulators m_Simulators;
long long m_Ticks;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
if (m_Socket.Connect(a_Address, a_Port) != 0)
{
//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) ) )
{
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();
}
}

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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;
cCriticalSection* m_ClientHandleCriticalSection;
cCriticalSection* m_EntitiesCriticalSection;
cCriticalSection* m_ChunksCriticalSection;
// The cRedstone class simulates redstone and needs access to m_RSList
friend class cRedstone;
std::vector<int> m_RSList;
cWorldGenerator* m_WorldGenerator;
cSimulatorManager * m_SimulatorManager;
cSandSimulator * m_SandSimulator;
cWaterSimulator * m_WaterSimulator;
cLavaSimulator * m_LavaSimulator;
cFireSimulator * m_FireSimulator;
cCriticalSection m_CSClients;
cCriticalSection m_CSEntities;
cCriticalSection m_CSPlayers;
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;
cEntityList m_RemoveEntityQueue;
cEntityList m_AllEntities;
cClientHandleList m_Clients;
cPlayerList m_Players;
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 AddToRemoveEntityQueue( cEntity & a_Entity );
void RemoveEntity( cEntity* a_Entity );
void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick
void RemoveEntity( cEntity * a_Entity );
void UnloadUnusedChunks();
}; //tolua_export

View File

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

View File

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

View File

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

View File

@ -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 );
};
virtual void GenerateTerrain( cChunkPtr a_Chunk ) override;
virtual void GenerateFoliage( cChunkPtr a_Chunk ) override;
};

View File

@ -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
// _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
#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;
}

View File

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

View File

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

View File

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

View File

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

View File

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