Converted cServer to use the cNetwork API.
This commit is contained in:
parent
1f2f8b553b
commit
7dfeb67f01
@ -15,7 +15,6 @@
|
|||||||
#include "Item.h"
|
#include "Item.h"
|
||||||
#include "Mobs/Monster.h"
|
#include "Mobs/Monster.h"
|
||||||
#include "ChatColor.h"
|
#include "ChatColor.h"
|
||||||
#include "OSSupport/Socket.h"
|
|
||||||
#include "Items/ItemHandler.h"
|
#include "Items/ItemHandler.h"
|
||||||
#include "Blocks/BlockHandler.h"
|
#include "Blocks/BlockHandler.h"
|
||||||
#include "Blocks/BlockSlab.h"
|
#include "Blocks/BlockSlab.h"
|
||||||
@ -56,16 +55,15 @@ int cClientHandle::s_ClientCount = 0;
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// cClientHandle:
|
// cClientHandle:
|
||||||
|
|
||||||
cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
|
cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
|
||||||
m_CurrentViewDistance(a_ViewDistance),
|
m_CurrentViewDistance(a_ViewDistance),
|
||||||
m_RequestedViewDistance(a_ViewDistance),
|
m_RequestedViewDistance(a_ViewDistance),
|
||||||
m_IPString(a_Socket->GetIPString()),
|
m_IPString(a_IPString),
|
||||||
m_OutgoingData(64 KiB),
|
|
||||||
m_Player(nullptr),
|
m_Player(nullptr),
|
||||||
m_HasSentDC(false),
|
m_HasSentDC(false),
|
||||||
m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login
|
m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login
|
||||||
m_LastStreamedChunkZ(0x7fffffff),
|
m_LastStreamedChunkZ(0x7fffffff),
|
||||||
m_TimeSinceLastPacket(0),
|
m_TicksSinceLastPacket(0),
|
||||||
m_Ping(1000),
|
m_Ping(1000),
|
||||||
m_PingID(1),
|
m_PingID(1),
|
||||||
m_BlockDigAnimStage(-1),
|
m_BlockDigAnimStage(-1),
|
||||||
@ -135,9 +133,6 @@ cClientHandle::~cClientHandle()
|
|||||||
SendDisconnect("Server shut down? Kthnxbai");
|
SendDisconnect("Server shut down? Kthnxbai");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the socket as soon as it sends all outgoing data:
|
|
||||||
cRoot::Get()->GetServer()->RemoveClient(this);
|
|
||||||
|
|
||||||
delete m_Protocol;
|
delete m_Protocol;
|
||||||
m_Protocol = nullptr;
|
m_Protocol = nullptr;
|
||||||
|
|
||||||
@ -150,6 +145,10 @@ cClientHandle::~cClientHandle()
|
|||||||
|
|
||||||
void cClientHandle::Destroy(void)
|
void cClientHandle::Destroy(void)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSOutgoingData);
|
||||||
|
m_Link.reset();
|
||||||
|
}
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSDestroyingState);
|
cCSLock Lock(m_CSDestroyingState);
|
||||||
if (m_State >= csDestroying)
|
if (m_State >= csDestroying)
|
||||||
@ -168,6 +167,10 @@ void cClientHandle::Destroy(void)
|
|||||||
RemoveFromAllChunks();
|
RemoveFromAllChunks();
|
||||||
m_Player->GetWorld()->RemoveClientFromChunkSender(this);
|
m_Player->GetWorld()->RemoveClientFromChunkSender(this);
|
||||||
}
|
}
|
||||||
|
if (m_Player != nullptr)
|
||||||
|
{
|
||||||
|
m_Player->RemoveClientHandle();
|
||||||
|
}
|
||||||
m_State = csDestroyed;
|
m_State = csDestroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +329,8 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
|
|||||||
m_Protocol->SendLoginSuccess();
|
m_Protocol->SendLoginSuccess();
|
||||||
|
|
||||||
// Spawn player (only serversided, so data is loaded)
|
// Spawn player (only serversided, so data is loaded)
|
||||||
m_Player = new cPlayer(this, GetUsername());
|
m_Player = new cPlayer(m_Self, GetUsername());
|
||||||
|
m_Self.reset();
|
||||||
|
|
||||||
cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName());
|
cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName());
|
||||||
if (World == nullptr)
|
if (World == nullptr)
|
||||||
@ -689,6 +693,47 @@ void cClientHandle::HandleCreativeInventory(short a_SlotNum, const cItem & a_Hel
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cClientHandle::HandleEnchantItem(Byte a_WindowID, Byte a_Enchantment)
|
||||||
|
{
|
||||||
|
if (a_Enchantment > 2)
|
||||||
|
{
|
||||||
|
LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str());
|
||||||
|
Kick("Invalid enchanting!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(m_Player->GetWindow() == nullptr) ||
|
||||||
|
(m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
|
||||||
|
(m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow();
|
||||||
|
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player);
|
||||||
|
int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
|
||||||
|
|
||||||
|
if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
|
||||||
|
{
|
||||||
|
if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0)
|
||||||
|
{
|
||||||
|
Window->m_SlotArea->SetSlot(0, *m_Player, Item);
|
||||||
|
Window->SendSlot(*m_Player, Window->m_SlotArea, 0);
|
||||||
|
Window->BroadcastWholeWindow();
|
||||||
|
|
||||||
|
Window->SetProperty(0, 0, *m_Player);
|
||||||
|
Window->SetProperty(1, 0, *m_Player);
|
||||||
|
Window->SetProperty(2, 0, *m_Player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
|
void cClientHandle::HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
|
||||||
{
|
{
|
||||||
UNUSED(FlyingSpeed); // Ignore the client values for these
|
UNUSED(FlyingSpeed); // Ignore the client values for these
|
||||||
@ -1778,43 +1823,11 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CSOutgoingData);
|
cCSLock Lock(m_CSOutgoingData);
|
||||||
|
if (m_Link != nullptr)
|
||||||
// _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks
|
|
||||||
if (m_OutgoingDataOverflow.empty())
|
|
||||||
{
|
{
|
||||||
// No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer:
|
m_Link->Send(a_Data, a_Size);
|
||||||
size_t CanFit = m_OutgoingData.GetFreeSpace();
|
|
||||||
if (CanFit > a_Size)
|
|
||||||
{
|
|
||||||
CanFit = a_Size;
|
|
||||||
}
|
}
|
||||||
if (CanFit > 0)
|
|
||||||
{
|
|
||||||
m_OutgoingData.Write(a_Data, CanFit);
|
|
||||||
}
|
|
||||||
if (a_Size > CanFit)
|
|
||||||
{
|
|
||||||
m_OutgoingDataOverflow.append(a_Data + CanFit, a_Size - CanFit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// There is a queued overflow. Append to it, then send as much from its front as possible
|
|
||||||
m_OutgoingDataOverflow.append(a_Data, a_Size);
|
|
||||||
size_t CanFit = m_OutgoingData.GetFreeSpace();
|
|
||||||
if (CanFit > 128)
|
|
||||||
{
|
|
||||||
// No point in moving the data over if it's not large enough - too much effort for too little an effect
|
|
||||||
m_OutgoingData.Write(m_OutgoingDataOverflow.data(), CanFit);
|
|
||||||
m_OutgoingDataOverflow.erase(0, CanFit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // Lock(m_CSOutgoingData)
|
|
||||||
|
|
||||||
// Notify SocketThreads that we have something to write:
|
|
||||||
cRoot::Get()->GetServer()->NotifyClientWrite(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1873,8 +1886,8 @@ void cClientHandle::Tick(float a_Dt)
|
|||||||
}
|
}
|
||||||
m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
|
m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
|
||||||
|
|
||||||
m_TimeSinceLastPacket += a_Dt;
|
m_TicksSinceLastPacket += 1;
|
||||||
if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
|
if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
|
||||||
{
|
{
|
||||||
SendDisconnect("Nooooo!! You timed out! D: Come back!");
|
SendDisconnect("Nooooo!! You timed out! D: Come back!");
|
||||||
Destroy();
|
Destroy();
|
||||||
@ -1970,8 +1983,8 @@ void cClientHandle::ServerTick(float a_Dt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_TimeSinceLastPacket += a_Dt;
|
m_TicksSinceLastPacket += 1;
|
||||||
if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
|
if (m_TicksSinceLastPacket > 600) // 30 seconds
|
||||||
{
|
{
|
||||||
SendDisconnect("Nooooo!! You timed out! D: Come back!");
|
SendDisconnect("Nooooo!! You timed out! D: Come back!");
|
||||||
Destroy();
|
Destroy();
|
||||||
@ -2843,49 +2856,13 @@ void cClientHandle::PacketError(UInt32 a_PacketType)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cClientHandle::DataReceived(const char * a_Data, size_t a_Size)
|
|
||||||
{
|
|
||||||
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
|
|
||||||
m_TimeSinceLastPacket = 0;
|
|
||||||
cCSLock Lock(m_CSIncomingData);
|
|
||||||
m_IncomingData.append(a_Data, a_Size);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::GetOutgoingData(AString & a_Data)
|
|
||||||
{
|
|
||||||
// Data can be sent to client
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CSOutgoingData);
|
|
||||||
m_OutgoingData.ReadAll(a_Data);
|
|
||||||
m_OutgoingData.CommitRead();
|
|
||||||
a_Data.append(m_OutgoingDataOverflow);
|
|
||||||
m_OutgoingDataOverflow.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disconnect player after all packets have been sent
|
|
||||||
if (m_HasSentDC && a_Data.empty())
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::SocketClosed(void)
|
void cClientHandle::SocketClosed(void)
|
||||||
{
|
{
|
||||||
// The socket has been closed for any reason
|
// The socket has been closed for any reason
|
||||||
|
|
||||||
LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
|
|
||||||
|
|
||||||
if (!m_Username.empty()) // Ignore client pings
|
if (!m_Username.empty()) // Ignore client pings
|
||||||
{
|
{
|
||||||
|
LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
|
||||||
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
|
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2896,41 +2873,62 @@ void cClientHandle::SocketClosed(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment)
|
void cClientHandle::SetSelf(cClientHandlePtr a_Self)
|
||||||
{
|
{
|
||||||
if (a_Enchantment > 2)
|
ASSERT(m_Self == nullptr);
|
||||||
{
|
m_Self = a_Self;
|
||||||
LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str());
|
}
|
||||||
Kick("Invalid enchanting!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(m_Player->GetWindow() == nullptr) ||
|
void cClientHandle::OnLinkCreated(cTCPLinkPtr a_Link)
|
||||||
(m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
|
{
|
||||||
(m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment)
|
m_Link = a_Link;
|
||||||
)
|
}
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow();
|
|
||||||
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player);
|
void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length)
|
||||||
int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
|
{
|
||||||
|
// Reset the timeout:
|
||||||
if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
|
m_TicksSinceLastPacket = 0;
|
||||||
{
|
|
||||||
if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0)
|
// Queue the incoming data to be processed in the tick thread:
|
||||||
{
|
cCSLock Lock(m_CSIncomingData);
|
||||||
Window->m_SlotArea->SetSlot(0, *m_Player, Item);
|
m_IncomingData.append(a_Data, a_Length);
|
||||||
Window->SendSlot(*m_Player, Window->m_SlotArea, 0);
|
}
|
||||||
Window->BroadcastWholeWindow();
|
|
||||||
|
|
||||||
Window->SetProperty(0, 0, *m_Player);
|
|
||||||
Window->SetProperty(1, 0, *m_Player);
|
|
||||||
Window->SetProperty(2, 0, *m_Player);
|
|
||||||
}
|
void cClientHandle::OnRemoteClosed(void)
|
||||||
}
|
{
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSOutgoingData);
|
||||||
|
m_Link.reset();
|
||||||
|
}
|
||||||
|
SocketClosed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
|
||||||
|
{
|
||||||
|
LOGD("An error has occurred on client link for %s @ %s: %d (%s). Client disconnected.",
|
||||||
|
m_Username.c_str(), m_IPString.c_str(), a_ErrorCode, a_ErrorMsg.c_str()
|
||||||
|
);
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSOutgoingData);
|
||||||
|
m_Link.reset();
|
||||||
|
}
|
||||||
|
SocketClosed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,12 +8,10 @@
|
|||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef CCLIENTHANDLE_H_INCLUDED
|
|
||||||
#define CCLIENTHANDLE_H_INCLUDED
|
|
||||||
|
|
||||||
|
#include "OSSupport/Network.h"
|
||||||
#include "Defines.h"
|
#include "Defines.h"
|
||||||
#include "Vector3.h"
|
#include "Vector3.h"
|
||||||
#include "OSSupport/SocketThreads.h"
|
|
||||||
#include "ChunkDef.h"
|
#include "ChunkDef.h"
|
||||||
#include "ByteBuffer.h"
|
#include "ByteBuffer.h"
|
||||||
#include "Scoreboard.h"
|
#include "Scoreboard.h"
|
||||||
@ -27,6 +25,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// fwd:
|
||||||
class cChunkDataSerializer;
|
class cChunkDataSerializer;
|
||||||
class cInventory;
|
class cInventory;
|
||||||
class cMonster;
|
class cMonster;
|
||||||
@ -42,15 +41,17 @@ class cItemHandler;
|
|||||||
class cWorld;
|
class cWorld;
|
||||||
class cCompositeChat;
|
class cCompositeChat;
|
||||||
class cStatManager;
|
class cStatManager;
|
||||||
|
class cClientHandle;
|
||||||
|
typedef SharedPtr<cClientHandle> cClientHandlePtr;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class cClientHandle : // tolua_export
|
class cClientHandle // tolua_export
|
||||||
public cSocketThreads::cCallback
|
: public cTCPLink::cCallbacks
|
||||||
{ // tolua_export
|
{ // tolua_export
|
||||||
public:
|
public: // tolua_export
|
||||||
|
|
||||||
#if defined(ANDROID_NDK)
|
#if defined(ANDROID_NDK)
|
||||||
static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
|
static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
|
||||||
@ -60,7 +61,9 @@ public:
|
|||||||
static const int MAX_VIEW_DISTANCE = 32;
|
static const int MAX_VIEW_DISTANCE = 32;
|
||||||
static const int MIN_VIEW_DISTANCE = 1;
|
static const int MIN_VIEW_DISTANCE = 1;
|
||||||
|
|
||||||
cClientHandle(const cSocket * a_Socket, int a_ViewDistance);
|
/** Creates a new client with the specified IP address in its description and the specified initial view distance. */
|
||||||
|
cClientHandle(const AString & a_IPString, int a_ViewDistance);
|
||||||
|
|
||||||
virtual ~cClientHandle();
|
virtual ~cClientHandle();
|
||||||
|
|
||||||
const AString & GetIPString(void) const { return m_IPString; } // tolua_export
|
const AString & GetIPString(void) const { return m_IPString; } // tolua_export
|
||||||
@ -276,6 +279,10 @@ public:
|
|||||||
void HandleCommandBlockEntityChange(int a_EntityID, const AString & a_NewCommand);
|
void HandleCommandBlockEntityChange(int a_EntityID, const AString & a_NewCommand);
|
||||||
|
|
||||||
void HandleCreativeInventory (short a_SlotNum, const cItem & a_HeldItem);
|
void HandleCreativeInventory (short a_SlotNum, const cItem & a_HeldItem);
|
||||||
|
|
||||||
|
/** Called when the player enchants an Item in the Enchanting table UI. */
|
||||||
|
void HandleEnchantItem(Byte a_WindowID, Byte a_Enchantment);
|
||||||
|
|
||||||
void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching);
|
void HandleEntityCrouch (int a_EntityID, bool a_IsCrouching);
|
||||||
void HandleEntityLeaveBed (int a_EntityID);
|
void HandleEntityLeaveBed (int a_EntityID);
|
||||||
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
|
void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting);
|
||||||
@ -329,9 +336,6 @@ public:
|
|||||||
Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */
|
Sends an UnloadChunk packet for each loaded chunk and resets the streamed chunks. */
|
||||||
void RemoveFromWorld(void);
|
void RemoveFromWorld(void);
|
||||||
|
|
||||||
/** Called when the player will enchant a Item */
|
|
||||||
void HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment);
|
|
||||||
|
|
||||||
/** Called by the protocol recognizer when the protocol version is known. */
|
/** Called by the protocol recognizer when the protocol version is known. */
|
||||||
void SetProtocolVersion(UInt32 a_ProtocolVersion) { m_ProtocolVersion = a_ProtocolVersion; }
|
void SetProtocolVersion(UInt32 a_ProtocolVersion) { m_ProtocolVersion = a_ProtocolVersion; }
|
||||||
|
|
||||||
@ -340,6 +344,9 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
friend class cServer; // Needs access to SetSelf()
|
||||||
|
|
||||||
|
|
||||||
/** The type used for storing the names of registered plugin channels. */
|
/** The type used for storing the names of registered plugin channels. */
|
||||||
typedef std::set<AString> cChannels;
|
typedef std::set<AString> cChannels;
|
||||||
|
|
||||||
@ -362,12 +369,15 @@ private:
|
|||||||
|
|
||||||
cProtocol * m_Protocol;
|
cProtocol * m_Protocol;
|
||||||
|
|
||||||
|
/** Protects m_IncomingData against multithreaded access. */
|
||||||
cCriticalSection m_CSIncomingData;
|
cCriticalSection m_CSIncomingData;
|
||||||
|
|
||||||
|
/** Queue for the incoming data received on the link until it is processed in Tick().
|
||||||
|
Protected by m_CSIncomingData. */
|
||||||
AString m_IncomingData;
|
AString m_IncomingData;
|
||||||
|
|
||||||
|
/** Protects data going out through m_Link against multi-threaded sending. */
|
||||||
cCriticalSection m_CSOutgoingData;
|
cCriticalSection m_CSOutgoingData;
|
||||||
cByteBuffer m_OutgoingData;
|
|
||||||
AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily
|
|
||||||
|
|
||||||
Vector3d m_ConfirmPosition;
|
Vector3d m_ConfirmPosition;
|
||||||
|
|
||||||
@ -379,8 +389,8 @@ private:
|
|||||||
int m_LastStreamedChunkX;
|
int m_LastStreamedChunkX;
|
||||||
int m_LastStreamedChunkZ;
|
int m_LastStreamedChunkZ;
|
||||||
|
|
||||||
/** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
|
/** Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedData()) */
|
||||||
float m_TimeSinceLastPacket;
|
int m_TicksSinceLastPacket;
|
||||||
|
|
||||||
/** Duration of the last completed client ping. */
|
/** Duration of the last completed client ping. */
|
||||||
std::chrono::steady_clock::duration m_Ping;
|
std::chrono::steady_clock::duration m_Ping;
|
||||||
@ -458,6 +468,13 @@ private:
|
|||||||
/** The version of the protocol that the client is talking, or 0 if unknown. */
|
/** The version of the protocol that the client is talking, or 0 if unknown. */
|
||||||
UInt32 m_ProtocolVersion;
|
UInt32 m_ProtocolVersion;
|
||||||
|
|
||||||
|
/** The link that is used for network communication.
|
||||||
|
m_CSOutgoingData is used to synchronize access for sending data. */
|
||||||
|
cTCPLinkPtr m_Link;
|
||||||
|
|
||||||
|
/** Shared pointer to self, so that this instance can keep itself alive when needed. */
|
||||||
|
cClientHandlePtr m_Self;
|
||||||
|
|
||||||
|
|
||||||
/** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
|
/** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
|
||||||
bool CheckBlockInteractionsRate(void);
|
bool CheckBlockInteractionsRate(void);
|
||||||
@ -483,17 +500,20 @@ private:
|
|||||||
/** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
|
/** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
|
||||||
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
|
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
|
||||||
|
|
||||||
// cSocketThreads::cCallback overrides:
|
/** Called when the network socket has been closed. */
|
||||||
virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
|
void SocketClosed(void);
|
||||||
virtual void GetOutgoingData(AString & a_Data) override; // Data can be sent to client
|
|
||||||
virtual void SocketClosed (void) override; // The socket has been closed for any reason
|
/** Called right after the instance is created to store its SharedPtr inside. */
|
||||||
|
void SetSelf(cClientHandlePtr a_Self);
|
||||||
|
|
||||||
|
// cTCPLink::cCallbacks overrides:
|
||||||
|
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override;
|
||||||
|
virtual void OnReceivedData(const char * a_Data, size_t a_Length) override;
|
||||||
|
virtual void OnRemoteClosed(void) override;
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg) override;
|
||||||
}; // tolua_export
|
}; // tolua_export
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // CCLIENTHANDLE_H_INCLUDED
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ const int cPlayer::EATING_TICKS = 30;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
|
cPlayer::cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName) :
|
||||||
super(etPlayer, 0.6, 1.8),
|
super(etPlayer, 0.6, 1.8),
|
||||||
m_bVisible(true),
|
m_bVisible(true),
|
||||||
m_FoodLevel(MAX_FOOD_LEVEL),
|
m_FoodLevel(MAX_FOOD_LEVEL),
|
||||||
@ -174,7 +174,7 @@ void cPlayer::Destroyed()
|
|||||||
|
|
||||||
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
void cPlayer::SpawnOn(cClientHandle & a_Client)
|
||||||
{
|
{
|
||||||
if (!m_bVisible || (m_ClientHandle == (&a_Client)))
|
if (!m_bVisible || (m_ClientHandle.get() == (&a_Client)))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
|
|
||||||
if (CanMove)
|
if (CanMove)
|
||||||
{
|
{
|
||||||
BroadcastMovementUpdate(m_ClientHandle);
|
BroadcastMovementUpdate(m_ClientHandle.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Health > 0) // make sure player is alive
|
if (m_Health > 0) // make sure player is alive
|
||||||
@ -419,7 +419,7 @@ void cPlayer::StartChargingBow(void)
|
|||||||
LOGD("Player \"%s\" started charging their bow", GetName().c_str());
|
LOGD("Player \"%s\" started charging their bow", GetName().c_str());
|
||||||
m_IsChargingBow = true;
|
m_IsChargingBow = true;
|
||||||
m_BowCharge = 0;
|
m_BowCharge = 0;
|
||||||
m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
|
m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -432,7 +432,7 @@ int cPlayer::FinishChargingBow(void)
|
|||||||
int res = m_BowCharge;
|
int res = m_BowCharge;
|
||||||
m_IsChargingBow = false;
|
m_IsChargingBow = false;
|
||||||
m_BowCharge = 0;
|
m_BowCharge = 0;
|
||||||
m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
|
m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get());
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -446,7 +446,7 @@ void cPlayer::CancelChargingBow(void)
|
|||||||
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge);
|
LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge);
|
||||||
m_IsChargingBow = false;
|
m_IsChargingBow = false;
|
||||||
m_BowCharge = 0;
|
m_BowCharge = 0;
|
||||||
m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
|
m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1391,7 +1391,7 @@ void cPlayer::SetVisible(bool a_bVisible)
|
|||||||
if (!a_bVisible && m_bVisible)
|
if (!a_bVisible && m_bVisible)
|
||||||
{
|
{
|
||||||
m_bVisible = false;
|
m_bVisible = false;
|
||||||
m_World->BroadcastDestroyEntity(*this, m_ClientHandle); // Destroy on all clients
|
m_World->BroadcastDestroyEntity(*this, m_ClientHandle.get()); // Destroy on all clients
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2294,6 +2294,16 @@ void cPlayer::Detach()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cPlayer::RemoveClientHandle(void)
|
||||||
|
{
|
||||||
|
ASSERT(m_ClientHandle != nullptr);
|
||||||
|
m_ClientHandle.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
AString cPlayer::GetUUIDFileName(const AString & a_UUID)
|
AString cPlayer::GetUUIDFileName(const AString & a_UUID)
|
||||||
{
|
{
|
||||||
AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID);
|
AString UUID = cMojangAPI::MakeUUIDDashed(a_UUID);
|
||||||
|
@ -40,7 +40,7 @@ public:
|
|||||||
CLASS_PROTODEF(cPlayer)
|
CLASS_PROTODEF(cPlayer)
|
||||||
|
|
||||||
|
|
||||||
cPlayer(cClientHandle * a_Client, const AString & a_PlayerName);
|
cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName);
|
||||||
|
|
||||||
virtual ~cPlayer();
|
virtual ~cPlayer();
|
||||||
|
|
||||||
@ -222,7 +222,15 @@ public:
|
|||||||
/** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */
|
/** Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow */
|
||||||
void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true);
|
void CloseWindowIfID(char a_WindowID, bool a_CanRefuse = true);
|
||||||
|
|
||||||
cClientHandle * GetClientHandle(void) const { return m_ClientHandle; }
|
/** Returns the raw client handle associated with the player. */
|
||||||
|
cClientHandle * GetClientHandle(void) const { return m_ClientHandle.get(); }
|
||||||
|
|
||||||
|
// tolua_end
|
||||||
|
|
||||||
|
/** Returns the SharedPtr to client handle associated with the player. */
|
||||||
|
cClientHandlePtr GetClientHandlePtr(void) const { return m_ClientHandle; }
|
||||||
|
|
||||||
|
// tolua_begin
|
||||||
|
|
||||||
void SendMessage (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtCustom); }
|
void SendMessage (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtCustom); }
|
||||||
void SendMessageInfo (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtInformation); }
|
void SendMessageInfo (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtInformation); }
|
||||||
@ -468,6 +476,10 @@ public:
|
|||||||
|
|
||||||
virtual void Detach(void);
|
virtual void Detach(void);
|
||||||
|
|
||||||
|
/** Called by cClientHandle when the client is being destroyed.
|
||||||
|
The player removes its m_ClientHandle ownership so that the ClientHandle gets deleted. */
|
||||||
|
void RemoveClientHandle(void);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
typedef std::vector<std::vector<AString> > AStringVectorVector;
|
typedef std::vector<std::vector<AString> > AStringVectorVector;
|
||||||
@ -537,7 +549,7 @@ protected:
|
|||||||
|
|
||||||
std::chrono::steady_clock::time_point m_LastPlayerListTime;
|
std::chrono::steady_clock::time_point m_LastPlayerListTime;
|
||||||
|
|
||||||
cClientHandle * m_ClientHandle;
|
cClientHandlePtr m_ClientHandle;
|
||||||
|
|
||||||
cSlotNums m_InventoryPaintSlots;
|
cSlotNums m_InventoryPaintSlots;
|
||||||
|
|
||||||
|
268
src/Server.cpp
268
src/Server.cpp
@ -5,7 +5,6 @@
|
|||||||
#include "Server.h"
|
#include "Server.h"
|
||||||
#include "ClientHandle.h"
|
#include "ClientHandle.h"
|
||||||
#include "Mobs/Monster.h"
|
#include "Mobs/Monster.h"
|
||||||
#include "OSSupport/Socket.h"
|
|
||||||
#include "Root.h"
|
#include "Root.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "ChunkDef.h"
|
#include "ChunkDef.h"
|
||||||
@ -57,6 +56,39 @@ typedef std::list< cClientHandle* > ClientList;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// cServerListenCallbacks:
|
||||||
|
|
||||||
|
class cServerListenCallbacks:
|
||||||
|
public cNetwork::cListenCallbacks
|
||||||
|
{
|
||||||
|
cServer & m_Server;
|
||||||
|
UInt16 m_Port;
|
||||||
|
|
||||||
|
virtual cTCPLink::cCallbacksPtr OnIncomingConnection(const AString & a_RemoteIPAddress, UInt16 a_RemotePort) override
|
||||||
|
{
|
||||||
|
return m_Server.OnConnectionAccepted(a_RemoteIPAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnAccepted(cTCPLink & a_Link) override {}
|
||||||
|
|
||||||
|
virtual void OnError(int a_ErrorCode, const AString & a_ErrorMsg)
|
||||||
|
{
|
||||||
|
LOGWARNING("Cannot listen on port %d: %d (%s).", m_Port, a_ErrorCode, a_ErrorMsg.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
cServerListenCallbacks(cServer & a_Server, UInt16 a_Port):
|
||||||
|
m_Server(a_Server),
|
||||||
|
m_Port(a_Port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// cServer::cTickThread:
|
// cServer::cTickThread:
|
||||||
|
|
||||||
@ -100,8 +132,6 @@ void cServer::cTickThread::Execute(void)
|
|||||||
// cServer:
|
// cServer:
|
||||||
|
|
||||||
cServer::cServer(void) :
|
cServer::cServer(void) :
|
||||||
m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"),
|
|
||||||
m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"),
|
|
||||||
m_PlayerCount(0),
|
m_PlayerCount(0),
|
||||||
m_PlayerCountDiff(0),
|
m_PlayerCountDiff(0),
|
||||||
m_ClientViewDistance(0),
|
m_ClientViewDistance(0),
|
||||||
@ -121,42 +151,6 @@ cServer::cServer(void) :
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::ClientDestroying(const cClientHandle * a_Client)
|
|
||||||
{
|
|
||||||
m_SocketThreads.RemoveClient(a_Client);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::NotifyClientWrite(const cClientHandle * a_Client)
|
|
||||||
{
|
|
||||||
m_NotifyWriteThread.NotifyClientWrite(a_Client);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Data)
|
|
||||||
{
|
|
||||||
m_SocketThreads.Write(a_Client, a_Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::RemoveClient(const cClientHandle * a_Client)
|
|
||||||
{
|
|
||||||
m_SocketThreads.RemoveClient(a_Client);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
|
void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
@ -211,32 +205,7 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
|||||||
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
|
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
|
||||||
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
|
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
|
||||||
|
|
||||||
if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever
|
m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565");
|
||||||
{
|
|
||||||
LOGERROR("WSAStartup() != 0");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HasAnyPorts = false;
|
|
||||||
AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565");
|
|
||||||
m_ListenThreadIPv4.SetReuseAddr(true);
|
|
||||||
if (m_ListenThreadIPv4.Initialize(Ports))
|
|
||||||
{
|
|
||||||
HasAnyPorts = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565");
|
|
||||||
m_ListenThreadIPv6.SetReuseAddr(true);
|
|
||||||
if (m_ListenThreadIPv6.Initialize(Ports))
|
|
||||||
{
|
|
||||||
HasAnyPorts = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!HasAnyPorts)
|
|
||||||
{
|
|
||||||
LOGERROR("Couldn't open any ports. Aborting the server");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_RCONServer.Initialize(a_SettingsIni);
|
m_RCONServer.Initialize(a_SettingsIni);
|
||||||
|
|
||||||
@ -278,8 +247,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
|
|||||||
LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance);
|
LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_NotifyWriteThread.Start(this);
|
|
||||||
|
|
||||||
PrepareKeys();
|
PrepareKeys();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -327,36 +294,14 @@ void cServer::PrepareKeys(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::OnConnectionAccepted(cSocket & a_Socket)
|
cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIPAddress)
|
||||||
{
|
{
|
||||||
if (!a_Socket.IsValid())
|
LOGD("Client \"%s\" connected!", a_RemoteIPAddress.c_str());
|
||||||
{
|
cClientHandlePtr NewHandle = std::make_shared<cClientHandle>(a_RemoteIPAddress, m_ClientViewDistance);
|
||||||
return;
|
NewHandle->SetSelf(NewHandle);
|
||||||
}
|
|
||||||
|
|
||||||
const AString & ClientIP = a_Socket.GetIPString();
|
|
||||||
if (ClientIP.empty())
|
|
||||||
{
|
|
||||||
LOGWARN("cServer: A client connected, but didn't present its IP, disconnecting.");
|
|
||||||
a_Socket.CloseSocket();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGD("Client \"%s\" connected!", ClientIP.c_str());
|
|
||||||
|
|
||||||
cClientHandle * NewHandle = new cClientHandle(&a_Socket, m_ClientViewDistance);
|
|
||||||
if (!m_SocketThreads.AddClient(a_Socket, NewHandle))
|
|
||||||
{
|
|
||||||
// For some reason SocketThreads have rejected the handle, clean it up
|
|
||||||
LOGERROR("Client \"%s\" cannot be handled, server probably unstable", ClientIP.c_str());
|
|
||||||
a_Socket.CloseSocket();
|
|
||||||
delete NewHandle;
|
|
||||||
NewHandle = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
m_Clients.push_back(NewHandle);
|
m_Clients.push_back(NewHandle);
|
||||||
|
return NewHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -403,23 +348,30 @@ bool cServer::Tick(float a_Dt)
|
|||||||
|
|
||||||
void cServer::TickClients(float a_Dt)
|
void cServer::TickClients(float a_Dt)
|
||||||
{
|
{
|
||||||
cClientHandleList RemoveClients;
|
cClientHandlePtrs RemoveClients;
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
|
|
||||||
// Remove clients that have moved to a world (the world will be ticking them from now on)
|
// 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)
|
for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
||||||
{
|
{
|
||||||
m_Clients.remove(*itr);
|
for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC)
|
||||||
|
{
|
||||||
|
if (itrC->get() == *itr)
|
||||||
|
{
|
||||||
|
m_Clients.erase(itrC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // for itr - m_ClientsToRemove[]
|
} // for itr - m_ClientsToRemove[]
|
||||||
m_ClientsToRemove.clear();
|
m_ClientsToRemove.clear();
|
||||||
|
|
||||||
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
|
// Tick the remaining clients, take out those that have been destroyed into RemoveClients
|
||||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end();)
|
||||||
{
|
{
|
||||||
if ((*itr)->IsDestroyed())
|
if ((*itr)->IsDestroyed())
|
||||||
{
|
{
|
||||||
// Remove the client later, when CS is not held, to avoid deadlock: http://forum.mc-server.org/showthread.php?tid=374
|
// Delete the client later, when CS is not held, to avoid deadlock: http://forum.mc-server.org/showthread.php?tid=374
|
||||||
RemoveClients.push_back(*itr);
|
RemoveClients.push_back(*itr);
|
||||||
itr = m_Clients.erase(itr);
|
itr = m_Clients.erase(itr);
|
||||||
continue;
|
continue;
|
||||||
@ -430,10 +382,7 @@ void cServer::TickClients(float a_Dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete the clients that have been destroyed
|
// Delete the clients that have been destroyed
|
||||||
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
RemoveClients.clear();
|
||||||
{
|
|
||||||
delete *itr;
|
|
||||||
} // for itr - RemoveClients[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -442,12 +391,23 @@ void cServer::TickClients(float a_Dt)
|
|||||||
|
|
||||||
bool cServer::Start(void)
|
bool cServer::Start(void)
|
||||||
{
|
{
|
||||||
if (!m_ListenThreadIPv4.Start())
|
for (auto port: m_Ports)
|
||||||
{
|
{
|
||||||
return false;
|
UInt16 PortNum = static_cast<UInt16>(atoi(port.c_str()));
|
||||||
|
if (PortNum == 0)
|
||||||
|
{
|
||||||
|
LOGWARNING("Invalid port specified for server: \"%s\". Ignoring.", port.c_str());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!m_ListenThreadIPv6.Start())
|
auto Handle = cNetwork::Listen(PortNum, std::make_shared<cServerListenCallbacks>(*this, PortNum));
|
||||||
|
if (Handle->IsListening())
|
||||||
{
|
{
|
||||||
|
m_ServerHandles.push_back(Handle);
|
||||||
|
}
|
||||||
|
} // for port - Ports[]
|
||||||
|
if (m_ServerHandles.empty())
|
||||||
|
{
|
||||||
|
LOGERROR("Couldn't open any ports. Aborting the server");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!m_TickThread.Start())
|
if (!m_TickThread.Start())
|
||||||
@ -669,19 +629,24 @@ void cServer::BindBuiltInConsoleCommands(void)
|
|||||||
|
|
||||||
void cServer::Shutdown(void)
|
void cServer::Shutdown(void)
|
||||||
{
|
{
|
||||||
m_ListenThreadIPv4.Stop();
|
// Stop listening on all sockets:
|
||||||
m_ListenThreadIPv6.Stop();
|
for (auto srv: m_ServerHandles)
|
||||||
|
{
|
||||||
|
srv->Close();
|
||||||
|
}
|
||||||
|
m_ServerHandles.clear();
|
||||||
|
|
||||||
|
// Notify the tick thread and wait for it to terminate:
|
||||||
m_bRestarting = true;
|
m_bRestarting = true;
|
||||||
m_RestartEvent.Wait();
|
m_RestartEvent.Wait();
|
||||||
|
|
||||||
cRoot::Get()->SaveAllChunks();
|
cRoot::Get()->SaveAllChunks();
|
||||||
|
|
||||||
|
// Remove all clients:
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||||
{
|
{
|
||||||
(*itr)->Destroy();
|
(*itr)->Destroy();
|
||||||
delete *itr;
|
|
||||||
}
|
}
|
||||||
m_Clients.clear();
|
m_Clients.clear();
|
||||||
}
|
}
|
||||||
@ -693,7 +658,7 @@ void cServer::Shutdown(void)
|
|||||||
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);
|
||||||
for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||||
{
|
{
|
||||||
if ((*itr)->GetUniqueID() == a_ClientID)
|
if ((*itr)->GetUniqueID() == a_ClientID)
|
||||||
{
|
{
|
||||||
@ -709,7 +674,7 @@ void cServer::KickUser(int a_ClientID, const AString & a_Reason)
|
|||||||
void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
|
void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
for (ClientList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||||
{
|
{
|
||||||
if ((*itr)->GetUniqueID() == a_ClientID)
|
if ((*itr)->GetUniqueID() == a_ClientID)
|
||||||
{
|
{
|
||||||
@ -723,82 +688,3 @@ void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const ASt
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// cServer::cNotifyWriteThread:
|
|
||||||
|
|
||||||
cServer::cNotifyWriteThread::cNotifyWriteThread(void) :
|
|
||||||
super("ClientPacketThread"),
|
|
||||||
m_Server(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cServer::cNotifyWriteThread::~cNotifyWriteThread()
|
|
||||||
{
|
|
||||||
m_ShouldTerminate = true;
|
|
||||||
m_Event.Set();
|
|
||||||
Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool cServer::cNotifyWriteThread::Start(cServer * a_Server)
|
|
||||||
{
|
|
||||||
m_Server = a_Server;
|
|
||||||
return super::Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::cNotifyWriteThread::Execute(void)
|
|
||||||
{
|
|
||||||
cClientHandleList Clients;
|
|
||||||
while (!m_ShouldTerminate)
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CS);
|
|
||||||
while (m_Clients.empty())
|
|
||||||
{
|
|
||||||
cCSUnlock Unlock(Lock);
|
|
||||||
m_Event.Wait();
|
|
||||||
if (m_ShouldTerminate)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy the clients to notify and unlock the CS:
|
|
||||||
Clients.splice(Clients.begin(), m_Clients);
|
|
||||||
Lock.Unlock();
|
|
||||||
|
|
||||||
for (cClientHandleList::iterator itr = Clients.begin(); itr != Clients.end(); ++itr)
|
|
||||||
{
|
|
||||||
m_Server->m_SocketThreads.NotifyWrite(*itr);
|
|
||||||
} // for itr - Clients[]
|
|
||||||
Clients.clear();
|
|
||||||
} // while (!mShouldTerminate)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cServer::cNotifyWriteThread::NotifyClientWrite(const cClientHandle * a_Client)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
cCSLock Lock(m_CS);
|
|
||||||
m_Clients.remove(const_cast<cClientHandle *>(a_Client)); // Put it there only once
|
|
||||||
m_Clients.push_back(const_cast<cClientHandle *>(a_Client));
|
|
||||||
}
|
|
||||||
m_Event.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
91
src/Server.h
91
src/Server.h
@ -9,10 +9,9 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "OSSupport/SocketThreads.h"
|
|
||||||
#include "OSSupport/ListenThread.h"
|
|
||||||
|
|
||||||
#include "RCONServer.h"
|
#include "RCONServer.h"
|
||||||
|
#include "OSSupport/IsThread.h"
|
||||||
|
#include "OSSupport/Network.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
@ -36,10 +35,12 @@
|
|||||||
// fwd:
|
// fwd:
|
||||||
class cPlayer;
|
class cPlayer;
|
||||||
class cClientHandle;
|
class cClientHandle;
|
||||||
|
typedef SharedPtr<cClientHandle> cClientHandlePtr;
|
||||||
|
typedef std::list<cClientHandlePtr> cClientHandlePtrs;
|
||||||
|
typedef std::list<cClientHandle *> cClientHandles;
|
||||||
class cIniFile;
|
class cIniFile;
|
||||||
class cCommandOutputCallback;
|
class cCommandOutputCallback;
|
||||||
|
|
||||||
typedef std::list<cClientHandle *> cClientHandleList;
|
|
||||||
|
|
||||||
namespace Json
|
namespace Json
|
||||||
{
|
{
|
||||||
@ -50,10 +51,11 @@ namespace Json
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class cServer // tolua_export
|
// tolua_begin
|
||||||
: public cListenThread::cCallback
|
class cServer
|
||||||
{ // tolua_export
|
{
|
||||||
public: // tolua_export
|
public:
|
||||||
|
// tolua_end
|
||||||
|
|
||||||
virtual ~cServer() {}
|
virtual ~cServer() {}
|
||||||
bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth);
|
bool InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth);
|
||||||
@ -105,13 +107,6 @@ public: // tolua_export
|
|||||||
/** Called by cClientHandle's destructor; stop m_SocketThreads from calling back into a_Client */
|
/** Called by cClientHandle's destructor; stop m_SocketThreads from calling back into a_Client */
|
||||||
void ClientDestroying(const cClientHandle * a_Client);
|
void ClientDestroying(const cClientHandle * a_Client);
|
||||||
|
|
||||||
/** Notifies m_SocketThreads that client has something to be written */
|
|
||||||
void NotifyClientWrite(const cClientHandle * a_Client);
|
|
||||||
|
|
||||||
void WriteToClient(const cClientHandle * a_Client, const AString & a_Data); // Queues outgoing data for the client through 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 */
|
/** Don't tick a_Client anymore, it will be ticked from its cPlayer instead */
|
||||||
void ClientMovedToWorld(const cClientHandle * a_Client);
|
void ClientMovedToWorld(const cClientHandle * a_Client);
|
||||||
|
|
||||||
@ -147,30 +142,7 @@ public: // tolua_export
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
friend class cRoot; // so cRoot can create and destroy cServer
|
friend class cRoot; // so cRoot can create and destroy cServer
|
||||||
|
friend class cServerListenCallbacks; // Accessing OnConnectionAccepted()
|
||||||
/** When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) */
|
|
||||||
class cNotifyWriteThread :
|
|
||||||
public cIsThread
|
|
||||||
{
|
|
||||||
typedef cIsThread super;
|
|
||||||
|
|
||||||
cEvent m_Event; // Set when m_Clients gets appended
|
|
||||||
cServer * m_Server;
|
|
||||||
|
|
||||||
cCriticalSection m_CS;
|
|
||||||
cClientHandleList m_Clients;
|
|
||||||
|
|
||||||
virtual void Execute(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
cNotifyWriteThread(void);
|
|
||||||
~cNotifyWriteThread();
|
|
||||||
|
|
||||||
bool Start(cServer * a_Server);
|
|
||||||
|
|
||||||
void NotifyClientWrite(const cClientHandle * a_Client);
|
|
||||||
} ;
|
|
||||||
|
|
||||||
/** The server tick thread takes care of the players who aren't yet spawned in a world */
|
/** The server tick thread takes care of the players who aren't yet spawned in a world */
|
||||||
class cTickThread :
|
class cTickThread :
|
||||||
@ -189,21 +161,29 @@ private:
|
|||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
|
||||||
cNotifyWriteThread m_NotifyWriteThread;
|
/** The network sockets listening for client connections. */
|
||||||
|
cServerHandlePtrs m_ServerHandles;
|
||||||
|
|
||||||
cListenThread m_ListenThreadIPv4;
|
/** Protects m_Clients and m_ClientsToRemove against multithreaded access. */
|
||||||
cListenThread m_ListenThreadIPv6;
|
cCriticalSection m_CSClients;
|
||||||
|
|
||||||
cCriticalSection m_CSClients; ///< Locks client lists
|
/** Clients that are connected to the server and not yet assigned to a cWorld. */
|
||||||
cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld
|
cClientHandlePtrs m_Clients;
|
||||||
cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick()
|
|
||||||
|
|
||||||
mutable cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount
|
/** Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick(). */
|
||||||
int m_PlayerCount; ///< Number of players currently playing in the server
|
cClientHandles m_ClientsToRemove;
|
||||||
cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff
|
|
||||||
int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread
|
|
||||||
|
|
||||||
cSocketThreads m_SocketThreads;
|
/** Protects m_PlayerCount against multithreaded access. */
|
||||||
|
mutable cCriticalSection m_CSPlayerCount;
|
||||||
|
|
||||||
|
/** Number of players currently playing in the server. */
|
||||||
|
int m_PlayerCount;
|
||||||
|
|
||||||
|
/** Protects m_PlayerCountDiff against multithreaded access. */
|
||||||
|
cCriticalSection m_CSPlayerCountDiff;
|
||||||
|
|
||||||
|
/** Adjustment to m_PlayerCount to be applied in the Tick thread. */
|
||||||
|
int m_PlayerCountDiff;
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
@ -250,19 +230,24 @@ private:
|
|||||||
/** True if BungeeCord handshake packets (with player UUID) should be accepted. */
|
/** True if BungeeCord handshake packets (with player UUID) should be accepted. */
|
||||||
bool m_ShouldAllowBungeeCord;
|
bool m_ShouldAllowBungeeCord;
|
||||||
|
|
||||||
|
/** The list of ports on which the server should listen for connections.
|
||||||
|
Initialized in InitServer(), used in Start(). */
|
||||||
|
AStringVector m_Ports;
|
||||||
|
|
||||||
|
|
||||||
cServer(void);
|
cServer(void);
|
||||||
|
|
||||||
/** 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);
|
||||||
|
|
||||||
|
/** Creates a new cClientHandle instance and adds it to the list of clients.
|
||||||
|
Returns the cClientHandle reinterpreted as cTCPLink callbacks. */
|
||||||
|
cTCPLink::cCallbacksPtr OnConnectionAccepted(const AString & a_RemoteIPAddress);
|
||||||
|
|
||||||
bool Tick(float a_Dt);
|
bool Tick(float a_Dt);
|
||||||
|
|
||||||
/** Ticks the clients in m_Clients, manages the list in respect to removing clients */
|
/** Ticks the clients in m_Clients, manages the list in respect to removing clients */
|
||||||
void TickClients(float a_Dt);
|
void TickClients(float a_Dt);
|
||||||
|
|
||||||
// cListenThread::cCallback overrides:
|
|
||||||
virtual void OnConnectionAccepted(cSocket & a_Socket) override;
|
|
||||||
}; // tolua_export
|
}; // tolua_export
|
||||||
|
|
||||||
|
|
||||||
|
@ -815,10 +815,9 @@ void cWorld::Stop(void)
|
|||||||
// Delete the clients that have been in this world:
|
// Delete the clients that have been in this world:
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
|
||||||
{
|
{
|
||||||
(*itr)->Destroy();
|
(*itr)->Destroy();
|
||||||
delete *itr;
|
|
||||||
} // for itr - m_Clients[]
|
} // for itr - m_Clients[]
|
||||||
m_Clients.clear();
|
m_Clients.clear();
|
||||||
}
|
}
|
||||||
@ -1093,19 +1092,26 @@ void cWorld::TickScheduledTasks(void)
|
|||||||
|
|
||||||
void cWorld::TickClients(float a_Dt)
|
void cWorld::TickClients(float a_Dt)
|
||||||
{
|
{
|
||||||
cClientHandleList RemoveClients;
|
cClientHandlePtrs RemoveClients;
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
|
|
||||||
// Remove clients scheduled for removal:
|
// Remove clients scheduled for removal:
|
||||||
for (cClientHandleList::iterator itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr)
|
||||||
{
|
{
|
||||||
m_Clients.remove(*itr);
|
for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC)
|
||||||
|
{
|
||||||
|
if (itrC->get() == *itr)
|
||||||
|
{
|
||||||
|
m_Clients.erase(itrC);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} // for itr - m_ClientsToRemove[]
|
} // for itr - m_ClientsToRemove[]
|
||||||
m_ClientsToRemove.clear();
|
m_ClientsToRemove.clear();
|
||||||
|
|
||||||
// Add clients scheduled for adding:
|
// Add clients scheduled for adding:
|
||||||
for (cClientHandleList::iterator itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
|
for (auto itr = m_ClientsToAdd.begin(), end = m_ClientsToAdd.end(); itr != end; ++itr)
|
||||||
{
|
{
|
||||||
ASSERT(std::find(m_Clients.begin(), m_Clients.end(), *itr) == m_Clients.end());
|
ASSERT(std::find(m_Clients.begin(), m_Clients.end(), *itr) == m_Clients.end());
|
||||||
m_Clients.push_back(*itr);
|
m_Clients.push_back(*itr);
|
||||||
@ -1113,7 +1119,7 @@ void cWorld::TickClients(float a_Dt)
|
|||||||
m_ClientsToAdd.clear();
|
m_ClientsToAdd.clear();
|
||||||
|
|
||||||
// Tick the clients, take out those that have been destroyed into RemoveClients
|
// Tick the clients, take out those that have been destroyed into RemoveClients
|
||||||
for (cClientHandleList::iterator itr = m_Clients.begin(); itr != m_Clients.end();)
|
for (auto itr = m_Clients.begin(); itr != m_Clients.end();)
|
||||||
{
|
{
|
||||||
if ((*itr)->IsDestroyed())
|
if ((*itr)->IsDestroyed())
|
||||||
{
|
{
|
||||||
@ -1127,11 +1133,8 @@ void cWorld::TickClients(float a_Dt)
|
|||||||
} // for itr - m_Clients[]
|
} // for itr - m_Clients[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the clients that have been destroyed
|
// Delete the clients queued for removal:
|
||||||
for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
|
RemoveClients.clear();
|
||||||
{
|
|
||||||
delete *itr;
|
|
||||||
} // for itr - RemoveClients[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3525,7 +3528,7 @@ void cWorld::AddQueuedPlayers(void)
|
|||||||
cCSLock Lock(m_CSClients);
|
cCSLock Lock(m_CSClients);
|
||||||
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
|
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
|
||||||
{
|
{
|
||||||
cClientHandle * Client = (*itr)->GetClientHandle();
|
cClientHandlePtr Client = (*itr)->GetClientHandlePtr();
|
||||||
if (Client != nullptr)
|
if (Client != nullptr)
|
||||||
{
|
{
|
||||||
m_Clients.push_back(Client);
|
m_Clients.push_back(Client);
|
||||||
|
@ -38,6 +38,9 @@ class cRedstoneSimulator;
|
|||||||
class cItem;
|
class cItem;
|
||||||
class cPlayer;
|
class cPlayer;
|
||||||
class cClientHandle;
|
class cClientHandle;
|
||||||
|
typedef SharedPtr<cClientHandle> cClientHandlePtr;
|
||||||
|
typedef std::list<cClientHandlePtr> cClientHandlePtrs;
|
||||||
|
typedef std::list<cClientHandle *> cClientHandles;
|
||||||
class cEntity;
|
class cEntity;
|
||||||
class cBlockEntity;
|
class cBlockEntity;
|
||||||
class cWorldGenerator; // The generator that actually generates the chunks for a single world
|
class cWorldGenerator; // The generator that actually generates the chunks for a single world
|
||||||
@ -1019,13 +1022,13 @@ private:
|
|||||||
cCriticalSection m_CSClients;
|
cCriticalSection m_CSClients;
|
||||||
|
|
||||||
/** List of clients in this world, these will be ticked by this world */
|
/** List of clients in this world, these will be ticked by this world */
|
||||||
cClientHandleList m_Clients;
|
cClientHandlePtrs m_Clients;
|
||||||
|
|
||||||
/** Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them */
|
/** Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them */
|
||||||
cClientHandleList m_ClientsToRemove;
|
cClientHandles m_ClientsToRemove;
|
||||||
|
|
||||||
/** Clients that are scheduled for adding, waiting for TickClients to add them */
|
/** Clients that are scheduled for adding, waiting for TickClients to add them */
|
||||||
cClientHandleList m_ClientsToAdd;
|
cClientHandlePtrs m_ClientsToAdd;
|
||||||
|
|
||||||
/** Guards m_EntitiesToAdd */
|
/** Guards m_EntitiesToAdd */
|
||||||
cCriticalSection m_CSEntitiesToAdd;
|
cCriticalSection m_CSEntitiesToAdd;
|
||||||
|
Loading…
Reference in New Issue
Block a user