refactor client disconnect, client interation

anywhere a client is disconnected, TNetwork::Disconnect is called now.
Nothing else is valid.
ForEachClientWeak() was fully removed.
This commit is contained in:
Lion Kortlepel 2024-01-11 14:09:50 +01:00
parent aa29d04b60
commit 130e1acdb3
No known key found for this signature in database
GPG Key ID: 4322FF2B4C71259B
10 changed files with 79 additions and 106 deletions

View File

@ -44,7 +44,6 @@ public:
void SetIdentifier(const std::string& key, const std::string& value);
std::string GetCarData(int Ident);
std::string GetCarPositionRaw(int Ident);
void Disconnect(std::string_view Reason);
bool IsDisconnected() const { return !TCPSocket->is_open(); }
// locks
void DeleteCar(int Ident);
@ -75,7 +74,12 @@ public:
Sync<std::queue<std::vector<uint8_t>>> MissedPacketsQueue;
Sync<std::chrono::time_point<std::chrono::high_resolution_clock>> LastPingTime;
friend class TNetwork;
private:
/// ONLY call after the client has been cleaned up, all cars deleted, etc.
void CloseSockets(std::string_view Reason);
void InsertVehicle(int ID, const std::string& Data);
TServer& mServer;

View File

@ -19,6 +19,9 @@ public:
std::shared_ptr<TClient> CreateClient(ip::tcp::socket&& TCPSock);
std::vector<uint8_t> TCPRcv(TClient& c);
void ClientKick(TClient& c, const std::string& R);
void Disconnect(const std::shared_ptr<TClient>& ClientPtr);
void Disconnect(const std::weak_ptr<TClient>& ClientPtr);
void Disconnect(TClient& Client);
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
void Identify(TConnection&& client);
std::shared_ptr<TClient> Authentication(TConnection&& ClientConnection);
@ -43,14 +46,11 @@ private:
void OnConnect(const std::weak_ptr<TClient>& c);
void TCPClient(const std::weak_ptr<TClient>& c);
void Looper(const std::weak_ptr<TClient>& c);
void OnDisconnect(const std::shared_ptr<TClient>& ClientPtr);
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr);
void OnDisconnect(TClient& Client);
void Parse(TClient& c, const std::vector<uint8_t>& Packet);
void SendFile(TClient& c, const std::string& Name);
static bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size);
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size);
void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
};
std::string HashPassword(const std::string& str);

View File

@ -24,7 +24,6 @@ public:
void RemoveClient(const std::weak_ptr<TClient>&);
void RemoveClient(TClient&);
// in Fn, return true to continue, return false to break
[[deprecated("Use ForEachClient instead")]] void ForEachClientWeak(const std::function<bool(std::weak_ptr<TClient>)>& Fn);
void ForEachClient(const std::function<bool(const std::shared_ptr<TClient>&)> Fn);
size_t ClientCount() const;

View File

