summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ClientHandle.cpp214
-rw-r--r--src/ClientHandle.h80
-rw-r--r--src/Entities/Player.cpp24
-rw-r--r--src/Entities/Player.h18
-rw-r--r--src/Server.cpp270
-rw-r--r--src/Server.h97
-rw-r--r--src/World.cpp31
-rw-r--r--src/World.h9
8 files changed, 330 insertions, 413 deletions
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 387cc4628..e90114d1b 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -15,7 +15,6 @@
#include "Item.h"
#include "Mobs/Monster.h"
#include "ChatColor.h"
-#include "OSSupport/Socket.h"
#include "Items/ItemHandler.h"
#include "Blocks/BlockHandler.h"
#include "Blocks/BlockSlab.h"
@@ -56,16 +55,15 @@ int cClientHandle::s_ClientCount = 0;
////////////////////////////////////////////////////////////////////////////////
// cClientHandle:
-cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
+cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
m_CurrentViewDistance(a_ViewDistance),
m_RequestedViewDistance(a_ViewDistance),
- m_IPString(a_Socket->GetIPString()),
- m_OutgoingData(64 KiB),
+ m_IPString(a_IPString),
m_Player(nullptr),
m_HasSentDC(false),
m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login
m_LastStreamedChunkZ(0x7fffffff),
- m_TimeSinceLastPacket(0),
+ m_TicksSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
m_BlockDigAnimStage(-1),
@@ -135,9 +133,6 @@ cClientHandle::~cClientHandle()
SendDisconnect("Server shut down? Kthnxbai");
}
- // Close the socket as soon as it sends all outgoing data:
- cRoot::Get()->GetServer()->RemoveClient(this);
-
delete m_Protocol;
m_Protocol = nullptr;
@@ -151,6 +146,10 @@ cClientHandle::~cClientHandle()
void cClientHandle::Destroy(void)
{
{
+ cCSLock Lock(m_CSOutgoingData);
+ m_Link.reset();
+ }
+ {
cCSLock Lock(m_CSDestroyingState);
if (m_State >= csDestroying)
{
@@ -168,6 +167,10 @@ void cClientHandle::Destroy(void)
RemoveFromAllChunks();
m_Player->GetWorld()->RemoveClientFromChunkSender(this);
}
+ if (m_Player != nullptr)
+ {
+ m_Player->RemoveClientHandle();
+ }
m_State = csDestroyed;
}
@@ -326,7 +329,8 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
m_Protocol->SendLoginSuccess();
// 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());
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)
{
UNUSED(FlyingSpeed); // Ignore the client values for these
@@ -1777,44 +1822,12 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
// This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
return;
}
-
+
+ cCSLock Lock(m_CSOutgoingData);
+ if (m_Link != nullptr)
{
- cCSLock Lock(m_CSOutgoingData);
-
- // _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:
- 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);
+ m_Link->Send(a_Data, a_Size);
+ }
}
@@ -1873,8 +1886,8 @@ void cClientHandle::Tick(float a_Dt)
}
m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
- m_TimeSinceLastPacket += a_Dt;
- if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
+ m_TicksSinceLastPacket += 1;
+ if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
{
SendDisconnect("Nooooo!! You timed out! D: Come back!");
Destroy();
@@ -1970,8 +1983,8 @@ void cClientHandle::ServerTick(float a_Dt)
return;
}
- m_TimeSinceLastPacket += a_Dt;
- if (m_TimeSinceLastPacket > 30000.f) // 30 seconds time-out
+ m_TicksSinceLastPacket += 1;
+ if (m_TicksSinceLastPacket > 600) // 30 seconds
{
SendDisconnect("Nooooo!! You timed out! D: Come back!");
Destroy();
@@ -2843,94 +2856,79 @@ void cClientHandle::PacketError(UInt32 a_PacketType)
-bool cClientHandle::DataReceived(const char * a_Data, size_t a_Size)
+void cClientHandle::SocketClosed(void)
{
- // 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;
+ // The socket has been closed for any reason
+
+ 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");
+ }
+
+ Destroy();
}
-void cClientHandle::GetOutgoingData(AString & a_Data)
+void cClientHandle::SetSelf(cClientHandlePtr a_Self)
{
- // 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();
- }
+ ASSERT(m_Self == nullptr);
+ m_Self = a_Self;
}
-void cClientHandle::SocketClosed(void)
+void cClientHandle::OnLinkCreated(cTCPLinkPtr a_Link)
{
- // The socket has been closed for any reason
-
- LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
+ m_Link = a_Link;
+}
- if (!m_Username.empty()) // Ignore client pings
- {
- cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
- }
- Destroy();
+
+
+
+void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length)
+{
+ // Reset the timeout:
+ m_TicksSinceLastPacket = 0;
+
+ // Queue the incoming data to be processed in the tick thread:
+ cCSLock Lock(m_CSIncomingData);
+ m_IncomingData.append(a_Data, a_Length);
}
-void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment)
+void cClientHandle::OnRemoteClosed(void)
{
- if (a_Enchantment > 2)
{
- LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str());
- Kick("Invalid enchanting!");
- return;
+ cCSLock Lock(m_CSOutgoingData);
+ m_Link.reset();
}
+ SocketClosed();
+}
- 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::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();
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 03ae38cfd..f5b7faede 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -8,12 +8,10 @@
#pragma once
-#ifndef CCLIENTHANDLE_H_INCLUDED
-#define CCLIENTHANDLE_H_INCLUDED
+#include "OSSupport/Network.h"
#include "Defines.h"
#include "Vector3.h"
-#include "OSSupport/SocketThreads.h"
#include "ChunkDef.h"
#include "ByteBuffer.h"
#include "Scoreboard.h"
@@ -27,6 +25,7 @@
+// fwd:
class cChunkDataSerializer;
class cInventory;
class cMonster;
@@ -42,25 +41,29 @@ class cItemHandler;
class cWorld;
class cCompositeChat;
class cStatManager;
+class cClientHandle;
+typedef SharedPtr<cClientHandle> cClientHandlePtr;
-class cClientHandle : // tolua_export
- public cSocketThreads::cCallback
+class cClientHandle // tolua_export
+ : public cTCPLink::cCallbacks
{ // tolua_export
-public:
-
-#if defined(ANDROID_NDK)
- static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
-#else
- static const int DEFAULT_VIEW_DISTANCE = 10;
-#endif
+public: // tolua_export
+
+ #if defined(ANDROID_NDK)
+ static const int DEFAULT_VIEW_DISTANCE = 4; // The default ViewDistance (used when no value is set in Settings.ini)
+ #else
+ static const int DEFAULT_VIEW_DISTANCE = 10;
+ #endif
static const int MAX_VIEW_DISTANCE = 32;
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();
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 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 HandleEntityLeaveBed (int a_EntityID);
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. */
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. */
void SetProtocolVersion(UInt32 a_ProtocolVersion) { m_ProtocolVersion = a_ProtocolVersion; }
@@ -340,6 +344,9 @@ public:
private:
+ friend class cServer; // Needs access to SetSelf()
+
+
/** The type used for storing the names of registered plugin channels. */
typedef std::set<AString> cChannels;
@@ -361,13 +368,16 @@ private:
cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client
cProtocol * m_Protocol;
-
+
+ /** Protects m_IncomingData against multithreaded access. */
cCriticalSection m_CSIncomingData;
- AString m_IncomingData;
-
+
+ /** Queue for the incoming data received on the link until it is processed in Tick().
+ Protected by m_CSIncomingData. */
+ AString m_IncomingData;
+
+ /** Protects data going out through m_Link against multi-threaded sending. */
cCriticalSection m_CSOutgoingData;
- cByteBuffer m_OutgoingData;
- AString m_OutgoingDataOverflow; ///< For data that didn't fit into the m_OutgoingData ringbuffer temporarily
Vector3d m_ConfirmPosition;
@@ -379,8 +389,8 @@ private:
int m_LastStreamedChunkX;
int m_LastStreamedChunkZ;
- /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */
- float m_TimeSinceLastPacket;
+ /** Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedData()) */
+ int m_TicksSinceLastPacket;
/** Duration of the last completed client 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. */
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) */
bool CheckBlockInteractionsRate(void);
@@ -483,16 +500,19 @@ private:
/** Removes all of the channels from the list of current plugin channels. Ignores channels that are not found. */
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
- // cSocketThreads::cCallback overrides:
- virtual bool DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client
- 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
-}; // tolua_export
-
+ /** Called when the network socket has been closed. */
+ void SocketClosed(void);
+ /** 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
-#endif // CCLIENTHANDLE_H_INCLUDED
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 1ca131375..527380761 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -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),
m_bVisible(true),
m_FoodLevel(MAX_FOOD_LEVEL),
@@ -174,7 +174,7 @@ void cPlayer::Destroyed()
void cPlayer::SpawnOn(cClientHandle & a_Client)
{
- if (!m_bVisible || (m_ClientHandle == (&a_Client)))
+ if (!m_bVisible || (m_ClientHandle.get() == (&a_Client)))
{
return;
}
@@ -246,7 +246,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (CanMove)
{
- BroadcastMovementUpdate(m_ClientHandle);
+ BroadcastMovementUpdate(m_ClientHandle.get());
}
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());
m_IsChargingBow = true;
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;
m_IsChargingBow = false;
m_BowCharge = 0;
- m_World->BroadcastEntityMetadata(*this, m_ClientHandle);
+ m_World->BroadcastEntityMetadata(*this, m_ClientHandle.get());
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);
m_IsChargingBow = false;
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)
{
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 UUID = cMojangAPI::MakeUUIDDashed(a_UUID);
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index d3ed46db6..fa9ac7cad 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -40,7 +40,7 @@ public:
CLASS_PROTODEF(cPlayer)
- cPlayer(cClientHandle * a_Client, const AString & a_PlayerName);
+ cPlayer(cClientHandlePtr a_Client, const AString & a_PlayerName);
virtual ~cPlayer();
@@ -222,7 +222,15 @@ public:
/** 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);
- 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 SendMessageInfo (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtInformation); }
@@ -467,6 +475,10 @@ public:
virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); }
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:
@@ -537,7 +549,7 @@ protected:
std::chrono::steady_clock::time_point m_LastPlayerListTime;
- cClientHandle * m_ClientHandle;
+ cClientHandlePtr m_ClientHandle;
cSlotNums m_InventoryPaintSlots;
diff --git a/src/Server.cpp b/src/Server.cpp
index df6e5ec8a..783b12947 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -5,7 +5,6 @@
#include "Server.h"
#include "ClientHandle.h"
#include "Mobs/Monster.h"
-#include "OSSupport/Socket.h"
#include "Root.h"
#include "World.h"
#include "ChunkDef.h"
@@ -58,6 +57,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::cTickThread(cServer & a_Server) :
@@ -100,8 +132,6 @@ void cServer::cTickThread::Execute(void)
// cServer:
cServer::cServer(void) :
- m_ListenThreadIPv4(*this, cSocket::IPv4, "Client"),
- m_ListenThreadIPv6(*this, cSocket::IPv6, "Client"),
m_PlayerCount(0),
m_PlayerCountDiff(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)
{
cCSLock Lock(m_CSClients);
@@ -211,33 +205,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
LOGINFO("Compatible clients: %s", MCS_CLIENT_VERSIONS);
LOGINFO("Compatible protocol versions %s", MCS_PROTOCOL_VERSIONS);
- if (cSocket::WSAStartup() != 0) // Only does anything on Windows, but whatever
- {
- 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;
- }
+ m_Ports = ReadUpgradeIniPorts(a_SettingsIni, "Server", "Ports", "Port", "PortsIPv6", "25565");
- if (!HasAnyPorts)
- {
- LOGERROR("Couldn't open any ports. Aborting the server");
- return false;
- }
-
m_RCONServer.Initialize(a_SettingsIni);
m_bIsConnected = true;
@@ -278,8 +247,6 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth)
LOGINFO("Setting default viewdistance to the maximum of %d", m_ClientViewDistance);
}
- m_NotifyWriteThread.Start(this);
-
PrepareKeys();
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())
- {
- return;
- }
-
- 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;
- }
-
+ LOGD("Client \"%s\" connected!", a_RemoteIPAddress.c_str());
+ cClientHandlePtr NewHandle = std::make_shared<cClientHandle>(a_RemoteIPAddress, m_ClientViewDistance);
+ NewHandle->SetSelf(NewHandle);
cCSLock Lock(m_CSClients);
m_Clients.push_back(NewHandle);
+ return NewHandle;
}
@@ -403,23 +348,30 @@ bool cServer::Tick(float a_Dt)
void cServer::TickClients(float a_Dt)
{
- cClientHandleList RemoveClients;
+ cClientHandlePtrs RemoveClients;
{
cCSLock Lock(m_CSClients);
// 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[]
m_ClientsToRemove.clear();
// 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())
{
- // 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);
itr = m_Clients.erase(itr);
continue;
@@ -430,10 +382,7 @@ void cServer::TickClients(float a_Dt)
}
// Delete the clients that have been destroyed
- for (cClientHandleList::iterator itr = RemoveClients.begin(); itr != RemoveClients.end(); ++itr)
- {
- delete *itr;
- } // for itr - RemoveClients[]
+ RemoveClients.clear();
}
@@ -442,12 +391,23 @@ void cServer::TickClients(float a_Dt)
bool cServer::Start(void)
{
- if (!m_ListenThreadIPv4.Start())
+ for (auto port: m_Ports)
{
- return false;
- }
- if (!m_ListenThreadIPv6.Start())
+ UInt16 PortNum = static_cast<UInt16>(atoi(port.c_str()));
+ if (PortNum == 0)
+ {
+ LOGWARNING("Invalid port specified for server: \"%s\". Ignoring.", port.c_str());
+ continue;
+ }
+ 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;
}
if (!m_TickThread.Start())
@@ -669,19 +629,24 @@ void cServer::BindBuiltInConsoleCommands(void)
void cServer::Shutdown(void)
{
- m_ListenThreadIPv4.Stop();
- m_ListenThreadIPv6.Stop();
+ // Stop listening on all sockets:
+ for (auto srv: m_ServerHandles)
+ {
+ srv->Close();
+ }
+ m_ServerHandles.clear();
+ // Notify the tick thread and wait for it to terminate:
m_bRestarting = true;
m_RestartEvent.Wait();
cRoot::Get()->SaveAllChunks();
+ // Remove all clients:
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();
- delete *itr;
}
m_Clients.clear();
}
@@ -693,7 +658,7 @@ void cServer::Shutdown(void)
void cServer::KickUser(int a_ClientID, const AString & a_Reason)
{
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)
{
@@ -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)
{
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)
{
@@ -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();
-}
-
-
-
-
diff --git a/src/Server.h b/src/Server.h
index aab47987f..1f30295b7 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -9,10 +9,9 @@
#pragma once
-#include "OSSupport/SocketThreads.h"
-#include "OSSupport/ListenThread.h"
-
#include "RCONServer.h"
+#include "OSSupport/IsThread.h"
+#include "OSSupport/Network.h"
#ifdef _MSC_VER
#pragma warning(push)
@@ -36,10 +35,12 @@
// fwd:
class cPlayer;
class cClientHandle;
+typedef SharedPtr<cClientHandle> cClientHandlePtr;
+typedef std::list<cClientHandlePtr> cClientHandlePtrs;
+typedef std::list<cClientHandle *> cClientHandles;
class cIniFile;
class cCommandOutputCallback;
-typedef std::list<cClientHandle *> cClientHandleList;
namespace Json
{
@@ -50,10 +51,11 @@ namespace Json
-class cServer // tolua_export
- : public cListenThread::cCallback
-{ // tolua_export
-public: // tolua_export
+// tolua_begin
+class cServer
+{
+public:
+ // tolua_end
virtual ~cServer() {}
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 */
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 */
void ClientMovedToWorld(const cClientHandle * a_Client);
@@ -147,30 +142,7 @@ public: // tolua_export
private:
friend class cRoot; // so cRoot can create and destroy cServer
-
- /** 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);
- } ;
+ friend class cServerListenCallbacks; // Accessing OnConnectionAccepted()
/** The server tick thread takes care of the players who aren't yet spawned in a world */
class cTickThread :
@@ -189,21 +161,29 @@ private:
} ;
- cNotifyWriteThread m_NotifyWriteThread;
-
- cListenThread m_ListenThreadIPv4;
- cListenThread m_ListenThreadIPv6;
-
- cCriticalSection m_CSClients; ///< Locks client lists
- 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()
-
- mutable 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
+ /** The network sockets listening for client connections. */
+ cServerHandlePtrs m_ServerHandles;
+
+ /** Protects m_Clients and m_ClientsToRemove against multithreaded access. */
+ cCriticalSection m_CSClients;
+
+ /** Clients that are connected to the server and not yet assigned to a cWorld. */
+ cClientHandlePtrs m_Clients;
+
+ /** Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick(). */
+ cClientHandles m_ClientsToRemove;
- 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
@@ -250,19 +230,24 @@ private:
/** True if BungeeCord handshake packets (with player UUID) should be accepted. */
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);
/** Loads, or generates, if missing, RSA keys for protocol encryption */
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);
/** Ticks the clients in m_Clients, manages the list in respect to removing clients */
void TickClients(float a_Dt);
-
- // cListenThread::cCallback overrides:
- virtual void OnConnectionAccepted(cSocket & a_Socket) override;
}; // tolua_export
diff --git a/src/World.cpp b/src/World.cpp
index 24b1a9b40..474f77b81 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -815,10 +815,9 @@ 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)
+ for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr)
{
(*itr)->Destroy();
- delete *itr;
} // for itr - m_Clients[]
m_Clients.clear();
}
@@ -1093,19 +1092,26 @@ void cWorld::TickScheduledTasks(void)
void cWorld::TickClients(float a_Dt)
{
- cClientHandleList RemoveClients;
+ cClientHandlePtrs RemoveClients;
{
cCSLock Lock(m_CSClients);
// 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[]
m_ClientsToRemove.clear();
// 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());
m_Clients.push_back(*itr);
@@ -1113,7 +1119,7 @@ void cWorld::TickClients(float a_Dt)
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();)
+ for (auto itr = m_Clients.begin(); itr != m_Clients.end();)
{
if ((*itr)->IsDestroyed())
{
@@ -1126,12 +1132,9 @@ void cWorld::TickClients(float 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[]
+
+ // Delete the clients queued for removal:
+ RemoveClients.clear();
}
@@ -3525,7 +3528,7 @@ void cWorld::AddQueuedPlayers(void)
cCSLock Lock(m_CSClients);
for (cPlayerList::iterator itr = PlayersToAdd.begin(), end = PlayersToAdd.end(); itr != end; ++itr)
{
- cClientHandle * Client = (*itr)->GetClientHandle();
+ cClientHandlePtr Client = (*itr)->GetClientHandlePtr();
if (Client != nullptr)
{
m_Clients.push_back(Client);
diff --git a/src/World.h b/src/World.h
index e7519dab8..3cac71a36 100644
--- a/src/World.h
+++ b/src/World.h
@@ -38,6 +38,9 @@ class cRedstoneSimulator;
class cItem;
class cPlayer;
class cClientHandle;
+typedef SharedPtr<cClientHandle> cClientHandlePtr;
+typedef std::list<cClientHandlePtr> cClientHandlePtrs;
+typedef std::list<cClientHandle *> cClientHandles;
class cEntity;
class cBlockEntity;
class cWorldGenerator; // The generator that actually generates the chunks for a single world
@@ -1019,13 +1022,13 @@ private:
cCriticalSection m_CSClients;
/** 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 */
- cClientHandleList m_ClientsToRemove;
+ cClientHandles m_ClientsToRemove;
/** Clients that are scheduled for adding, waiting for TickClients to add them */
- cClientHandleList m_ClientsToAdd;
+ cClientHandlePtrs m_ClientsToAdd;
/** Guards m_EntitiesToAdd */
cCriticalSection m_CSEntitiesToAdd;