Ticket #3556: t3556_dedicated_server_WIP_v0.3.patch
File t3556_dedicated_server_WIP_v0.3.patch, 54.1 KB (added by , 8 years ago) |
---|
-
binaries/system/readme.txt
Basic gameplay: 4 4 -autostart=... load a map instead of showing main menu (see below) 5 5 -editor launch the Atlas scenario editor 6 6 -mod=NAME start the game using NAME mod 7 7 -quickstart load faster (disables audio and some system info logging) 8 8 9 Dedicated Server: 10 -dedicated-host starts a dedicated multiplayer server (without graphics) 11 please specify the mods, for example -mod="public" 12 -dedicated-lobby advertize the game of the dedicated host in the lobby 13 9 14 Autostart: 10 15 -autostart="TYPEDIR/MAPNAME" enables autostart and sets MAPNAME; TYPEDIR is skirmishes, scenarios, or random 11 16 -autostart-ai=PLAYER:AI sets the AI for PLAYER (e.g. 2:petra) 12 17 -autostart-aidiff=PLAYER:DIFF sets the DIFFiculty of PLAYER's AI (0: sandbox, 5: very hard) 13 18 -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only) -
source/main.cpp
static void RunGameOrAtlas(int argc, con 527 527 { 528 528 flags &= ~INIT_MODS; 529 529 Shutdown(SHUTDOWN_FROM_CONFIG); 530 530 continue; 531 531 } 532 533 if (args.Has("dedicated-host")) 534 { 535 g_DedicatedServer = new CDedicatedServer(); 536 g_DedicatedServer->StartHosting(); 537 delete g_DedicatedServer; 538 Shutdown(0); 539 continue; 540 } 541 532 542 InitGraphics(args, 0); 543 533 544 MainControllerInit(); 534 545 while (!quit) 535 546 Frame(); 536 547 Shutdown(0); 537 548 MainControllerShutdown(); … … extern "C" int main(int argc, char* argv 579 590 RunGameOrAtlas(argc, const_cast<const char**>(argv)); 580 591 581 592 // Shut down profiler initialised by EarlyInit 582 593 g_Profiler2.Shutdown(); 583 594 595 // TODO: crash after leaving main with dedicated server! 596 584 597 return EXIT_SUCCESS; 585 598 } -
source/network/DedicatedServer.cpp
1 /* Copyright (C) 2015 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 // TODO: use lobby 19 // TODO: manage SERVER_STATE 20 // TODO: we should let the clients chose their civs and team numbers (but nothing else) via the regular GUI elements 21 // TODO: use ReplayLogger 22 // TODO: end the game when somebody won or everybody left 23 // TODO: start a new game after one finished 24 25 // TODO: config option 1: who is allowed to assign? (everyone|first) 26 // TODO: config option 2: autocontrol pop limit ? (yes|no) 27 28 // TODO: implement a "bouncer"? the server shall allow 9 clients to be connected. 8 players and one observer slot for the host. 29 // Maybe the server could list that observer slot as always connected. 30 31 // TODO: #include "precompiled.h" ?? 32 //#include "DedicatedServer.h" 33 #include "NetServer.h" 34 #include "ps/CLogger.h" 35 36 CDedicatedServer* g_DedicatedServer = nullptr; 37 38 CDedicatedServer::CDedicatedServer() 39 : m_IsHosting(false), m_Quit(false) 40 { 41 } 42 43 CDedicatedServer::~CDedicatedServer() 44 { 45 } 46 47 void CDedicatedServer::StartHosting() 48 { 49 LOGERROR("[HOST] Starting dedicated host"); 50 51 g_NetServer = new CNetServer(true); 52 53 if (!g_NetServer->m_Worker->SetupConnection()) 54 { 55 LOGERROR("[HOST] Could not setup the connection!"); 56 Shutdown(); 57 return; 58 } 59 60 m_Quit = false; 61 while (!m_Quit) 62 HandleInput(); 63 } 64 65 void CDedicatedServer::Shutdown() 66 { 67 LOGERROR("[HOST] Shutting down server"); 68 delete g_NetServer; 69 m_Quit = true; 70 } 71 72 void CDedicatedServer::Restart() 73 { 74 Shutdown(); 75 StartHosting(); 76 } 77 78 void CDedicatedServer::HandleInput() 79 { 80 CStrW input; 81 std::wcin >> input; 82 83 if (input.at(0) == L'/') 84 ParseHostCommand(input); 85 else 86 SendPublicChat(input); 87 } 88 89 void CDedicatedServer::ParseHostCommand(CStrW input) 90 { 91 CStrW command = input.substr(0, input.find(L" ")); 92 93 if (command == L"/exit") Shutdown(); 94 else if (command == L"/quit") Shutdown(); 95 else if (command == L"/restart") Restart(); 96 // TODO: else if (command == "/shutdown 60") will shutdown in 60 minutes 97 // TODO: else if (command == "/list") 98 // TODO: else if (command == "/kick") 99 // TODO: else if (command == "/ban") 100 } 101 102 void CDedicatedServer::SendChat(CNetServerSession* session, CStrW text, bool log) 103 { 104 CChatMessage* message = new CChatMessage(); 105 message->m_GUID = "-1"; 106 message->m_Message = text; 107 108 if (session) 109 session->SendMessage(message); 110 else 111 g_NetServer->m_Worker->Broadcast(message); 112 113 if (log) 114 LOGERROR("[HOST] <host>: %s", utf8_from_wstring(text)); 115 } 116 117 void CDedicatedServer::SendPublicChat(CStrW text, bool log) 118 { 119 SendChat(nullptr, text, log); 120 } 121 122 void CDedicatedServer::StartGame() 123 { 124 // TODO: return if not everybody is ready 125 g_DedicatedServer_Gamesetup->StartGame(); 126 g_NetServer->m_Worker->StartGame(); 127 } 128 129 void CDedicatedServer::OnTick() 130 { 131 } 132 133 void CDedicatedServer::OnChat(CNetServerSession* session, CChatMessage* message) 134 { 135 LOGERROR("[HOST] %s: %s", utf8_from_wstring(session->GetUserName().c_str()), utf8_from_wstring(message->m_Message)); 136 137 // TODO: only if game has started 138 g_DedicatedServer_Gamesetup->ParsePlayerChatCommand(session, message->m_Message); 139 140 // TODO: @report command 141 142 // TODO: say something if the name of the host is mentioned? 143 } 144 145 void CDedicatedServer::OnReady(CNetServerSession* session, CReadyMessage* message) 146 { 147 LOGERROR("[HOST] %s is %s", utf8_from_wstring(session->GetUserName().c_str()), message->m_Status ? "ready " : "not ready"); 148 StartGame(); 149 } 150 151 void CDedicatedServer::OnUserJoin(CNetServerSession* session) 152 { 153 LOGERROR("[HOST] Authenticating %s [%s]", utf8_from_wstring(session->GetUserName()).c_str(), session->GetIPAddress().c_str()); 154 } 155 156 void CDedicatedServer::OnGamesetupJoin(CNetServerSession* session) 157 { 158 LOGERROR("[HOST] %s has joined the gamesetup.", utf8_from_wstring(session->GetUserName()).c_str()); 159 g_DedicatedServer_Gamesetup->SendWelcomeMessage(session); 160 g_DedicatedServer_Gamesetup->UpdatePlayerAssignments(); 161 } 162 163 void CDedicatedServer::OnUserLeave(CNetServerSession* session) 164 { 165 LOGERROR("[HOST] %s has left", utf8_from_wstring(session->GetUserName())); 166 g_DedicatedServer_Gamesetup->UpdatePlayerAssignments(); 167 } 168 169 void CDedicatedServer::OnUpdateGameAttributes() 170 { 171 LOGERROR("[HOST] Updated game attributes"); 172 } 173 174 void CDedicatedServer::OnUserRejoining(CNetServerSession* session) 175 { 176 LOGERROR("[HOST] %s is starting to rejoin the match.", utf8_from_wstring(g_NetServer->m_Worker->m_PlayerAssignments[session->GetGUID()].m_Name.c_str())); 177 } 178 179 void CDedicatedServer::OnUserRejoined(CNetServerSession* session) 180 { 181 LOGERROR("[HOST] %s has finished rejoining.", utf8_from_wstring(g_NetServer->m_Worker->m_PlayerAssignments[session->GetGUID()].m_Name.c_str())); 182 } 183 184 void CDedicatedServer::OnStartGame() 185 { 186 LOGERROR("[HOST] The game has started."); 187 } -
source/network/DedicatedServer.h
1 /* Copyright (C) 2015 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 // TODO: some ifndef define NETSERVER_H ? 19 20 #include "NetServer.h" 21 #include "NetSession.h" 22 #include "NetMessage.h" 23 24 #include "scriptinterface/ScriptInterface.h" 25 26 /** 27 * Contains only network functions, no gamesetup. 28 */ 29 class CDedicatedServer 30 { 31 NONCOPYABLE(CDedicatedServer); 32 33 private: 34 friend class NetServer; 35 friend class NetServerWorker; 36 friend class DedicatedServer_Gamesetup; 37 38 /** 39 * Whether or not the service is actually running. 40 */ 41 bool m_IsHosting; 42 43 /** 44 * Whether or not we wan't to exit the server. 45 */ 46 bool m_Quit; 47 48 /** 49 * Processes command-line-interface keystrokes. 50 */ 51 void HandleInput(); 52 53 public: // TODO: can we make this private? 54 55 CDedicatedServer(); 56 ~CDedicatedServer(); 57 58 /** 59 * Create the host. Starts listening on UDP port 20595 for player connects. 60 */ 61 void StartHosting(); 62 63 /** 64 * Exit gracefully and safely disconnect all clients. 65 */ 66 void Shutdown(); 67 68 /** 69 * Exit gracefully and host a fresh instance. 70 */ 71 void Restart(); 72 73 /* 74 * Sends the given text message to either a client (if session was provided) 75 * or broadcasts to all clients otherwise. 76 */ 77 void SendChat(CNetServerSession* session, CStrW text, bool log = false); 78 79 /* 80 * Sends the given text message to either a client (if session was provided) 81 * or broadcasts to all clients otherwise. 82 */ 83 void SendPublicChat(CStrW text, bool log = true); 84 85 /* 86 * Searches for commands in the given commandline text-input. 87 */ 88 void ParseHostCommand(CStrW input); 89 90 /* 91 * Should not be called after before all players are ready. 92 */ 93 void StartGame(); 94 95 /** 96 * Called by periodically CNetServerWorker::Run(). 97 * Can be used to process asynchronously while the main thread is waiting for user input. 98 */ 99 void OnTick(); 100 101 /** 102 * Useless as we didn't authenticate the client yet. 103 */ 104 void OnUserJoin(CNetServerSession* session); 105 106 /** 107 * A player has joined the gamesetup. 108 */ 109 void OnGamesetupJoin(CNetServerSession* session); 110 111 /** 112 * Updates the player assignments. 113 */ 114 void OnUserLeave(CNetServerSession* session); 115 116 /** 117 * Lets the users chose their own civs and team numbers. 118 */ 119 void OnChat(CNetServerSession* session, CChatMessage* message); 120 121 /** 122 * Called whenever the server updated its settings. 123 */ 124 void OnUpdateGameAttributes(); 125 126 /** 127 * Starts the game if all players are ready. 128 */ 129 void OnReady(CNetServerSession* session, CReadyMessage* message); 130 131 /** 132 * Used for logging only. 133 */ 134 void OnStartGame(); 135 136 /** 137 * Used for logging only. 138 */ 139 void OnUserRejoining(CNetServerSession* session); 140 141 /** 142 * Used for logging only. 143 */ 144 void OnUserRejoined(CNetServerSession* session); 145 }; 146 147 extern CDedicatedServer *g_DedicatedServer; -
source/network/DedicatedServer_Gamesetup.cpp
1 /* Copyright (C) 2015 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #include "NetServer.h" 19 20 #include "graphics/MapReader.h" 21 #include "ps/CLogger.h" 22 #include "ps/GUID.h" 23 24 // TODO: for A20, remove the whole class and let one/every user in gamesetup.js control through the GUI 25 26 CDedicatedServer_Gamesetup* g_DedicatedServer_Gamesetup = nullptr; 27 28 // TODO: define default skirmish / scenario maps 29 30 /** 31 * Version of this component. Notice the server can be updated without new releases of 0 A.D. 32 */ 33 const CStrW g_Version = L"0.0.15"; 34 35 /** 36 * Date of the last update of this component. 37 */ 38 const CStrW g_LastUpdate = L"9th Nov 2015"; 39 40 const int g_MaxPlayers = 8; 41 42 const std::set<CStrW> g_MapTypes = { L"random", L"skirmish", L"scenario" }; 43 44 // TODO: move to mapfilters.json and unify with gamesetup.js? 45 const std::set<CStrW> g_MapFilters = { L"default", L"all", L"naval", L"demo" }; 46 47 // TODO: load from game_speeds.json 48 const std::set<float> g_GameSpeeds = { 0.10, 0.25, 0.50, 0.75, 1.0, 1.25, 1.5, 2.0 }; 49 50 // TODO: load from population_capacities.json 51 const std::set<int> g_PopulationCapacities = { 50, 100, 150, 200, 250, 300 }; 52 53 // TODO: load from starting_resources.json 54 const std::set<int> g_Resources = { 100, 300, 500, 1000, 3000, 50000 }; 55 56 // TODO: load from map_sizes.json 57 const std::set<int> g_MapSizes = { 128, 192, 256, 320, 384, 448, 512 }; 58 59 CDedicatedServer_Gamesetup::CDedicatedServer_Gamesetup(ScriptInterface* scriptInterface) : 60 m_ScriptInterface(scriptInterface), 61 m_MapType(L"random"), 62 m_MapPath(L"/maps/random") 63 // m_PlayerCount(0) 64 { 65 Initialize(); 66 } 67 68 CDedicatedServer_Gamesetup::~CDedicatedServer_Gamesetup() 69 { 70 } 71 72 void CDedicatedServer_Gamesetup::SendWelcomeMessage(CNetServerSession* session) 73 { 74 g_DedicatedServer->SendChat(session, L"Welcome. This is a public hosting service provided by elexis."); 75 g_DedicatedServer->SendChat(session, L"Type '@help' for a list of available commands."); 76 g_DedicatedServer->SendChat(session, L"This server is running dedicated autohost " + g_Version + L", last update on " + g_LastUpdate); 77 g_DedicatedServer->SendChat(session, L"Abuse will be reported."); 78 } 79 80 void CDedicatedServer_Gamesetup::SendChatCommandList(CNetServerSession* session) 81 { 82 // TODO: list available options from constants like g_MapFilters 83 LOGERROR("[HOST] Sending gamesetup command list"); 84 g_DedicatedServer->SendChat(session, L"Available commands:"); 85 g_DedicatedServer->SendChat(session, L" - @help - Shows this message."); 86 g_DedicatedServer->SendChat(session, L" - @maptype [type] - Decide whether to play skirmish, scenario or random map type. Example: @maptype random"); 87 g_DedicatedServer->SendChat(session, L" - @mapfilter [type] - Select which maps to list. Options: default, all, naval, demo. Example: @mapfilter all"); 88 g_DedicatedServer->SendChat(session, L" - @listmaps - Lists valid maps of the current map type."); 89 g_DedicatedServer->SendChat(session, L" - @selectmap [map] - Changes the map. Example: @selectmap Random"); 90 g_DedicatedServer->SendChat(session, L" - @mapsize [size] - Changes the size of the map. Example: @mapsize Medium"); 91 g_DedicatedServer->SendChat(session, L" - @numplayers [num] - Changes number of players. Example: @numplayers 2"); 92 g_DedicatedServer->SendChat(session, L" - @setplayer [num] - Assigns yourself to a player slot. Example: @setplayer 1"); 93 g_DedicatedServer->SendChat(session, L" - @spectate - Do not play but observe the match."); 94 g_DedicatedServer->SendChat(session, L" - @civ [civName] - Selects your civilization. Example: @civ Athenians"); 95 g_DedicatedServer->SendChat(session, L" - @team [teamNum] - Chose your team. Valid teams are 0 (None), 1, 2, 3, and 4. Example: @team 1"); 96 g_DedicatedServer->SendChat(session, L" - @popmax [popMax] - Changes the maximum number of units per player. Example: @popmax 200"); 97 g_DedicatedServer->SendChat(session, L" - @resources [amount] - Changes the amount of starting resources. Example: @resources 500"); 98 g_DedicatedServer->SendChat(session, L" - @ceasefire [min] - Changes the time until players can attack each other. Example: @ceasefire 3"); 99 g_DedicatedServer->SendChat(session, L" - @gametype [type] - Changes the victory condition. Example @gametype wonder"); 100 g_DedicatedServer->SendChat(session, L" - @treasure [allowed] - Whether or not treasures should be disabled. Example @treasure: false"); 101 } 102 103 void CDedicatedServer_Gamesetup::ParsePlayerChatCommand(CNetServerSession* session, CStrW input) 104 { 105 // Parse command and argument as different types 106 CStrW command = input.substr(0, input.find(L" ")); 107 CStrW argument = input.substr(input.find(L" ") + 1); 108 int argumentInt = std::atoi(utf8_from_wstring(argument).c_str()); 109 float argumentFloat = std::atof(utf8_from_wstring(argument).c_str()); 110 111 // Notice: cheats not implemented 112 // TODO: set rated game, set locked teams 113 114 if (command == L"@help") SendChatCommandList(session); 115 else if (command == L"@maptype") SetMaptype(session, argument); 116 else if (command == L"@mapfilter") SetMapFilter(session, argument); 117 else if (command == L"@listmaps") SendMapList(session); 118 else if (command == L"@selectmap") SelectMap(session, argument); 119 else if (command == L"@mapsize") SetMapsize(session, argumentInt); 120 else if (command == L"@numplayers") SetNumberOfPlayers(session, argumentInt); 121 else if (command == L"@setplayer") SetPlayerID(session, argumentInt); 122 else if (command == L"@spectate") SetPlayerID(session, -1); 123 else if (command == L"@civ") SetPlayerCiv(session, argument); 124 else if (command == L"@team") SetPlayerTeam(session, argumentInt); 125 else if (command == L"@ceasefire") SetCeasefire(session, argumentInt); 126 else if (command == L"@gamespeed") SetGamespeed(session, argumentFloat); 127 else if (command == L"@gametype") SetGametype(session, argument); 128 else if (command == L"@popmax") SetPopulationCapacity(session, argumentInt); 129 else if (command == L"@resources") SetResources(session, argumentInt); 130 else if (command == L"@treasure") DisableTreasure(session, argument); 131 else 132 g_DedicatedServer->SendPublicChat(L"Unknown command: " + command); 133 } 134 135 void CDedicatedServer_Gamesetup::Initialize() 136 { 137 LOGERROR("[HOST] Initial gamesetup"); 138 139 // the interface is initialized in CNetServerWorker::Run 140 m_ScriptInterface = g_NetServer->m_Worker->m_ScriptInterface; 141 142 // TODO: get username from config and create servername based on that 143 CStrW serverName(L"elexis' dedicated autohost"); 144 //if (g_args.Has("dedicated-host-name")) 145 // serverName = wstring_from_utf8(g_args.Get("dedicated-host-name")); 146 147 JSContext* cx = m_ScriptInterface->GetContext(); 148 JSAutoRequest rq(cx); 149 150 // Needs to be kept in sync with gamesetup.js 151 // TODO: load from a JSON file 152 JS::RootedValue attribs(cx); 153 JS::RootedValue settings(cx); 154 JS::RootedValue playerAssignments(cx); 155 JS::RootedValue PlayerData(cx); 156 JS::RootedValue VictoryScripts(cx); 157 158 m_ScriptInterface->Eval("({})", &attribs); 159 m_ScriptInterface->Eval("({})", &settings); 160 m_ScriptInterface->Eval("({})", &playerAssignments); 161 m_ScriptInterface->Eval("([])", &PlayerData); 162 m_ScriptInterface->Eval("([\"scripts/TriggerHelper.js\",\"scripts/ConquestCommon.js\",\"scripts/Conquest.js\"])", &VictoryScripts); 163 164 m_ScriptInterface->SetProperty(settings, "AISeed", rand()); 165 m_ScriptInterface->SetProperty(settings, "Seed", rand()); 166 m_ScriptInterface->SetProperty(settings, "Ceasefire", 0); 167 m_ScriptInterface->SetProperty(settings, "CheatsEnabled", false); 168 m_ScriptInterface->SetProperty(settings, "GameType", std::wstring(L"conquest")); 169 m_ScriptInterface->SetProperty(settings, "ObserverLateJoin", true); 170 m_ScriptInterface->SetProperty(settings, "LockTeams", true); 171 m_ScriptInterface->SetProperty(settings, "PlayerData", PlayerData); 172 m_ScriptInterface->SetProperty(settings, "PopulationCap", 300); 173 m_ScriptInterface->SetProperty(settings, "RevealMap", false); 174 m_ScriptInterface->SetProperty(settings, "ExploredMap", false); 175 m_ScriptInterface->SetProperty(settings, "Size", 320); // map size 4 176 m_ScriptInterface->SetProperty(settings, "StartingResources", 500); // medium res for nomad 177 m_ScriptInterface->SetProperty(settings, "VictoryScripts", VictoryScripts); 178 // disable treasure, no revealed map, no explored map 179 // "mods": ["mod","public"] 180 //"Preview": "acropolis_bay.png", 181 //CircularMap 182 //Description 183 m_ScriptInterface->SetProperty(attribs, "gameSpeed", 1); 184 m_ScriptInterface->SetProperty(attribs, "isNetworked", true); 185 m_ScriptInterface->SetProperty(attribs, "map", std::wstring(L"random")); 186 m_ScriptInterface->SetProperty(attribs, "mapFilter", std::wstring(L"all")); 187 m_ScriptInterface->SetProperty(attribs, "mapPath", m_MapPath); 188 m_ScriptInterface->SetProperty(attribs, "mapType", m_MapType); 189 m_ScriptInterface->SetProperty(attribs, "matchID", ps_generate_guid().FromUTF8()); 190 m_ScriptInterface->SetProperty(attribs, "playerAssignments", playerAssignments); 191 m_ScriptInterface->SetProperty(attribs, "serverName", serverName); 192 m_ScriptInterface->SetProperty(attribs, "settings", settings); 193 194 g_NetServer->m_Worker->UpdateGameAttributes(&attribs); 195 } 196 197 void CDedicatedServer_Gamesetup::UpdatePlayerAssignments() 198 { 199 return; 200 LOGERROR("[HOST] Updating player assignments"); 201 202 JSContext* cx = m_ScriptInterface->GetContext(); 203 JSAutoRequest rq(cx); 204 205 JS::RootedValue attribs(cx); 206 JS::RootedValue settings(cx); 207 JS::RootedValue PlayerData(cx); 208 209 attribs.set(g_NetServer->m_Worker->m_GameAttributes.get()); 210 m_ScriptInterface->GetProperty(attribs, "settings", &settings); 211 m_ScriptInterface->Eval("([])", &PlayerData); 212 213 // Find all player IDs in active use 214 /* std::set<i32> usedIDs; 215 for (PlayerAssignmentMap::iterator it = m_Worker->.m_PlayerAssignments.begin(); it != m_PlayerAssignments.end(); ++it) 216 if (it->second.m_Enabled && it->second.m_PlayerID != -1) 217 usedIDs.insert(it->second.m_PlayerID); 218 */ 219 // {"type":"players","hosts":{"DDA0E9792D9168E2":{"name":"elexis3","player":-1,"status":0}}} 220 221 for (PlayerAssignmentMap::iterator it = g_NetServer->m_Worker->m_PlayerAssignments.begin(); it != g_NetServer->m_Worker->m_PlayerAssignments.end(); ++it) 222 { 223 LOGERROR("%s, PlayerID %d", utf8_from_wstring(it->second.m_Name), it->second.m_PlayerID); 224 /* assignment.m_Enabled = true; 225 assignment.m_Name = name; 226 assignment.m_PlayerID = playerID; 227 assignment.m_Status = 0; 228 */ } 229 230 // Update player data 231 for (u8 i=0; i<g_NetServer->m_Worker->m_PlayerAssignments.size(); ++i) 232 { 233 JS::RootedValue player(cx); 234 m_ScriptInterface->Eval("({})", &player); 235 //m_ScriptInterface->SetProperty(player, "Name", "Some player"); 236 m_ScriptInterface->SetProperty(player, "Team", -1); 237 m_ScriptInterface->SetProperty(player, "Civ", std::wstring(L"random")); 238 m_ScriptInterface->SetProperty(player, "AI", std::wstring(L"")); 239 m_ScriptInterface->SetProperty(player, "AiDiff", 3); 240 m_ScriptInterface->SetPropertyInt(PlayerData, i, player); 241 } 242 m_ScriptInterface->SetProperty(settings, "PlayerData", PlayerData); 243 244 // TODO SPLIT INTO FUNCTION 245 // Limit pop cap according to playercount 246 std::map<int,int> popCaps = { 247 { 1, 300 }, // 600 pop total, 700 with wonder 248 { 2, 300 }, // 600 pop total, 750 with wonder 249 { 3, 200 }, // 600 pop total, 750 with wonder 250 { 4, 200 }, // 800 pop total, 1000 with wonder 251 { 5, 150 }, // 750 pop total, 1000 with wonder 252 { 6, 150 }, // 900 pop total, 1200 with wonder 253 { 7, 100 }, // 700 pop total, 1050 with wonder 254 { 8, 100 } // 800 pop total, 1200 with wonder 255 }; 256 m_ScriptInterface->SetProperty(settings, "PopulationCap", popCaps[g_NetServer->m_Worker->m_PlayerAssignments.size()]); 257 258 // TODO: send chat that pop cap has been adopted 259 260 m_ScriptInterface->SetProperty(attribs, "settings", settings); 261 262 g_NetServer->m_Worker->UpdateGameAttributes(&attribs); 263 264 // Sends the player assignments 265 g_NetServer->m_Worker->ClearAllPlayerReady(); 266 } 267 268 void CDedicatedServer_Gamesetup::ChangeAttribute(CStr attribute, JS::HandleValue value) 269 { 270 JSContext* cx = m_ScriptInterface->GetContext(); 271 JSAutoRequest rq(cx); 272 273 // Display the change to the host 274 CStr value_string; 275 if (m_ScriptInterface->FromJSVal(cx, value, value_string)) 276 LOGERROR("[HOST] Change gamesetup attribute (%s to %s)", attribute, value_string); 277 else 278 LOGERROR("[HOST] Change gamesetup attribute (%s)", attribute); 279 280 // Save the value 281 JS::RootedValue attribs(cx); 282 attribs.set(g_NetServer->m_Worker->m_GameAttributes.get()); 283 m_ScriptInterface->SetProperty(attribs, attribute.c_str(), value); 284 g_NetServer->m_Worker->UpdateGameAttributes(&attribs); 285 g_NetServer->m_Worker->ClearAllPlayerReady(); 286 } 287 288 void CDedicatedServer_Gamesetup::ChangeSetting(CStr setting, JS::HandleValue value) 289 { 290 JSContext* cx = m_ScriptInterface->GetContext(); 291 JSAutoRequest rq(cx); 292 293 // Display the change to the host 294 CStr value_string; 295 if (m_ScriptInterface->FromJSVal(cx, value, value_string)) 296 LOGERROR("[HOST] Change gamesetup setting (%s to %s)", setting, value_string); 297 else 298 LOGERROR("[HOST] Change gamesetup setting (%s)", setting); 299 300 // Save the value 301 JS::RootedValue attribs(cx); 302 JS::RootedValue settings(cx); 303 304 attribs.set(g_NetServer->m_Worker->m_GameAttributes.get()); 305 306 m_ScriptInterface->GetProperty(attribs, "settings", &settings); 307 m_ScriptInterface->SetProperty(settings, setting.c_str(), value); 308 m_ScriptInterface->SetProperty(attribs, "settings", settings); 309 310 g_NetServer->m_Worker->UpdateGameAttributes(&attribs); 311 g_NetServer->m_Worker->ClearAllPlayerReady(); 312 } 313 314 void CDedicatedServer_Gamesetup::SetMaptype(CNetServerSession* UNUSED(session), CStrW mapType) 315 { 316 if (g_MapTypes.find(mapType) == g_MapTypes.end()) 317 { 318 g_DedicatedServer->SendPublicChat(L"Error: unknown maptype: " + mapType); 319 return; 320 } 321 322 m_MapType = mapType; 323 324 if (mapType == L"skirmish") 325 m_MapPath = L"maps/skirmishes/"; 326 else if (mapType == L"scenario") 327 m_MapPath = L"maps/scenarios/"; 328 else 329 m_MapPath = L"maps/random/"; 330 331 JSContext* cx = m_ScriptInterface->GetContext(); 332 JSAutoRequest rq(cx); 333 JS::RootedValue jsVal(cx); 334 335 // TODO: only one attribute change 336 m_ScriptInterface->ToJSVal(cx, &jsVal, m_MapType); 337 ChangeAttribute("mapType", jsVal); 338 m_ScriptInterface->ToJSVal(cx, &jsVal, m_MapPath); 339 ChangeAttribute("mapPath", jsVal); 340 341 g_DedicatedServer->SendPublicChat(L"Set map type to " + mapType); 342 343 // TODO: select random map!! previous map can be invalidated 344 } 345 346 void CDedicatedServer_Gamesetup::SetMapFilter(CNetServerSession* UNUSED(session), CStrW mapFilter) 347 { 348 if (g_MapFilters.find(mapFilter) == g_MapFilters.end()) 349 { 350 g_DedicatedServer->SendPublicChat(L"Error: unknown mapfilter: " + mapFilter); 351 return; 352 } 353 354 JSContext* cx = m_ScriptInterface->GetContext(); 355 JSAutoRequest rq(cx); 356 JS::RootedValue jsVal(cx); 357 358 m_ScriptInterface->ToJSVal(cx, &jsVal, mapFilter); 359 ChangeAttribute("mapFilter", jsVal); 360 361 g_DedicatedServer->SendPublicChat(L"Set map filter to " + mapFilter); 362 } 363 364 void CDedicatedServer_Gamesetup::SendMapList(CNetServerSession* session) 365 { 366 CFileInfos fileInfos; 367 if (g_VFS->GetDirectoryEntries(VfsPath(m_MapPath), &fileInfos, NULL) != INFO::OK) 368 { 369 g_DedicatedServer->SendPublicChat(L"Couldn't list files in map-path"); 370 return; 371 } 372 373 LOGERROR("[HOST] Sending map list"); 374 375 // TODO: dont log spam 376 g_DedicatedServer->SendPublicChat(L"Sent map list to " + session->GetUserName()); 377 g_DedicatedServer->SendChat(session, L"Maps"); 378 379 for (CFileInfo &fileInfo : fileInfos) 380 { 381 CStrW filename = fileInfo.Name().string(); 382 CStrW fileEnding = (m_MapType == L"random") ? L".json" : L".xml"; 383 384 if (filename.substr(filename.length() - fileEnding.length()) != fileEnding) 385 continue; 386 387 // TODO: load & cache map properties 388 // TODO: skip if filter doesnt apply 389 390 CStrW mapname = filename.substr(0, filename.length() - fileEnding.length()); 391 g_DedicatedServer->SendChat(session, mapname); 392 } 393 } 394 395 void CDedicatedServer_Gamesetup::SelectMap(CNetServerSession* UNUSED(session), CStrW map) 396 { 397 if (m_MapType == L"random") 398 SelectRandomMap(map); 399 else 400 SelectXMLMap(map); 401 } 402 403 void CDedicatedServer_Gamesetup::SelectRandomMap(CStrW mapFilename) 404 { 405 VfsPath filePath(m_MapPath + mapFilename + L".json"); 406 407 if (!VfsFileExists(filePath)) 408 { 409 g_DedicatedServer->SendPublicChat(L"Error: map not found: " + mapFilename); 410 return; 411 } 412 413 JSContext* cx = m_ScriptInterface->GetContext(); 414 JSAutoRequest rq(cx); 415 416 // Load the file 417 JS::RootedValue mapAttributes(cx); 418 m_ScriptInterface->ReadJSONFile(filePath, &mapAttributes); 419 if (mapAttributes.isNull()) 420 { 421 g_DedicatedServer->SendPublicChat(L"Could not load map"); 422 return; 423 } 424 425 JS::RootedValue mapSettings(cx); 426 m_ScriptInterface->GetProperty(mapAttributes, "settings", &mapSettings); 427 SaveMapSettings(mapFilename, mapSettings); 428 429 g_NetServer->m_Worker->ClearAllPlayerReady(); 430 431 g_DedicatedServer->SendPublicChat(L"Set map: " + mapFilename); 432 } 433 434 void CDedicatedServer_Gamesetup::SelectXMLMap(CStrW mapFilename) 435 { 436 VfsPath filePath(m_MapPath + mapFilename + L".xml"); 437 if (!VfsFileExists(filePath)) 438 { 439 g_DedicatedServer->SendPublicChat(L"Error: unknown map: " + mapFilename); 440 return; 441 } 442 443 CMapSummaryReader reader; 444 if (reader.LoadMap(filePath) != PSRETURN_OK) 445 { 446 g_DedicatedServer->SendPublicChat(L"Error: Could not load map: " + mapFilename); 447 return; 448 } 449 450 JSContext* cx = m_ScriptInterface->GetContext(); 451 JSAutoRequest rq(cx); 452 453 JS::RootedValue mapAttributes(cx); 454 JS::RootedValue mapSettings(cx); 455 reader.GetMapSettings(*m_ScriptInterface, &mapAttributes); 456 m_ScriptInterface->GetProperty(mapAttributes, "settings", &mapSettings); 457 SaveMapSettings(mapFilename, mapSettings); 458 459 // TODO: load victory scripts according to gametype 460 461 g_NetServer->m_Worker->ClearAllPlayerReady(); 462 463 g_DedicatedServer->SendPublicChat(L"Set map " + mapFilename); 464 } 465 466 void CDedicatedServer_Gamesetup::SaveMapSettings(CStrW mapFilename, JS::HandleValue mapSettings) 467 { 468 JSContext* cx = m_ScriptInterface->GetContext(); 469 JSAutoRequest rq(cx); 470 471 // Load current game settings 472 JS::RootedValue attribs(cx); 473 JS::RootedValue settings(cx); 474 attribs.set(g_NetServer->m_Worker->m_GameAttributes.get()); 475 m_ScriptInterface->GetProperty(attribs, "settings", &settings); 476 477 // Copy map settings to game settings 478 std::vector<std::string> settingNames; 479 m_ScriptInterface->EnumeratePropertyNamesWithPrefix(mapSettings, "", settingNames); 480 for (const std::string& settingName : settingNames) 481 { 482 JS::RootedValue value(cx); 483 m_ScriptInterface->GetProperty(mapSettings, settingName.c_str(), &value); 484 m_ScriptInterface->SetProperty(settings, settingName.c_str(), value); 485 } 486 487 // TODO: to ensure previous mapsettings dont remain, we should replace the whole settings property with a clean one 488 // but we lose all previous settings then. 489 // TODO: implement RemoveProperty using JS_DeleteProperty? :-L 490 491 // Updated game attributes 492 m_ScriptInterface->SetProperty(attribs, "map", m_MapPath + mapFilename); 493 m_ScriptInterface->SetProperty(attribs, "settings", settings); 494 if (m_MapType == L"random") 495 m_ScriptInterface->SetProperty(attribs, "script", mapFilename + L".js"); 496 else 497 m_ScriptInterface->SetProperty(attribs, "script", NULL); 498 499 g_NetServer->m_Worker->UpdateGameAttributes(&attribs); 500 } 501 502 void CDedicatedServer_Gamesetup::SetMapsize(CNetServerSession* UNUSED(session), int tiles) 503 { 504 // TODO: parse names instead of tiles 505 if (g_MapSizes.find(tiles) == std::end(g_MapSizes)) 506 { 507 g_DedicatedServer->SendPublicChat(L"Error: unknown map size"); 508 return; 509 } 510 511 JSContext* cx = m_ScriptInterface->GetContext(); 512 JSAutoRequest rq(cx); 513 JS::RootedValue jsVal(cx); 514 515 m_ScriptInterface->ToJSVal(cx, &jsVal, tiles); 516 ChangeSetting("Size", jsVal); 517 518 g_DedicatedServer->SendPublicChat(L"Set map size to " + std::to_wstring(tiles)); 519 } 520 521 void CDedicatedServer_Gamesetup::SetNumberOfPlayers(CNetServerSession* UNUSED(session), int UNUSED(players)) 522 { 523 /* 524 // Only meaningful for random maps 525 if (g_GameAttributes.mapType != "random") 526 return; 527 528 LOGERROR("Setting %d number of players", players); 529 */ /* 530 // Update player data 531 var pData = g_GameAttributes.settings.PlayerData; 532 if (pData && num < pData.length) 533 { 534 // Remove extra player data 535 g_GameAttributes.settings.PlayerData = pData.slice(0, num); 536 } 537 else 538 { 539 // Add player data from defaults 540 for (var i = pData.length; i < num; ++i) 541 g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]); 542 } 543 544 // Some players may have lost their assigned slot 545 for (var guid in g_PlayerAssignments) 546 { 547 var player = g_PlayerAssignments[guid].player; 548 if (player > num) 549 { 550 if (g_IsNetworked) 551 Engine.AssignNetworkPlayer(player, ""); 552 else 553 g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1, "civ": "", "team": -1, "ready": 0} }; 554 } 555 } 556 557 updateGameAttributes(); 558 */ 559 } 560 561 void CDedicatedServer_Gamesetup::SetGametype(CNetServerSession* UNUSED(session), CStrW UNUSED(gameType)) 562 { 563 // m_ScriptInterface->SetProperty(settings, "GameType", std::wstring(L"conquest")); 564 // m_ScriptInterface->SetProperty(settings, "VictoryScripts", VictoryScripts); 565 // g_DedicatedServer->SendPublicChat(L"Set game type to " + gameType); 566 } 567 568 void CDedicatedServer_Gamesetup::SetGamespeed(CNetServerSession* UNUSED(session), float gameSpeed) 569 { 570 if (g_GameSpeeds.find(gameSpeed) == std::end(g_GameSpeeds)) 571 { 572 g_DedicatedServer->SendPublicChat(L"Error: unknown gamespeed"); 573 return; 574 } 575 576 JSContext* cx = m_ScriptInterface->GetContext(); 577 JSAutoRequest rq(cx); 578 JS::RootedValue jsVal(cx); 579 580 m_ScriptInterface->ToJSVal(cx, &jsVal, gameSpeed); 581 ChangeAttribute("gameSpeed", jsVal); 582 583 g_DedicatedServer->SendPublicChat(L"Set game speed to " + std::to_wstring(gameSpeed)); 584 } 585 586 void CDedicatedServer_Gamesetup::SetPopulationCapacity(CNetServerSession* UNUSED(session), int populationCapacity) 587 { 588 if (g_PopulationCapacities.find(populationCapacity) == std::end(g_PopulationCapacities)) 589 { 590 g_DedicatedServer->SendPublicChat(L"Error: unknown population capacity"); 591 return; 592 } 593 594 JSContext* cx = m_ScriptInterface->GetContext(); 595 JSAutoRequest rq(cx); 596 JS::RootedValue jsVal(cx); 597 598 m_ScriptInterface->ToJSVal(cx, &jsVal, populationCapacity); 599 ChangeSetting("PopulationCap", jsVal); 600 601 g_DedicatedServer->SendPublicChat(L"Set population capacity to " + std::to_wstring(populationCapacity)); 602 } 603 604 void CDedicatedServer_Gamesetup::SetResources(CNetServerSession* UNUSED(session), int resources) 605 { 606 if (g_Resources.find(resources) == std::end(g_Resources)) 607 { 608 g_DedicatedServer->SendPublicChat(L"Error: unknown resources"); 609 return; 610 } 611 612 JSContext* cx = m_ScriptInterface->GetContext(); 613 JSAutoRequest rq(cx); 614 JS::RootedValue jsVal(cx); 615 616 m_ScriptInterface->ToJSVal(cx, &jsVal, resources); 617 ChangeSetting("StartingResources", jsVal); 618 619 g_DedicatedServer->SendPublicChat(L"Set starting resources to " + std::to_wstring(resources)); 620 } 621 622 void CDedicatedServer_Gamesetup::SetCeasefire(CNetServerSession* UNUSED(session), int ceasefire) 623 { 624 JSContext* cx = m_ScriptInterface->GetContext(); 625 JSAutoRequest rq(cx); 626 JS::RootedValue jsVal(cx); 627 628 m_ScriptInterface->ToJSVal(cx, &jsVal, ceasefire); 629 ChangeSetting("Ceasefire", jsVal); 630 631 g_DedicatedServer->SendPublicChat(L"Set ceasefire to " + std::to_wstring(ceasefire)); 632 } 633 634 void CDedicatedServer_Gamesetup::DisableTreasure(CNetServerSession* UNUSED(session), CStrW allowTreasure) 635 { 636 bool disableTreasure; 637 if (allowTreasure == L"false") 638 disableTreasure = true; 639 else if (allowTreasure == L"true") 640 disableTreasure = false; 641 else 642 { 643 g_DedicatedServer->SendPublicChat(L"Error: unknown treasure setting"); 644 return; 645 } 646 647 JSContext* cx = m_ScriptInterface->GetContext(); 648 JSAutoRequest rq(cx); 649 JS::RootedValue jsVal(cx); 650 651 m_ScriptInterface->ToJSVal(cx, &jsVal, disableTreasure); 652 ChangeSetting("DisableTreasures", jsVal); 653 654 g_DedicatedServer->SendPublicChat(disableTreasure ? L"Forbidding treasure." : L"Allowing treasure."); 655 } 656 657 void CDedicatedServer_Gamesetup::StartGame() 658 { 659 // "timestamp": "std::time(nullptr)", 660 // "engine_version": "0.0.19", 661 // "mods":... 662 //m_ScriptInterface->SetProperty(settings, "AISeed", rand()); 663 //m_ScriptInterface->SetProperty(settings, "Seed", rand()); 664 // TODO: send final game attributes 665 } 666 667 void CDedicatedServer_Gamesetup::SetPlayerID(CNetServerSession* session, int playerID) 668 { 669 // JSContext* cx = m_ScriptInterface->GetContext(); 670 //JSAutoRequest rq(cx); 671 //JS::RootedValue jsVal(cx); 672 673 //g_NetServer->m_Worker->m_PlayerAssignments[session->GetGUID()].m_PlayerID = playerID; 674 675 g_NetServer->AssignPlayer(playerID, session->GetGUID()); 676 677 g_DedicatedServer->SendPublicChat(L"Assigning " + session->GetUserName() + L" to slot " + std::to_wstring(playerID)); 678 } 679 680 void CDedicatedServer_Gamesetup::SetPlayerCiv(CNetServerSession* UNUSED(session), CStrW UNUSED(civilization)) 681 { 682 //g_DedicatedServer->SendPublicChat(""); 683 } 684 685 void CDedicatedServer_Gamesetup::SetPlayerTeam(CNetServerSession* UNUSED(session), int UNUSED(team)) 686 { 687 //g_DedicatedServer->SendPublicChat(""); 688 } -
source/network/DedicatedServer_Gamesetup.h
1 /* Copyright (C) 2015 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 // TODO: some ifndef define NETSERVER_H ? 19 20 //#include "NetServer.h" 21 #include "NetSession.h" 22 //#include "DedicatedServer.h" 23 //#include "NetMessage.h" 24 25 #include "lib/file/file_system.h" 26 #include "lib/file/vfs/vfs_util.h" 27 #include "ps/Filesystem.h" 28 //#include "ps/scripting/JSInterface_VFS.h" 29 #include "scriptinterface/ScriptInterface.h" 30 31 /** 32 * Contains only gamesetup function, no hosting. 33 */ 34 class CDedicatedServer_Gamesetup 35 { 36 NONCOPYABLE(CDedicatedServer_Gamesetup); 37 38 private: 39 friend class NetServer; // TODO: needed ? 40 friend class NetServerWorker; // TODO: needed ? 41 friend class DedicatedServer; // TODO: needed ? 42 43 /** 44 * Used to control the server. 45 */ 46 ScriptInterface* m_ScriptInterface; 47 48 /** 49 * Used to setup the game. 50 */ 51 //u8 m_PlayerCount; 52 53 /** 54 * Cached values for easier access. 55 */ 56 CStrW m_MapType; 57 CStrW m_MapPath; 58 59 public: 60 CDedicatedServer_Gamesetup(ScriptInterface* scriptInterface); 61 ~CDedicatedServer_Gamesetup(); 62 63 public: // TODO: can we make this private? 64 /* 65 * Contains the default gamesetup atributes. Called after CNetServerWorker::Run() initializes the ScriptInterface. 66 */ 67 void Initialize(); 68 69 /** 70 * Changes a property in the gameAttributes object. 71 */ 72 void ChangeAttribute(CStr attribute, JS::HandleValue value); 73 74 /** 75 * Changes a property in the settings object of the gameAttributes object. 76 */ 77 void ChangeSetting(CStr setting, JS::HandleValue value); 78 79 /** 80 * Sets the number of players to the number of connected clients, 81 * assigns unassigned players to unassigned slots, 82 * reduces the population count accordingly, 83 * resets ready states, 84 * sends the player assignments. 85 */ 86 void UpdatePlayerAssignments(); 87 88 /** 89 * Choses whether a random, skirmish or scenario map is going to be played. 90 */ 91 void SetMaptype(CNetServerSession* session, CStrW mapType); 92 93 /** 94 * 95 */ 96 void SetMapFilter(CNetServerSession* session, CStrW mapFilter); 97 98 /** 99 * 100 */ 101 void SelectMap(CNetServerSession* session, CStrW map); 102 103 /** 104 * Choses whether a random, skirmish or scenario map is going to be played. 105 */ 106 void SetMapsize(CNetServerSession* session, int tiles); 107 108 /** 109 * Saves preview image, description, scripts and more and saves it to the game attributes. 110 */ 111 void SelectRandomMap(CStrW mapFilename); 112 113 /** 114 * Saves player assignments, preview image, description, scripts and more and saves it to the game attributes. 115 */ 116 void SelectXMLMap(CStrW mapFilename); 117 118 /* 119 * Shared function. Stores the settings loaded from the mapfile to the game attributes. 120 */ 121 void SaveMapSettings(CStrW mapFilename, JS::HandleValue mapSettings); 122 123 /** 124 * 125 */ 126 void SetNumberOfPlayers(CNetServerSession* session, int players); 127 128 /** 129 * 130 */ 131 void SetGametype(CNetServerSession* session, CStrW gameType); 132 133 /** 134 * 135 */ 136 void SetGamespeed(CNetServerSession* session, float gameSpeed); 137 138 /** 139 * 140 */ 141 void SetPopulationCapacity(CNetServerSession* session, int populationCapacity); 142 143 /** 144 * 145 */ 146 void SetResources(CNetServerSession* session, int resources); 147 148 /** 149 * 150 */ 151 void DisableTreasure(CNetServerSession* session, CStrW allowTreasure); 152 153 /** 154 * 155 */ 156 void SetCeasefire(CNetServerSession* session, int ceasefire); 157 158 /* 159 * Shows some version information and some miscellaneous info. 160 */ 161 void SendWelcomeMessage(CNetServerSession* session); 162 163 /** 164 * Shows a list of available commands to select gamesetup options. 165 */ 166 void SendChatCommandList(CNetServerSession* session); 167 168 /** 169 * Shows the user which maps can be chosen. 170 */ 171 void SendMapList(CNetServerSession* session); 172 173 /** 174 * Allows users to influence the gamesetup via chat. 175 */ 176 void ParsePlayerChatCommand(CNetServerSession* session, CStrW input); 177 178 /** 179 * 180 */ 181 void SetPlayerID(CNetServerSession* session, int playerID); 182 183 /** 184 * 185 */ 186 void SetPlayerCiv(CNetServerSession* session, CStrW civilization); 187 188 /** 189 * 190 */ 191 void SetPlayerTeam(CNetServerSession* session, int team); 192 193 /** 194 * 195 */ 196 void StartGame(); 197 }; 198 199 extern CDedicatedServer_Gamesetup *g_DedicatedServer_Gamesetup; -
source/network/NetServer.cpp
void* CNetServerWorker::RunThread(void* 364 364 365 365 void CNetServerWorker::Run() 366 366 { 367 367 // The script runtime uses the profiler and therefore the thread must be registered before the runtime is created 368 368 g_Profiler2.RegisterCurrentThread("Net server"); 369 369 370 370 // To avoid the need for JS_SetContextThread, we create and use and destroy 371 371 // the script interface entirely within this network thread 372 372 m_ScriptInterface = new ScriptInterface("Engine", "Net server", ScriptInterface::CreateRuntime(g_ScriptRuntime)); 373 373 m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), JS::UndefinedValue()); 374 374 375 if (g_DedicatedServer) 376 g_DedicatedServer_Gamesetup = new CDedicatedServer_Gamesetup(m_ScriptInterface); 377 375 378 while (true) 376 379 { 377 380 if (!RunStep()) 378 381 break; 379 382 383 if (g_DedicatedServer) 384 g_DedicatedServer->OnTick(); 380 385 // Implement autostart mode 381 if (m_State == SERVER_STATE_PREGAME && (int)m_PlayerAssignments.size() == m_AutostartPlayers)386 else if (m_State == SERVER_STATE_PREGAME && (int)m_PlayerAssignments.size() == m_AutostartPlayers) 382 387 StartGame(); 383 388 384 389 // Update profiler stats 385 390 m_Stats->LatchHostState(m_Host); 386 391 } … … bool CNetServerWorker::HandleConnect(CNe 617 622 return session->SendMessage(&handshake); 618 623 } 619 624 620 625 void CNetServerWorker::OnUserJoin(CNetServerSession* session) 621 626 { 627 LOGERROR("CNetServerWorker::OnUserJoin"); 628 if (g_DedicatedServer) 629 g_DedicatedServer->OnUserJoin(session); 630 631 LOGERROR("CNetServerWorker::OnUserJoin AddPlayer"); 622 632 AddPlayer(session->GetGUID(), session->GetUserName()); 623 633 624 634 CGameSetupMessage gameSetupMessage(GetScriptInterface()); 625 635 gameSetupMessage.m_Data = m_GameAttributes.get(); 626 636 session->SendMessage(&gameSetupMessage); 627 637 638 LOGERROR("CNetServerWorker::OnUserJoin PlayerAssignmentMessage"); 628 639 CPlayerAssignmentMessage assignMessage; 629 640 ConstructPlayerAssignmentMessage(assignMessage); 630 641 session->SendMessage(&assignMessage); 631 642 } 632 643 … … void CNetServerWorker::OnUserLeave(CNetS 637 648 if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING) 638 649 m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers 639 650 640 651 // TODO: ought to switch the player controlled by that client 641 652 // back to AI control, or something? 653 654 if (g_DedicatedServer) 655 g_DedicatedServer->OnUserLeave(session); 642 656 } 643 657 644 658 void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name) 645 659 { 646 660 // Find all player IDs in active use; we mustn't give them to a second player (excluding the unassigned ID: -1) … … bool CNetServerWorker::OnAuthenticate(vo 858 872 // Request a copy of the current game state from an existing player, 859 873 // so we can send it on to the new player 860 874 861 875 // Assume session 0 is most likely the local player, so they're 862 876 // the most efficient client to request a copy from 877 878 // TODO: When using a dedicated server we should use the client that didn't reconnect for the longest time 863 879 CNetServerSession* sourceSession = server.m_Sessions.at(0); 864 880 sourceSession->GetFileTransferer().StartTask( 865 881 shared_ptr<CNetFileReceiveTask>(new CNetFileReceiveTask_ServerRejoin(server, newHostID)) 866 882 ); 867 883 868 884 session->SetNextState(NSS_JOIN_SYNCING); 869 885 } 870 886 887 if (g_DedicatedServer) 888 { 889 if (isRejoining) 890 g_DedicatedServer->OnUserRejoining(session); 891 else if (g_DedicatedServer) 892 g_DedicatedServer->OnGamesetupJoin(session); 893 } 894 871 895 return true; 872 896 } 873 897 874 898 bool CNetServerWorker::OnInGame(void* context, CFsmEvent* event) 875 899 { … … bool CNetServerWorker::OnChat(void* cont 935 959 936 960 message->m_GUID = session->GetGUID(); 937 961 938 962 server.Broadcast(message); 939 963 964 if (g_DedicatedServer) 965 g_DedicatedServer->OnChat(session, message); 966 940 967 return true; 941 968 } 942 969 943 970 bool CNetServerWorker::OnReady(void* context, CFsmEvent* event) 944 971 { … … bool CNetServerWorker::OnReady(void* con 951 978 952 979 message->m_GUID = session->GetGUID(); 953 980 954 981 server.Broadcast(message); 955 982 983 if (g_DedicatedServer) 984 g_DedicatedServer->OnReady(session, message); 985 956 986 return true; 957 987 } 958 988 959 989 bool CNetServerWorker::OnLoadedGame(void* context, CFsmEvent* event) 960 990 { … … bool CNetServerWorker::OnRejoined(void* 1034 1064 1035 1065 message->m_GUID = session->GetGUID(); 1036 1066 1037 1067 server.Broadcast(message); 1038 1068 1069 if (g_DedicatedServer) 1070 g_DedicatedServer->OnUserRejoined(session); 1071 1039 1072 return true; 1040 1073 } 1041 1074 1042 1075 bool CNetServerWorker::OnDisconnect(void* context, CFsmEvent* event) 1043 1076 { … … void CNetServerWorker::StartGame() 1087 1120 1088 1121 SendPlayerAssignments(); 1089 1122 1090 1123 CGameStartMessage gameStart; 1091 1124 Broadcast(&gameStart); 1125 1126 if (g_DedicatedServer) 1127 g_DedicatedServer->OnStartGame(); 1092 1128 } 1093 1129 1094 1130 void CNetServerWorker::UpdateGameAttributes(JS::MutableHandleValue attrs) 1095 1131 { 1096 1132 m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), attrs); … … void CNetServerWorker::UpdateGameAttribu 1099 1135 return; 1100 1136 1101 1137 CGameSetupMessage gameSetupMessage(GetScriptInterface()); 1102 1138 gameSetupMessage.m_Data.set(m_GameAttributes.get()); 1103 1139 Broadcast(&gameSetupMessage); 1140 1141 if (g_DedicatedServer) 1142 g_DedicatedServer->OnUpdateGameAttributes(); 1104 1143 } 1105 1144 1106 1145 CStrW CNetServerWorker::SanitisePlayerName(const CStrW& original) 1107 1146 { 1108 1147 const size_t MAX_LENGTH = 32; -
source/network/NetServer.h
16 16 */ 17 17 18 18 #ifndef NETSERVER_H 19 19 #define NETSERVER_H 20 20 21 #include "DedicatedServer.h" 22 #include "DedicatedServer_Gamesetup.h" 21 23 #include "NetFileTransfer.h" 22 24 #include "NetHost.h" 23 25 24 26 #include "lib/config2.h" 25 27 #include "ps/ThreadUtil.h" … … enum NetServerSessionState 89 91 NSS_INGAME 90 92 }; 91 93 92 94 /** 93 95 * Network server interface. Handles all the coordination between players. 94 * One person runs this object, and every player (including the host)connects their CNetClient to it.96 * The host runs this object and every player connects their CNetClient to it. 95 97 * 96 * The actual work is performed by CNetServerWorker in a separate thread. 98 * If it is a dedicated server, the host will not be a player (nor observer). 99 * 100 * The actual work is performed by CNetServerWorker and CDedicatedServer in a separate thread. 97 101 */ 98 102 class CNetServer 99 103 { 100 104 NONCOPYABLE(CNetServer); 101 105 public: … … public: 153 157 * TODO: we should replace this with some adapative lag-dependent computation. 154 158 */ 155 159 void SetTurnLength(u32 msecs); 156 160 157 161 private: 162 friend class CDedicatedServer; // TODO: needed? 163 friend class CDedicatedServer_Gamesetup; 164 158 165 CNetServerWorker* m_Worker; 159 166 }; 160 167 161 168 /** 162 169 * Network server worker thread. … … public: 187 194 * (i.e. are in the pre-game or in-game states). 188 195 */ 189 196 bool Broadcast(const CNetMessage* message); 190 197 191 198 private: 199 friend class CDedicatedServer; // TODO: needed? 200 friend class CDedicatedServer_Gamesetup; 192 201 friend class CNetServer; 193 202 friend class CNetFileReceiveTask_ServerRejoin; 194 203 195 204 CNetServerWorker(int autostartPlayers); 196 205 ~CNetServerWorker(); -
source/network/NetSession.cpp
bool CNetClientSession::Connect(u16 port 76 76 g_ProfileViewer.AddRootTable(m_Stats); 77 77 78 78 return true; 79 79 } 80 80 81 CStr CNetServerSession::GetIPAddress() 82 { 83 char ipAddress[256] = "(error)"; 84 enet_address_get_host_ip(&(m_Peer->address), ipAddress, ARRAY_SIZE(ipAddress)); 85 return CStr(ipAddress); 86 } 87 81 88 void CNetClientSession::Disconnect(u32 reason) 82 89 { 83 90 ENSURE(m_Host && m_Server); 84 91 85 92 // TODO: ought to do reliable async disconnects, probably -
source/network/NetSession.h
public: 121 121 122 122 u32 GetHostID() const { return m_HostID; } 123 123 void SetHostID(u32 id) { m_HostID = id; } 124 124 125 125 /** 126 * Returns the IP address of the client. 127 */ 128 CStr GetIPAddress(); 129 130 /** 126 131 * Sends a disconnection notification to the client, 127 132 * and sends a NMT_CONNECTION_LOST message to the session FSM. 128 133 * The server will receive a disconnection notification after a while. 129 134 * The server will not receive any further messages sent via this session. 130 135 */ -
source/network/NetTurnManager.cpp
void CNetServerTurnManager::NotifyFinish 635 635 { 636 636 if (it->first > newest) 637 637 break; 638 638 639 639 // Assume the host is correct (maybe we should choose the most common instead to help debugging) 640 // TODO: for dedicated hosts, we have to chose the hash of a client that didn't rejoin for the longest period 640 641 std::string expected = it->second.begin()->second; 641 642 642 643 // Find all players that are OOS on that turn 643 644 std::vector<CStrW> OOSPlayerNames; 644 645 for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit) -
source/ps/GameSetup/GameSetup.cpp
void EndGame() 684 684 { 685 685 SAFE_DELETE(g_NetClient); 686 686 SAFE_DELETE(g_NetServer); 687 687 SAFE_DELETE(g_Game); 688 688 689 ISoundManager::CloseGame(); 689 if (!g_args.Has("dedicated-host")) 690 ISoundManager::CloseGame(); 690 691 } 691 692 692 693 693 694 void Shutdown(int flags) 694 695 { … … void Shutdown(int flags) 699 700 700 701 SAFE_DELETE(g_XmppClient); 701 702 702 703 ShutdownPs(); 703 704 704 TIMER_BEGIN(L"shutdown TexMan"); 705 delete &g_TexMan; 706 TIMER_END(L"shutdown TexMan"); 707 708 // destroy renderer 709 TIMER_BEGIN(L"shutdown Renderer"); 710 delete &g_Renderer; 711 g_VBMan.Shutdown(); 712 TIMER_END(L"shutdown Renderer"); 705 if (!g_args.Has("dedicated-host")) 706 { 707 TIMER_BEGIN(L"shutdown TexMan"); 708 delete &g_TexMan; 709 TIMER_END(L"shutdown TexMan"); 710 711 // destroy renderer 712 TIMER_BEGIN(L"shutdown Renderer"); 713 delete &g_Renderer; 714 g_VBMan.Shutdown(); 715 TIMER_END(L"shutdown Renderer"); 716 } 713 717 714 718 g_Profiler2.ShutdownGPU(); 715 719 716 720 #if OS_WIN 717 721 TIMER_BEGIN(L"shutdown wmi"); … … void Shutdown(int flags) 720 724 #endif 721 725 722 726 // Free cursors before shutting down SDL, as they may depend on SDL. 723 727 cursor_shutdown(); 724 728 725 TIMER_BEGIN(L"shutdown SDL"); 726 ShutdownSDL(); 727 TIMER_END(L"shutdown SDL"); 728 729 g_VideoMode.Shutdown(); 730 731 TIMER_BEGIN(L"shutdown UserReporter"); 732 g_UserReporter.Deinitialize(); 733 TIMER_END(L"shutdown UserReporter"); 734 729 if (!g_args.Has("dedicated-host")) 730 { 731 TIMER_BEGIN(L"shutdown SDL"); 732 ShutdownSDL(); 733 TIMER_END(L"shutdown SDL"); 734 735 g_VideoMode.Shutdown(); 736 737 TIMER_BEGIN(L"shutdown UserReporter"); 738 g_UserReporter.Deinitialize(); 739 TIMER_END(L"shutdown UserReporter"); 740 } 735 741 736 742 // JS debugger temporarily disabled during the SpiderMonkey upgrade (check trac ticket #2348 for details) 737 743 //TIMER_BEGIN(L"shutdown DebuggingServer (if active)"); 738 744 //delete g_DebuggingServer; 739 745 //TIMER_END(L"shutdown DebuggingServer (if active)"); … … from_config: 744 750 TIMER_BEGIN(L"shutdown ConfigDB"); 745 751 delete &g_ConfigDB; 746 752 TIMER_END(L"shutdown ConfigDB"); 747 753 748 754 SAFE_DELETE(g_Console); 755 LOGERROR("test1"); 749 756 750 757 // This is needed to ensure that no callbacks from the JSAPI try to use 751 758 // the profiler when it's already destructed 752 g_ScriptRuntime.reset(); 759 //if (!g_args.Has("dedicated-host")) 760 g_ScriptRuntime.reset(); 761 LOGERROR("test2"); 753 762 754 763 // resource 755 764 // first shut down all resource owners, and then the handle manager. 756 765 TIMER_BEGIN(L"resource modules"); 757 766 … … bool Init(const CmdLineArgs& args, int f 933 942 } 934 943 935 944 CNetHost::Initialize(); 936 945 937 946 #if CONFIG2_AUDIO 938 ISoundManager::CreateSoundManager(); 947 if (!args.Has("dedicated-host")) 948 ISoundManager::CreateSoundManager(); 939 949 #endif 940 950 941 951 // Check if there are mods specified on the command line, 942 952 // or if we already set the mods (~INIT_MODS), 943 953 // else check if there are mods that should be loaded specified