summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/Bindings.cpp267
-rw-r--r--source/Bindings.h2
-rw-r--r--source/ClientHandle.cpp14
-rw-r--r--source/OSSupport/IsThread.cpp44
-rw-r--r--source/OSSupport/IsThread.h6
-rw-r--r--source/Protocol/ProtocolRecognizer.cpp17
-rw-r--r--source/Root.cpp15
-rw-r--r--source/Root.h5
-rw-r--r--source/Server.cpp171
-rw-r--r--source/Server.h54
-rw-r--r--source/World.cpp315
-rw-r--r--source/World.h85
-rw-r--r--source/WorldStorage/WorldStorage.cpp9
-rw-r--r--source/WorldStorage/WorldStorage.h1
14 files changed, 608 insertions, 397 deletions
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index fb532b6f2..46c5f0f07 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 08/11/13 14:53:45.
+** Generated automatically by tolua++-1.0.92 on 08/11/13 20:59:51.
*/
#ifndef __cplusplus
@@ -11306,6 +11306,135 @@ static int tolua_get_cPlugin_NewLua___cWebPlugin__(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: GetDescription of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetDescription00
+static int tolua_AllToLua_cServer_GetDescription00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetDescription'", NULL);
+#endif
+ {
+ const AString tolua_ret = (const AString) self->GetDescription();
+ tolua_pushcppstring(tolua_S,(const char*)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetDescription'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetMaxPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetMaxPlayers00
+static int tolua_AllToLua_cServer_GetMaxPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetMaxPlayers();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: GetNumPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_GetNumPlayers00
+static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,2,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL);
+#endif
+ {
+ int tolua_ret = (int) self->GetNumPlayers();
+ tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
+ }
+ }
+ return 1;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
+/* method: SetMaxPlayers of class cServer */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_SetMaxPlayers00
+static int tolua_AllToLua_cServer_SetMaxPlayers00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) ||
+ !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
+ !tolua_isnoobj(tolua_S,3,&tolua_err)
+ )
+ goto tolua_lerror;
+ else
+#endif
+ {
+ cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0);
+ int a_MaxPlayers = ((int) tolua_tonumber(tolua_S,2,0));
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL);
+#endif
+ {
+ self->SetMaxPlayers(a_MaxPlayers);
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: BroadcastChat of class cServer */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_BroadcastChat00
static int tolua_AllToLua_cServer_BroadcastChat00(lua_State* tolua_S)
@@ -11954,103 +12083,6 @@ static int tolua_AllToLua_cWorld_UnloadUnusedChunks00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
-/* method: GetMaxPlayers of class cWorld */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetMaxPlayers00
-static int tolua_AllToLua_cWorld_GetMaxPlayers00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"const cWorld",0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,2,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- const cWorld* self = (const cWorld*) tolua_tousertype(tolua_S,1,0);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetMaxPlayers'", NULL);
-#endif
- {
- unsigned int tolua_ret = (unsigned int) self->GetMaxPlayers();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetMaxPlayers'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: SetMaxPlayers of class cWorld */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_SetMaxPlayers00
-static int tolua_AllToLua_cWorld_SetMaxPlayers00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"cWorld",0,&tolua_err) ||
- !tolua_isnumber(tolua_S,2,0,&tolua_err) ||
- !tolua_isnoobj(tolua_S,3,&tolua_err)
- )
- goto tolua_lerror;
- else
-#endif
- {
- cWorld* self = (cWorld*) tolua_tousertype(tolua_S,1,0);
- int iMax = ((int) tolua_tonumber(tolua_S,2,0));
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SetMaxPlayers'", NULL);
-#endif
- {
- self->SetMaxPlayers(iMax);
- }
- }
- return 0;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'SetMaxPlayers'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
-/* method: GetNumPlayers of class cWorld */
-#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumPlayers00
-static int tolua_AllToLua_cWorld_GetNumPlayers00(lua_State* tolua_S)
-{
-#ifndef TOLUA_RELEASE
- tolua_Error tolua_err;
- if (
- !tolua_isusertype(tolua_S,1,"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);
-#ifndef TOLUA_RELEASE
- if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL);
-#endif
- {
- unsigned int tolua_ret = (unsigned int) self->GetNumPlayers();
- tolua_pushnumber(tolua_S,(lua_Number)tolua_ret);
- }
- }
- return 1;
-#ifndef TOLUA_RELEASE
- tolua_lerror:
- tolua_error(tolua_S,"#ferror in function 'GetNumPlayers'.",&tolua_err);
- return 0;
-#endif
-}
-#endif //#ifndef TOLUA_DISABLE
-
/* method: RegenerateChunk of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_RegenerateChunk00
static int tolua_AllToLua_cWorld_RegenerateChunk00(lua_State* tolua_S)
@@ -13402,6 +13434,37 @@ static int tolua_AllToLua_cWorld_SaveAllChunks00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
+/* method: QueueSaveAllChunks of class cWorld */
+#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_QueueSaveAllChunks00
+static int tolua_AllToLua_cWorld_QueueSaveAllChunks00(lua_State* tolua_S)
+{
+#ifndef TOLUA_RELEASE
+ tolua_Error tolua_err;
+ if (
+ !tolua_isusertype(tolua_S,1,"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);
+#ifndef TOLUA_RELEASE
+ if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueSaveAllChunks'", NULL);
+#endif
+ {
+ self->QueueSaveAllChunks();
+ }
+ }
+ return 0;
+#ifndef TOLUA_RELEASE
+ tolua_lerror:
+ tolua_error(tolua_S,"#ferror in function 'QueueSaveAllChunks'.",&tolua_err);
+ return 0;
+#endif
+}
+#endif //#ifndef TOLUA_DISABLE
+
/* method: GetNumChunks of class cWorld */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cWorld_GetNumChunks00
static int tolua_AllToLua_cWorld_GetNumChunks00(lua_State* tolua_S)
@@ -29783,6 +29846,10 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cServer","cServer","",NULL);
tolua_beginmodule(tolua_S,"cServer");
+ tolua_function(tolua_S,"GetDescription",tolua_AllToLua_cServer_GetDescription00);
+ tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cServer_GetMaxPlayers00);
+ tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cServer_GetNumPlayers00);
+ tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cServer_SetMaxPlayers00);
tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cServer_BroadcastChat00);
tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cServer_SendMessage00);
tolua_function(tolua_S,"GetServerID",tolua_AllToLua_cServer_GetServerID00);
@@ -29806,9 +29873,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetDimension",tolua_AllToLua_cWorld_GetDimension00);
tolua_function(tolua_S,"GetHeight",tolua_AllToLua_cWorld_GetHeight00);
tolua_function(tolua_S,"UnloadUnusedChunks",tolua_AllToLua_cWorld_UnloadUnusedChunks00);
- tolua_function(tolua_S,"GetMaxPlayers",tolua_AllToLua_cWorld_GetMaxPlayers00);
- tolua_function(tolua_S,"SetMaxPlayers",tolua_AllToLua_cWorld_SetMaxPlayers00);
- tolua_function(tolua_S,"GetNumPlayers",tolua_AllToLua_cWorld_GetNumPlayers00);
tolua_function(tolua_S,"RegenerateChunk",tolua_AllToLua_cWorld_RegenerateChunk00);
tolua_function(tolua_S,"GenerateChunk",tolua_AllToLua_cWorld_GenerateChunk00);
tolua_function(tolua_S,"SetBlock",tolua_AllToLua_cWorld_SetBlock00);
@@ -29845,6 +29909,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetBiomeAt",tolua_AllToLua_cWorld_GetBiomeAt00);
tolua_function(tolua_S,"GetName",tolua_AllToLua_cWorld_GetName00);
tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cWorld_SaveAllChunks00);
+ tolua_function(tolua_S,"QueueSaveAllChunks",tolua_AllToLua_cWorld_QueueSaveAllChunks00);
tolua_function(tolua_S,"GetNumChunks",tolua_AllToLua_cWorld_GetNumChunks00);
tolua_function(tolua_S,"GetGeneratorQueueLength",tolua_AllToLua_cWorld_GetGeneratorQueueLength00);
tolua_function(tolua_S,"GetLightingQueueLength",tolua_AllToLua_cWorld_GetLightingQueueLength00);
diff --git a/source/Bindings.h b/source/Bindings.h
index ec1e372f7..10f874e41 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
-** Generated automatically by tolua++-1.0.92 on 08/11/13 14:53:46.
+** Generated automatically by tolua++-1.0.92 on 08/11/13 20:59:52.
*/
/* Exported function */
diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp
index 102153de1..14b052652 100644
--- a/source/ClientHandle.cpp
+++ b/source/ClientHandle.cpp
@@ -421,11 +421,11 @@ void cClientHandle::HandlePing(void)
// Somebody tries to retrieve information about the server
AString Reply;
Printf(Reply, "%s%s%i%s%i",
- cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
+ cRoot::Get()->GetServer()->GetDescription().c_str(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetNumPlayers(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetMaxPlayers()
);
Kick(Reply.c_str());
}
@@ -1176,7 +1176,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
{
if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, a_Username))
{
- if (cRoot::Get()->GetDefaultWorld()->GetNumPlayers() >= cRoot::Get()->GetDefaultWorld()->GetMaxPlayers())
+ if (cRoot::Get()->GetServer()->GetNumPlayers() >= cRoot::Get()->GetServer()->GetMaxPlayers())
{
Kick("The server is currently full :(-- Try again later");
return false;
@@ -1191,7 +1191,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
{
- if( a_EntityID != m_Player->GetUniqueID() )
+ if (a_EntityID != m_Player->GetUniqueID())
{
// We should only receive entity actions from the entity that is performing the action
return;
diff --git a/source/OSSupport/IsThread.cpp b/source/OSSupport/IsThread.cpp
index 257c5c876..45e329a68 100644
--- a/source/OSSupport/IsThread.cpp
+++ b/source/OSSupport/IsThread.cpp
@@ -116,36 +116,38 @@ bool cIsThread::Start(void)
+void cIsThread::Stop(void)
+{
+ if (m_Handle == NULL)
+ {
+ return;
+ }
+ m_ShouldTerminate = true;
+ Wait();
+}
+
+
+
+
+
bool cIsThread::Wait(void)
{
- #ifdef _WIN32
+ if (m_Handle == NULL)
+ {
+ return true;
+ }
+ LOGD("Waiting for thread %s to finish", m_ThreadName.c_str());
- if (m_Handle == NULL)
- {
- return true;
- }
- // Cannot log, logger may already be stopped:
- // LOG("Waiting for thread \"%s\" to terminate.", m_ThreadName.c_str());
+ #ifdef _WIN32
int res = WaitForSingleObject(m_Handle, INFINITE);
m_Handle = NULL;
- // Cannot log, logger may already be stopped:
- // LOG("Thread \"%s\" %s terminated, GLE = %d", m_ThreadName.c_str(), (res == WAIT_OBJECT_0) ? "" : "not", GetLastError());
+ LOGD("Thread %s finished", m_ThreadName.c_str());
return (res == WAIT_OBJECT_0);
-
#else // _WIN32
-
- if (!m_HasStarted)
- {
- return true;
- }
- // Cannot log, logger may already be stopped:
- // LOG("Waiting for thread \"%s\" to terminate.", m_ThreadName.c_str());
int res = pthread_join(m_Handle, NULL);
- m_HasStarted = false;
- // Cannot log, logger may already be stopped:
- // LOG("Thread \"%s\" %s terminated, errno = %d", m_ThreadName.c_str(), (res == 0) ? "" : "not", errno);
+ m_Handle = NULL;
+ LOGD("Thread %s finished", m_ThreadName.c_str());
return (res == 0);
-
#endif // else _WIN32
}
diff --git a/source/OSSupport/IsThread.h b/source/OSSupport/IsThread.h
index 2a4451a4a..9b7f0b73e 100644
--- a/source/OSSupport/IsThread.h
+++ b/source/OSSupport/IsThread.h
@@ -39,6 +39,9 @@ public:
/// Starts the thread; returns without waiting for the actual start
bool Start(void);
+ /// Signals the thread to terminate and waits until it's finished
+ void Stop(void);
+
/// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag
bool Wait(void);
@@ -54,7 +57,9 @@ private:
static DWORD_PTR __stdcall thrExecute(LPVOID a_Param)
{
+ HWND IdentificationWnd = CreateWindow("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL);
((cIsThread *)a_Param)->Execute();
+ DestroyWindow(IdentificationWnd);
return 0;
}
@@ -70,7 +75,6 @@ private:
}
#endif // else _WIN32
-
} ;
diff --git a/source/Protocol/ProtocolRecognizer.cpp b/source/Protocol/ProtocolRecognizer.cpp
index 290f2df75..35c8fe993 100644
--- a/source/Protocol/ProtocolRecognizer.cpp
+++ b/source/Protocol/ProtocolRecognizer.cpp
@@ -14,6 +14,7 @@
#include "Protocol16x.h"
#include "../ClientHandle.h"
#include "../Root.h"
+#include "../Server.h"
#include "../World.h"
#include "../ChatColor.h"
@@ -729,11 +730,11 @@ void cProtocolRecognizer::HandleServerPing(void)
{
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
Printf(Reply, "%s%s%i%s%i",
- cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
- cChatColor::Delimiter.c_str(),
- cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
+ cRoot::Get()->GetServer()->GetDescription().c_str(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetNumPlayers(),
+ cChatColor::Delimiter.c_str(),
+ cRoot::Get()->GetServer()->GetMaxPlayers()
);
break;
}
@@ -759,9 +760,9 @@ void cProtocolRecognizer::HandleServerPing(void)
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
AString NumPlayers;
- Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers());
+ Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers());
AString MaxPlayers;
- Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers());
+ Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
AString ProtocolVersionNum;
Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
@@ -775,7 +776,7 @@ void cProtocolRecognizer::HandleServerPing(void)
Reply.push_back(0);
Reply.append(ProtocolVersionTxt);
Reply.push_back(0);
- Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription());
+ Reply.append(cRoot::Get()->GetServer()->GetDescription());
Reply.push_back(0);
Reply.append(NumPlayers);
Reply.push_back(0);
diff --git a/source/Root.cpp b/source/Root.cpp
index 5ec27aa0d..166932cf2 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -270,8 +270,9 @@ void cRoot::LoadWorlds(void)
void cRoot::StartWorlds(void)
{
- for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr )
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
+ itr->second->Start();
itr->second->InitializeSpawn();
}
}
@@ -282,9 +283,9 @@ void cRoot::StartWorlds(void)
void cRoot::StopWorlds(void)
{
- for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr )
+ for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
- itr->second->StopThreads();
+ itr->second->Stop();
}
}
@@ -344,7 +345,7 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
-void cRoot::TickWorlds(float a_Dt)
+void cRoot::TickCommands(void)
{
// Execute any pending commands:
cCommandQueue PendingCommands;
@@ -356,12 +357,6 @@ void cRoot::TickWorlds(float a_Dt)
{
ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output));
}
-
- // Tick the worlds:
- for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
- {
- itr->second->Tick(a_Dt);
- }
}
diff --git a/source/Root.h b/source/Root.h
index 1e2befcd4..262c9b0e5 100644
--- a/source/Root.h
+++ b/source/Root.h
@@ -85,9 +85,10 @@ public:
/// Called by cAuthenticator to auth the specified user
void AuthenticateUser(int a_ClientID);
-
- void TickWorlds(float a_Dt);
+ /// Executes commands queued in the command queue
+ void TickCommands(void);
+
/// Returns the number of chunks loaded
int GetTotalChunkCount(void); // tolua_export
diff --git a/source/Server.cpp b/source/Server.cpp
index b07c65f04..0045d4808 100644
--- a/source/Server.cpp
+++ b/source/Server.cpp
@@ -59,18 +59,59 @@ typedef std::list< cClientHandle* > ClientList;
-struct cServer::sServerState
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cServer::cTickThread:
+
+cServer::cTickThread::cTickThread(cServer & a_Server) :
+ super("ServerTickThread"),
+ m_Server(a_Server)
{
- sServerState()
- : pTickThread(NULL)
- , bStopTickThread(false)
- {}
+}
+
+
+
+
+
+void cServer::cTickThread::Execute(void)
+{
+ cTimer Timer;
+
+ long long msPerTick = 50; // TODO - Put this in server config file
+ long long LastTime = Timer.GetNowTime();
+
+ while (!m_ShouldTerminate)
+ {
+ long long NowTime = Timer.GetNowTime();
+ float DeltaTime = (float)(NowTime-LastTime);
+ m_ShouldTerminate = !m_Server.Tick(DeltaTime);
+ long long TickTime = Timer.GetNowTime() - NowTime;
+
+ if (TickTime < msPerTick)
+ {
+ // Stretch tick time until it's at least msPerTick
+ cSleep::MilliSleep((unsigned int)(msPerTick - TickTime));
+ }
- cThread* pTickThread; bool bStopTickThread;
+ LastTime = NowTime;
+ }
+}
- cEvent RestartEvent;
- std::string ServerID;
-};
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cServer:
+
+cServer::cServer(void) :
+ m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"),
+ m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"),
+ m_bIsConnected(false),
+ m_bRestarting(false),
+ m_RCONServer(*this),
+ m_TickThread(*this)
+{
+}
@@ -123,6 +164,9 @@ void cServer::RemoveClient(const cClientHandle * a_Client)
bool cServer::InitServer(cIniFile & a_SettingsIni)
{
+ m_Description = a_SettingsIni.GetValue ("Server", "Description", "MCServer! - In C++!").c_str();
+ m_MaxPlayers = a_SettingsIni.GetValueI("Server", "MaxPlayers", 100);
+
if (m_bIsConnected)
{
LOGERROR("ERROR: Trying to initialize server while server is already running!");
@@ -164,18 +208,17 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_bIsConnected = true;
- m_pState->ServerID = "-";
+ m_ServerID = "-";
if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
{
MTRand mtrand1;
- unsigned int r1 = (mtrand1.randInt()%1147483647) + 1000000000;
- unsigned int r2 = (mtrand1.randInt()%1147483647) + 1000000000;
+ unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
+ unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000;
std::ostringstream sid;
sid << std::hex << r1;
sid << std::hex << r2;
- std::string ServerID = sid.str();
- ServerID.resize(16, '0');
- m_pState->ServerID = ServerID;
+ m_ServerID = sid.str();
+ m_ServerID.resize(16, '0');
}
m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
@@ -201,35 +244,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
-cServer::cServer(void)
- : m_pState(new sServerState)
- , m_ListenThreadIPv4(*this, cSocket::IPv4, "Client")
- , m_ListenThreadIPv6(*this, cSocket::IPv6, "Client")
- , m_Millisecondsf(0)
- , m_Milliseconds(0)
- , m_bIsConnected(false)
- , m_bRestarting(false)
- , m_RCONServer(*this)
-{
-}
-
-
-
-
-
-cServer::~cServer()
-{
- // TODO: Shut down the server gracefully
- m_pState->bStopTickThread = true;
- delete m_pState->pTickThread; m_pState->pTickThread = NULL;
-
- delete m_pState;
-}
-
-
-
-
-
void cServer::PrepareKeys(void)
{
// TODO: Save and load key for persistence across sessions
@@ -303,15 +317,8 @@ void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_E
bool cServer::Tick(float a_Dt)
{
- m_Millisecondsf += a_Dt;
- if (m_Millisecondsf > 1.f)
- {
- m_Milliseconds += (int)m_Millisecondsf;
- m_Millisecondsf = m_Millisecondsf - (int)m_Millisecondsf;
- }
-
- cRoot::Get()->TickWorlds(a_Dt); // TODO - Maybe give all worlds their own thread?
-
+ cRoot::Get()->TickCommands();
+
cClientHandleList RemoveClients;
{
cCSLock Lock(m_CSClients);
@@ -332,8 +339,6 @@ bool cServer::Tick(float a_Dt)
delete *itr;
} // for itr - RemoveClients[]
- cRoot::Get()->GetPluginManager()->Tick(a_Dt);
-
if (!m_bRestarting)
{
return true;
@@ -341,7 +346,7 @@ bool cServer::Tick(float a_Dt)
else
{
m_bRestarting = false;
- m_pState->RestartEvent.Set();
+ m_RestartEvent.Set();
return false;
}
}
@@ -350,42 +355,8 @@ bool cServer::Tick(float a_Dt)
-void ServerTickThread( void * a_Param )
-{
- LOG("ServerTickThread");
- cServer *CServerObj = (cServer*)a_Param;
-
- cTimer Timer;
-
- long long msPerTick = 50; // TODO - Put this in server config file
- long long LastTime = Timer.GetNowTime();
-
- bool bKeepGoing = true;
- while( bKeepGoing )
- {
- long long NowTime = Timer.GetNowTime();
- float DeltaTime = (float)(NowTime-LastTime);
- bKeepGoing = CServerObj->Tick( DeltaTime );
- long long TickTime = Timer.GetNowTime() - NowTime;
-
- if( TickTime < msPerTick ) // Stretch tick time until it's at least msPerTick
- {
- cSleep::MilliSleep( (unsigned int)( msPerTick - TickTime ) );
- }
-
- LastTime = NowTime;
- }
-
- LOG("TICK THREAD STOPPED");
-}
-
-
-
-
-
bool cServer::Start(void)
{
- m_pState->pTickThread = new cThread( ServerTickThread, this, "cServer::ServerTickThread" );
if (!m_ListenThreadIPv4.Start())
{
return false;
@@ -394,7 +365,10 @@ bool cServer::Start(void)
{
return false;
}
- m_pState->pTickThread->Start( true );
+ if (!m_TickThread.Start())
+ {
+ return false;
+ }
return true;
}
@@ -497,13 +471,13 @@ void cServer::SendMessage(const AString & a_Message, cPlayer * a_Player /* = NUL
-void cServer::Shutdown()
+void cServer::Shutdown(void)
{
m_ListenThreadIPv4.Stop();
m_ListenThreadIPv6.Stop();
m_bRestarting = true;
- m_pState->RestartEvent.Wait();
+ m_RestartEvent.Wait();
cRoot::Get()->SaveAllChunks();
@@ -520,15 +494,6 @@ void cServer::Shutdown()
-const AString & cServer::GetServerID(void) const
-{
- return m_pState->ServerID;
-}
-
-
-
-
-
void cServer::KickUser(int a_ClientID, const AString & a_Reason)
{
cCSLock Lock(m_CSClients);
@@ -562,7 +527,7 @@ void cServer::AuthenticateUser(int a_ClientID)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cServer::cClientPacketThread:
+// cServer::cNotifyWriteThread:
cServer::cNotifyWriteThread::cNotifyWriteThread(void) :
super("ClientPacketThread"),
diff --git a/source/Server.h b/source/Server.h
index dd7a08735..a00485fa2 100644
--- a/source/Server.h
+++ b/source/Server.h
@@ -37,11 +37,20 @@ class cServer // tolua_export
public: // tolua_export
bool InitServer(cIniFile & a_SettingsIni);
- bool IsConnected(void) const { return m_bIsConnected;} // returns connection status
+ // tolua_begin
- void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export
+ const AString & GetDescription(void) const {return m_Description; }
- bool Tick(float a_Dt);
+ // Player counts:
+ int GetMaxPlayers(void) const {return m_MaxPlayers; }
+ int GetNumPlayers(void) const { return m_NumPlayers; }
+ void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
+
+ // tolua_end
+
+ // bool IsConnected(void) const { return m_bIsConnected;} // returns connection status
+
+ void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export
bool Start(void);
@@ -53,14 +62,14 @@ public: // tolua_export
/// Binds the built-in console commands with the plugin manager
static void BindBuiltInConsoleCommands(void);
- void Shutdown();
+ void Shutdown(void);
void SendMessage(const AString & a_Message, cPlayer * a_Player = NULL, bool a_bExclude = false ); // tolua_export
void KickUser(int a_ClientID, const AString & a_Reason);
void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user
- const AString & GetServerID(void) const; // tolua_export
+ const AString & GetServerID(void) const { return m_ServerID; } // tolua_export
void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client
@@ -103,8 +112,22 @@ private:
void NotifyClientWrite(const cClientHandle * a_Client);
} ;
- struct sServerState;
- sServerState * m_pState;
+ /// The server tick thread takes care of the players who aren't yet spawned in a world
+ class cTickThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+
+ public:
+ cTickThread(cServer & a_Server);
+
+ protected:
+ cServer & m_Server;
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+ } ;
+
cNotifyWriteThread m_NotifyWriteThread;
@@ -118,10 +141,6 @@ private:
int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini
- // Time since server was started
- float m_Millisecondsf;
- unsigned int m_Milliseconds;
-
bool m_bIsConnected; // true - connected false - not connected
bool m_bRestarting;
@@ -131,13 +150,24 @@ private:
cRCONServer m_RCONServer;
+ AString m_Description;
+ int m_MaxPlayers;
+ int m_NumPlayers;
+
+ cTickThread m_TickThread;
+ cEvent m_RestartEvent;
+
+ /// The server ID used for client authentication
+ AString m_ServerID;
+
cServer(void);
- ~cServer();
/// Loads, or generates, if missing, RSA keys for protocol encryption
void PrepareKeys(void);
+ bool Tick(float a_Dt);
+
// cListenThread::cCallback overrides:
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
}; // tolua_export
diff --git a/source/World.cpp b/source/World.cpp
index 3955b6136..341682a2a 100644
--- a/source/World.cpp
+++ b/source/World.cpp
@@ -1,3 +1,4 @@
+
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "BlockID.h"
@@ -128,6 +129,9 @@ protected:
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorldLightingProgress:
+
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
class cWorldLightingProgress :
public cIsThread
@@ -188,120 +192,60 @@ cWorld::cLock::cLock(cWorld & a_World) :
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cWorld:
+// cWorld::cTickThread:
-cWorld::cWorld(const AString & a_WorldName) :
- m_WorldAgeSecs(0),
- m_TimeOfDaySecs(0),
- m_WorldAge(0),
- m_TimeOfDay(0),
- m_LastTimeUpdate(0),
- m_LastSpawnMonster(0),
- m_RSList(0),
- m_Weather(eWeather_Sunny),
- m_WeatherInterval(24000) // Guaranteed 1 day of sunshine at server start :)
+cWorld::cTickThread::cTickThread(cWorld & a_World) :
+ super(Printf("WorldTickThread: %s", a_World.GetName().c_str())),
+ m_World(a_World)
{
- LOGD("cWorld::cWorld(%s)", a_WorldName.c_str());
- m_WorldName = a_WorldName;
- m_IniFileName = m_WorldName + "/world.ini";
+}
- cMakeDir::MakeDir(m_WorldName.c_str());
- MTRand r1;
- m_SpawnX = (double)((r1.randInt() % 1000) - 500);
- m_SpawnY = cChunkDef::Height;
- m_SpawnZ = (double)((r1.randInt() % 1000) - 500);
- m_GameMode = eGameMode_Creative;
- AString StorageSchema("Default");
- cIniFile IniFile(m_IniFileName);
- IniFile.ReadFile();
- AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
- m_Dimension = StringToDimension(Dimension);
- switch (m_Dimension)
+
+void cWorld::cTickThread::Execute(void)
+{
+ const int ClocksPerTick = CLOCKS_PER_SEC / 20;
+ clock_t LastTime = clock();
+ while (!m_ShouldTerminate)
{
- case dimNether:
- case dimOverworld:
- case dimEnd:
+ clock_t Start = clock();
+ m_World.Tick((float)(LastTime - Start) / CLOCKS_PER_SEC);
+ clock_t Now = clock();
+ if (Now - Start < ClocksPerTick)
{
- break;
+ cSleep::MilliSleep(1000 * (ClocksPerTick - (Now - Start)) / CLOCKS_PER_SEC);
}
- default:
- {
- LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str());
- m_Dimension = dimOverworld;
- break;
- }
- } // switch (m_Dimension)
- m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX);
- m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY);
- m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ);
- StorageSchema = IniFile.GetValueSet ("Storage", "Schema", StorageSchema);
- m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
- m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
- m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
- m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true);
- m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true);
- m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true);
- m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true);
- m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false);
- m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true);
- m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true);
- m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
- m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
- m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
- m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
- m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
-
- m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
-
- m_Lighting.Start(this);
- m_Storage.Start(this, StorageSchema);
- m_Generator.Start(this, IniFile);
-
- m_bAnimals = true;
- m_SpawnMonsterRate = 200; // 1 mob each 10 seconds
- cIniFile IniFile2("settings.ini");
- if (IniFile2.ReadFile())
- {
- m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true);
- m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks
-
- // TODO: Move this into cServer instead:
- SetMaxPlayers(IniFile2.GetValueI("Server", "MaxPlayers", 100));
- m_Description = IniFile2.GetValue("Server", "Description", "MCServer! - In C++!").c_str();
+ LastTime = Start;
}
+}
- m_ChunkMap = new cChunkMap(this);
-
- m_ChunkSender.Start(this);
- m_LastSave = 0;
- m_LastUnload = 0;
- // preallocate some memory for ticking blocks so we don�t need to allocate that often
- m_BlockTickQueue.reserve(1000);
- m_BlockTickQueueCopy.reserve(1000);
- // Simulators:
- m_SimulatorManager = new cSimulatorManager(*this);
- m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
- m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
- m_SandSimulator = new cSandSimulator(*this, IniFile);
- m_FireSimulator = new cFireSimulator(*this, IniFile);
- m_RedstoneSimulator = new cRedstoneSimulator(*this);
- // Water and Lava simulators get registered in InitializeFluidSimulator()
- m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
- m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1);
- m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld:
- // Save any changes that the defaults may have done to the ini file:
- if (!IniFile.WriteFile())
- {
- LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
- }
+cWorld::cWorld(const AString & a_WorldName) :
+ m_WorldName(a_WorldName),
+ m_IniFileName(m_WorldName + "/world.ini"),
+ m_StorageSchema("Default"),
+ m_WorldAgeSecs(0),
+ m_TimeOfDaySecs(0),
+ m_WorldAge(0),
+ m_TimeOfDay(0),
+ m_LastTimeUpdate(0),
+ m_LastSpawnMonster(0),
+ m_RSList(0),
+ m_Weather(eWeather_Sunny),
+ m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
+ m_TickThread(*this)
+{
+ LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
+
+ cMakeDir::MakeDir(m_WorldName.c_str());
}
@@ -504,10 +448,115 @@ void cWorld::InitializeSpawn(void)
-void cWorld::StopThreads(void)
+void cWorld::Start(void)
+{
+ // TODO: Find a proper spawn location, based on the biomes (not in ocean)
+ m_SpawnX = (double)((m_TickRand.randInt() % 1000) - 500);
+ m_SpawnY = cChunkDef::Height;
+ m_SpawnZ = (double)((m_TickRand.randInt() % 1000) - 500);
+ m_GameMode = eGameMode_Creative;
+
+ cIniFile IniFile(m_IniFileName);
+ if (!IniFile.ReadFile())
+ {
+ LOGWARNING("Cannot read world settings from \"%s\", defaults will be used.", m_IniFileName.c_str());
+ }
+ AString Dimension = IniFile.GetValueSet("General", "Dimension", "Overworld");
+ m_Dimension = StringToDimension(Dimension);
+ switch (m_Dimension)
+ {
+ case dimNether:
+ case dimOverworld:
+ case dimEnd:
+ {
+ break;
+ }
+ default:
+ {
+ LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", Dimension.c_str());
+ m_Dimension = dimOverworld;
+ break;
+ }
+ } // switch (m_Dimension)
+ m_SpawnX = IniFile.GetValueSetF("SpawnPosition", "X", m_SpawnX);
+ m_SpawnY = IniFile.GetValueSetF("SpawnPosition", "Y", m_SpawnY);
+ m_SpawnZ = IniFile.GetValueSetF("SpawnPosition", "Z", m_SpawnZ);
+ m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema);
+ m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
+ m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
+ m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
+ m_IsCarrotsBonemealable = IniFile.GetValueSetB("Plants", "IsCarrotsBonemealable", true);
+ m_IsCropsBonemealable = IniFile.GetValueSetB("Plants", "IsCropsBonemealable", true);
+ m_IsGrassBonemealable = IniFile.GetValueSetB("Plants", "IsGrassBonemealable", true);
+ m_IsMelonStemBonemealable = IniFile.GetValueSetB("Plants", "IsMelonStemBonemealable", true);
+ m_IsMelonBonemealable = IniFile.GetValueSetB("Plants", "IsMelonBonemealable", false);
+ m_IsPotatoesBonemealable = IniFile.GetValueSetB("Plants", "IsPotatoesBonemealable", true);
+ m_IsPumpkinStemBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinStemBonemealable", true);
+ m_IsPumpkinBonemealable = IniFile.GetValueSetB("Plants", "IsPumpkinBonemealable", false);
+ m_IsSaplingBonemealable = IniFile.GetValueSetB("Plants", "IsSaplingBonemealable", true);
+ m_IsSugarcaneBonemealable = IniFile.GetValueSetB("Plants", "IsSugarcaneBonemealable", false);
+ m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
+ m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
+
+ m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
+
+ m_bAnimals = true;
+ m_SpawnMonsterRate = 200; // 1 mob each 10 seconds
+ cIniFile IniFile2("settings.ini");
+ if (IniFile2.ReadFile())
+ {
+ m_bAnimals = IniFile2.GetValueB("Monsters", "AnimalsOn", true);
+ m_SpawnMonsterRate = (Int64)(IniFile2.GetValueF("Monsters", "AnimalSpawnInterval", 10) * 20); // Convert from secs to ticks
+
+ }
+
+ m_ChunkMap = new cChunkMap(this);
+
+ m_LastSave = 0;
+ m_LastUnload = 0;
+
+ // preallocate some memory for ticking blocks so we don�t need to allocate that often
+ m_BlockTickQueue.reserve(1000);
+ m_BlockTickQueueCopy.reserve(1000);
+
+ // Simulators:
+ m_SimulatorManager = new cSimulatorManager(*this);
+ m_WaterSimulator = InitializeFluidSimulator(IniFile, "Water", E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER);
+ m_LavaSimulator = InitializeFluidSimulator(IniFile, "Lava", E_BLOCK_LAVA, E_BLOCK_STATIONARY_LAVA);
+ m_SandSimulator = new cSandSimulator(*this, IniFile);
+ m_FireSimulator = new cFireSimulator(*this, IniFile);
+ m_RedstoneSimulator = new cRedstoneSimulator(*this);
+
+ // Water and Lava simulators get registered in InitializeFluidSimulator()
+ m_SimulatorManager->RegisterSimulator(m_SandSimulator, 1);
+ m_SimulatorManager->RegisterSimulator(m_FireSimulator, 1);
+ m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
+
+ m_Lighting.Start(this);
+ m_Storage.Start(this, m_StorageSchema);
+ m_Generator.Start(this, IniFile);
+ m_ChunkSender.Start(this);
+ m_TickThread.Start();
+
+ // Save any changes that the defaults may have done to the ini file:
+ if (!IniFile.WriteFile())
+ {
+ LOGWARNING("Could not write world config to %s", m_IniFileName.c_str());
+ }
+
+}
+
+
+
+
+
+void cWorld::Stop(void)
{
+ m_TickThread.Stop();
+ m_Lighting.Stop();
m_Generator.Stop();
m_ChunkSender.Stop();
+ m_Storage.Stop();
}
@@ -539,6 +588,7 @@ void cWorld::Tick(float a_Dt)
m_ChunkMap->Tick(a_Dt);
TickQueuedBlocks(a_Dt);
+ TickQueuedTasks();
GetSimulatorManager()->Simulate(a_Dt);
@@ -740,6 +790,27 @@ void cWorld::TickSpawnMobs(float a_Dt)
+void cWorld::TickQueuedTasks(void)
+{
+ // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
+ cTasks Tasks;
+ {
+ cCSLock Lock(m_CSTasks);
+ std::swap(Tasks, m_Tasks);
+ }
+
+ // Execute and delete each task:
+ for (cTasks::iterator itr = m_Tasks.begin(), end = m_Tasks.end(); itr != end; ++itr)
+ {
+ (*itr)->Run(*this);
+ delete *itr;
+ } // for itr - m_Tasks[]
+}
+
+
+
+
+
void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
{
return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
@@ -1900,19 +1971,6 @@ void cWorld::CollectPickupsByPlayer(cPlayer * a_Player)
-void cWorld::SetMaxPlayers(int iMax)
-{
- m_MaxPlayers = MAX_PLAYERS;
- if (iMax > 0 && iMax < MAX_PLAYERS)
- {
- m_MaxPlayers = iMax;
- }
-}
-
-
-
-
-
void cWorld::AddPlayer(cPlayer * a_Player)
{
cCSLock Lock(m_CSPlayers);
@@ -2279,6 +2337,25 @@ void cWorld::SaveAllChunks(void)
+void cWorld::QueueSaveAllChunks(void)
+{
+ QueueTask(new cWorld::cTaskSaveAllChunks);
+}
+
+
+
+
+
+void cWorld::QueueTask(cTask * a_Task)
+{
+ cCSLock Lock(m_CSTasks);
+ m_Tasks.push_back(a_Task);
+}
+
+
+
+
+
void cWorld::AddEntity(cEntity * a_Entity)
{
m_ChunkMap->AddEntity(a_Entity);
@@ -2306,11 +2383,13 @@ void cWorld::RemoveEntity(cEntity * a_Entity)
+/*
unsigned int cWorld::GetNumPlayers(void)
{
cCSLock Lock(m_CSPlayers);
return m_Players.size();
}
+*/
@@ -2522,3 +2601,15 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld::cTaskSaveAllChunks:
+
+void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
+{
+ a_World.SaveAllChunks();
+}
+
+
+
+
diff --git a/source/World.h b/source/World.h
index b9182e300..d84920470 100644
--- a/source/World.h
+++ b/source/World.h
@@ -69,6 +69,24 @@ public:
public:
cLock(cWorld & a_World);
} ;
+
+ /// A common ancestor for all tasks queued onto the tick thread
+ class cTask
+ {
+ public:
+ virtual void Run(cWorld & a_World) = 0;
+ } ;
+
+ typedef std::vector<cTask *> cTasks;
+
+ class cTaskSaveAllChunks :
+ public cTask
+ {
+ protected:
+ // cTask overrides:
+ virtual void Run(cWorld & a_World) override;
+ } ;
+
// tolua_begin
@@ -204,13 +222,6 @@ public:
void CollectPickupsByPlayer(cPlayer * a_Player);
- // MOTD
- const AString & GetDescription(void) const {return m_Description; } // FIXME: This should not be in cWorld
-
- // Max Players
- unsigned int GetMaxPlayers(void) const {return m_MaxPlayers; } // tolua_export
- void SetMaxPlayers(int iMax); // tolua_export
-
void AddPlayer( cPlayer* a_Player );
void RemovePlayer( cPlayer* a_Player );
@@ -223,8 +234,6 @@ public:
/// Finds a player from a partial or complete player name and calls the callback - case-insensitive
bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- unsigned int GetNumPlayers(); // tolua_export
-
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit);
@@ -470,10 +479,17 @@ public:
if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--;
}
- void SaveAllChunks(void); // tolua_export
+ /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead
+ void SaveAllChunks(void); // tolua_export
+
+ /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources
+ void QueueSaveAllChunks(void); // tolua_export
+
+ /// Queues a task onto the tick thread. The task object will be deleted once the task is finished
+ void QueueTask(cTask * a_Task);
/// Returns the number of chunks loaded
- int GetNumChunks() const; // tolua_export
+ int GetNumChunks() const; // tolua_export
/// Returns the number of chunks loaded and dirty, and in the lighting queue
void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
@@ -484,12 +500,14 @@ public:
inline int GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
- void Tick(float a_Dt);
-
void InitializeSpawn(void);
+ /// Starts threads that belong to this world
+ void Start(void);
+
/// Stops threads that belong to this world (part of deinit)
- void StopThreads(void);
+ void Stop(void);
+
void TickQueuedBlocks(float a_Dt);
struct BlockTickQueueItem
@@ -540,7 +558,28 @@ public:
private:
friend class cRoot;
+
+ class cTickThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+ public:
+ cTickThread(cWorld & a_World);
+
+ protected:
+ cWorld & m_World;
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+ } ;
+
+ AString m_WorldName;
+ AString m_IniFileName;
+
+ /// Name of the storage schema used to load and save chunks
+ AString m_StorageSchema;
+
/// The dimension of the world, used by the client to provide correct lighting scheme
eDimension m_Dimension;
@@ -583,8 +622,6 @@ private:
cWorldStorage m_Storage;
- AString m_Description;
-
unsigned int m_MaxPlayers;
cChunkMap * m_ChunkMap;
@@ -616,16 +653,26 @@ private:
cChunkSender m_ChunkSender;
cLightingThread m_Lighting;
-
- AString m_WorldName;
- AString m_IniFileName;
+ cTickThread m_TickThread;
+ /// Guards the m_Tasks
+ cCriticalSection m_CSTasks;
+
+ /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks
+ cTasks m_Tasks;
+
+
cWorld(const AString & a_WorldName);
~cWorld();
+ void Tick(float a_Dt);
+
void TickWeather(float a_Dt); // Handles weather each tick
void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick
+ /// Executes all tasks queued onto the tick thread
+ void TickQueuedTasks(void);
+
/// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section)
cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock);
}; // tolua_export
diff --git a/source/WorldStorage/WorldStorage.cpp b/source/WorldStorage/WorldStorage.cpp
index 8b055b240..7ff5ae8e8 100644
--- a/source/WorldStorage/WorldStorage.cpp
+++ b/source/WorldStorage/WorldStorage.cpp
@@ -84,6 +84,15 @@ bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName)
+void cWorldStorage::Stop(void)
+{
+ WaitForFinish();
+}
+
+
+
+
+
void cWorldStorage::WaitForFinish(void)
{
LOG("Waiting for the world storage to finish saving");
diff --git a/source/WorldStorage/WorldStorage.h b/source/WorldStorage/WorldStorage.h
index 064b2ffaf..bf8dbd3d5 100644
--- a/source/WorldStorage/WorldStorage.h
+++ b/source/WorldStorage/WorldStorage.h
@@ -75,6 +75,7 @@ public:
void UnqueueSave(const cChunkCoords & a_Chunk);
bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args
+ void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event
void WaitForFinish(void);
void WaitForQueuesEmpty(void);