@ -51,17 +51,25 @@ std::string TClient::GetCarPositionRaw(int Ident) {
}
}
void TClient::Disconnect(std::string_view Reason) {
void TClient::CloseSockets(std::string_view Reason) {
auto LockedSocket = TCPSocket.synchronize();
beammp_debugf("Disconnecting client {} for reason: {}", int(ID), Reason);
boost::system::error_code ec;
LockedSocket->shutdown(socket_base::shutdown_both, ec);
if (ec) {
beammp_debugf("Failed to shutdown client socket: {}", ec.message());
beammp_debugf("Failed to shutdown client socket of client {}: {}", ID.get(), ec.message());
}
LockedSocket->close(ec);
if (ec) {
beammp_debugf("Failed to close client socket: {}", ec.message());
beammp_debugf("Failed to close client socket of client {}: {}", ID.get(), ec.message());
}
DownSocket->shutdown(socket_base::shutdown_both, ec);
if (ec) {
beammp_debugf("Failed to shutdown client download socket of client {}: {}", ID.get(), ec.message());
}
DownSocket->close(ec);
if (ec) {
beammp_debugf("Failed to close client download socket of client {}: {}", ID.get(), ec.message());
}
}

View File

@ -127,7 +127,7 @@ static inline std::pair<bool, std::string> InternalTriggerClientEvent(int Player
auto c = MaybeClient.value();
if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) {
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
LuaAPI::MP::Engine->Network().Disconnect(*c);
return { false, "Respond failed, dropping client" };
}
return { true, "" };
@ -169,7 +169,8 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
LogChatMessage("<Server> (to \"" + c->Name.get() + "\")", -1, Message);
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID);
// TODO: should we return an error here?
beammp_infof("Disconnecting client {} for failure to receive a chat message (TCP disconnect)", c->Name.get());
Engine->Network().Disconnect(c);
}
Result.first = true;
} else {

View File

@ -266,15 +266,12 @@ void TConsole::Command_Kick(const std::string&, const std::vector<std::string>&
std::for_each(Name2.begin(), Name2.end(), [](char& c) { c = char(std::tolower(char(c))); });
return StringStartsWith(Name1, Name2) || StringStartsWith(Name2, Name1);
};
mLuaEngine->Server().ForEachClientWeak([&](std::weak_ptr<TClient> Client) -> bool {
if (!Client.expired()) {
auto locked = Client.lock();
if (NameCompare(locked->Name.get(), Name)) {
mLuaEngine->Network().ClientKick(*locked, Reason);
mLuaEngine->Server().ForEachClient([&](const std::shared_ptr<TClient>& Client) -> bool {
if (NameCompare(Client->Name.get(), Name)) {
mLuaEngine->Network().ClientKick(*Client, Reason);
Kicked = true;
return false;
}
}
return true;
});
if (!Kicked) {
@ -364,13 +361,10 @@ void TConsole::Command_List(const std::string&, const std::vector<std::string>&
} else {
std::stringstream ss;
ss << std::left << std::setw(25) << "Name" << std::setw(6) << "ID" << std::setw(6) << "Cars" << std::endl;
mLuaEngine->Server().ForEachClientWeak([&](std::weak_ptr<TClient> Client) -> bool {
if (!Client.expired()) {
auto locked = Client.lock();
ss << std::left << std::setw(25) << locked->Name.get()
<< std::setw(6) << locked->ID.get()
<< std::setw(6) << locked->GetCarCount() << "\n";
}
mLuaEngine->Server().ForEachClient([&](const std::shared_ptr<TClient>& Client) -> bool {
ss << std::left << std::setw(25) << Client->Name.get()
<< std::setw(6) << Client->ID.get()
<< std::setw(6) << Client->GetCarCount() << "\n";
return true;
});
auto Str = ss.str();

View File

@ -497,11 +497,8 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayerIdentifiers(int ID) {
sol::table TLuaEngine::StateThreadData::Lua_GetPlayers() {
sol::table Result = mStateView.create_table();
mEngine->Server().ForEachClientWeak([&](std::weak_ptr<TClient> Client) -> bool {
if (!Client.expired()) {
auto locked = Client.lock();
Result[locked->ID.get()] = locked->Name.get();
}
mEngine->Server().ForEachClient([&](const std::shared_ptr<TClient>& Client) -> bool {
Result[Client->ID.get()] = Client->Name.get();
return true;
});
return Result;
@ -509,14 +506,11 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayers() {
int TLuaEngine::StateThreadData::Lua_GetPlayerIDByName(const std::string& Name) {
int Id = -1;
mEngine->mServer->ForEachClientWeak([&Id, &Name](std::weak_ptr<TClient> Client) -> bool {
if (!Client.expired()) {
auto locked = Client.lock();
if (locked->Name.get() == Name) {
Id = locked->ID.get();
mEngine->mServer->ForEachClient([&Id, &Name](const std::shared_ptr<TClient>& Client) -> bool {
if (Client->Name.get() == Name) {
Id = Client->ID.get();
return false;
}
}
return true;
});
return Id;

View File

@ -246,7 +246,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
}
if (!TCPSend(*Client, StringToVector("A"))) { // changed to A for Accepted version
OnDisconnect(Client);
Disconnect(Client);
return nullptr;
}
@ -304,7 +304,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
if (!Application::Settings.Password.empty()) { // ask password
if (!TCPSend(*Client, StringToVector("S"))) {
OnDisconnect(Client);
Disconnect(Client);
return {};
}
beammp_info("Waiting for password");
@ -320,20 +320,11 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
}
beammp_debug("Name-> " + Client->Name.get() + ", Guest-> " + std::to_string(Client->IsGuest.get()) + ", Roles-> " + Client->Role.get());
mServer.ForEachClientWeak([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
std::shared_ptr<TClient> Cl;
{
ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) {
Cl = ClientPtr.lock();
} else
return true;
}
mServer.ForEachClient([&](const std::shared_ptr<TClient>& Cl) -> bool {
if (Cl->Name.get() == Client->Name.get() && Cl->IsGuest == Client->IsGuest) {
Cl->Disconnect("Stale Client (not a real player)");
Disconnect(Cl);
return false;
}
return true;
});
@ -368,7 +359,7 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
TCPClient(Client);
} catch (const std::exception& e) {
beammp_infof("Client {} disconnected: {}", Client->ID.get(), e.what());
OnDisconnect(Client);
Disconnect(Client);
return {};
}
} else {
@ -412,7 +403,7 @@ bool TNetwork::TCPSend(TClient& c, const std::vector<uint8_t>& Data, bool IsSync
write(*c.TCPSocket.synchronize(), buffer(ToSend), ec);
if (ec) {
beammp_debugf("write(): {}", ec.message());
c.Disconnect("write() failed");
Disconnect(c);
return false;
}
c.UpdatePingTime();
@ -477,7 +468,7 @@ void TNetwork::ClientKick(TClient& c, const std::string& R) {
if (!TCPSend(c, StringToVector("K" + R))) {
beammp_debugf("tried to kick player '{}' (id {}), but was already disconnected", c.Name.get(), c.ID.get());
}
c.Disconnect("Kicked");
Disconnect(c);
}
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
@ -502,7 +493,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
} // end locked context
// beammp_debug("sending a missed packet: " + QData);
if (!TCPSend(*Client, QData, true)) {
Client->Disconnect("Failed to TCPSend while clearing the missed packet queue");
Disconnect(Client);
auto Lock = Client->MissedPacketsQueue;
while (!Lock->empty()) {
Lock->pop();
@ -539,7 +530,7 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
auto res = TCPRcv(*Client);
if (res.empty()) {
beammp_debug("TCPRcv empty");
Client->Disconnect("TCPRcv failed");
Disconnect(Client);
break;
}
mServer.GlobalParser(c, std::move(res), mPPSMonitor, *this);
@ -550,7 +541,7 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
if (!c.expired()) {
auto Client = c.lock();
OnDisconnect(c);
Disconnect(c);
return;
} else {
beammp_warn("client expired in TCPClient, should never happen");
@ -569,7 +560,7 @@ void TNetwork::UpdatePlayer(TClient& Client) {
//(void)Respond(Client, Packet, true);
}
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
void TNetwork::Disconnect(const std::weak_ptr<TClient>& ClientPtr) {
// this is how one checks that the ClientPtr is not empty (as opposed to expired)
if (ClientPtr.owner_before(std::weak_ptr<TClient> {})) {
return;
@ -578,37 +569,37 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
try {
LockedClientPtr = ClientPtr.lock();
} catch (const std::exception&) {
beammp_warn("Client expired in OnDisconnect, this is unexpected");
beammp_warn("Client expired in CloseSockets, this is unexpected");
return;
}
beammp_assert(LockedClientPtr != nullptr);
TClient& c = *LockedClientPtr;
OnDisconnect(c);
Disconnect(c);
}
void TNetwork::OnDisconnect(TClient& c) {
beammp_info(c.Name.get() + (" Connection Terminated"));
void TNetwork::Disconnect(TClient& Client) {
beammp_info(Client.Name.get() + (" Connection Terminated"));
std::string Packet;
{
auto Locked = c.VehicleData.synchronize();
auto Locked = Client.VehicleData.synchronize();
for (auto& v : *Locked) {
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", c.ID.get(), v.ID()));
Packet = "Od:" + std::to_string(c.ID.get()) + "-" + std::to_string(v.ID());
SendToAll(&c, StringToVector(Packet), false, true);
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", Client.ID.get(), v.ID()));
Packet = "Od:" + std::to_string(Client.ID.get()) + "-" + std::to_string(v.ID());
SendToAll(&Client, StringToVector(Packet), false, true);
}
}
Packet = ("L") + c.Name.get() + (" left the server!");
SendToAll(&c, StringToVector(Packet), false, true);
Packet = ("L") + Client.Name.get() + (" left the server!");
SendToAll(&Client, StringToVector(Packet), false, true);
Packet.clear();
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.ID.get());
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", Client.ID.get());
TLuaEngine::WaitForAll(Futures);
c.Disconnect("Already Disconnected (OnDisconnect)");
mServer.RemoveClient(c);
Client.CloseSockets("Normal disconnect");
mServer.RemoveClient(Client);
}
void TNetwork::OnDisconnect(const std::shared_ptr<TClient>& ClientPtr) {
void TNetwork::Disconnect(const std::shared_ptr<TClient>& ClientPtr) {
if (ClientPtr == nullptr) {
return;
}
OnDisconnect(*ClientPtr);
Disconnect(*ClientPtr);
}
void TNetwork::OnConnect(const std::weak_ptr<TClient>& c) {
@ -674,7 +665,8 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
if (!fs::path(UnsafeName).has_filename()) {
if (!TCPSend(c, StringToVector("CO"))) {
OnDisconnect(c);
Disconnect(c);
return;
}
beammp_warn("File " + UnsafeName + " is not a file!");
return;
@ -684,14 +676,16 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
if (!std::filesystem::exists(FileName)) {
if (!TCPSend(c, StringToVector("CO"))) {
OnDisconnect(c);
Disconnect(c);
return;
}
beammp_warn("File " + UnsafeName + " could not be accessed!");
return;
}
if (!TCPSend(c, StringToVector("AG"))) {
OnDisconnect(c);
Disconnect(c);
return;
}
/// Wait for connections
@ -704,7 +698,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
if (!c.DownSocket->is_open()) {
beammp_error("Client doesn't have a download socket!");
if (!c.IsDisconnected())
c.Disconnect("Missing download socket");
Disconnect(c);
return;
}
@ -794,7 +788,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.read(reinterpret_cast<char*>(Data.data()), Split);
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
if (!c.IsDisconnected())
c.Disconnect("TCPSendRaw failed in mod download (1)");
Disconnect(c);
break;
}
Sent += Split;
@ -803,7 +797,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.read(reinterpret_cast<char*>(Data.data()), Diff);
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
if (!c.IsDisconnected())
c.Disconnect("TCPSendRaw failed in mod download (2)");
Disconnect(c);
break;
}
Sent += Diff;
@ -818,7 +812,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.read(reinterpret_cast<char*>(Data.data()), Split);
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
if (!c.IsDisconnected())
c.Disconnect("TCPSendRaw failed in mod download (1)");
Disconnect(c);
break;
}
Sent += Split;
@ -827,7 +821,7 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
f.read(reinterpret_cast<char*>(Data.data()), Diff);
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
if (!c.IsDisconnected())
c.Disconnect("TCPSendRaw failed in mod download (2)");
Disconnect(c);
break;
}
Sent += Diff;
@ -931,7 +925,7 @@ void TNetwork::SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self
}
} else {
if (!UDPSend(*Client, Data)) {
OnDisconnect(Client);
Disconnect(Client);
}
}
}
@ -957,7 +951,7 @@ bool TNetwork::UDPSend(TClient& Client, std::vector<uint8_t> Data) {
if (ec) {
beammp_debugf("UDP sendto() failed: {}", ec.message());
if (!Client.IsDisconnected())
Client.Disconnect("UDP send failed");
Disconnect(Client);
return false;
}
return true;

View File

@ -33,15 +33,7 @@ void TPPSMonitor::operator()() {
Application::SetPPS("-");
continue;
}
mServer.ForEachClientWeak([&](const std::weak_ptr<TClient>& ClientPtr) -> bool {
std::shared_ptr<TClient> c;
{
ReadLock Lock(mServer.GetClientMutex());
if (!ClientPtr.expired()) {
c = ClientPtr.lock();
} else
return true;
}
mServer.ForEachClient([&](const std::shared_ptr<TClient>& c) -> bool {
if (c->GetCarCount() > 0) {
C++;
V += c->GetCarCount();

View File

@ -171,19 +171,6 @@ void TServer::ForEachClient(const std::function<bool(const std::shared_ptr<TClie
}
}
void TServer::ForEachClientWeak(const std::function<bool(std::weak_ptr<TClient>)>& Fn) {
decltype(mClients) Clients;
{
ReadLock lock(mClientsMutex);
Clients = mClients;
}
for (auto& Client : Clients) {
if (!Fn(Client)) {
break;
}
}
}
size_t TServer::ClientCount() const {
ReadLock Lock(mClientsMutex);
return mClients.size();
@ -224,7 +211,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
case 'p':
if (!Network.Respond(*LockedClient, StringToVector("p"), false)) {
// failed to send
LockedClient->Disconnect("Failed to send ping");
Disconnect(LockedClient);
} else {
Network.UpdatePlayer(*LockedClient);
}