void dm_bcast_agesdl_hook(GameHost_Private* host) { Game_AgeInfo info = s_ages[host->m_ageFilename]; MOUL::NetMsgSDLStateBCast* bcast = MOUL::NetMsgSDLStateBCast::Create(); bcast->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_NeedsReliableSend; bcast->m_timestamp.setNow(); bcast->m_isInitial = true; bcast->m_persistOnServer = true; bcast->m_isAvatar = false; bcast->m_object.m_location = MOUL::Location::Make(info.m_seqPrefix, -2, MOUL::Location::e_BuiltIn); bcast->m_object.m_name = "AgeSDLHook"; bcast->m_object.m_type = 1; // SceneObject bcast->m_object.m_id = 1; bcast->m_sdlBlob = host->m_ageSdlHook.toBlob(); std::lock_guard<std::mutex> clientGuard(host->m_clientMutex); for (auto client_iter = host->m_clients.begin(); client_iter != host->m_clients.end(); ++client_iter) { try { DM_WRITEMSG(host, bcast); DS::CryptSendBuffer(client_iter->second->m_sock, client_iter->second->m_crypt, host->m_buffer.buffer(), host->m_buffer.size()); } catch (DS::SockHup) { // This is handled below too, but we don't want to skip the rest // of the client list if one hung up } } bcast->unref(); }
void DS::GateKeeper_DisplayClients() { std::lock_guard<std::mutex> clientGuard(s_clientMutex); if (s_clients.size()) fputs("Gate Keeper:\n", stdout); for (auto client_iter = s_clients.begin(); client_iter != s_clients.end(); ++client_iter) printf(" * %s\n", DS::SockIpAddress((*client_iter)->m_sock).c_str()); }
void dm_game_shutdown(GameHost_Private* host) { { std::lock_guard<std::mutex> clientGuard(host->m_clientMutex); for (auto client_iter = host->m_clients.begin(); client_iter != host->m_clients.end(); ++client_iter) DS::CloseSock(client_iter->second->m_sock); } for (auto clone_iter = host->m_clones.begin(); clone_iter != host->m_clones.end(); ++clone_iter) clone_iter->second->unref(); host->m_clones.clear(); bool complete = false; for (int i=0; i<50 && !complete; ++i) { host->m_clientMutex.lock(); size_t alive = host->m_clients.size(); host->m_clientMutex.unlock(); if (alive == 0) complete = true; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (!complete) fputs("[Game] Clients didn't die after 5 seconds!\n", stderr); s_gameHostMutex.lock(); hostmap_t::iterator host_iter = s_gameHosts.begin(); while (host_iter != s_gameHosts.end()) { if (host_iter->second == host) host_iter = s_gameHosts.erase(host_iter); else ++host_iter; } s_gameHostMutex.unlock(); if (host->m_temp) { PostgresStrings<1> params; params.set(0, host->m_serverIdx); PQexecParams(host->m_postgres, "DELETE FROM game.\"Servers\" " " WHERE \"idx\"=$1;", 1, 0, params.m_values, 0, 0, 0); } PQfinish(host->m_postgres); delete host; }
void dm_propagate(GameHost_Private* host, MOUL::NetMessage* msg, uint32_t sender) { DM_WRITEMSG(host, msg); std::lock_guard<std::mutex> clientGuard(host->m_clientMutex); DS::SendFlag mode = (msg->m_contentFlags & MOUL::NetMessage::e_NeedsReliableSend) ? DS::e_SendNoRetry : DS::e_SendSpam; for (auto client_iter = host->m_clients.begin(); client_iter != host->m_clients.end(); ++client_iter) { if (client_iter->second->m_clientInfo.m_PlayerId == sender && !(msg->m_contentFlags & MOUL::NetMessage::e_EchoBackToSender)) continue; try { DS::CryptSendBuffer(client_iter->second->m_sock, client_iter->second->m_crypt, host->m_buffer.buffer(), host->m_buffer.size(), mode); } catch (DS::SockHup) { // This is handled below too, but we don't want to skip the rest // of the client list if one hung up } } }
void dm_broadcast(GameHost_Private* host, MOUL::NetMessage* msg, uint32_t sender) { DM_WRITEMSG(host, msg); DS::SendFlag mode = (msg->m_contentFlags & MOUL::NetMessage::e_NeedsReliableSend) ? DS::e_SendNoRetry : DS::e_SendSpam; std::lock_guard<std::mutex> hostGuard(s_gameHostMutex); for (auto host_it = s_gameHosts.begin(); host_it != s_gameHosts.end(); ++host_it) { std::lock_guard<std::mutex> clientGuard(host_it->second->m_clientMutex); for (auto client_it = host_it->second->m_clients.begin(); client_it != host_it->second->m_clients.end(); ++client_it) { if (client_it->second->m_clientInfo.m_PlayerId == sender && !(msg->m_contentFlags & MOUL::NetMessage::e_EchoBackToSender)) continue; try { DS::CryptSendBuffer(client_it->second->m_sock, client_it->second->m_crypt, host->m_buffer.buffer(), host->m_buffer.size(), mode); } catch (DS::SockHup&) { } } } }
void DS::GateKeeper_Shutdown() { { std::lock_guard<std::mutex> clientGuard(s_clientMutex); for (auto client_iter = s_clients.begin(); client_iter != s_clients.end(); ++client_iter) DS::CloseSock((*client_iter)->m_sock); } bool complete = false; for (int i=0; i<50 && !complete; ++i) { s_clientMutex.lock(); size_t alive = s_clients.size(); s_clientMutex.unlock(); if (alive == 0) complete = true; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (!complete) fputs("[GateKeeper] Clients didn't die after 5 seconds!\n", stderr); }
void dm_propagate_to(GameHost_Private* host, MOUL::NetMessage* msg, const std::vector<uint32_t>& receivers) { DM_WRITEMSG(host, msg); DS::SendFlag mode = (msg->m_contentFlags & MOUL::NetMessage::e_NeedsReliableSend) ? DS::e_SendNoRetry : DS::e_SendSpam; for (auto rcvr_iter = receivers.begin(); rcvr_iter != receivers.end(); ++rcvr_iter) { std::lock_guard<std::mutex> hostGuard(s_gameHostMutex); for (hostmap_t::iterator recv_host = s_gameHosts.begin(); recv_host != s_gameHosts.end(); ++recv_host) { std::lock_guard<std::mutex> clientGuard(recv_host->second->m_clientMutex); auto client = recv_host->second->m_clients.find(*rcvr_iter); if (client != recv_host->second->m_clients.end()) { try { DS::CryptSendBuffer(client->second->m_sock, client->second->m_crypt, host->m_buffer.buffer(), host->m_buffer.size(), mode); } catch (DS::SockHup) { // This is handled below too, but we don't want to skip the rest // of the client list if one hung up } break; // Don't bother checking the rest of the hosts, we found the one we're looking for } } } }
void dm_send_members(GameHost_Private* host, GameClient_Private* client) { MOUL::NetMsgMembersList* members = MOUL::NetMsgMembersList::Create(); members->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_HasPlayerID | MOUL::NetMessage::e_IsSystemMessage | MOUL::NetMessage::e_NeedsReliableSend; members->m_timestamp.setNow(); members->m_playerId = client->m_clientInfo.m_PlayerId; host->m_clientMutex.lock(); members->m_members.reserve(host->m_clients.size() - 1); for (auto client_iter = host->m_clients.begin(); client_iter != host->m_clients.end(); ++client_iter) { if (client_iter->second->m_clientInfo.m_PlayerId != client->m_clientInfo.m_PlayerId && !client->m_clientKey.isNull()) { MOUL::NetMsgMemberInfo info; info.m_client = client_iter->second->m_clientInfo; info.m_avatarKey = client_iter->second->m_clientKey; members->m_members.push_back(info); } } host->m_clientMutex.unlock(); DM_WRITEMSG(host, members); DS::CryptSendBuffer(client->m_sock, client->m_crypt, host->m_buffer.buffer(), host->m_buffer.size()); members->unref(); // Load non-avatar clones (ie NPC quabs) for (auto clone_iter = host->m_clones.begin(); clone_iter != host->m_clones.end(); ++clone_iter) { DM_WRITEMSG(host, clone_iter->second); DS::CryptSendBuffer(client->m_sock, client->m_crypt, host->m_buffer.buffer(), host->m_buffer.size()); } // Load clones for players already in the age MOUL::NetMsgLoadClone* cloneMsg = MOUL::NetMsgLoadClone::Create(); cloneMsg->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_NeedsReliableSend; cloneMsg->m_timestamp.setNow(); cloneMsg->m_isPlayer = true; cloneMsg->m_isLoading = true; cloneMsg->m_isInitialState = true; MOUL::LoadAvatarMsg* avatarMsg = MOUL::LoadAvatarMsg::Create(); avatarMsg->m_bcastFlags = MOUL::Message::e_NetPropagate | MOUL::Message::e_LocalPropagate; avatarMsg->m_receivers.push_back(MOUL::Key::NetClientMgrKey); avatarMsg->m_requestorKey = MOUL::Key::AvatarMgrKey; avatarMsg->m_userData = 0; avatarMsg->m_validMsg = true; avatarMsg->m_isLoading = true; avatarMsg->m_isPlayer = true; cloneMsg->m_message = avatarMsg; { std::lock_guard<std::mutex> clientGuard(host->m_clientMutex); for (auto client_iter = host->m_clients.begin(); client_iter != host->m_clients.end(); ++client_iter) { if (client_iter->second->m_clientInfo.m_PlayerId != client->m_clientInfo.m_PlayerId && !client->m_clientKey.isNull()) { avatarMsg->m_cloneKey = client_iter->second->m_clientKey; avatarMsg->m_originPlayerId = client_iter->second->m_clientInfo.m_PlayerId; DM_WRITEMSG(host, cloneMsg); DS::CryptSendBuffer(client->m_sock, client->m_crypt, host->m_buffer.buffer(), host->m_buffer.size()); } } } cloneMsg->unref(); }
void dm_game_disconnect(GameHost_Private* host, Game_ClientMessage* msg) { // Unload the avatar clone if the client committed hara-kiri if (msg->m_client->m_isLoaded && !msg->m_client->m_clientKey.isNull()) { MOUL::LoadCloneMsg* cloneMsg = MOUL::LoadCloneMsg::Create(); cloneMsg->m_bcastFlags = MOUL::Message::e_NetPropagate | MOUL::Message::e_LocalPropagate; cloneMsg->m_receivers.push_back(MOUL::Key::NetClientMgrKey); cloneMsg->m_cloneKey = msg->m_client->m_clientKey; cloneMsg->m_requestorKey = MOUL::Key::AvatarMgrKey; cloneMsg->m_userData = 0; cloneMsg->m_originPlayerId = msg->m_client->m_clientInfo.m_PlayerId; cloneMsg->m_validMsg = true; cloneMsg->m_isLoading = false; MOUL::NetMsgLoadClone* netMsg = MOUL::NetMsgLoadClone::Create(); netMsg->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_NeedsReliableSend; netMsg->m_timestamp.setNow(); netMsg->m_isPlayer = true; netMsg->m_isLoading = false; netMsg->m_isInitialState = false; netMsg->m_message = cloneMsg; netMsg->m_object = msg->m_client->m_clientKey; dm_propagate(host, netMsg, msg->m_client->m_clientInfo.m_PlayerId); netMsg->unref(); host->m_states.erase(msg->m_client->m_clientKey); } MOUL::NetMsgMemberUpdate* memberMsg = MOUL::NetMsgMemberUpdate::Create(); memberMsg->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_IsSystemMessage | MOUL::NetMessage::e_NeedsReliableSend; memberMsg->m_timestamp.setNow(); memberMsg->m_member.m_client = msg->m_client->m_clientInfo; memberMsg->m_member.m_avatarKey = msg->m_client->m_clientKey; memberMsg->m_addMember = false; dm_propagate(host, memberMsg, msg->m_client->m_clientInfo.m_PlayerId); memberMsg->unref(); SEND_REPLY(msg, DS::e_NetSuccess); // Release any stale locks host->m_lockMutex.lock(); for (auto it = host->m_locks.begin(); it != host->m_locks.end(); ) { if (it->second == msg->m_client->m_clientInfo.m_PlayerId) it = host->m_locks.erase(it); else ++it; } host->m_lockMutex.unlock(); // Reassign game-mastership if needed... { std::lock_guard<std::mutex> gmGuard(host->m_gmMutex); if (host->m_gameMaster == msg->m_client->m_clientInfo.m_PlayerId) { MOUL::NetMsgGroupOwner* groupMsg = MOUL::NetMsgGroupOwner::Create(); groupMsg->m_contentFlags = MOUL::NetMessage::e_HasTimeSent | MOUL::NetMessage::e_IsSystemMessage | MOUL::NetMessage::e_NeedsReliableSend; groupMsg->m_timestamp.setNow(); groupMsg->m_groups.resize(1); groupMsg->m_groups[0].m_own = true; DM_WRITEMSG(host, groupMsg); // This client has already been removed from the map, so we can just // pick the 0th client and call him the new owner :) std::lock_guard<std::mutex> clientGuard(host->m_clientMutex); if (host->m_clients.size()) { GameClient_Private* newOwner = host->m_clients.begin()->second; host->m_gameMaster = newOwner->m_clientInfo.m_PlayerId; try { DS::CryptSendBuffer(newOwner->m_sock, newOwner->m_crypt, host->m_buffer.buffer(), host->m_buffer.size()); } catch (...) { fprintf(stderr, "[Game] Ownership transfer to %i from %i failed.", msg->m_client->m_clientInfo.m_PlayerId, newOwner->m_clientInfo.m_PlayerId); DS_DASSERT(false); } } else { host->m_gameMaster = 0; } groupMsg->unref(); } } // Good time to write this back to the vault dm_local_sdl_update(host, host->m_localState.toBlob()); // TODO: This should probably respect the age's LingerTime // As it is, there might be a race condition if another player is // joining just as the last player is leaving. if (host->m_clients.size() == 0) host->m_channel.putMessage(e_GameShutdown, 0); }