Fixed segfault when some clients disconnect from server
This commit is contained in:
parent
aebbd49ccb
commit
40c6ee4cd9
3 changed files with 53 additions and 45 deletions
|
|
@ -14,18 +14,24 @@ NetServer::NetServer(const std::string& serverLocalID) {
|
|||
void NetServer::update() {
|
||||
if(isDisconnected)
|
||||
return;
|
||||
std::erase_if(clients, [&](auto& client) {
|
||||
bool timedOut = std::chrono::steady_clock::now() - client->lastMessageTime > NetLibrary::TIMEOUT_DURATION;
|
||||
if(client->setToDisconnect || timedOut) {
|
||||
if(disconnectCallback)
|
||||
disconnectCallback(*client);
|
||||
if(timedOut)
|
||||
for(auto& client : clients) {
|
||||
if(client) {
|
||||
bool timedOut = std::chrono::steady_clock::now() - client->lastMessageTime > NetLibrary::TIMEOUT_DURATION;
|
||||
if(timedOut) {
|
||||
client->setToDisconnect = true;
|
||||
Logger::get().log("INFO", "Connection timed out");
|
||||
return true;
|
||||
}
|
||||
if(client->setToDisconnect)
|
||||
disconnectCallback(client);
|
||||
else {
|
||||
client->parse_received_messages(*this);
|
||||
client->send_queued_messages(*this);
|
||||
}
|
||||
}
|
||||
client->parse_received_messages(*this);
|
||||
client->send_queued_messages(*this);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::erase_if(clients, [&](auto& client) {
|
||||
return !client || client->setToDisconnect.load();
|
||||
});
|
||||
|
||||
std::scoped_lock clientAddLock(clientsToAddMutex);
|
||||
|
|
@ -171,7 +177,7 @@ void NetServer::ClientData::parse_received_messages(NetServer& server) {
|
|||
cereal::PortableBinaryInputArchive completeArchive(completeStrm);
|
||||
MessageCommandType completeCommandID;
|
||||
completeArchive(completeCommandID);
|
||||
server.recvCallbacks[completeCommandID](*this, completeArchive);
|
||||
server.recvCallbacks[completeCommandID](shared_from_this(), completeArchive);
|
||||
spfm.partialFragmentMessage.clear();
|
||||
spfm.partialFragmentMessageLoc = 0;
|
||||
}
|
||||
|
|
@ -181,7 +187,7 @@ void NetServer::ClientData::parse_received_messages(NetServer& server) {
|
|||
}
|
||||
}
|
||||
else
|
||||
server.recvCallbacks[commandID](*this, inArchive);
|
||||
server.recvCallbacks[commandID](shared_from_this(), inArchive);
|
||||
|
||||
receivedMessages.pop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
class NetServer : public std::enable_shared_from_this<NetServer> {
|
||||
public:
|
||||
struct ClientData;
|
||||
typedef std::function<void(ClientData&, cereal::PortableBinaryInputArchive&)> NetServerRecvCallback;
|
||||
typedef std::function<void(ClientData&)> NetServerConnectCallback;
|
||||
typedef std::function<void(ClientData&)> NetServerDisconnectCallback;
|
||||
struct ClientData {
|
||||
typedef std::function<void(std::shared_ptr<ClientData>, cereal::PortableBinaryInputArchive&)> NetServerRecvCallback;
|
||||
typedef std::function<void(std::shared_ptr<ClientData>)> NetServerConnectCallback;
|
||||
typedef std::function<void(std::shared_ptr<ClientData>)> NetServerDisconnectCallback;
|
||||
struct ClientData : public std::enable_shared_from_this<ClientData> {
|
||||
std::unordered_map<std::string, std::queue<std::shared_ptr<std::stringstream>>> messages;
|
||||
struct PartialFragmentMessage {
|
||||
std::string partialFragmentMessage;
|
||||
|
|
@ -53,7 +53,10 @@ class NetServer : public std::enable_shared_from_this<NetServer> {
|
|||
NetServer(const std::string& serverLocalID);
|
||||
~NetServer();
|
||||
void update();
|
||||
template <typename... Args> void send_items_to_client(ClientData& client, const std::string& channel, Args&&... items) {
|
||||
template <typename... Args> void send_items_to_client(std::shared_ptr<ClientData> client, const std::string& channel, Args&&... items) {
|
||||
if(!client)
|
||||
return;
|
||||
|
||||
auto ss(std::make_shared<std::stringstream>());
|
||||
{
|
||||
cereal::PortableBinaryOutputArchive m(*ss);
|
||||
|
|
@ -61,7 +64,7 @@ class NetServer : public std::enable_shared_from_this<NetServer> {
|
|||
}
|
||||
|
||||
std::vector<std::shared_ptr<std::stringstream>> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE);
|
||||
auto& messageQueue = client.messageQueues[channel];
|
||||
auto& messageQueue = client->messageQueues[channel];
|
||||
if(fragmentedMessage.empty())
|
||||
messageQueue.emplace(ss);
|
||||
else if(channel != UNRELIABLE_COMMAND_CHANNEL) { // Just drop unreliable messages that are fragmented
|
||||
|
|
@ -70,27 +73,26 @@ class NetServer : public std::enable_shared_from_this<NetServer> {
|
|||
}
|
||||
}
|
||||
template <typename... Args> void send_items_to_all_clients(const std::string& channel, Args&&... items) {
|
||||
send_items_to_client_if([&](const ClientData& c) {
|
||||
send_items_to_client_if([&](std::shared_ptr<ClientData> c) {
|
||||
return true;
|
||||
}, channel, items...);
|
||||
}
|
||||
template <typename... Args> void send_items_to_all_clients_except(ClientData& client, const std::string& channel, Args&&... items) {
|
||||
send_items_to_client_if([&](const ClientData& c) {
|
||||
return (&c) != (&client);
|
||||
template <typename... Args> void send_items_to_all_clients_except(std::shared_ptr<ClientData> client, const std::string& channel, Args&&... items) {
|
||||
send_items_to_client_if([&](std::shared_ptr<ClientData> c) {
|
||||
return c != client;
|
||||
}, channel, items...);
|
||||
}
|
||||
template <typename... Args> void send_items_to_client_if(std::function<bool(const ClientData&)> clientChecker, const std::string& channel, Args&&... items) {
|
||||
template <typename... Args> void send_items_to_client_if(std::function<bool(std::shared_ptr<ClientData>)> clientChecker, const std::string& channel, Args&&... items) {
|
||||
auto ss(std::make_shared<std::stringstream>());
|
||||
{
|
||||
cereal::PortableBinaryOutputArchive m(*ss);
|
||||
(m(items), ...);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<std::stringstream>> fragmentedMessage = fragment_message(ss->view(), NetLibrary::FRAGMENT_MESSAGE_STRIDE);
|
||||
|
||||
for(auto& client : clients) {
|
||||
if(clientChecker(*client)) {
|
||||
if(client && clientChecker(client)) {
|
||||
auto& messageQueue = client->messageQueues[channel];
|
||||
if(fragmentedMessage.empty())
|
||||
messageQueue.emplace(ss);
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
netServer = std::make_shared<NetServer>(serverLocalID);
|
||||
NetLibrary::register_server(netServer);
|
||||
|
||||
netServer->add_recv_callback(SERVER_INITIAL_DATA, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_INITIAL_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
bool isDirectConnect;
|
||||
ClientData newClient;
|
||||
Vector3f hsv;
|
||||
|
|
@ -85,7 +85,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
fileDisplayName = newClient.displayName;
|
||||
|
||||
newClient.id.first = Random::get().int_range<ServerPortionID>(1, std::numeric_limits<ServerPortionID>::max());
|
||||
client.customID = newClient.id.first;
|
||||
client->customID = newClient.id.first;
|
||||
newClient.id.second = data.get_max_id(newClient.id.first);
|
||||
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_CONNECT, newClient.id.first, newClient.displayName, newClient.cursorColor);
|
||||
if(isDirectConnect)
|
||||
|
|
@ -97,29 +97,29 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
}
|
||||
clients.emplace(newClient.id.first, newClient);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_MOVE_MOUSE, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
auto& c = clients[client.customID];
|
||||
netServer->add_recv_callback(SERVER_MOVE_MOUSE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
auto& c = clients[client->customID];
|
||||
message(c.camCoords, c.windowSize, c.cursorPos);
|
||||
netServer->send_items_to_all_clients_except(client, UNRELIABLE_COMMAND_CHANNEL, CLIENT_MOVE_MOUSE, c.id.first, c.camCoords, c.windowSize, c.cursorPos);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_PLACE_COMPONENT, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_PLACE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
uint64_t placement;
|
||||
DrawComponentType type;
|
||||
message(placement, type);
|
||||
auto newComp = DrawComponent::allocate_comp_type(type);
|
||||
message(newComp->coords, *newComp);
|
||||
ServerClientID id = clients[client.customID].get_next_id();
|
||||
ServerClientID id = clients[client->customID].get_next_id();
|
||||
placement = std::min<uint64_t>(placement, data.components.size());
|
||||
data.components.insert(data.components.begin() + placement, {id, newComp});
|
||||
newComp->server_send_place(*this, id, placement);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_ERASE_COMPONENT, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_ERASE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
ServerClientID compToRemove;
|
||||
message(compToRemove);
|
||||
std::erase_if(data.components, [&](auto& c){ return c.first == compToRemove; });
|
||||
DrawComponent::server_send_erase(*this, compToRemove);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_TRANSFORM_COMPONENT, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_TRANSFORM_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
bool isTemp;
|
||||
ServerClientID idToTransform;
|
||||
message(isTemp, idToTransform);
|
||||
|
|
@ -135,7 +135,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
comp->server_send_transform_final(*this, idToTransform);
|
||||
}
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_UPDATE_COMPONENT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
bool isTemp;
|
||||
ServerClientID idToTransform;
|
||||
message(isTemp, idToTransform);
|
||||
|
|
@ -151,8 +151,8 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
comp->server_send_update_final(*this, idToTransform);
|
||||
}
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_RESOURCE_INIT, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
auto& c = clients[client.customID];
|
||||
netServer->add_recv_callback(SERVER_RESOURCE_INIT, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
auto& c = clients[client->customID];
|
||||
ServerClientID newID = c.get_next_id();
|
||||
if(c.pendingResourceData.empty())
|
||||
c.pendingResourceID.emplace(newID);
|
||||
|
|
@ -162,10 +162,10 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
c.pendingResourceData.pop();
|
||||
}
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_RESOURCE_DATA, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_RESOURCE_DATA, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
ResourceData resourceData;
|
||||
message(resourceData);
|
||||
auto& c = clients[client.customID];
|
||||
auto& c = clients[client->customID];
|
||||
if(c.pendingResourceID.empty()) {
|
||||
c.pendingResourceData.emplace();
|
||||
c.pendingResourceData.back() = std::move(resourceData);
|
||||
|
|
@ -176,31 +176,31 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID):
|
|||
c.pendingResourceID.pop();
|
||||
}
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_CHAT_MESSAGE, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_CHAT_MESSAGE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::string chatMessage;
|
||||
Vector4f c{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
message(chatMessage);
|
||||
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_CHAT_MESSAGE, chatMessage, c);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_NEW_BOOKMARK, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_NEW_BOOKMARK, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::string name;
|
||||
message(name);
|
||||
Bookmark& b = data.bookmarks[name];
|
||||
message(b);
|
||||
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_NEW_BOOKMARK, name, b);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_REMOVE_BOOKMARK, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_REMOVE_BOOKMARK, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
std::string name;
|
||||
message(name);
|
||||
data.bookmarks.erase(name);
|
||||
netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_REMOVE_BOOKMARK, name);
|
||||
});
|
||||
netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](NetServer::ClientData& client, cereal::PortableBinaryInputArchive& message) {
|
||||
netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](std::shared_ptr<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
|
||||
});
|
||||
netServer->add_disconnect_callback([&](NetServer::ClientData& client) {
|
||||
auto it = clients.find(client.customID);
|
||||
netServer->add_disconnect_callback([&](std::shared_ptr<NetServer::ClientData> client) {
|
||||
auto it = clients.find(client->customID);
|
||||
if(it != clients.end()) {
|
||||
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_DISCONNECT, client.customID);
|
||||
netServer->send_items_to_all_clients_except(client, RELIABLE_COMMAND_CHANNEL, CLIENT_USER_DISCONNECT, client->customID);
|
||||
clients.erase(it);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue