summaryrefslogtreecommitdiffstats
path: root/src/ClientHandle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ClientHandle.cpp')
-rw-r--r--src/ClientHandle.cpp249
1 files changed, 138 insertions, 111 deletions
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 387cc4628..2e0e86653 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 = reinterpret_cast<cEnchantingWindow *>(m_Player->GetWindow());
+ cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item
+ short 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,9 @@ 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);
-
- // _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);
+
+ cCSLock Lock(m_CSOutgoingData);
+ m_OutgoingData.append(a_Data, a_Size);
}
@@ -1871,10 +1881,28 @@ void cClientHandle::Tick(float a_Dt)
cCSLock Lock(m_CSIncomingData);
std::swap(IncomingData, m_IncomingData);
}
- m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
+ if (!IncomingData.empty())
+ {
+ m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
+ }
+
+ // Send any queued outgoing data:
+ AString OutgoingData;
+ {
+ cCSLock Lock(m_CSOutgoingData);
+ std::swap(OutgoingData, m_OutgoingData);
+ }
+ if (!OutgoingData.empty())
+ {
+ cTCPLinkPtr Link(m_Link); // Grab a copy of the link in a multithread-safe way
+ if ((Link != nullptr))
+ {
+ Link->Send(OutgoingData.data(), OutgoingData.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();
@@ -1955,7 +1983,21 @@ void cClientHandle::ServerTick(float a_Dt)
cCSLock Lock(m_CSIncomingData);
std::swap(IncomingData, m_IncomingData);
}
- m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
+ if (!IncomingData.empty())
+ {
+ m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
+ }
+
+ // Send any queued outgoing data:
+ AString OutgoingData;
+ {
+ cCSLock Lock(m_CSOutgoingData);
+ std::swap(OutgoingData, m_OutgoingData);
+ }
+ if ((m_Link != nullptr) && !OutgoingData.empty())
+ {
+ m_Link->Send(OutgoingData.data(), OutgoingData.size());
+ }
if (m_State == csAuthenticated)
{
@@ -1970,8 +2012,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 +2885,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();
}