Ticket #3556: t3556_dedicated_server_WIP_v0.3.patch

File t3556_dedicated_server_WIP_v0.3.patch, 54.1 KB (added by elexis, 8 years ago)
  • binaries/system/readme.txt

    Basic gameplay:  
    44-autostart=...      load a map instead of showing main menu (see below)
    55-editor             launch the Atlas scenario editor
    66-mod=NAME           start the game using NAME mod
    77-quickstart         load faster (disables audio and some system info logging)
    88
     9Dedicated 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
    914Autostart:
    1015-autostart="TYPEDIR/MAPNAME"    enables autostart and sets MAPNAME; TYPEDIR is skirmishes, scenarios, or random
    1116-autostart-ai=PLAYER:AI         sets the AI for PLAYER (e.g. 2:petra)
    1217-autostart-aidiff=PLAYER:DIFF   sets the DIFFiculty of PLAYER's AI (0: sandbox, 5: very hard)
    1318-autostart-civ=PLAYER:CIV       sets PLAYER's civilisation to CIV (skirmish and random maps only)
  • source/main.cpp

    static void RunGameOrAtlas(int argc, con  
    527527        {
    528528            flags &= ~INIT_MODS;
    529529            Shutdown(SHUTDOWN_FROM_CONFIG);
    530530            continue;
    531531        }
     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
    532542        InitGraphics(args, 0);
     543
    533544        MainControllerInit();
    534545        while (!quit)
    535546            Frame();
    536547        Shutdown(0);
    537548        MainControllerShutdown();
    extern "C" int main(int argc, char* argv  
    579590    RunGameOrAtlas(argc, const_cast<const char**>(argv));
    580591
    581592    // Shut down profiler initialised by EarlyInit
    582593    g_Profiler2.Shutdown();
    583594
     595    // TODO: crash after leaving main with dedicated server!
     596
    584597    return EXIT_SUCCESS;
    585598}
  • 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
     36CDedicatedServer* g_DedicatedServer = nullptr;
     37
     38CDedicatedServer::CDedicatedServer()
     39: m_IsHosting(false), m_Quit(false)
     40{
     41}
     42
     43CDedicatedServer::~CDedicatedServer()
     44{
     45}
     46
     47void 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
     65void CDedicatedServer::Shutdown()
     66{
     67    LOGERROR("[HOST] Shutting down server");
     68    delete g_NetServer;
     69    m_Quit = true;
     70}
     71
     72void CDedicatedServer::Restart()
     73{
     74    Shutdown();
     75    StartHosting();
     76}
     77
     78void 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
     89void 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
     102void 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
     117void CDedicatedServer::SendPublicChat(CStrW text, bool log)
     118{
     119    SendChat(nullptr, text, log);
     120}
     121
     122void CDedicatedServer::StartGame()
     123{
     124    // TODO: return if not everybody is ready
     125    g_DedicatedServer_Gamesetup->StartGame();
     126    g_NetServer->m_Worker->StartGame();
     127}
     128
     129void CDedicatedServer::OnTick()
     130{
     131}
     132
     133void 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
     145void 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
     151void CDedicatedServer::OnUserJoin(CNetServerSession* session)
     152{
     153    LOGERROR("[HOST] Authenticating %s [%s]", utf8_from_wstring(session->GetUserName()).c_str(), session->GetIPAddress().c_str());
     154}
     155
     156void 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
     163void CDedicatedServer::OnUserLeave(CNetServerSession* session)
     164{
     165    LOGERROR("[HOST] %s has left", utf8_from_wstring(session->GetUserName()));
     166    g_DedicatedServer_Gamesetup->UpdatePlayerAssignments();
     167}
     168
     169void CDedicatedServer::OnUpdateGameAttributes()
     170{
     171    LOGERROR("[HOST] Updated game attributes");
     172}
     173
     174void 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
     179void 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
     184void 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 */
     29class CDedicatedServer
     30{
     31    NONCOPYABLE(CDedicatedServer);
     32
     33private:
     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
     53public: // 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
     147extern 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
     26CDedicatedServer_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 */
     33const CStrW g_Version = L"0.0.15";
     34
     35/**
     36 * Date of the last update of this component.
     37 */
     38const CStrW g_LastUpdate = L"9th Nov 2015";
     39
     40const int g_MaxPlayers = 8;
     41
     42const std::set<CStrW> g_MapTypes = { L"random", L"skirmish", L"scenario" };
     43
     44// TODO: move to mapfilters.json and unify with gamesetup.js?
     45const std::set<CStrW> g_MapFilters = { L"default", L"all", L"naval", L"demo" };
     46
     47// TODO: load from game_speeds.json
     48const 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
     51const std::set<int> g_PopulationCapacities = { 50, 100, 150, 200, 250, 300 };
     52
     53// TODO: load from starting_resources.json
     54const std::set<int> g_Resources = { 100, 300, 500, 1000, 3000, 50000 };
     55
     56// TODO: load from map_sizes.json
     57const std::set<int> g_MapSizes = { 128, 192, 256, 320, 384, 448, 512 };
     58
     59CDedicatedServer_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
     68CDedicatedServer_Gamesetup::~CDedicatedServer_Gamesetup()
     69{
     70}
     71
     72void 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
     80void 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
     103void 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
     135void 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
     197void 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
     268void 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
     288void 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
     314void 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
     346void 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
     364void 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
     395void 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
     403void 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
     434void 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
     466void 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
     502void 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
     521void 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
     561void 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
     568void 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
     586void 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
     604void 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
     622void 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
     634void 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
     657void 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
     667void 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
     680void CDedicatedServer_Gamesetup::SetPlayerCiv(CNetServerSession* UNUSED(session), CStrW UNUSED(civilization))
     681{
     682    //g_DedicatedServer->SendPublicChat("");
     683}
     684
     685void 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 */
     34class CDedicatedServer_Gamesetup
     35{
     36    NONCOPYABLE(CDedicatedServer_Gamesetup);
     37
     38private:
     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
     59public:
     60    CDedicatedServer_Gamesetup(ScriptInterface* scriptInterface);
     61    ~CDedicatedServer_Gamesetup();
     62
     63public: // 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
     199extern CDedicatedServer_Gamesetup *g_DedicatedServer_Gamesetup;
  • source/network/NetServer.cpp

    void* CNetServerWorker::RunThread(void*  
    364364
    365365void CNetServerWorker::Run()
    366366{
    367367    // The script runtime uses the profiler and therefore the thread must be registered before the runtime is created
    368368    g_Profiler2.RegisterCurrentThread("Net server");
    369    
     369
    370370    // To avoid the need for JS_SetContextThread, we create and use and destroy
    371371    // the script interface entirely within this network thread
    372372    m_ScriptInterface = new ScriptInterface("Engine", "Net server", ScriptInterface::CreateRuntime(g_ScriptRuntime));
    373373    m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), JS::UndefinedValue());
    374374
     375    if (g_DedicatedServer)
     376        g_DedicatedServer_Gamesetup = new CDedicatedServer_Gamesetup(m_ScriptInterface);
     377
    375378    while (true)
    376379    {
    377380        if (!RunStep())
    378381            break;
    379382
     383        if (g_DedicatedServer)
     384            g_DedicatedServer->OnTick();
    380385        // 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)
    382387            StartGame();
    383388
    384389        // Update profiler stats
    385390        m_Stats->LatchHostState(m_Host);
    386391    }
    bool CNetServerWorker::HandleConnect(CNe  
    617622    return session->SendMessage(&handshake);
    618623}
    619624
    620625void CNetServerWorker::OnUserJoin(CNetServerSession* session)
    621626{
     627    LOGERROR("CNetServerWorker::OnUserJoin");
     628    if (g_DedicatedServer)
     629        g_DedicatedServer->OnUserJoin(session);
     630
     631    LOGERROR("CNetServerWorker::OnUserJoin AddPlayer");
    622632    AddPlayer(session->GetGUID(), session->GetUserName());
    623633
    624634    CGameSetupMessage gameSetupMessage(GetScriptInterface());
    625635    gameSetupMessage.m_Data = m_GameAttributes.get();
    626636    session->SendMessage(&gameSetupMessage);
    627637
     638    LOGERROR("CNetServerWorker::OnUserJoin PlayerAssignmentMessage");
    628639    CPlayerAssignmentMessage assignMessage;
    629640    ConstructPlayerAssignmentMessage(assignMessage);
    630641    session->SendMessage(&assignMessage);
    631642}
    632643
    void CNetServerWorker::OnUserLeave(CNetS  
    637648    if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING)
    638649        m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers
    639650
    640651    // TODO: ought to switch the player controlled by that client
    641652    // back to AI control, or something?
     653
     654    if (g_DedicatedServer)
     655        g_DedicatedServer->OnUserLeave(session);
    642656}
    643657
    644658void CNetServerWorker::AddPlayer(const CStr& guid, const CStrW& name)
    645659{
    646660    // 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  
    858872        // Request a copy of the current game state from an existing player,
    859873        // so we can send it on to the new player
    860874
    861875        // Assume session 0 is most likely the local player, so they're
    862876        // 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
    863879        CNetServerSession* sourceSession = server.m_Sessions.at(0);
    864880        sourceSession->GetFileTransferer().StartTask(
    865881            shared_ptr<CNetFileReceiveTask>(new CNetFileReceiveTask_ServerRejoin(server, newHostID))
    866882        );
    867883
    868884        session->SetNextState(NSS_JOIN_SYNCING);
    869885    }
    870886
     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
    871895    return true;
    872896}
    873897
    874898bool CNetServerWorker::OnInGame(void* context, CFsmEvent* event)
    875899{
    bool CNetServerWorker::OnChat(void* cont  
    935959
    936960    message->m_GUID = session->GetGUID();
    937961
    938962    server.Broadcast(message);
    939963
     964    if (g_DedicatedServer)
     965        g_DedicatedServer->OnChat(session, message);
     966
    940967    return true;
    941968}
    942969
    943970bool CNetServerWorker::OnReady(void* context, CFsmEvent* event)
    944971{
    bool CNetServerWorker::OnReady(void* con  
    951978
    952979    message->m_GUID = session->GetGUID();
    953980
    954981    server.Broadcast(message);
    955982
     983    if (g_DedicatedServer)
     984        g_DedicatedServer->OnReady(session, message);
     985
    956986    return true;
    957987}
    958988
    959989bool CNetServerWorker::OnLoadedGame(void* context, CFsmEvent* event)
    960990{
    bool CNetServerWorker::OnRejoined(void*  
    10341064
    10351065    message->m_GUID = session->GetGUID();
    10361066
    10371067    server.Broadcast(message);
    10381068
     1069    if (g_DedicatedServer)
     1070        g_DedicatedServer->OnUserRejoined(session);
     1071
    10391072    return true;
    10401073}
    10411074
    10421075bool CNetServerWorker::OnDisconnect(void* context, CFsmEvent* event)
    10431076{
    void CNetServerWorker::StartGame()  
    10871120
    10881121    SendPlayerAssignments();
    10891122
    10901123    CGameStartMessage gameStart;
    10911124    Broadcast(&gameStart);
     1125
     1126    if (g_DedicatedServer)
     1127        g_DedicatedServer->OnStartGame();
    10921128}
    10931129
    10941130void CNetServerWorker::UpdateGameAttributes(JS::MutableHandleValue attrs)
    10951131{
    10961132    m_GameAttributes.set(m_ScriptInterface->GetJSRuntime(), attrs);
    void CNetServerWorker::UpdateGameAttribu  
    10991135        return;
    11001136
    11011137    CGameSetupMessage gameSetupMessage(GetScriptInterface());
    11021138    gameSetupMessage.m_Data.set(m_GameAttributes.get());
    11031139    Broadcast(&gameSetupMessage);
     1140
     1141    if (g_DedicatedServer)
     1142        g_DedicatedServer->OnUpdateGameAttributes();
    11041143}
    11051144
    11061145CStrW CNetServerWorker::SanitisePlayerName(const CStrW& original)
    11071146{
    11081147    const size_t MAX_LENGTH = 32;
  • source/network/NetServer.h

     
    1616 */
    1717
    1818#ifndef NETSERVER_H
    1919#define NETSERVER_H
    2020
     21#include "DedicatedServer.h"
     22#include "DedicatedServer_Gamesetup.h"
    2123#include "NetFileTransfer.h"
    2224#include "NetHost.h"
    2325
    2426#include "lib/config2.h"
    2527#include "ps/ThreadUtil.h"
    enum NetServerSessionState  
    8991    NSS_INGAME
    9092};
    9193
    9294/**
    9395 * 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.
    9597 *
    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.
    97101 */
    98102class CNetServer
    99103{
    100104    NONCOPYABLE(CNetServer);
    101105public:
    public:  
    153157     * TODO: we should replace this with some adapative lag-dependent computation.
    154158     */
    155159    void SetTurnLength(u32 msecs);
    156160
    157161private:
     162    friend class CDedicatedServer; // TODO: needed?
     163    friend class CDedicatedServer_Gamesetup;
     164
    158165    CNetServerWorker* m_Worker;
    159166};
    160167
    161168/**
    162169 * Network server worker thread.
    public:  
    187194     * (i.e. are in the pre-game or in-game states).
    188195     */
    189196    bool Broadcast(const CNetMessage* message);
    190197
    191198private:
     199    friend class CDedicatedServer; // TODO: needed?
     200    friend class CDedicatedServer_Gamesetup;
    192201    friend class CNetServer;
    193202    friend class CNetFileReceiveTask_ServerRejoin;
    194203
    195204    CNetServerWorker(int autostartPlayers);
    196205    ~CNetServerWorker();
  • source/network/NetSession.cpp

    bool CNetClientSession::Connect(u16 port  
    7676        g_ProfileViewer.AddRootTable(m_Stats);
    7777
    7878    return true;
    7979}
    8080
     81CStr 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
    8188void CNetClientSession::Disconnect(u32 reason)
    8289{
    8390    ENSURE(m_Host && m_Server);
    8491
    8592    // TODO: ought to do reliable async disconnects, probably
  • source/network/NetSession.h

    public:  
    121121
    122122    u32 GetHostID() const { return m_HostID; }
    123123    void SetHostID(u32 id) { m_HostID = id; }
    124124
    125125    /**
     126     * Returns the IP address of the client.
     127     */
     128    CStr GetIPAddress();
     129
     130    /**
    126131     * Sends a disconnection notification to the client,
    127132     * and sends a NMT_CONNECTION_LOST message to the session FSM.
    128133     * The server will receive a disconnection notification after a while.
    129134     * The server will not receive any further messages sent via this session.
    130135     */
  • source/network/NetTurnManager.cpp

    void CNetServerTurnManager::NotifyFinish  
    635635    {
    636636        if (it->first > newest)
    637637            break;
    638638
    639639        // 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
    640641        std::string expected = it->second.begin()->second;
    641642
    642643        // Find all players that are OOS on that turn
    643644        std::vector<CStrW> OOSPlayerNames;
    644645        for (std::map<int, std::string>::iterator cit = it->second.begin(); cit != it->second.end(); ++cit)
  • source/ps/GameSetup/GameSetup.cpp

    void EndGame()  
    684684{
    685685    SAFE_DELETE(g_NetClient);
    686686    SAFE_DELETE(g_NetServer);
    687687    SAFE_DELETE(g_Game);
    688688
    689     ISoundManager::CloseGame();
     689    if (!g_args.Has("dedicated-host"))
     690        ISoundManager::CloseGame();
    690691}
    691692
    692693
    693694void Shutdown(int flags)
    694695{
    void Shutdown(int flags)  
    699700
    700701    SAFE_DELETE(g_XmppClient);
    701702
    702703    ShutdownPs();
    703704
    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    }
    713717
    714718    g_Profiler2.ShutdownGPU();
    715719
    716720    #if OS_WIN
    717721        TIMER_BEGIN(L"shutdown wmi");
    void Shutdown(int flags)  
    720724    #endif
    721725
    722726    // Free cursors before shutting down SDL, as they may depend on SDL.
    723727    cursor_shutdown();
    724728
    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    }
    735741
    736742    // JS debugger temporarily disabled during the SpiderMonkey upgrade (check trac ticket #2348 for details)
    737743    //TIMER_BEGIN(L"shutdown DebuggingServer (if active)");
    738744    //delete g_DebuggingServer;
    739745    //TIMER_END(L"shutdown DebuggingServer (if active)");
    from_config:  
    744750    TIMER_BEGIN(L"shutdown ConfigDB");
    745751    delete &g_ConfigDB;
    746752    TIMER_END(L"shutdown ConfigDB");
    747753
    748754    SAFE_DELETE(g_Console);
     755    LOGERROR("test1");
    749756
    750757    // This is needed to ensure that no callbacks from the JSAPI try to use
    751758    // 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");
    753762
    754763    // resource
    755764    // first shut down all resource owners, and then the handle manager.
    756765    TIMER_BEGIN(L"resource modules");
    757766
    bool Init(const CmdLineArgs& args, int f  
    933942    }
    934943
    935944    CNetHost::Initialize();
    936945
    937946#if CONFIG2_AUDIO
    938     ISoundManager::CreateSoundManager();
     947    if (!args.Has("dedicated-host"))
     948        ISoundManager::CreateSoundManager();
    939949#endif
    940950
    941951    // Check if there are mods specified on the command line,
    942952    // or if we already set the mods (~INIT_MODS),
    943953    // else check if there are mods that should be loaded specified