/* Starshatter: The Open Source Project Copyright (c) 2021-2024, Starshatter: The Open Source Project Contributors Copyright (c) 2011-2012, Starshatter OpenSource Distribution Contributors Copyright (c) 1997-2006, Destroyer Studios LLC. AUTHOR: John DiCamillo OVERVIEW ======== NetLink Engine for Multiplayer Lobby */ #include "NetLobbyServer.h" #include #include "NetServerConfig.h" #include "NetClientConfig.h" #include "NetBrokerClient.h" #include "NetAuth.h" #include "NetChat.h" #include "NetUser.h" #include "Campaign.h" #include "Mission.h" #include "ShipDesign.h" #include "Sim.h" #include "Text.h" #include "ModConfig.h" #include "ModInfo.h" #include "NetGame.h" #include "NetPlayer.h" #include "NetUtil.h" #include "NetPeer.h" #include "NetLayer.h" #include "NetHost.h" #include "NetMsg.h" #include "Clock.h" #include "FormatUtil.h" #include "VersionInfo.h" #include "Game.h" // +-------------------------------------------------------------------+ static NetLobbyServer* net_lobby_server = 0; NetLobbyServer::NetLobbyServer() : announce_time(0), server_config(0), motd_index(1) { status = NetServerInfo::LOBBY; server_name = Text("Starshatter NetLobbyServer ") + versionInfo; start_time = NetLayer::GetUTC(); selected_mission = 0; Text hostname = "0.0.0.0"; WORD server_port = 11100; server_config = NetServerConfig::GetInstance(); if (server_config) { hostname = server_config->Hostname(); server_name = server_config->Name(); server_port = server_config->GetLobbyPort(); server_mission = server_config->GetMission(); NetAuth::SetAuthLevel(server_config->GetAuthLevel()); server_addr = NetAddr(hostname.data(), server_port); link = new NetLink(server_addr); } LoadMOTD(); auto game = Game::GetInstance(); DWORD mission_id = 0; // only one mission: if (game->Server() && server_mission.length() > 0) { NetCampaignInfo* c = new NetCampaignInfo; c->id = Campaign::MULTIPLAYER_MISSIONS; c->name = "Persistent Multiplayer"; campaigns.append(c); ListIter c_iter = Campaign::GetAllCampaigns(); while (++c_iter && !mission_id) { Campaign* campaign = c_iter.value(); if (campaign->GetCampaignId() == Campaign::MULTIPLAYER_MISSIONS) { ListIter m_iter = campaign->GetMissionList(); while (++m_iter && !mission_id) { MissionInfo* m = m_iter.value(); if (m->script == server_mission) { c->missions.append(m); mission_id = (Campaign::MULTIPLAYER_MISSIONS << NET_CAMPAIGN_SHIFT) + m->id; SelectMission(mission_id); game->SetGameMode(Game::LOAD_MODE); // lock in mission: SetStatus(NetServerInfo::PERSISTENT); } } } } } // player host may select mission: if (!mission_id) { campaigns.destroy(); ListIter c_iter = Campaign::GetAllCampaigns(); while (++c_iter) { Campaign* campaign = c_iter.value(); if (campaign->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) { NetCampaignInfo* c = new NetCampaignInfo; c->id = campaign->GetCampaignId(); c->name = campaign->Name(); campaigns.append(c); ListIter m_iter = campaign->GetMissionList(); while (++m_iter) { MissionInfo* m = m_iter.value(); c->missions.append(m); } } } } ModConfig* config = ModConfig::GetInstance(); List& mods = config->GetModInfoList(); server_mods.clear(); server_mods.append(mods); net_lobby_server = this; } NetLobbyServer::~NetLobbyServer() { ListIter iter = users; while (++iter) { NetUser* u = iter.value(); SendData(u, NET_LOBBY_EXIT, Text()); ExecFrame(); } Sleep(500); unit_map.destroy(); chat_log.destroy(); users.destroy(); motd.destroy(); if (net_lobby_server == this) net_lobby_server = 0; } NetLobbyServer* NetLobbyServer::GetInstance() { return net_lobby_server; } // +--------------------------------------------------------------------+ void NetLobbyServer::LoadMOTD() { motd.destroy(); FILE* f; fopen_s(&f, "motd.txt", "rb"); if (f) { char line[256]; while (fgets(line, 256, f)) { int n = strlen(line) - 1; while (n >= 0 && isspace(line[n])) line[n--] = 0; motd.append(new Text(line)); } } } void NetLobbyServer::SendMOTD(NetUser* user) { if (motd.size() < 1) return; char buffer[512]; for (int i = 0; i < motd.size(); i++) { Text* line = motd[i]; sprintf_s(buffer, "id %d user \" \" msg \"%s\"", motd_index++, line->data()); SendData(user, NET_LOBBY_CHAT, buffer); } sprintf_s(buffer, "id %d user \" \" msg \" \"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); } void NetLobbyServer::SendMods(NetUser* user) { char buffer[300]; ModConfig* config = ModConfig::GetInstance(); List& mods = config->GetModInfoList(); if (mods.size() < 1) return; for (int i = 0; i < mods.size(); i++) { ModInfo* info = mods[i]; sprintf_s(buffer, "id %d user \"Enabled Mods:\" msg \"%d. '%s' ", motd_index++, i+1, info->Name().data()); Text msg = buffer; if (info->Version().length() > 0) { msg += "version "; msg += info->Version().data(); } if (info->URL().length() > 0) { msg += " - "; msg += info->URL().data(); } msg += "\""; SendData(user, NET_LOBBY_CHAT, msg); } sprintf_s(buffer, "id %d user \" \" msg \" \"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); } // +--------------------------------------------------------------------+ void NetLobbyServer::ExecFrame() { NetLobby::ExecFrame(); if (announce_time == 0 || Clock::GetInstance()->RealTime() - announce_time > 200000) { GameOn(); announce_time = Clock::GetInstance()->RealTime(); } if (GetStatus() == NetServerInfo::BRIEFING) { NetGame* net_game = NetGame::GetInstance(); if (net_game && net_game->NumPlayers() > 0) { SetStatus(NetServerInfo::ACTIVE); } } auto game = Game::GetInstance(); DWORD mission_id = 0; // restart persistent mission? if (game->Server() && game->GetGameMode() == Game::MENU_MODE && server_mission.length() > 0) { NetCampaignInfo* c = campaigns.last(); if (!c || c->name != "Persistent Multiplayer") { c = new NetCampaignInfo; c->id = Campaign::MULTIPLAYER_MISSIONS; c->name = "Persistent Multiplayer"; campaigns.append(c); } else { c->missions.clear(); } ListIter c_iter = Campaign::GetAllCampaigns(); while (++c_iter && !mission_id) { Campaign* campaign = c_iter.value(); if (campaign->GetCampaignId() == Campaign::MULTIPLAYER_MISSIONS) { ListIter m_iter = campaign->GetMissionList(); while (++m_iter && !mission_id) { MissionInfo* m = m_iter.value(); if (m->script == server_mission) { c->missions.append(m); mission_id = (Campaign::MULTIPLAYER_MISSIONS << NET_CAMPAIGN_SHIFT) + m->id; // unlock old mission: SetStatus(NetServerInfo::LOBBY); SelectMission(mission_id); if (game->GetGameMode() == Game::MENU_MODE) { game->SetGameMode(Game::LOAD_MODE); } // lock in new mission: SetStatus(NetServerInfo::PERSISTENT); } } } } } CheckSessions(); } // +-------------------------------------------------------------------+ void NetLobbyServer::SendData(NetUser* dst, int type, Text msg) { if (link && dst && type > 0 && type < 255) { if (msg.length()) link->SendMessage(dst->GetNetID(), (BYTE) type, msg.data(), msg.length(), NetMsg::RELIABLE); else link->SendMessage(dst->GetNetID(), (BYTE) type, 0, 0, NetMsg::RELIABLE); } } // +-------------------------------------------------------------------+ void NetLobbyServer::CheckSessions() { if (!link) return; bool dropped = false; ListIter u_iter = users; while (++u_iter) { NetUser* u = u_iter.value(); NetPeer* p = link->FindPeer(u->GetNetID()); if (p && (NetLayer::GetUTC() - p->LastReceiveTime()) > NET_DISCONNECT_TIME) { // check game peer for activity: NetGame* game = NetGame::GetInstance(); NetPlayer* player = 0; NetPeer* p2 = 0; if (game) { player = game->FindPlayerByName(u->Name()); if (player) { p2 = game->GetPeer(player); if (p2 && (NetLayer::GetUTC() - p2->LastReceiveTime()) < NET_DISCONNECT_TIME) { p->SetLastReceiveTime(p2->LastReceiveTime()); continue; } } else { ::Print("NetLobbyServer::CheckSessions() Could not find player for '%s'\n", u->Name().data()); } } else { ::Print("NetLobbyServer::CheckSessions() Could not find net game for '%s'\n", u->Name().data()); } // announce drop: char timestr[64]; FormatTime(timestr, Clock::GetInstance()->RealTime()/1000); Print("NetLobbyServer: Dropped inactive connection '%s' %s\n", u->Name().data(), timestr); if (u->IsHost()) { Print(" User was host - ending net game.\n"); GameStop(); } u_iter.removeItem(); // first remove user from list NetLobby::UnmapUnit(u->Name()); // then unmap unit delete u; // now it is safe to discard the inactive user dropped = true; } } if (dropped) { SendUsers(); SendUnits(); } } // +-------------------------------------------------------------------+ void NetLobbyServer::GameStart() { if (status < NetServerInfo::ACTIVE) { SetStatus(NetServerInfo::BRIEFING); auto game = Game::GetInstance(); if (game->Server()) { if (game->GetGameMode() == Game::MENU_MODE) { game->SetGameMode(Game::LOAD_MODE); } } else { game->SetGameMode(Game::PREP_MODE); } } } void NetLobbyServer::GameStop() { if (GetStatus() != NetServerInfo::PERSISTENT) { SetStatus(NetServerInfo::LOBBY); auto game = Game::GetInstance(); if (game->Server() && game->GetGameMode() != Game::MENU_MODE) { game->SetGameMode(Game::MENU_MODE); } } } // +-------------------------------------------------------------------+ void NetLobbyServer::BanUser(NetUser* user) { if (user && !user->IsHost()) { ::Print("NetLobbyServer::BanUser name '%s' addr %d.%d.%d.%d\n", user->Name().data(), user->GetAddress().B1(), user->GetAddress().B2(), user->GetAddress().B3(), user->GetAddress().B4()); SendData(user, NET_LOBBY_EXIT, Text()); if (server_config) server_config->BanUser(user); DelUser(user); } } void NetLobbyServer::AddUser(NetUser* user) { if (server_config && server_config->IsUserBanned(user)) { delete user; return; } NetLobby::AddUser(user); SendUsers(); } void NetLobbyServer::DelUser(NetUser* user) { NetLobby::DelUser(user); SendUsers(); } void NetLobbyServer::SendUsers() { Text content; ListIter u_iter = users; while (++u_iter) { NetUser* u = u_iter.value(); content += u->GetDescription(); } u_iter.reset(); while (++u_iter) { NetUser* u = u_iter.value(); SendData(u, NET_LOBBY_USER_LIST, content); } } // +-------------------------------------------------------------------+ void NetLobbyServer::RequestAuth(NetUser* user) { if (user) { Text request = NetAuth::CreateAuthRequest(user); if (request.length() > 0) SendData(user, NET_LOBBY_AUTH_USER, request); } } // +-------------------------------------------------------------------+ void NetLobbyServer::AddChat(NetUser* user, const char* msg, bool route) { NetChatEntry* entry = 0; if (user && msg && *msg) { bool msg_ok = false; const char* p = msg; while (*p && !msg_ok) { if (!isspace(*p++)) msg_ok = true; } if (msg_ok) { entry = new NetChatEntry(user, msg); chat_log.append(entry); // forward to all clients: if (users.size()) { char buffer[768]; char msg_buf[256]; char usr_buf[256]; // safe quotes uses a static buffer, // so make sure to save copies of the // results when using more than one in // a function call... strcpy_s(msg_buf, SafeQuotes(msg)); strcpy_s(usr_buf, SafeQuotes(user->Name())); sprintf_s(buffer, "id %d user \"%s\" msg \"%s\"", entry->GetID(), usr_buf, msg_buf); ListIter iter = users; while (++iter) { NetUser* u = iter.value(); SendData(u, NET_LOBBY_CHAT, buffer); } if (route) { // send to active game: NetUtil::SendChat(0xffff, usr_buf, msg_buf); } } } } } void NetLobbyServer::ClearChat() { NetLobby::ClearChat(); } void NetLobbyServer::SaveChat() { FILE* f; fopen_s(&f, "chat.txt", "wb"); if (f) { for (int i = 0; i < chat_log.size(); i++) { NetChatEntry* c = chat_log[i]; fprintf(f, "%08x [%s] %s\n", c->GetTime(), c->GetUser().data(), c->GetMessage().data()); } fclose(f); } } // +-------------------------------------------------------------------+ void NetLobbyServer::SelectMission(DWORD id) { if (GetStatus() == NetServerInfo::PERSISTENT) return; NetLobby::SelectMission(id); // inform all users of the selection: char buffer[32]; sprintf_s(buffer, "m_id 0x%08x", selected_mission); ListIter iter = users; while (++iter) { NetUser* u = iter.value(); SendData(u, NET_LOBBY_MISSION_SELECT, buffer); } } // +-------------------------------------------------------------------+ List& NetLobbyServer::GetUnitMap() { if (!mission) { unit_map.destroy(); return unit_map; } List units; ListIter iter = mission->GetElements(); int i = 0; Sim* sim = Sim::GetSim(); if (sim && sim->GetElements().size() > 0) iter = sim->GetMissionElements(); // create new entries for the playable elements in the mission or simulation: while (++iter) { MissionElement* elem = iter.value(); if (elem->IsPlayable()) { NetUnitEntry* u = 0; if (elem->Count() == 1) { u = new NetUnitEntry(elem, 0); u->SetLives(elem->RespawnCount() + 1); u->SetMissionRole(elem->MissionRole()); u->SetIFF(elem->GetIFF()); if (elem->GetDesign()) u->SetDesign(elem->GetDesign()->name); if (elem->Ships().size() > 0) { MissionShip* s = elem->Ships()[0]; u->SetIntegrity((int) s->Integrity()); } units.append(u); } else { for (int i = 0; i < elem->Count(); i++) { u = new NetUnitEntry(elem, i+1); u->SetMissionRole(elem->MissionRole()); u->SetIFF(elem->GetIFF()); if (elem->GetDesign()) u->SetDesign(elem->GetDesign()->name); if (elem->Ships().size() > i) { MissionShip* s = elem->Ships()[i]; u->SetLives(s->Respawns() + 1); u->SetIntegrity((int) s->Integrity()); } units.append(u); } } } } // match new entries with any existing map entries: if (unit_map.size()) { for (i = 0; i < units.size(); i++) { NetUnitEntry* e_new = units[i]; NetUnitEntry* e_old = unit_map.find(e_new); if (e_old) { e_new->SetUserName(e_old->GetUserName()); e_new->SetLock(e_old->GetLocked()); } } } // rewrite the unit map with the new entries: ClearUnitMap(); for (i = 0; i < units.size(); i++) { unit_map.append(units[i]); } return unit_map; } void NetLobbyServer::MapUnit(int n, const char* user, bool lock) { NetLobby::MapUnit(n, user, lock); Text reply; ListIter map_iter = GetUnitMap(); while (++map_iter) { NetUnitEntry* unit = map_iter.value(); reply += unit->GetDescription(); } ListIter u_iter = users; while (++u_iter) { NetUser* u = u_iter.value(); SendData(u, NET_LOBBY_UNIT_LIST, reply); } } void NetLobbyServer::UnmapUnit(const char* user) { NetLobby::UnmapUnit(user); Text reply; ListIter map_iter = GetUnitMap(); while (++map_iter) { NetUnitEntry* unit = map_iter.value(); reply += unit->GetDescription(); } ListIter u_iter = users; while (++u_iter) { NetUser* u = u_iter.value(); SendData(u, NET_LOBBY_UNIT_LIST, reply); } } void NetLobbyServer::SendUnits() { Text content; ListIter map_iter = GetUnitMap(); while (++map_iter) { NetUnitEntry* unit = map_iter.value(); content += unit->GetDescription(); } ListIter u_iter = users; while (++u_iter) { NetUser* u = u_iter.value(); SendData(u, NET_LOBBY_UNIT_LIST, content); } } // +-------------------------------------------------------------------+ Text NetLobbyServer::Serialize(Mission* m, NetUser* user) { Text s; if (!m || !user) return s; NetUnitEntry* unit = 0; ListIter u_iter = GetUnitMap(); while (++u_iter && !unit) { NetUnitEntry* u = u_iter.value(); if (u->GetUserName() == user->Name()) unit = u; } if (unit) s = m->Serialize(unit->GetElemName(), unit->GetIndex()); return s; } Mission* NetLobbyServer::GetSelectedMission() { if (mission) { Text content = Serialize(mission, GetLocalUser()); Campaign* c = Campaign::SelectCampaign("Multiplayer Missions"); if (c) { c->LoadNetMission(99999, content.data()); return c->GetMission(99999); } } return mission; } // +-------------------------------------------------------------------+ void NetLobbyServer::GameOn() { NetHost host; const char* type = "Starshatter"; const char* password = "No"; char address[32]; strcpy_s(address, "0"); if (server_config) { if (server_config->GetGameType() == NetServerConfig::NET_GAME_PRIVATE) return; if (server_config->GetGameType() == NetServerConfig::NET_GAME_LAN) { type = "Starshatter-LAN"; sprintf_s(address, "%d.%d.%d.%d", host.Address().B1(), host.Address().B2(), host.Address().B3(), host.Address().B4()); } else { type = "Starshatter"; sprintf_s(address, "0.0.0.0"); } if (server_config->GetGamePass().length() > 0) password = "Yes"; } NetBrokerClient::GameOn(server_name, type, address, server_addr.Port(), password); } void NetLobbyServer::GameOff() { } // +-------------------------------------------------------------------+ void NetLobbyServer::DoPing(NetPeer* peer, Text s) { } void NetLobbyServer::DoServerInfo(NetPeer* peer, Text s) { if (peer && peer->NetID()) { char buffer[1024]; WORD gameport = 11101; if (server_config) gameport = server_config->GetGamePort(); sprintf_s(buffer, "info \"%s\" version \"%s\" mode %d users %d host %s port %d", InfoEx::ShortDescription().data(), versionInfo, GetStatus(), NumUsers(), HasHost() ? "true" : "false", gameport); link->SendMessage(peer->NetID(), (BYTE) NET_LOBBY_SERVER_INFO, buffer, strlen(buffer), NetMsg::RELIABLE); } } void NetLobbyServer::DoServerMods(NetPeer* peer, Text s) { if (peer && peer->NetID()) { Text response; ModConfig* config = ModConfig::GetInstance(); List& mods = config->GetModInfoList(); ListIter mod_iter = mods; char buffer[32]; sprintf_s(buffer, "num %d ", mods.size()); response += buffer; while (++mod_iter) { ModInfo* info = mod_iter.value(); response += "mod \""; response += info->Name(); response += "\" url \""; response += info->URL(); response += "\" ver \""; response += info->Version(); response += "\" "; } link->SendMessage(peer->NetID(), (BYTE) NET_LOBBY_SERVER_MODS, response, response.length(), NetMsg::RELIABLE); } } // +-------------------------------------------------------------------+ void NetLobbyServer::DoLogin(NetPeer* peer, Text msg) { List params; ParseMsg(msg, params); Text name; Text pass; Text host; Text gamepass; Text signature; Text squadron; Text version; int rank = 0; int flight_time = 0; int missions = 0; int kills = 0; int losses = 0; for (int i = 0; i < params.size(); i++) { NetLobbyParam* p = params[i]; int num = 0; sscanf_s(p->value, "%d", &num); if (p->name == "name") name = p->value; else if (p->name == "pass") pass = p->value; else if (p->name == "gamepass") gamepass = p->value; else if (p->name == "host") host = p->value; else if (p->name == "sig") signature = p->value; else if (p->name == "squad") squadron = p->value; else if (p->name == "version") version = p->value; else if (p->name == "rank") rank = num; else if (p->name == "time") flight_time = num; else if (p->name == "miss") missions = num; else if (p->name == "kill") kills = num; else if (p->name == "loss") losses = num; } params.destroy(); // first check the game version: if (version != versionInfo) { Print("NetLobbyServer - user '%s' tried to login with invalid game version '%s'\n", name.data(), version.data()); return; } // next check the game password: if (server_config && server_config->GetGamePass().length() > 0) { if (gamepass != server_config->GetGamePass()) { Print("NetLobbyServer - user '%s' tried to login with invalid game password '%s'\n", name.data(), gamepass.data()); return; } } // now try to log the user in: NetUser* pre_existing = FindUserByName(name); // is user already logged in? if (pre_existing) { if (pre_existing->Pass() == pass && pre_existing->GetAddress().IPAddr() == peer->Address().IPAddr()) { } } // otherwise, create a new user: else { NetUser* user = new NetUser(name); user->SetAddress(peer->Address()); user->SetNetID(peer->NetID()); user->SetPass(pass); user->SetSignature(signature); user->SetSquadron(squadron); user->SetRank(rank); user->SetFlightTime(flight_time); user->SetMissions(missions); user->SetKills(kills); user->SetLosses(losses); if (host == "true" && !HasHost()) user->SetHost(true); AddUser(user); RequestAuth(user); SendMOTD(user); SendMods(user); } } void NetLobbyServer::DoLogout(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user) { if (user->IsHost()) GameStop(); DelUser(user); } } void NetLobbyServer::DoUserAuth(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user) { NetAuth::AuthUser(user, msg); if (!user->IsAuthOK()) { char buffer[256]; sprintf_s(buffer, "id %d user \"SERVER\" msg \"**********\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** Your game configuration does not match the server.\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); if (server_mods.size() > 0) { sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** Please check that you have the proper mods deployed in\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** the order shown above.\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); } else { sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** Please verify that you have no mods deployed.\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); } sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** You will not be permitted to join the game with an invalid\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** configuration. You may reconnect to this server after you\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \"SERVER\" msg \"*** have corrected your mod configuration.\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \"SERVER\" msg \"**********\"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); sprintf_s(buffer, "id %d user \" \" msg \" \"", motd_index++); SendData(user, NET_LOBBY_CHAT, buffer); } } } void NetLobbyServer::DoChat(NetPeer* peer, Text msg) { List params; ParseMsg(msg, params); Text chat_msg; for (int i = 0; i < params.size(); i++) { NetLobbyParam* p = params[i]; int num = 0; sscanf_s(p->value, "%d", &num); if (p->name == "msg") { chat_msg = p->value; } } params.destroy(); NetUser* user = FindUserByNetID(peer->NetID()); if (user) { // receive chat from client: if (chat_msg.length()) { AddChat(user, chat_msg); } // request for chat log: else { ListIter iter = chat_log; while (++iter) { NetChatEntry* entry = iter.value(); char buffer[512]; char msg_buf[256]; char usr_buf[256]; // safe quotes uses a static buffer, // so make sure to save copies of the // results when using more than one in // a function call... strcpy_s(msg_buf, SafeQuotes(entry->GetMessage())); strcpy_s(usr_buf, SafeQuotes(entry->GetUser())); sprintf_s(buffer, "id %d user \"%s\" msg \"%s\"", entry->GetID(), usr_buf, msg_buf); SendData(user, NET_LOBBY_CHAT, buffer); } } } } void NetLobbyServer::DoUserList(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user) { Text content; if (local_user) content += local_user->GetDescription(); ListIter iter = users; while (++iter) { NetUser* u = iter.value(); content += u->GetDescription(); } SendData(user, NET_LOBBY_USER_LIST, content); } } void NetLobbyServer::DoBanUser(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user && user->IsHost() && user->IsAuthOK()) { List params; ParseMsg(msg, params); if (params.size() > 0) { NetLobbyParam* p = params[0]; if (p->name == "user") { Text user_name = p->value; NetUser* u = FindUserByName(user_name); if (u && !u->IsHost()) BanUser(u); } } params.destroy(); } } void NetLobbyServer::DoMissionList(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user) { Text reply; char buffer[4096]; ListIter c_iter = Campaign::GetAllCampaigns(); while (++c_iter) { Campaign* c = c_iter.value(); if (c->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) { sprintf_s(buffer, "c_id 0x%08x c_name \"%s\" ", c->GetCampaignId(), SafeQuotes(c->Name())); reply += buffer; } } c_iter.reset(); while (++c_iter) { Campaign* c = c_iter.value(); if (c->GetCampaignId() >= Campaign::MULTIPLAYER_MISSIONS) { ListIter m_iter = c->GetMissionList(); while (++m_iter) { MissionInfo* m = m_iter.value(); int mission_id = (c->GetCampaignId() << NET_CAMPAIGN_SHIFT) + m->id; sprintf_s(buffer, "m_id 0x%08x ", mission_id); reply += buffer; reply += "m_name \""; reply += SafeQuotes(m->name); // long version of safe quotes: int n = 0; const char* s = m->description.data(); while (*s && n < 4090) { if (*s == '"') { buffer[n++] = '\''; s++; } else if (*s == '\n') { buffer[n++] = '\\'; buffer[n++] = 'n'; s++; } else if (*s == '\t') { buffer[n++] = '\\'; buffer[n++] = 't'; s++; } else { buffer[n++] = *s++; } } // don't forget the null terminator! buffer[n] = 0; reply += "\" m_desc \""; reply += buffer; reply += "\" "; } } } SendData(user, NET_LOBBY_MISSION_LIST, reply); sprintf_s(buffer, "m_id 0x%08x", selected_mission); SendData(user, NET_LOBBY_MISSION_SELECT, buffer); } } void NetLobbyServer::DoMissionSelect(NetPeer* peer, Text msg) { if (GetStatus() == NetServerInfo::PERSISTENT) return; NetUser* user = FindUserByNetID(peer->NetID()); if (user && user->IsHost() && user->IsAuthOK()) { List params; ParseMsg(msg, params); for (int i = 0; i < params.size(); i++) { NetLobbyParam* p = params[i]; int num = 0; sscanf_s(p->value, "0x%x", &num); if (p->name == "m_id") { SelectMission(num); } } params.destroy(); } } void NetLobbyServer::DoMissionData(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user && mission && user->IsAuthOK()) { Text reply = Serialize(mission, user); SendData(user, NET_LOBBY_MISSION_DATA, reply); FILE* f = ::fopen("multi_mission_send.def", "wb"); if (f) { ::fwrite(reply.data(), reply.length(), 1, f); ::fclose(f); } } } void NetLobbyServer::DoUnitList(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user && unit_map.size() && user->IsAuthOK()) { Text reply; ListIter iter = GetUnitMap(); while (++iter) { NetUnitEntry* unit = iter.value(); reply += unit->GetDescription(); } SendData(user, NET_LOBBY_UNIT_LIST, reply); } } void NetLobbyServer::DoMapUnit(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user && unit_map.size() && user->IsAuthOK()) { List params; ParseMsg(msg, params); int id = 0; bool lock = false; Text user_name; for (int i = 0; i < params.size(); i++) { NetLobbyParam* p = params[i]; if (p->name == "id") { sscanf_s(p->value, "%d", &id); } else if (p->name == "user") { user_name = p->value; } else if (p->name == "lock") { lock = (p->value == "true") ? true : false; } } params.destroy(); MapUnit(id, user_name, lock); } } void NetLobbyServer::DoGameStart(NetPeer* peer, Text msg) { GameStart(); } void NetLobbyServer::DoGameStop(NetPeer* peer, Text msg) { NetUser* user = FindUserByNetID(peer->NetID()); if (user && user->IsHost() && user->IsAuthOK()) GameStop(); }