Merge pull request #93 from mc-server/PerWorldThreads
Per world threads
This commit is contained in:
commit
1a52e89177
@ -1,24 +1,23 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function OnPlayerJoined(Player)
|
function OnPlayerJoined(Player)
|
||||||
--if( BannedPlayersIni:GetValueB("Banned", Player:GetName(), false) == true ) then
|
ShowMOTDTo(Player)
|
||||||
-- LOGINFO( Player:GetName() .. " tried to join, but is banned!" )
|
Player:GetWorld():BroadcastChat(cChatColor.Yellow .. "[JOIN] " .. cChatColor.White .. Player:GetName() .. " has joined the game")
|
||||||
-- KickPlayer(Player:GetName(), cChatColor.Red .. "You are banned!" )
|
return false
|
||||||
-- return true
|
|
||||||
--elseif( WhiteListIni:GetValueB("WhiteListSettings", "WhiteListOn", false ) == true ) then
|
|
||||||
-- if( WhiteListIni:GetValueB("WhiteList", Player:GetName(), false ) == false ) then
|
|
||||||
-- LOGINFO( Player:GetName() .. " tried to join, but is not whitelisted!" )
|
|
||||||
-- KickPlayer(Player:GetName(), cChatColor.Red .. "You are not whitelisted!" )
|
|
||||||
-- end
|
|
||||||
--else
|
|
||||||
ShowMOTDTo( Player )
|
|
||||||
local Server = cRoot:Get():GetServer()
|
|
||||||
Server:SendMessage(cChatColor.Yellow .. "[JOIN] " .. cChatColor.White .. Player:GetName() .. " has joined the game" )
|
|
||||||
return false
|
|
||||||
--end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function OnDisconnect(Player, Reason)
|
function OnDisconnect(Player, Reason)
|
||||||
local Server = cRoot:Get():GetServer()
|
Player:GetWorld():BroadcastChat(cChatColor.Yellow .. "[LEAVE] " .. cChatColor.White .. Player:GetName() .. " has left the game")
|
||||||
Server:SendMessage(cChatColor.Yellow .. "[LEAVE] " .. cChatColor.White .. Player:GetName() .. " has left the game" )
|
|
||||||
LOG("Player " .. Player:GetName() .. " has left the game.")
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -390,6 +390,14 @@
|
|||||||
RelativePath="..\source\Cuboid.h"
|
RelativePath="..\source\Cuboid.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\source\DeadlockDetect.cpp"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\source\DeadlockDetect.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\source\Defines.h"
|
RelativePath="..\source\Defines.h"
|
||||||
>
|
>
|
||||||
@ -2449,7 +2457,15 @@
|
|||||||
Name="Core"
|
Name="Core"
|
||||||
>
|
>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\ban.lua"
|
RelativePath="..\MCServer\Plugins\Core\back.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\ban-unban.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\clear.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2457,15 +2473,19 @@
|
|||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\coords.lua"
|
RelativePath="..\MCServer\Plugins\Core\do.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\gamemode.lua"
|
RelativePath="..\MCServer\Plugins\Core\functions.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\gotoworld.lua"
|
RelativePath="..\MCServer\Plugins\Core\give.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\gm.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2476,32 +2496,44 @@
|
|||||||
RelativePath="..\MCServer\Plugins\Core\item.lua"
|
RelativePath="..\MCServer\Plugins\Core\item.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\itemrepair.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\kick.lua"
|
RelativePath="..\MCServer\Plugins\Core\kick.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\kill.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\locate.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\main.lua"
|
RelativePath="..\MCServer\Plugins\Core\main.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\me.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\motd.lua"
|
RelativePath="..\MCServer\Plugins\Core\motd.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\onblockdig.lua"
|
RelativePath="..\MCServer\Plugins\Core\onbreakplaceblock.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\onblockplace.lua"
|
RelativePath="..\MCServer\Plugins\Core\ondeath.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\oncraftingnorecipe.lua"
|
RelativePath="..\MCServer\Plugins\Core\onjoinleave.lua"
|
||||||
>
|
|
||||||
</File>
|
|
||||||
<File
|
|
||||||
RelativePath="..\MCServer\Plugins\Core\onkilled.lua"
|
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2509,23 +2541,23 @@
|
|||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\onplayerjoin.lua"
|
RelativePath="..\MCServer\Plugins\Core\plugins.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\playerlist.lua"
|
RelativePath="..\MCServer\Plugins\Core\portal-worlds.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\pluginlist.lua"
|
RelativePath="..\MCServer\Plugins\Core\rank-groups.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\regeneratechunk.lua"
|
RelativePath="..\MCServer\Plugins\Core\regen.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\reload.lua"
|
RelativePath="..\MCServer\Plugins\Core\save-reload-stop.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2533,11 +2565,11 @@
|
|||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\stop.lua"
|
RelativePath="..\MCServer\Plugins\Core\teleport.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\teleport.lua"
|
RelativePath="..\MCServer\Plugins\Core\tell.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2549,11 +2581,11 @@
|
|||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\unban.lua"
|
RelativePath="..\MCServer\Plugins\Core\viewdistance.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\viewdistance.lua"
|
RelativePath="..\MCServer\Plugins\Core\weather.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
<File
|
<File
|
||||||
@ -2564,6 +2596,10 @@
|
|||||||
RelativePath="..\MCServer\Plugins\Core\web_manageplugins.lua"
|
RelativePath="..\MCServer\Plugins\Core\web_manageplugins.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\web_manageserver.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\MCServer\Plugins\Core\web_permissions.lua"
|
RelativePath="..\MCServer\Plugins\Core\web_permissions.lua"
|
||||||
>
|
>
|
||||||
@ -2580,6 +2616,10 @@
|
|||||||
RelativePath="..\MCServer\Plugins\Core\web_whitelist.lua"
|
RelativePath="..\MCServer\Plugins\Core\web_whitelist.lua"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\MCServer\Plugins\Core\worldlimiter.lua"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="ChatLog"
|
Name="ChatLog"
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
|
|
||||||
|
|
||||||
:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway
|
:: If there was a Git conflict, resolve it by resetting to HEAD; we're regenerating the files from scratch anyway
|
||||||
git checkout -- Bindings.cpp
|
git checkout --ours Bindings.cpp
|
||||||
git checkout -- Bindings.h
|
git add -u Bindings.cpp
|
||||||
|
git checkout --ours Bindings.h
|
||||||
|
git add -u Bindings.h
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
61106
source/Bindings.cpp
61106
source/Bindings.cpp
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
** Lua binding: AllToLua
|
** Lua binding: AllToLua
|
||||||
** Generated automatically by tolua++-1.0.92 on 08/12/13 11:15:34.
|
** Generated automatically by tolua++-1.0.92 on 08/14/13 19:48:00.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Exported function */
|
/* Exported function */
|
||||||
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
|
TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S);
|
||||||
|
|
||||||
|
@ -478,16 +478,16 @@ enum
|
|||||||
E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block
|
E_META_TORCH_ZP = 4, // Torch attached to the ZP side of its block
|
||||||
|
|
||||||
// E_BLOCK_WOODEN_DOUBLE_STEP metas:
|
// E_BLOCK_WOODEN_DOUBLE_STEP metas:
|
||||||
E_BLOCK_WOODEN_DOUBLE_STEP_APPLE = 0,
|
E_META_WOODEN_DOUBLE_STEP_APPLE = 0,
|
||||||
E_BLOCK_WOODEN_DOUBLE_STEP_CONIFER = 1,
|
E_META_WOODEN_DOUBLE_STEP_CONIFER = 1,
|
||||||
E_BLOCK_WOODEN_DOUBLE_STEP_BIRCH = 2,
|
E_META_WOODEN_DOUBLE_STEP_BIRCH = 2,
|
||||||
E_BLOCK_WOODEN_DOUBLE_STEP_JUNGLE = 3,
|
E_META_WOODEN_DOUBLE_STEP_JUNGLE = 3,
|
||||||
|
|
||||||
// E_BLOCK_WOODEN_STEP metas:
|
// E_BLOCK_WOODEN_STEP metas:
|
||||||
E_BLOCK_WOODEN_STEP_APPLE = 0,
|
E_META_WOODEN_STEP_APPLE = 0,
|
||||||
E_BLOCK_WOODEN_STEP_CONIFER = 1,
|
E_META_WOODEN_STEP_CONIFER = 1,
|
||||||
E_BLOCK_WOODEN_STEP_BIRCH = 2,
|
E_META_WOODEN_STEP_BIRCH = 2,
|
||||||
E_BLOCK_WOODEN_STEP_JUNGLE = 3,
|
E_META_WOODEN_STEP_JUNGLE = 3,
|
||||||
|
|
||||||
// E_BLOCK_WOOL metas:
|
// E_BLOCK_WOOL metas:
|
||||||
E_META_WOOL_WHITE = 0,
|
E_META_WOOL_WHITE = 0,
|
||||||
|
@ -170,13 +170,21 @@ cClientHandle::~cClientHandle()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::Destroy()
|
void cClientHandle::Destroy(void)
|
||||||
{
|
{
|
||||||
// Setting m_bDestroyed was moved to the bottom of Destroy(),
|
{
|
||||||
// otherwise the destructor may be called within another thread before the client is removed from chunks
|
cCSLock Lock(m_CSDestroyingState);
|
||||||
// http://forum.mc-server.org/showthread.php?tid=366
|
if (m_State >= csDestroying)
|
||||||
|
{
|
||||||
|
// Already called
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_State = csDestroying;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEBUG:
|
||||||
|
LOGD("%s: client %p, \"%s\"", __FUNCTION__, this, m_Username.c_str());
|
||||||
|
|
||||||
m_State = csDestroying;
|
|
||||||
if ((m_Player != NULL) && (m_Player->GetWorld() != NULL))
|
if ((m_Player != NULL) && (m_Player->GetWorld() != NULL))
|
||||||
{
|
{
|
||||||
RemoveFromAllChunks();
|
RemoveFromAllChunks();
|
||||||
@ -253,9 +261,8 @@ void cClientHandle::Authenticate(void)
|
|||||||
SendGameMode(m_Player->GetGameMode());
|
SendGameMode(m_Player->GetGameMode());
|
||||||
|
|
||||||
m_Player->Initialize(World);
|
m_Player->Initialize(World);
|
||||||
StreamChunks();
|
m_State = csAuthenticated;
|
||||||
m_State = csDownloadingWorld;
|
|
||||||
|
|
||||||
// Broadcast this player's spawning to all other players in the same chunk
|
// Broadcast this player's spawning to all other players in the same chunk
|
||||||
m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this);
|
m_Player->GetWorld()->BroadcastSpawnEntity(*m_Player, this);
|
||||||
|
|
||||||
@ -268,7 +275,7 @@ void cClientHandle::Authenticate(void)
|
|||||||
|
|
||||||
void cClientHandle::StreamChunks(void)
|
void cClientHandle::StreamChunks(void)
|
||||||
{
|
{
|
||||||
if ((m_State < csAuthenticating) || (m_State >= csDestroying))
|
if ((m_State < csAuthenticated) || (m_State >= csDestroying))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -421,11 +428,11 @@ void cClientHandle::HandlePing(void)
|
|||||||
// Somebody tries to retrieve information about the server
|
// Somebody tries to retrieve information about the server
|
||||||
AString Reply;
|
AString Reply;
|
||||||
Printf(Reply, "%s%s%i%s%i",
|
Printf(Reply, "%s%s%i%s%i",
|
||||||
cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
|
cRoot::Get()->GetServer()->GetDescription().c_str(),
|
||||||
cChatColor::Delimiter.c_str(),
|
cChatColor::Delimiter.c_str(),
|
||||||
cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
|
cRoot::Get()->GetServer()->GetNumPlayers(),
|
||||||
cChatColor::Delimiter.c_str(),
|
cChatColor::Delimiter.c_str(),
|
||||||
cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
|
cRoot::Get()->GetServer()->GetMaxPlayers()
|
||||||
);
|
);
|
||||||
Kick(Reply.c_str());
|
Kick(Reply.c_str());
|
||||||
}
|
}
|
||||||
@ -914,11 +921,24 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
|
|||||||
|
|
||||||
void cClientHandle::HandleChat(const AString & a_Message)
|
void cClientHandle::HandleChat(const AString & a_Message)
|
||||||
{
|
{
|
||||||
// We need to process messages in the Tick thread, to avoid deadlocks resulting from player-commands being processed
|
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
|
||||||
// in the SocketThread and waiting for acquiring the ChunkMap CS with Plugin CS locked
|
|
||||||
|
|
||||||
cCSLock Lock(m_CSMessages);
|
// If a command, perform it:
|
||||||
m_PendingMessages.push_back(a_Message);
|
AString Message(a_Message);
|
||||||
|
if (cRoot::Get()->GetServer()->Command(*this, Message))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a command, broadcast as a simple message:
|
||||||
|
AString Msg;
|
||||||
|
Printf(Msg, "<%s%s%s> %s",
|
||||||
|
m_Player->GetColor().c_str(),
|
||||||
|
m_Player->GetName().c_str(),
|
||||||
|
cChatColor::White.c_str(),
|
||||||
|
Message.c_str()
|
||||||
|
);
|
||||||
|
m_Player->GetWorld()->BroadcastChat(Msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1176,7 +1196,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
|
|||||||
{
|
{
|
||||||
if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(this, 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");
|
Kick("The server is currently full :(-- Try again later");
|
||||||
return false;
|
return false;
|
||||||
@ -1191,7 +1211,7 @@ bool cClientHandle::HandleHandshake(const AString & a_Username)
|
|||||||
|
|
||||||
void cClientHandle::HandleEntityAction(int a_EntityID, char a_ActionID)
|
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
|
// We should only receive entity actions from the entity that is performing the action
|
||||||
return;
|
return;
|
||||||
@ -1307,6 +1327,42 @@ void cClientHandle::SendData(const char * a_Data, int a_Size)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
|
||||||
|
{
|
||||||
|
ASSERT(m_Player != NULL);
|
||||||
|
|
||||||
|
if (a_SendRespawnPacket)
|
||||||
|
{
|
||||||
|
SendRespawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
cWorld * World = m_Player->GetWorld();
|
||||||
|
|
||||||
|
// Remove all associated chunks:
|
||||||
|
cChunkCoordsList Chunks;
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSChunkLists);
|
||||||
|
std::swap(Chunks, m_LoadedChunks);
|
||||||
|
m_ChunksToSend.clear();
|
||||||
|
}
|
||||||
|
for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
|
||||||
|
m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||||
|
} // for itr - Chunks[]
|
||||||
|
|
||||||
|
// Do NOT stream new chunks, the new world runs its own tick thread and may deadlock
|
||||||
|
// Instead, the chunks will be streamed when the client is moved to the new world's Tick list,
|
||||||
|
// by setting state to csAuthenticated
|
||||||
|
m_State = csAuthenticated;
|
||||||
|
m_LastStreamedChunkX = 0x7fffffff;
|
||||||
|
m_LastStreamedChunkZ = 0x7fffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cClientHandle::CheckBlockInteractionsRate(void)
|
bool cClientHandle::CheckBlockInteractionsRate(void)
|
||||||
{
|
{
|
||||||
ASSERT(m_Player != NULL);
|
ASSERT(m_Player != NULL);
|
||||||
@ -1342,6 +1398,20 @@ bool cClientHandle::CheckBlockInteractionsRate(void)
|
|||||||
|
|
||||||
void cClientHandle::Tick(float a_Dt)
|
void cClientHandle::Tick(float a_Dt)
|
||||||
{
|
{
|
||||||
|
// Process received network data:
|
||||||
|
AString IncomingData;
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSIncomingData);
|
||||||
|
std::swap(IncomingData, m_IncomingData);
|
||||||
|
}
|
||||||
|
m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
|
||||||
|
|
||||||
|
if (m_State == csAuthenticated)
|
||||||
|
{
|
||||||
|
StreamChunks();
|
||||||
|
m_State = csDownloadingWorld;
|
||||||
|
}
|
||||||
|
|
||||||
m_TimeSinceLastPacket += a_Dt;
|
m_TimeSinceLastPacket += a_Dt;
|
||||||
if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
|
if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
|
||||||
{
|
{
|
||||||
@ -1355,12 +1425,14 @@ void cClientHandle::Tick(float a_Dt)
|
|||||||
m_ShouldCheckDownloaded = false;
|
m_ShouldCheckDownloaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_Player == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Send a ping packet:
|
// Send a ping packet:
|
||||||
cTimer t1;
|
cTimer t1;
|
||||||
if (
|
if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime()))
|
||||||
(m_Player != NULL) && // Is logged in?
|
|
||||||
(m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
m_PingID++;
|
m_PingID++;
|
||||||
m_PingStartTime = t1.GetNowTime();
|
m_PingStartTime = t1.GetNowTime();
|
||||||
@ -1369,7 +1441,7 @@ void cClientHandle::Tick(float a_Dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle block break animation:
|
// Handle block break animation:
|
||||||
if ((m_Player != NULL) && (m_BlockDigAnimStage > -1))
|
if (m_BlockDigAnimStage > -1)
|
||||||
{
|
{
|
||||||
int lastAnimVal = m_BlockDigAnimStage;
|
int lastAnimVal = m_BlockDigAnimStage;
|
||||||
m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt);
|
m_BlockDigAnimStage += (int)(m_BlockDigAnimSpeed * a_Dt);
|
||||||
@ -1387,9 +1459,6 @@ void cClientHandle::Tick(float a_Dt)
|
|||||||
m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick);
|
m_CurrentExplosionTick = (m_CurrentExplosionTick + 1) % ARRAYCOUNT(m_NumExplosionsPerTick);
|
||||||
m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick];
|
m_RunningSumExplosions -= m_NumExplosionsPerTick[m_CurrentExplosionTick];
|
||||||
m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0;
|
m_NumExplosionsPerTick[m_CurrentExplosionTick] = 0;
|
||||||
|
|
||||||
// Process the queued messages:
|
|
||||||
ProcessPendingMessages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1955,7 +2024,7 @@ void cClientHandle::SendConfirmPosition(void)
|
|||||||
if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
|
if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
|
||||||
{
|
{
|
||||||
// Broadcast that this player has joined the game! Yay~
|
// Broadcast that this player has joined the game! Yay~
|
||||||
cRoot::Get()->GetServer()->BroadcastChat(m_Username + " joined the game!", this);
|
m_Player->GetWorld()->BroadcastChat(m_Username + " joined the game!", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
SendPlayerMoveLook();
|
SendPlayerMoveLook();
|
||||||
@ -2037,46 +2106,6 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::ProcessPendingMessages(void)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
AString Message;
|
|
||||||
|
|
||||||
// Extract one message from the PendingMessages buffer:
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CSMessages);
|
|
||||||
if (m_PendingMessages.empty())
|
|
||||||
{
|
|
||||||
// No more messages in the buffer, bail out
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Message = m_PendingMessages.front();
|
|
||||||
m_PendingMessages.pop_front();
|
|
||||||
} // Lock(m_CSMessages)
|
|
||||||
|
|
||||||
// If a command, perform it:
|
|
||||||
if (cRoot::Get()->GetServer()->Command(*this, Message))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a command, broadcast as a simple message:
|
|
||||||
AString Msg;
|
|
||||||
Printf(Msg, "<%s%s%s> %s",
|
|
||||||
m_Player->GetColor().c_str(),
|
|
||||||
m_Player->GetName().c_str(),
|
|
||||||
cChatColor::White.c_str(),
|
|
||||||
Message.c_str()
|
|
||||||
);
|
|
||||||
m_Player->GetWorld()->BroadcastChat(Msg);
|
|
||||||
} // while (true)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::PacketBufferFull(void)
|
void cClientHandle::PacketBufferFull(void)
|
||||||
{
|
{
|
||||||
// Too much data in the incoming queue, the server is probably too busy, kick the client:
|
// Too much data in the incoming queue, the server is probably too busy, kick the client:
|
||||||
@ -2116,30 +2145,10 @@ void cClientHandle::PacketError(unsigned char a_PacketType)
|
|||||||
|
|
||||||
void cClientHandle::DataReceived(const char * a_Data, int a_Size)
|
void cClientHandle::DataReceived(const char * a_Data, int a_Size)
|
||||||
{
|
{
|
||||||
// Data is received from the client, hand it off to the protocol:
|
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
|
||||||
if ((m_Player != NULL) && (m_Player->GetWorld() != NULL))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
_X: Lock the world, so that plugins reacting to protocol events have already the chunkmap locked.
|
|
||||||
There was a possibility of a deadlock between SocketThreads and TickThreads, resulting from each
|
|
||||||
holding one CS an requesting the other one (ChunkMap CS vs Plugin CS) (FS #375). To break this, it's
|
|
||||||
sufficient to break any of the four Coffman conditions for a deadlock. We'll solve this by requiring
|
|
||||||
the ChunkMap CS for all SocketThreads operations before they lock the PluginCS - thus creating a kind
|
|
||||||
of a lock hierarchy. However, this incurs a performance penalty, we're de facto locking the chunkmap
|
|
||||||
for each incoming packet. A better, but more involved solutin would be to lock the chunkmap only when
|
|
||||||
the incoming packet really has a plugin CS lock request.
|
|
||||||
Also, it is still possible for a packet to slip through - when a player still doesn't have their world
|
|
||||||
assigned and several packets arrive at once.
|
|
||||||
*/
|
|
||||||
cWorld::cLock(*m_Player->GetWorld());
|
|
||||||
|
|
||||||
m_Protocol->DataReceived(a_Data, a_Size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Protocol->DataReceived(a_Data, a_Size);
|
|
||||||
}
|
|
||||||
m_TimeSinceLastPacket = 0;
|
m_TimeSinceLastPacket = 0;
|
||||||
|
cCSLock Lock(m_CSIncomingData);
|
||||||
|
m_IncomingData.append(a_Data, a_Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ class cRedstone;
|
|||||||
class cWindow;
|
class cWindow;
|
||||||
class cFallingBlock;
|
class cFallingBlock;
|
||||||
class cItemHandler;
|
class cItemHandler;
|
||||||
|
class cWorld;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -194,6 +195,9 @@ public:
|
|||||||
|
|
||||||
void SendData(const char * a_Data, int a_Size);
|
void SendData(const char * a_Data, int a_Size);
|
||||||
|
|
||||||
|
/// Called when the player moves into a different world; queues sreaming the new chunks
|
||||||
|
void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
int m_ViewDistance; // 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 )
|
int m_ViewDistance; // 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 )
|
||||||
@ -212,11 +216,12 @@ private:
|
|||||||
|
|
||||||
cProtocol * m_Protocol;
|
cProtocol * m_Protocol;
|
||||||
|
|
||||||
|
cCriticalSection m_CSIncomingData;
|
||||||
|
AString m_IncomingData;
|
||||||
|
|
||||||
cCriticalSection m_CSOutgoingData;
|
cCriticalSection m_CSOutgoingData;
|
||||||
cByteBuffer m_OutgoingData;
|
cByteBuffer m_OutgoingData;
|
||||||
AString m_OutgoingDataOverflow; //< For data that didn't fit into the m_OutgoingData ringbuffer temporarily
|
AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily
|
||||||
|
|
||||||
cCriticalSection m_CriticalSection;
|
|
||||||
|
|
||||||
Vector3d m_ConfirmPosition;
|
Vector3d m_ConfirmPosition;
|
||||||
|
|
||||||
@ -252,18 +257,22 @@ private:
|
|||||||
|
|
||||||
enum eState
|
enum eState
|
||||||
{
|
{
|
||||||
csConnected, // The client has just connected, waiting for their handshake / login
|
csConnected, ///< The client has just connected, waiting for their handshake / login
|
||||||
csAuthenticating, // The client has logged in, waiting for external authentication
|
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
|
csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick
|
||||||
csConfirmingPos, // The client has been sent the position packet, waiting for them to repeat the position back
|
csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them
|
||||||
csPlaying, // Normal gameplay
|
csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back
|
||||||
csDestroying, // The client is being destroyed, don't queue any more packets / don't add to chunks
|
csPlaying, ///< Normal gameplay
|
||||||
csDestroyed, // The client has been destroyed, the destructor is to be called from the owner thread
|
csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks
|
||||||
|
csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread
|
||||||
|
|
||||||
// TODO: Add Kicking here as well
|
// TODO: Add Kicking here as well
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
eState m_State;
|
eState m_State;
|
||||||
|
|
||||||
|
/// m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads
|
||||||
|
cCriticalSection m_CSDestroyingState;
|
||||||
|
|
||||||
bool m_bKeepThreadGoing;
|
bool m_bKeepThreadGoing;
|
||||||
|
|
||||||
@ -279,12 +288,6 @@ private:
|
|||||||
/// Running sum of m_NumExplosionsPerTick[]
|
/// Running sum of m_NumExplosionsPerTick[]
|
||||||
int m_RunningSumExplosions;
|
int m_RunningSumExplosions;
|
||||||
|
|
||||||
/// Lock for the m_PendingMessages buffer
|
|
||||||
cCriticalSection m_CSMessages;
|
|
||||||
|
|
||||||
/// Buffer for received messages to be processed in the Tick thread
|
|
||||||
AStringList m_PendingMessages;
|
|
||||||
|
|
||||||
static int s_ClientCount;
|
static int s_ClientCount;
|
||||||
int m_UniqueID;
|
int m_UniqueID;
|
||||||
|
|
||||||
@ -311,9 +314,6 @@ private:
|
|||||||
/// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating)
|
/// Handles the block placing packet when it is a real block placement (not block-using, item-using or eating)
|
||||||
void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
|
void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
|
||||||
|
|
||||||
/// Processes the messages in m_PendingMessages; called from the Tick thread
|
|
||||||
void ProcessPendingMessages(void);
|
|
||||||
|
|
||||||
// cSocketThreads::cCallback overrides:
|
// cSocketThreads::cCallback overrides:
|
||||||
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
|
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
|
||||||
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
|
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
|
||||||
|
155
source/DeadlockDetect.cpp
Normal file
155
source/DeadlockDetect.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
|
||||||
|
// DeadlockDetect.cpp
|
||||||
|
|
||||||
|
// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "DeadlockDetect.h"
|
||||||
|
#include "Root.h"
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Number of milliseconds per cycle
|
||||||
|
const int CYCLE_MILLISECONDS = 500;
|
||||||
|
|
||||||
|
/// When the number of cycles for the same world age hits this value, it is considered a deadlock
|
||||||
|
const int NUM_CYCLES_LIMIT = 40; // 40 = twenty seconds
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cDeadlockDetect::cDeadlockDetect(void) :
|
||||||
|
super("DeadlockDetect")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cDeadlockDetect::Start(void)
|
||||||
|
{
|
||||||
|
// Read the initial world data:
|
||||||
|
class cFillIn :
|
||||||
|
public cWorldListCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cFillIn(cDeadlockDetect * a_Detect) :
|
||||||
|
m_Detect(a_Detect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool Item(cWorld * a_World) override
|
||||||
|
{
|
||||||
|
m_Detect->SetWorldAge(a_World->GetName(), a_World->GetWorldAge());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
cDeadlockDetect * m_Detect;
|
||||||
|
} FillIn(this);
|
||||||
|
cRoot::Get()->ForEachWorld(FillIn);
|
||||||
|
return super::Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cDeadlockDetect::Stop(void)
|
||||||
|
{
|
||||||
|
m_EvtTerminate.Set();
|
||||||
|
super::Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cDeadlockDetect::Execute(void)
|
||||||
|
{
|
||||||
|
// Loop until the event is signalled
|
||||||
|
while (m_EvtTerminate.Wait(CYCLE_MILLISECONDS) == cEvent::wrTimeout)
|
||||||
|
{
|
||||||
|
// Check the world ages:
|
||||||
|
class cChecker :
|
||||||
|
public cWorldListCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cChecker(cDeadlockDetect * a_Detect) :
|
||||||
|
m_Detect(a_Detect)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
cDeadlockDetect * m_Detect;
|
||||||
|
|
||||||
|
virtual bool Item(cWorld * a_World) override
|
||||||
|
{
|
||||||
|
m_Detect->CheckWorldAge(a_World->GetName(), a_World->GetWorldAge());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} Checker(this);
|
||||||
|
cRoot::Get()->ForEachWorld(Checker);
|
||||||
|
} // while (should run)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age)
|
||||||
|
{
|
||||||
|
m_WorldAges[a_WorldName].m_Age = a_Age;
|
||||||
|
m_WorldAges[a_WorldName].m_NumCyclesSame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
|
||||||
|
{
|
||||||
|
WorldAges::iterator itr = m_WorldAges.find(a_WorldName);
|
||||||
|
if (itr == m_WorldAges.end())
|
||||||
|
{
|
||||||
|
ASSERT(!"Unknown world in cDeadlockDetect");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (itr->second.m_Age == a_Age)
|
||||||
|
{
|
||||||
|
itr->second.m_NumCyclesSame += 1;
|
||||||
|
if (itr->second.m_NumCyclesSame > NUM_CYCLES_LIMIT)
|
||||||
|
{
|
||||||
|
DeadlockDetected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
itr->second.m_Age = a_Age;
|
||||||
|
itr->second.m_NumCyclesSame = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cDeadlockDetect::DeadlockDetected(void)
|
||||||
|
{
|
||||||
|
ASSERT(!"Deadlock detected");
|
||||||
|
|
||||||
|
// TODO: Make a crashdump / coredump
|
||||||
|
|
||||||
|
// Crash the server intentionally:
|
||||||
|
*((int *)0) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
70
source/DeadlockDetect.h
Normal file
70
source/DeadlockDetect.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
|
||||||
|
// DeadlockDetect.h
|
||||||
|
|
||||||
|
// Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one
|
||||||
|
|
||||||
|
/*
|
||||||
|
This class simply monitors each world's m_WorldAge, which is expected to grow on each tick.
|
||||||
|
If the world age doesn't grow for several seconds, it's either because the server is super-overloaded,
|
||||||
|
or because the world tick thread hangs in a deadlock. We presume the latter and therefore kill the server.
|
||||||
|
Once we learn to write crashdumps programmatically, we should do so just before killing, to enable debugging.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "OSSupport/IsThread.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class cDeadlockDetect :
|
||||||
|
public cIsThread
|
||||||
|
{
|
||||||
|
typedef cIsThread super;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cDeadlockDetect(void);
|
||||||
|
|
||||||
|
/// Starts the detection. Hides cIsThread's Start, because we need some initialization
|
||||||
|
bool Start(void);
|
||||||
|
|
||||||
|
/// Stops the detection. Hides cIsThread's Stop, because we need to signal m_EvtTerminate
|
||||||
|
void Stop(void);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct sWorldAge
|
||||||
|
{
|
||||||
|
/// Last m_WorldAge that has been detected in this world
|
||||||
|
Int64 m_Age;
|
||||||
|
|
||||||
|
/// Number of cycles for which the age has been the same
|
||||||
|
int m_NumCyclesSame;
|
||||||
|
} ;
|
||||||
|
|
||||||
|
/// Maps world name -> sWorldAge
|
||||||
|
typedef std::map<AString, sWorldAge> WorldAges;
|
||||||
|
|
||||||
|
WorldAges m_WorldAges;
|
||||||
|
|
||||||
|
cEvent m_EvtTerminate;
|
||||||
|
|
||||||
|
|
||||||
|
// cIsThread overrides:
|
||||||
|
virtual void Execute(void) override;
|
||||||
|
|
||||||
|
/// Sets the initial world age
|
||||||
|
void SetWorldAge(const AString & a_WorldName, Int64 a_Age);
|
||||||
|
|
||||||
|
/// Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected
|
||||||
|
void CheckWorldAge(const AString & a_WorldName, Int64 a_Age);
|
||||||
|
|
||||||
|
/// Called when a deadlock is detected. Aborts the server.
|
||||||
|
void DeadlockDetected(void);
|
||||||
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
|||||||
cEvent::cEvent(void)
|
cEvent::cEvent(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
m_Event = CreateEvent( 0, FALSE, FALSE, 0 );
|
m_Event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
if (m_Event == NULL)
|
if (m_Event == NULL)
|
||||||
{
|
{
|
||||||
LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError());
|
LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError());
|
||||||
@ -78,19 +78,19 @@ cEvent::~cEvent()
|
|||||||
|
|
||||||
void cEvent::Wait(void)
|
void cEvent::Wait(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
DWORD res = WaitForSingleObject(m_Event, INFINITE);
|
DWORD res = WaitForSingleObject(m_Event, INFINITE);
|
||||||
if (res != WAIT_OBJECT_0)
|
if (res != WAIT_OBJECT_0)
|
||||||
{
|
{
|
||||||
LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError());
|
LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int res = sem_wait(m_Event);
|
int res = sem_wait(m_Event);
|
||||||
if (res != 0 )
|
if (res != 0 )
|
||||||
{
|
{
|
||||||
LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
|
LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,18 +99,70 @@ void cEvent::Wait(void)
|
|||||||
|
|
||||||
void cEvent::Set(void)
|
void cEvent::Set(void)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!SetEvent(m_Event))
|
if (!SetEvent(m_Event))
|
||||||
{
|
{
|
||||||
LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError());
|
LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError());
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
int res = sem_post(m_Event);
|
int res = sem_post(m_Event);
|
||||||
if (res != 0)
|
if (res != 0)
|
||||||
{
|
{
|
||||||
LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno);
|
LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cEvent::eWaitResult cEvent::Wait(int a_TimeoutMilliSec)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD res = WaitForSingleObject(m_Event, (DWORD)a_TimeoutMilliSec);
|
||||||
|
switch (res)
|
||||||
|
{
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
{
|
||||||
|
// The semaphore was signalled
|
||||||
|
return wrSignalled;
|
||||||
|
}
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
{
|
||||||
|
// The timeout was hit
|
||||||
|
return wrTimeout;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LOGWARNING("cEvent: timed-waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError());
|
||||||
|
return wrError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
timespec timeout;
|
||||||
|
timeout.tv_sec = a_TimeoutMilliSec / 1000;
|
||||||
|
timeout.tv_nsec = (a_TimeoutMilliSec % 1000) * 1000000;
|
||||||
|
int res = sem_timedwait(m_Event, &timeout);
|
||||||
|
switch (res)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
// The semaphore was signalled
|
||||||
|
return wrSignalled;
|
||||||
|
}
|
||||||
|
case ETIMEDOUT:
|
||||||
|
{
|
||||||
|
// The timeout was hit
|
||||||
|
return wrTimeout;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
LOGWARNING("cEvent: timed-waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
|
||||||
|
return wrError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,12 +19,22 @@
|
|||||||
class cEvent
|
class cEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum eWaitResult
|
||||||
|
{
|
||||||
|
wrSignalled,
|
||||||
|
wrTimeout,
|
||||||
|
wrError,
|
||||||
|
} ;
|
||||||
|
|
||||||
cEvent(void);
|
cEvent(void);
|
||||||
~cEvent();
|
~cEvent();
|
||||||
|
|
||||||
void Wait(void);
|
void Wait(void);
|
||||||
void Set (void);
|
void Set (void);
|
||||||
|
|
||||||
|
/// Waits for the semaphore with a timeout
|
||||||
|
eWaitResult Wait(int a_TimeoutMilliSec);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -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)
|
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)
|
#ifdef _WIN32
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Cannot log, logger may already be stopped:
|
|
||||||
// LOG("Waiting for thread \"%s\" to terminate.", m_ThreadName.c_str());
|
|
||||||
int res = WaitForSingleObject(m_Handle, INFINITE);
|
int res = WaitForSingleObject(m_Handle, INFINITE);
|
||||||
m_Handle = NULL;
|
m_Handle = NULL;
|
||||||
// Cannot log, logger may already be stopped:
|
LOGD("Thread %s finished", m_ThreadName.c_str());
|
||||||
// LOG("Thread \"%s\" %s terminated, GLE = %d", m_ThreadName.c_str(), (res == WAIT_OBJECT_0) ? "" : "not", GetLastError());
|
|
||||||
return (res == WAIT_OBJECT_0);
|
return (res == WAIT_OBJECT_0);
|
||||||
|
|
||||||
#else // _WIN32
|
#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);
|
int res = pthread_join(m_Handle, NULL);
|
||||||
m_HasStarted = false;
|
m_Handle = NULL;
|
||||||
// Cannot log, logger may already be stopped:
|
LOGD("Thread %s finished", m_ThreadName.c_str());
|
||||||
// LOG("Thread \"%s\" %s terminated, errno = %d", m_ThreadName.c_str(), (res == 0) ? "" : "not", errno);
|
|
||||||
return (res == 0);
|
return (res == 0);
|
||||||
|
|
||||||
#endif // else _WIN32
|
#endif // else _WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ public:
|
|||||||
/// Starts the thread; returns without waiting for the actual start
|
/// Starts the thread; returns without waiting for the actual start
|
||||||
bool Start(void);
|
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
|
/// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag
|
||||||
bool Wait(void);
|
bool Wait(void);
|
||||||
|
|
||||||
@ -54,7 +57,9 @@ private:
|
|||||||
|
|
||||||
static DWORD_PTR __stdcall thrExecute(LPVOID a_Param)
|
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();
|
((cIsThread *)a_Param)->Execute();
|
||||||
|
DestroyWindow(IdentificationWnd);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +75,6 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif // else _WIN32
|
#endif // else _WIN32
|
||||||
|
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
@ -100,6 +100,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
|
|||||||
m_LastJumpHeight = (float)(GetPosY());
|
m_LastJumpHeight = (float)(GetPosY());
|
||||||
m_LastGroundHeight = (float)(GetPosY());
|
m_LastGroundHeight = (float)(GetPosY());
|
||||||
m_Stance = GetPosY() + 1.62;
|
m_Stance = GetPosY() + 1.62;
|
||||||
|
|
||||||
|
cRoot::Get()->GetServer()->PlayerCreated(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -110,6 +112,9 @@ cPlayer::~cPlayer(void)
|
|||||||
{
|
{
|
||||||
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
|
LOGD("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
|
||||||
|
|
||||||
|
// Notify the server that the player is being destroyed
|
||||||
|
cRoot::Get()->GetServer()->PlayerDestroying(this);
|
||||||
|
|
||||||
SaveToDisk();
|
SaveToDisk();
|
||||||
|
|
||||||
m_World->RemovePlayer( this );
|
m_World->RemovePlayer( this );
|
||||||
@ -127,8 +132,16 @@ cPlayer::~cPlayer(void)
|
|||||||
|
|
||||||
bool cPlayer::Initialize(cWorld * a_World)
|
bool cPlayer::Initialize(cWorld * a_World)
|
||||||
{
|
{
|
||||||
|
ASSERT(a_World != NULL);
|
||||||
|
|
||||||
if (super::Initialize(a_World))
|
if (super::Initialize(a_World))
|
||||||
{
|
{
|
||||||
|
// Remove the client handle from the server, it will be ticked from this object from now on
|
||||||
|
if (m_ClientHandle != NULL)
|
||||||
|
{
|
||||||
|
cRoot::Get()->GetServer()->ClientMovedToWorld(m_ClientHandle);
|
||||||
|
}
|
||||||
|
|
||||||
GetWorld()->AddPlayer(this);
|
GetWorld()->AddPlayer(this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -142,6 +155,7 @@ bool cPlayer::Initialize(cWorld * a_World)
|
|||||||
void cPlayer::Destroyed()
|
void cPlayer::Destroyed()
|
||||||
{
|
{
|
||||||
CloseWindow(false);
|
CloseWindow(false);
|
||||||
|
|
||||||
m_ClientHandle = NULL;
|
m_ClientHandle = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,22 +165,17 @@ void cPlayer::Destroyed()
|
|||||||
|
|
||||||
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
||||||
{
|
{
|
||||||
/*
|
if (!m_bVisible || (m_ClientHandle == (&a_Client)))
|
||||||
LOGD("cPlayer::SpawnOn(%s) for \"%s\" at pos {%.2f, %.2f, %.2f}",
|
|
||||||
a_Client.GetUsername().c_str(), m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (m_bVisible && (m_ClientHandle != (&a_Client)))
|
|
||||||
{
|
{
|
||||||
a_Client.SendPlayerSpawn(*this);
|
return;
|
||||||
a_Client.SendEntityHeadLook(*this);
|
|
||||||
a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() );
|
|
||||||
a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() );
|
|
||||||
a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() );
|
|
||||||
a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() );
|
|
||||||
a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() );
|
|
||||||
}
|
}
|
||||||
|
a_Client.SendPlayerSpawn(*this);
|
||||||
|
a_Client.SendEntityHeadLook(*this);
|
||||||
|
a_Client.SendEntityEquipment(*this, 0, m_Inventory.GetEquippedItem() );
|
||||||
|
a_Client.SendEntityEquipment(*this, 1, m_Inventory.GetEquippedBoots() );
|
||||||
|
a_Client.SendEntityEquipment(*this, 2, m_Inventory.GetEquippedLeggings() );
|
||||||
|
a_Client.SendEntityEquipment(*this, 3, m_Inventory.GetEquippedChestplate() );
|
||||||
|
a_Client.SendEntityEquipment(*this, 4, m_Inventory.GetEquippedHelmet() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -175,18 +184,28 @@ void cPlayer::SpawnOn(cClientHandle & a_Client)
|
|||||||
|
|
||||||
void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
|
void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
if (!m_ClientHandle->IsPlaying())
|
if (m_ClientHandle != NULL)
|
||||||
{
|
{
|
||||||
// We're not yet in the game, ignore everything
|
if (m_ClientHandle->IsDestroyed())
|
||||||
return;
|
{
|
||||||
|
// This should not happen, because destroying a client will remove it from the world, but just in case
|
||||||
|
m_ClientHandle = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_ClientHandle->IsPlaying())
|
||||||
|
{
|
||||||
|
// We're not yet in the game, ignore everything
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
|
||||||
// set player swimming state
|
// Set player swimming state
|
||||||
SetSwimState( a_Chunk);
|
SetSwimState(a_Chunk);
|
||||||
|
|
||||||
// handle air drowning stuff
|
// Handle air drowning stuff
|
||||||
HandleAir();
|
HandleAir();
|
||||||
|
|
||||||
if (m_bDirtyPosition)
|
if (m_bDirtyPosition)
|
||||||
@ -1106,20 +1125,15 @@ bool cPlayer::MoveToWorld(const char * a_WorldName)
|
|||||||
m_ClientHandle->RemoveFromAllChunks();
|
m_ClientHandle->RemoveFromAllChunks();
|
||||||
m_World->RemoveEntity(this);
|
m_World->RemoveEntity(this);
|
||||||
|
|
||||||
|
// If the dimension is different, we can send the respawn packet
|
||||||
|
// http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
|
||||||
|
m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension()));
|
||||||
|
|
||||||
// Add player to all the necessary parts of the new world
|
// Add player to all the necessary parts of the new world
|
||||||
SetWorld(World);
|
SetWorld(World);
|
||||||
World->AddEntity(this);
|
World->AddEntity(this);
|
||||||
World->AddPlayer(this);
|
World->AddPlayer(this);
|
||||||
|
|
||||||
// If the dimension is different, we can send the respawn packet
|
|
||||||
// http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02
|
|
||||||
if (OldDimension != World->GetDimension())
|
|
||||||
{
|
|
||||||
m_ClientHandle->SendRespawn();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stream the new chunks:
|
|
||||||
m_ClientHandle->StreamChunks();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ public:
|
|||||||
cPlayer(cClientHandle * a_Client, const AString & a_PlayerName);
|
cPlayer(cClientHandle * a_Client, const AString & a_PlayerName);
|
||||||
virtual ~cPlayer();
|
virtual ~cPlayer();
|
||||||
|
|
||||||
virtual bool Initialize(cWorld * a_World); // tolua_export
|
virtual bool Initialize(cWorld * a_World) override;
|
||||||
|
|
||||||
virtual void SpawnOn(cClientHandle & a_Client) override;
|
virtual void SpawnOn(cClientHandle & a_Client) override;
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "Protocol16x.h"
|
#include "Protocol16x.h"
|
||||||
#include "../ClientHandle.h"
|
#include "../ClientHandle.h"
|
||||||
#include "../Root.h"
|
#include "../Root.h"
|
||||||
|
#include "../Server.h"
|
||||||
#include "../World.h"
|
#include "../World.h"
|
||||||
#include "../ChatColor.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
|
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
|
||||||
Printf(Reply, "%s%s%i%s%i",
|
Printf(Reply, "%s%s%i%s%i",
|
||||||
cRoot::Get()->GetDefaultWorld()->GetDescription().c_str(),
|
cRoot::Get()->GetServer()->GetDescription().c_str(),
|
||||||
cChatColor::Delimiter.c_str(),
|
cChatColor::Delimiter.c_str(),
|
||||||
cRoot::Get()->GetDefaultWorld()->GetNumPlayers(),
|
cRoot::Get()->GetServer()->GetNumPlayers(),
|
||||||
cChatColor::Delimiter.c_str(),
|
cChatColor::Delimiter.c_str(),
|
||||||
cRoot::Get()->GetDefaultWorld()->GetMaxPlayers()
|
cRoot::Get()->GetServer()->GetMaxPlayers()
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -759,9 +760,9 @@ void cProtocolRecognizer::HandleServerPing(void)
|
|||||||
|
|
||||||
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
|
// http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
|
||||||
AString NumPlayers;
|
AString NumPlayers;
|
||||||
Printf(NumPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetNumPlayers());
|
Printf(NumPlayers, "%d", cRoot::Get()->GetServer()->GetNumPlayers());
|
||||||
AString MaxPlayers;
|
AString MaxPlayers;
|
||||||
Printf(MaxPlayers, "%d", cRoot::Get()->GetDefaultWorld()->GetMaxPlayers());
|
Printf(MaxPlayers, "%d", cRoot::Get()->GetServer()->GetMaxPlayers());
|
||||||
|
|
||||||
AString ProtocolVersionNum;
|
AString ProtocolVersionNum;
|
||||||
Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
|
Printf(ProtocolVersionNum, "%d", cRoot::Get()->m_PrimaryServerVersion);
|
||||||
@ -775,7 +776,7 @@ void cProtocolRecognizer::HandleServerPing(void)
|
|||||||
Reply.push_back(0);
|
Reply.push_back(0);
|
||||||
Reply.append(ProtocolVersionTxt);
|
Reply.append(ProtocolVersionTxt);
|
||||||
Reply.push_back(0);
|
Reply.push_back(0);
|
||||||
Reply.append(cRoot::Get()->GetDefaultWorld()->GetDescription());
|
Reply.append(cRoot::Get()->GetServer()->GetDescription());
|
||||||
Reply.push_back(0);
|
Reply.push_back(0);
|
||||||
Reply.append(NumPlayers);
|
Reply.append(NumPlayers);
|
||||||
Reply.push_back(0);
|
Reply.push_back(0);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Chunk.h"
|
#include "Chunk.h"
|
||||||
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
|
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
|
||||||
#include "CommandOutput.h"
|
#include "CommandOutput.h"
|
||||||
|
#include "DeadlockDetect.h"
|
||||||
|
|
||||||
#include "../iniFile/iniFile.h"
|
#include "../iniFile/iniFile.h"
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ void cRoot::InputThread(void * a_Params)
|
|||||||
|
|
||||||
void cRoot::Start(void)
|
void cRoot::Start(void)
|
||||||
{
|
{
|
||||||
|
cDeadlockDetect dd;
|
||||||
delete m_Log;
|
delete m_Log;
|
||||||
m_Log = new cMCLogger();
|
m_Log = new cMCLogger();
|
||||||
|
|
||||||
@ -162,6 +164,9 @@ void cRoot::Start(void)
|
|||||||
LOG("Starting worlds...");
|
LOG("Starting worlds...");
|
||||||
StartWorlds();
|
StartWorlds();
|
||||||
|
|
||||||
|
LOG("Starting deadlock detector...");
|
||||||
|
dd.Start();
|
||||||
|
|
||||||
LOG("Starting server...");
|
LOG("Starting server...");
|
||||||
m_Server->Start();
|
m_Server->Start();
|
||||||
|
|
||||||
@ -183,17 +188,21 @@ void cRoot::Start(void)
|
|||||||
|
|
||||||
// Deallocate stuffs
|
// Deallocate stuffs
|
||||||
LOG("Shutting down server...");
|
LOG("Shutting down server...");
|
||||||
m_Server->Shutdown(); // This waits for threads to stop and d/c clients
|
m_Server->Shutdown();
|
||||||
|
|
||||||
|
LOG("Shutting down deadlock detector...");
|
||||||
|
dd.Stop();
|
||||||
|
|
||||||
LOG("Stopping world threads...");
|
LOG("Stopping world threads...");
|
||||||
StopWorlds();
|
StopWorlds();
|
||||||
|
|
||||||
LOG("Stopping authenticator...");
|
LOG("Stopping authenticator...");
|
||||||
m_Authenticator.Stop();
|
m_Authenticator.Stop();
|
||||||
|
|
||||||
|
|
||||||
LOG("Freeing MonsterConfig...");
|
LOG("Freeing MonsterConfig...");
|
||||||
delete m_MonsterConfig; m_MonsterConfig = 0;
|
delete m_MonsterConfig; m_MonsterConfig = NULL;
|
||||||
LOG("Stopping WebAdmin...");
|
LOG("Stopping WebAdmin...");
|
||||||
delete m_WebAdmin; m_WebAdmin = 0;
|
delete m_WebAdmin; m_WebAdmin = NULL;
|
||||||
LOG("Unloading recipes...");
|
LOG("Unloading recipes...");
|
||||||
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
|
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
|
||||||
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
|
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
|
||||||
@ -270,8 +279,9 @@ void cRoot::LoadWorlds(void)
|
|||||||
|
|
||||||
void cRoot::StartWorlds(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();
|
itr->second->InitializeSpawn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,9 +292,9 @@ void cRoot::StartWorlds(void)
|
|||||||
|
|
||||||
void cRoot::StopWorlds(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 +354,7 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cRoot::TickWorlds(float a_Dt)
|
void cRoot::TickCommands(void)
|
||||||
{
|
{
|
||||||
// Execute any pending commands:
|
// Execute any pending commands:
|
||||||
cCommandQueue PendingCommands;
|
cCommandQueue PendingCommands;
|
||||||
@ -356,12 +366,6 @@ void cRoot::TickWorlds(float a_Dt)
|
|||||||
{
|
{
|
||||||
ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output));
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,9 +85,10 @@ public:
|
|||||||
|
|
||||||
/// Called by cAuthenticator to auth the specified user
|
/// Called by cAuthenticator to auth the specified user
|
||||||
void AuthenticateUser(int a_ClientID);
|
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
|
/// Returns the number of chunks loaded
|
||||||
int GetTotalChunkCount(void); // tolua_export
|
int GetTotalChunkCount(void); // tolua_export
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
{}
|
|
||||||
|
|
||||||
cThread* pTickThread; bool bStopTickThread;
|
|
||||||
|
|
||||||
cEvent RestartEvent;
|
|
||||||
std::string ServerID;
|
|
||||||
};
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
LastTime = NowTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -121,8 +162,45 @@ void cServer::RemoveClient(const cClientHandle * a_Client)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSClients);
|
||||||
|
m_ClientsToRemove.push_back(const_cast<cClientHandle *>(a_Client));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServer::PlayerCreated(const cPlayer * a_Player)
|
||||||
|
{
|
||||||
|
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
|
||||||
|
cCSLock Lock(m_CSPlayerCountDiff);
|
||||||
|
m_PlayerCountDiff += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cServer::PlayerDestroying(const cPlayer * a_Player)
|
||||||
|
{
|
||||||
|
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
|
||||||
|
cCSLock Lock(m_CSPlayerCountDiff);
|
||||||
|
m_PlayerCountDiff -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cServer::InitServer(cIniFile & a_SettingsIni)
|
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);
|
||||||
|
m_PlayerCount = 0;
|
||||||
|
m_PlayerCountDiff = 0;
|
||||||
|
|
||||||
if (m_bIsConnected)
|
if (m_bIsConnected)
|
||||||
{
|
{
|
||||||
LOGERROR("ERROR: Trying to initialize server while server is already running!");
|
LOGERROR("ERROR: Trying to initialize server while server is already running!");
|
||||||
@ -164,18 +242,17 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
|
|||||||
|
|
||||||
m_bIsConnected = true;
|
m_bIsConnected = true;
|
||||||
|
|
||||||
m_pState->ServerID = "-";
|
m_ServerID = "-";
|
||||||
if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
|
if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
|
||||||
{
|
{
|
||||||
MTRand mtrand1;
|
MTRand mtrand1;
|
||||||
unsigned int r1 = (mtrand1.randInt()%1147483647) + 1000000000;
|
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
|
||||||
unsigned int r2 = (mtrand1.randInt()%1147483647) + 1000000000;
|
unsigned int r2 = (mtrand1.randInt() % 1147483647) + 1000000000;
|
||||||
std::ostringstream sid;
|
std::ostringstream sid;
|
||||||
sid << std::hex << r1;
|
sid << std::hex << r1;
|
||||||
sid << std::hex << r2;
|
sid << std::hex << r2;
|
||||||
std::string ServerID = sid.str();
|
m_ServerID = sid.str();
|
||||||
ServerID.resize(16, '0');
|
m_ServerID.resize(16, '0');
|
||||||
m_pState->ServerID = ServerID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
|
m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE);
|
||||||
@ -201,29 +278,10 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
cServer::cServer(void)
|
int cServer::GetNumPlayers(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)
|
|
||||||
{
|
{
|
||||||
}
|
cCSLock Lock(m_CSPlayerCount);
|
||||||
|
return m_PlayerCount;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cServer::~cServer()
|
|
||||||
{
|
|
||||||
// TODO: Shut down the server gracefully
|
|
||||||
m_pState->bStopTickThread = true;
|
|
||||||
delete m_pState->pTickThread; m_pState->pTickThread = NULL;
|
|
||||||
|
|
||||||
delete m_pState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -284,55 +342,22 @@ void cServer::OnConnectionAccepted(cSocket & a_Socket)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude)
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CSClients);
|
|
||||||
for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
|
||||||
{
|
|
||||||
if ((*itr == a_Exclude) || !(*itr)->IsLoggedIn())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(*itr)->SendChat(a_Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cServer::Tick(float a_Dt)
|
bool cServer::Tick(float a_Dt)
|
||||||
{
|
{
|
||||||
m_Millisecondsf += a_Dt;
|
// Apply the queued playercount adjustments (postponed to avoid deadlocks)
|
||||||
if (m_Millisecondsf > 1.f)
|
int PlayerCountDiff = 0;
|
||||||
{
|
{
|
||||||
m_Milliseconds += (int)m_Millisecondsf;
|
cCSLock Lock(m_CSPlayerCountDiff);
|
||||||
m_Millisecondsf = m_Millisecondsf - (int)m_Millisecondsf;
|
std::swap(PlayerCountDiff, m_PlayerCountDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
cRoot::Get()->TickWorlds(a_Dt); // TODO - Maybe give all worlds their own thread?
|
|
||||||
|
|
||||||
cClientHandleList RemoveClients;
|
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSPlayerCount);
|
||||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
m_PlayerCount += PlayerCountDiff;
|
||||||
{
|
|
||||||
if ((*itr)->IsDestroyed())
|
|
||||||
{
|
|
||||||
RemoveClients.push_back(*itr); // Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 )
|
|
||||||
itr = m_Clients.erase(itr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(*itr)->Tick(a_Dt);
|
|
||||||
++itr;
|
|
||||||
} // for itr - m_Clients[]
|
|
||||||
}
|
}
|
||||||
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
|
||||||
{
|
cRoot::Get()->TickCommands();
|
||||||
delete *itr;
|
|
||||||
} // for itr - RemoveClients[]
|
TickClients(a_Dt);
|
||||||
|
|
||||||
cRoot::Get()->GetPluginManager()->Tick(a_Dt);
|
|
||||||
|
|
||||||
if (!m_bRestarting)
|
if (!m_bRestarting)
|
||||||
{
|
{
|
||||||
@ -341,7 +366,7 @@ bool cServer::Tick(float a_Dt)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_bRestarting = false;
|
m_bRestarting = false;
|
||||||
m_pState->RestartEvent.Set();
|
m_RestartEvent.Set();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,33 +375,39 @@ bool cServer::Tick(float a_Dt)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void ServerTickThread( void * a_Param )
|
void cServer::TickClients(float a_Dt)
|
||||||
{
|
{
|
||||||
LOG("ServerTickThread");
|
cClientHandleList RemoveClients;
|
||||||
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();
|
cCSLock Lock(m_CSClients);
|
||||||
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
|
// Remove clients that have moved to a world (the world will be ticking them from now on)
|
||||||
|
for (cClientHandleList::const_iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
||||||
{
|
{
|
||||||
cSleep::MilliSleep( (unsigned int)( msPerTick - TickTime ) );
|
m_Clients.remove(*itr);
|
||||||
}
|
} // for itr - m_ClientsToRemove[]
|
||||||
|
m_ClientsToRemove.clear();
|
||||||
LastTime = NowTime;
|
|
||||||
|
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
|
||||||
|
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
||||||
|
{
|
||||||
|
if ((*itr)->IsDestroyed())
|
||||||
|
{
|
||||||
|
// Remove the client later, when CS is not held, to avoid deadlock ( http://forum.mc-server.org/showthread.php?tid=374 )
|
||||||
|
RemoveClients.push_back(*itr);
|
||||||
|
itr = m_Clients.erase(itr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(*itr)->Tick(a_Dt);
|
||||||
|
++itr;
|
||||||
|
} // for itr - m_Clients[]
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG("TICK THREAD STOPPED");
|
// Delete the clients that have been destroyed
|
||||||
|
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
||||||
|
{
|
||||||
|
delete *itr;
|
||||||
|
} // for itr - RemoveClients[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -385,7 +416,6 @@ void ServerTickThread( void * a_Param )
|
|||||||
|
|
||||||
bool cServer::Start(void)
|
bool cServer::Start(void)
|
||||||
{
|
{
|
||||||
m_pState->pTickThread = new cThread( ServerTickThread, this, "cServer::ServerTickThread" );
|
|
||||||
if (!m_ListenThreadIPv4.Start())
|
if (!m_ListenThreadIPv4.Start())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -394,7 +424,10 @@ bool cServer::Start(void)
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
m_pState->pTickThread->Start( true );
|
if (!m_TickThread.Start())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -478,32 +511,13 @@ void cServer::BindBuiltInConsoleCommands(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::SendMessage(const AString & a_Message, cPlayer * a_Player /* = NULL */, bool a_bExclude /* = false */ )
|
void cServer::Shutdown(void)
|
||||||
{
|
|
||||||
if ((a_Player != NULL) && !a_bExclude)
|
|
||||||
{
|
|
||||||
cClientHandle * Client = a_Player->GetClientHandle();
|
|
||||||
if (Client != NULL)
|
|
||||||
{
|
|
||||||
Client->SendChat(a_Message);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BroadcastChat(a_Message, (a_Player != NULL) ? a_Player->GetClientHandle() : NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::Shutdown()
|
|
||||||
{
|
{
|
||||||
m_ListenThreadIPv4.Stop();
|
m_ListenThreadIPv4.Stop();
|
||||||
m_ListenThreadIPv6.Stop();
|
m_ListenThreadIPv6.Stop();
|
||||||
|
|
||||||
m_bRestarting = true;
|
m_bRestarting = true;
|
||||||
m_pState->RestartEvent.Wait();
|
m_RestartEvent.Wait();
|
||||||
|
|
||||||
cRoot::Get()->SaveAllChunks();
|
cRoot::Get()->SaveAllChunks();
|
||||||
|
|
||||||
@ -520,15 +534,6 @@ void cServer::Shutdown()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
const AString & cServer::GetServerID(void) const
|
|
||||||
{
|
|
||||||
return m_pState->ServerID;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::KickUser(int a_ClientID, const AString & a_Reason)
|
void cServer::KickUser(int a_ClientID, const AString & a_Reason)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
@ -553,6 +558,7 @@ void cServer::AuthenticateUser(int a_ClientID)
|
|||||||
if ((*itr)->GetUniqueID() == a_ClientID)
|
if ((*itr)->GetUniqueID() == a_ClientID)
|
||||||
{
|
{
|
||||||
(*itr)->Authenticate();
|
(*itr)->Authenticate();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} // for itr - m_Clients[]
|
} // for itr - m_Clients[]
|
||||||
}
|
}
|
||||||
@ -562,7 +568,7 @@ void cServer::AuthenticateUser(int a_ClientID)
|
|||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// cServer::cClientPacketThread:
|
// cServer::cNotifyWriteThread:
|
||||||
|
|
||||||
cServer::cNotifyWriteThread::cNotifyWriteThread(void) :
|
cServer::cNotifyWriteThread::cNotifyWriteThread(void) :
|
||||||
super("ClientPacketThread"),
|
super("ClientPacketThread"),
|
||||||
|
@ -37,11 +37,16 @@ class cServer // tolua_export
|
|||||||
public: // tolua_export
|
public: // tolua_export
|
||||||
bool InitServer(cIniFile & a_SettingsIni);
|
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);
|
||||||
|
void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; }
|
||||||
|
|
||||||
|
// tolua_end
|
||||||
|
|
||||||
bool Start(void);
|
bool Start(void);
|
||||||
|
|
||||||
@ -53,14 +58,12 @@ public: // tolua_export
|
|||||||
/// Binds the built-in console commands with the plugin manager
|
/// Binds the built-in console commands with the plugin manager
|
||||||
static void BindBuiltInConsoleCommands(void);
|
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 KickUser(int a_ClientID, const AString & a_Reason);
|
||||||
void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user
|
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
|
void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client
|
||||||
|
|
||||||
@ -72,6 +75,15 @@ public: // tolua_export
|
|||||||
|
|
||||||
void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
|
void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
|
||||||
|
|
||||||
|
/// Don't tick a_Client anymore, it will be ticked from its cPlayer instead
|
||||||
|
void ClientMovedToWorld(const cClientHandle * a_Client);
|
||||||
|
|
||||||
|
/// Notifies the server that a player was created; the server uses this to adjust the number of players
|
||||||
|
void PlayerCreated(const cPlayer * a_Player);
|
||||||
|
|
||||||
|
/// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players
|
||||||
|
void PlayerDestroying(const cPlayer * a_Player);
|
||||||
|
|
||||||
CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
|
CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
|
||||||
CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
|
CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
|
||||||
|
|
||||||
@ -103,25 +115,41 @@ private:
|
|||||||
void NotifyClientWrite(const cClientHandle * a_Client);
|
void NotifyClientWrite(const cClientHandle * a_Client);
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
struct sServerState;
|
/// The server tick thread takes care of the players who aren't yet spawned in a world
|
||||||
sServerState * m_pState;
|
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;
|
cNotifyWriteThread m_NotifyWriteThread;
|
||||||
|
|
||||||
cListenThread m_ListenThreadIPv4;
|
cListenThread m_ListenThreadIPv4;
|
||||||
cListenThread m_ListenThreadIPv6;
|
cListenThread m_ListenThreadIPv6;
|
||||||
|
|
||||||
cCriticalSection m_CSClients; // Locks client list
|
cCriticalSection m_CSClients; ///< Locks client lists
|
||||||
cClientHandleList m_Clients; // Clients that are connected to the server
|
cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld
|
||||||
|
cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick()
|
||||||
|
|
||||||
|
cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount
|
||||||
|
int m_PlayerCount; ///< Number of players currently playing in the server
|
||||||
|
cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff
|
||||||
|
int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread
|
||||||
|
|
||||||
cSocketThreads m_SocketThreads;
|
cSocketThreads m_SocketThreads;
|
||||||
|
|
||||||
int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini
|
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_bIsConnected; // true - connected false - not connected
|
||||||
|
|
||||||
bool m_bRestarting;
|
bool m_bRestarting;
|
||||||
@ -131,13 +159,26 @@ private:
|
|||||||
|
|
||||||
cRCONServer m_RCONServer;
|
cRCONServer m_RCONServer;
|
||||||
|
|
||||||
|
AString m_Description;
|
||||||
|
int m_MaxPlayers;
|
||||||
|
|
||||||
|
cTickThread m_TickThread;
|
||||||
|
cEvent m_RestartEvent;
|
||||||
|
|
||||||
|
/// The server ID used for client authentication
|
||||||
|
AString m_ServerID;
|
||||||
|
|
||||||
|
|
||||||
cServer(void);
|
cServer(void);
|
||||||
~cServer();
|
|
||||||
|
|
||||||
/// Loads, or generates, if missing, RSA keys for protocol encryption
|
/// Loads, or generates, if missing, RSA keys for protocol encryption
|
||||||
void PrepareKeys(void);
|
void PrepareKeys(void);
|
||||||
|
|
||||||
|
bool Tick(float a_Dt);
|
||||||
|
|
||||||
|
/// Ticks the clients in m_Clients, manages the list in respect to removing clients
|
||||||
|
void TickClients(float a_Dt);
|
||||||
|
|
||||||
// cListenThread::cCallback overrides:
|
// cListenThread::cCallback overrides:
|
||||||
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
|
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
|
||||||
}; // tolua_export
|
}; // tolua_export
|
||||||
|
418
source/World.cpp
418
source/World.cpp
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||||
|
|
||||||
#include "BlockID.h"
|
#include "BlockID.h"
|
||||||
@ -128,6 +129,9 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cWorldLightingProgress:
|
||||||
|
|
||||||
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
|
/// A simple thread that displays the progress of world lighting in cWorld::InitializeSpawn()
|
||||||
class cWorldLightingProgress :
|
class cWorldLightingProgress :
|
||||||
public cIsThread
|
public cIsThread
|
||||||
@ -187,10 +191,47 @@ cWorld::cLock::cLock(cWorld & a_World) :
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cWorld::cTickThread:
|
||||||
|
|
||||||
|
cWorld::cTickThread::cTickThread(cWorld & a_World) :
|
||||||
|
super(Printf("WorldTickThread: %s", a_World.GetName().c_str())),
|
||||||
|
m_World(a_World)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cWorld::cTickThread::Execute(void)
|
||||||
|
{
|
||||||
|
const int ClocksPerTick = CLOCKS_PER_SEC / 20;
|
||||||
|
clock_t LastTime = clock();
|
||||||
|
while (!m_ShouldTerminate)
|
||||||
|
{
|
||||||
|
clock_t Start = clock();
|
||||||
|
m_World.Tick((float)(1000 * (Start - LastTime)) / CLOCKS_PER_SEC);
|
||||||
|
clock_t Now = clock();
|
||||||
|
if (Now - Start < ClocksPerTick)
|
||||||
|
{
|
||||||
|
cSleep::MilliSleep(1000 * (ClocksPerTick - (Now - Start)) / CLOCKS_PER_SEC);
|
||||||
|
}
|
||||||
|
LastTime = Start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// cWorld:
|
// cWorld:
|
||||||
|
|
||||||
cWorld::cWorld(const AString & a_WorldName) :
|
cWorld::cWorld(const AString & a_WorldName) :
|
||||||
|
m_WorldName(a_WorldName),
|
||||||
|
m_IniFileName(m_WorldName + "/world.ini"),
|
||||||
|
m_StorageSchema("Default"),
|
||||||
m_WorldAgeSecs(0),
|
m_WorldAgeSecs(0),
|
||||||
m_TimeOfDaySecs(0),
|
m_TimeOfDaySecs(0),
|
||||||
m_WorldAge(0),
|
m_WorldAge(0),
|
||||||
@ -199,109 +240,12 @@ cWorld::cWorld(const AString & a_WorldName) :
|
|||||||
m_LastSpawnMonster(0),
|
m_LastSpawnMonster(0),
|
||||||
m_RSList(0),
|
m_RSList(0),
|
||||||
m_Weather(eWeather_Sunny),
|
m_Weather(eWeather_Sunny),
|
||||||
m_WeatherInterval(24000) // Guaranteed 1 day of sunshine at server start :)
|
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
|
||||||
|
m_TickThread(*this)
|
||||||
{
|
{
|
||||||
LOGD("cWorld::cWorld(%s)", a_WorldName.c_str());
|
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
|
||||||
m_WorldName = a_WorldName;
|
|
||||||
m_IniFileName = m_WorldName + "/world.ini";
|
|
||||||
|
|
||||||
cMakeDir::MakeDir(m_WorldName.c_str());
|
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ChunkMap = new cChunkMap(this);
|
|
||||||
|
|
||||||
m_ChunkSender.Start(this);
|
|
||||||
|
|
||||||
m_LastSave = 0;
|
|
||||||
m_LastUnload = 0;
|
|
||||||
|
|
||||||
// preallocate some memory for ticking blocks so we don<6F>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);
|
|
||||||
|
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -504,10 +448,126 @@ 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<6F>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)
|
||||||
|
{
|
||||||
|
// Delete the clients that have been in this world:
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSClients);
|
||||||
|
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||||
|
{
|
||||||
|
(*itr)->Destroy();
|
||||||
|
delete *itr;
|
||||||
|
} // for itr - m_Clients[]
|
||||||
|
m_Clients.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_TickThread.Stop();
|
||||||
|
m_Lighting.Stop();
|
||||||
m_Generator.Stop();
|
m_Generator.Stop();
|
||||||
m_ChunkSender.Stop();
|
m_ChunkSender.Stop();
|
||||||
|
m_Storage.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -529,7 +589,7 @@ void cWorld::Tick(float a_Dt)
|
|||||||
m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
|
m_WorldAge = (Int64)(m_WorldAgeSecs * 20.0);
|
||||||
m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0);
|
m_TimeOfDay = (Int64)(m_TimeOfDaySecs * 20.0);
|
||||||
|
|
||||||
// Broadcase time update every 40 ticks (2 seconds)
|
// Broadcast time update every 40 ticks (2 seconds)
|
||||||
if (m_LastTimeUpdate < m_WorldAge - 40)
|
if (m_LastTimeUpdate < m_WorldAge - 40)
|
||||||
{
|
{
|
||||||
BroadcastTimeUpdate();
|
BroadcastTimeUpdate();
|
||||||
@ -538,7 +598,9 @@ void cWorld::Tick(float a_Dt)
|
|||||||
|
|
||||||
m_ChunkMap->Tick(a_Dt);
|
m_ChunkMap->Tick(a_Dt);
|
||||||
|
|
||||||
|
TickClients(a_Dt);
|
||||||
TickQueuedBlocks(a_Dt);
|
TickQueuedBlocks(a_Dt);
|
||||||
|
TickQueuedTasks();
|
||||||
|
|
||||||
GetSimulatorManager()->Simulate(a_Dt);
|
GetSimulatorManager()->Simulate(a_Dt);
|
||||||
|
|
||||||
@ -740,6 +802,78 @@ 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::TickClients(float a_Dt)
|
||||||
|
{
|
||||||
|
cClientHandleList RemoveClients;
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSClients);
|
||||||
|
|
||||||
|
// Remove clients scheduled for removal:
|
||||||
|
for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
m_Clients.remove(*itr);
|
||||||
|
} // for itr - m_ClientsToRemove[]
|
||||||
|
m_ClientsToRemove.clear();
|
||||||
|
|
||||||
|
// Add clients scheduled for adding:
|
||||||
|
for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
|
||||||
|
{
|
||||||
|
if (std::find(m_Clients.begin(), m_Clients.end(), *itr) != m_Clients.end())
|
||||||
|
{
|
||||||
|
ASSERT(!"Adding a client that is already in the clientlist");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
m_Clients.push_back(*itr);
|
||||||
|
} // for itr - m_ClientsToRemove[]
|
||||||
|
m_ClientsToAdd.clear();
|
||||||
|
|
||||||
|
// Tick the clients, take out those that have been destroyed into RemoveClients
|
||||||
|
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
||||||
|
{
|
||||||
|
if ((*itr)->IsDestroyed())
|
||||||
|
{
|
||||||
|
// Remove the client later, when CS is not held, to avoid deadlock
|
||||||
|
RemoveClients.push_back(*itr);
|
||||||
|
itr = m_Clients.erase(itr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
(*itr)->Tick(a_Dt);
|
||||||
|
++itr;
|
||||||
|
} // for itr - m_Clients[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the clients that have been destroyed
|
||||||
|
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
||||||
|
{
|
||||||
|
delete *itr;
|
||||||
|
} // for itr - RemoveClients[]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
|
void cWorld::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||||
{
|
{
|
||||||
return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
|
return m_ChunkMap->WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
|
||||||
@ -1900,28 +2034,24 @@ 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)
|
void cWorld::AddPlayer(cPlayer * a_Player)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSPlayers);
|
{
|
||||||
|
cCSLock Lock(m_CSPlayers);
|
||||||
ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
|
|
||||||
|
ASSERT(std::find(m_Players.begin(), m_Players.end(), a_Player) == m_Players.end()); // Is it already in the list? HOW?
|
||||||
m_Players.remove(a_Player); // Make sure the player is registered only once
|
|
||||||
m_Players.push_back(a_Player);
|
m_Players.remove(a_Player); // Make sure the player is registered only once
|
||||||
|
m_Players.push_back(a_Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the player's client to the list of clients to be ticked:
|
||||||
|
if (a_Player->GetClientHandle() != NULL)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSClients);
|
||||||
|
m_ClientsToAdd.push_back(a_Player->GetClientHandle());
|
||||||
|
}
|
||||||
|
|
||||||
// The player has already been added to the chunkmap as the entity, do NOT add again!
|
// The player has already been added to the chunkmap as the entity, do NOT add again!
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1932,8 +2062,17 @@ void cWorld::AddPlayer(cPlayer * a_Player)
|
|||||||
void cWorld::RemovePlayer(cPlayer * a_Player)
|
void cWorld::RemovePlayer(cPlayer * a_Player)
|
||||||
{
|
{
|
||||||
m_ChunkMap->RemoveEntity(a_Player);
|
m_ChunkMap->RemoveEntity(a_Player);
|
||||||
cCSLock Lock(m_CSPlayers);
|
{
|
||||||
m_Players.remove(a_Player);
|
cCSLock Lock(m_CSPlayers);
|
||||||
|
m_Players.remove(a_Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the player's client from the list of clients to be ticked:
|
||||||
|
if (a_Player->GetClientHandle() != NULL)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSClients);
|
||||||
|
m_ClientsToRemove.push_back(a_Player->GetClientHandle());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2279,6 +2418,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)
|
void cWorld::AddEntity(cEntity * a_Entity)
|
||||||
{
|
{
|
||||||
m_ChunkMap->AddEntity(a_Entity);
|
m_ChunkMap->AddEntity(a_Entity);
|
||||||
@ -2306,11 +2464,13 @@ void cWorld::RemoveEntity(cEntity * a_Entity)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
unsigned int cWorld::GetNumPlayers(void)
|
unsigned int cWorld::GetNumPlayers(void)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSPlayers);
|
cCSLock Lock(m_CSPlayers);
|
||||||
return m_Players.size();
|
return m_Players.size();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2522,3 +2682,15 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cWorld::cTaskSaveAllChunks:
|
||||||
|
|
||||||
|
void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
|
||||||
|
{
|
||||||
|
a_World.SaveAllChunks();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
111
source/World.h
111
source/World.h
@ -69,6 +69,24 @@ public:
|
|||||||
public:
|
public:
|
||||||
cLock(cWorld & a_World);
|
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
|
// tolua_begin
|
||||||
|
|
||||||
@ -136,8 +154,8 @@ public:
|
|||||||
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
|
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
|
||||||
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
|
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
|
||||||
void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
|
void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
|
||||||
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
|
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
|
||||||
void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL);
|
void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export
|
||||||
void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);
|
void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL);
|
||||||
void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL);
|
void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL);
|
||||||
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
|
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
|
||||||
@ -204,13 +222,6 @@ public:
|
|||||||
|
|
||||||
void CollectPickupsByPlayer(cPlayer * a_Player);
|
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 AddPlayer( cPlayer* a_Player );
|
||||||
void RemovePlayer( 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
|
/// 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 <<
|
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)
|
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
|
||||||
cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit);
|
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--;
|
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
|
/// 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
|
/// Returns the number of chunks loaded and dirty, and in the lighting queue
|
||||||
void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
|
void 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 GetStorageLoadQueueLength(void) { return m_Storage.GetLoadQueueLength(); } // tolua_export
|
||||||
inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
|
inline int GetStorageSaveQueueLength(void) { return m_Storage.GetSaveQueueLength(); } // tolua_export
|
||||||
|
|
||||||
void Tick(float a_Dt);
|
|
||||||
|
|
||||||
void InitializeSpawn(void);
|
void InitializeSpawn(void);
|
||||||
|
|
||||||
|
/// Starts threads that belong to this world
|
||||||
|
void Start(void);
|
||||||
|
|
||||||
/// Stops threads that belong to this world (part of deinit)
|
/// Stops threads that belong to this world (part of deinit)
|
||||||
void StopThreads(void);
|
void Stop(void);
|
||||||
|
|
||||||
void TickQueuedBlocks(float a_Dt);
|
void TickQueuedBlocks(float a_Dt);
|
||||||
|
|
||||||
struct BlockTickQueueItem
|
struct BlockTickQueueItem
|
||||||
@ -540,7 +558,28 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
friend class cRoot;
|
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
|
/// The dimension of the world, used by the client to provide correct lighting scheme
|
||||||
eDimension m_Dimension;
|
eDimension m_Dimension;
|
||||||
|
|
||||||
@ -583,8 +622,6 @@ private:
|
|||||||
|
|
||||||
cWorldStorage m_Storage;
|
cWorldStorage m_Storage;
|
||||||
|
|
||||||
AString m_Description;
|
|
||||||
|
|
||||||
unsigned int m_MaxPlayers;
|
unsigned int m_MaxPlayers;
|
||||||
|
|
||||||
cChunkMap * m_ChunkMap;
|
cChunkMap * m_ChunkMap;
|
||||||
@ -616,15 +653,43 @@ private:
|
|||||||
|
|
||||||
cChunkSender m_ChunkSender;
|
cChunkSender m_ChunkSender;
|
||||||
cLightingThread m_Lighting;
|
cLightingThread m_Lighting;
|
||||||
|
cTickThread m_TickThread;
|
||||||
AString m_WorldName;
|
|
||||||
AString m_IniFileName;
|
|
||||||
|
|
||||||
|
/// Guards the m_Tasks
|
||||||
|
cCriticalSection m_CSTasks;
|
||||||
|
|
||||||
|
/// Tasks that have been queued onto the tick thread; guarded by m_CSTasks
|
||||||
|
cTasks m_Tasks;
|
||||||
|
|
||||||
|
/// Guards m_Clients
|
||||||
|
cCriticalSection m_CSClients;
|
||||||
|
|
||||||
|
/// List of clients in this world, these will be ticked by this world
|
||||||
|
cClientHandleList m_Clients;
|
||||||
|
|
||||||
|
/// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them
|
||||||
|
cClientHandleList m_ClientsToRemove;
|
||||||
|
|
||||||
|
/// Clients that are scheduled for adding, waiting for TickClients to add them
|
||||||
|
cClientHandleList m_ClientsToAdd;
|
||||||
|
|
||||||
|
|
||||||
cWorld(const AString & a_WorldName);
|
cWorld(const AString & a_WorldName);
|
||||||
~cWorld();
|
~cWorld();
|
||||||
|
|
||||||
void TickWeather(float a_Dt); // Handles weather each tick
|
void Tick(float a_Dt);
|
||||||
void TickSpawnMobs(float a_Dt); // Handles mob spawning each tick
|
|
||||||
|
/// Handles the weather in each tick
|
||||||
|
void TickWeather(float a_Dt);
|
||||||
|
|
||||||
|
/// Handles the mob spawning each tick
|
||||||
|
void TickSpawnMobs(float a_Dt);
|
||||||
|
|
||||||
|
/// Executes all tasks queued onto the tick thread
|
||||||
|
void TickQueuedTasks(void);
|
||||||
|
|
||||||
|
/// Ticks all clients that are in this world
|
||||||
|
void TickClients(float a_Dt);
|
||||||
|
|
||||||
/// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section)
|
/// 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);
|
cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock);
|
||||||
|
@ -84,6 +84,15 @@ bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cWorldStorage::Stop(void)
|
||||||
|
{
|
||||||
|
WaitForFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cWorldStorage::WaitForFinish(void)
|
void cWorldStorage::WaitForFinish(void)
|
||||||
{
|
{
|
||||||
LOG("Waiting for the world storage to finish saving");
|
LOG("Waiting for the world storage to finish saving");
|
||||||
|
@ -75,6 +75,7 @@ public:
|
|||||||
void UnqueueSave(const cChunkCoords & a_Chunk);
|
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
|
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 WaitForFinish(void);
|
||||||
void WaitForQueuesEmpty(void);
|
void WaitForQueuesEmpty(void);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user