File: ServerApp.h

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (366 lines) | stat: -rw-r--r-- 17,910 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
#ifndef _ServerApp_h_
#define _ServerApp_h_

#include <set>
#include <vector>
#include "../util/boost_fix.h"
#include <boost/circular_buffer.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#include "ServerFramework.h"
#include "ServerNetworking.h"
#include "../Empire/EmpireManager.h"
#include "../Empire/Supply.h"
#include "../universe/Species.h"
#include "../universe/Universe.h"
#include "../util/AppInterface.h"
#include "../util/MultiplayerCommon.h"
#include "../util/Process.h"


class OrderSet;
struct GalaxySetupData;
struct SaveGameUIData;
struct ServerFSM;

/** the application framework class for the FreeOrion server. */
class ServerApp final : public IApp {
public:
    ServerApp();
    ServerApp(const ServerApp&) = delete;
    ServerApp(ServerApp&&) = delete;
    ~ServerApp() override;

    const ServerApp& operator=(const ServerApp&) = delete;
    ServerApp& operator=(IApp&&) = delete;

    /** Returns a ClientApp pointer to the singleton instance of the app. */
    [[nodiscard]] static ServerApp* GetApp() noexcept { return static_cast<ServerApp*>(s_app); }
    [[nodiscard]] Universe& GetUniverse() noexcept override { return m_universe; }
    [[nodiscard]] EmpireManager& Empires() noexcept override { return m_empires; }
    [[nodiscard]] Empire* GetEmpire(int id) override;
    [[nodiscard]] SupplyManager& GetSupplyManager() noexcept override { return m_supply_manager; }
    [[nodiscard]] SpeciesManager& GetSpeciesManager() noexcept override { return m_species_manager; }

    [[nodiscard]] std::string GetVisibleObjectName(const UniverseObject& object) override;

    [[nodiscard]] int EmpireID() const noexcept override { return ALL_EMPIRES; }
    [[nodiscard]] int CurrentTurn() const noexcept override { return m_current_turn; }

    [[nodiscard]] int SelectedSystemID() const override { throw std::runtime_error{"Server cannot access selected object ID"}; }
    [[nodiscard]] int SelectedPlanetID() const override { throw std::runtime_error{"Server cannot access selected object ID"}; }
    [[nodiscard]] int SelectedFleetID() const override { throw std::runtime_error{"Server cannot access selected object ID"}; }
    [[nodiscard]] int SelectedShipID() const override { throw std::runtime_error{"Server cannot access selected object ID"}; }

    [[nodiscard]] const GalaxySetupData& GetGalaxySetupData() const noexcept override { return m_galaxy_setup_data; }

    /** Checks if player with ID \a player_id is a human player
        who's client runs on the same machine as the server */
    [[nodiscard]] bool IsLocalHumanPlayer(int player_id);

    /** Returns the networking client type for the given empire_id. */
    [[nodiscard]] Networking::ClientType GetEmpireClientType(int empire_id) const override;

    /** Returns the networking client type for the given player_id. */
    [[nodiscard]] Networking::ClientType GetPlayerClientType(int player_id) const override;

    [[nodiscard]] int EffectsProcessingThreads() const override;

    /** Returns the empire ID for the player with ID \a player_id */
    [[nodiscard]] int PlayerEmpireID(int player_id) const;

    /** Returns the player ID for the player controlling the empire with id \a
        empire_id */
    [[nodiscard]] int EmpirePlayerID(int empire_id) const;

    /** Checks if \a player_name are not used by other players. */
    [[nodiscard]] bool IsAvailableName(const std::string& player_name) const;

    /** Checks if server runs in a hostless mode. */
    [[nodiscard]] bool IsHostless() const;

    /** Returns chat history buffer. */
    [[nodiscard]] const boost::circular_buffer<ChatHistoryEntity>& GetChatHistory() const;

    /** Extracts player save game data. */
    [[nodiscard]] std::vector<PlayerSaveGameData> GetPlayerSaveGameData() const;

    [[nodiscard]] bool IsTurnExpired() const noexcept { return m_turn_expired; }

    [[nodiscard]] bool IsHaveWinner() const;

    void operator()(); ///< external interface to Run()

    void StartBackgroundParsing(const PythonParser& python, std::promise<void>&& barrier) override;

    /** Returns the galaxy setup data used for the current game */
    [[nodiscard]] GalaxySetupData&    GetGalaxySetupData() { return m_galaxy_setup_data; }

    /** creates an AI client child process for each element of \a AIs*/
    void CreateAIClients(const std::vector<PlayerSetupData>& player_setup_data, int max_aggression = 4);

    /** Adds save game data includes turn orders for the given empire for the current turn.
      * \a save_game_data will be freed when all processing is done for the turn */
    void SetEmpireSaveGameData(int empire_id, std::unique_ptr<PlayerSaveGameData>&& save_game_data);

    /** Updated empire orders without changes in readiness status. Removes all \a deleted orders
      * and insert \a added orders. */
    void UpdatePartialOrders(int empire_id, const OrderSet& added, const std::set<int>& deleted);

    /** Revokes turn order's ready state for the given empire. */
    void RevokeEmpireTurnReadyness(int empire_id);

    /** Sets all empire turn orders to an empty set. */
    void ClearEmpireTurnOrders(int empire_id = ALL_EMPIRES);

    /** Determines if all empired have submitted their orders for this turn It
      * will loop the turn squence vector and check for a set order_set. A
      * order_set of 0 indicates that the empire has not yet submitted their
      * orders for the given turn */
    bool AllOrdersReceived();

    /** Executes player orders, does colonization, does ordered scrapping, does
      * fleet movements, and updates visibility before combats are handled. */
    void PreCombatProcessTurns();

    /** Determines which combats will occur, handles running the combats and
      * updating the universe after the results are available. */
    void ProcessCombats();

    /** Used post combat, to selectively clear the m_arrival_starlane flag of monsters
     *  so that they can impose blockades */
    void UpdateMonsterTravelRestrictions();

    /** Determines resource and supply distribution pathes and connections,
      * updates research, production, influence spending,
      * does population growth, updates current turn number, checks for
      * eliminated or victorious empires / players, sends new turn updates. */
    void PostCombatProcessTurns();

    /** Determines if any empires are eliminated (for the first time this turn,
      * skipping any which were also eliminated previously) and if any empires
      * are thereby victorious. */
    void CheckForEmpireElimination();

    /** Intializes single player game universe.*/
    void NewSPGameInit(const SinglePlayerSetupData& single_player_setup_data);

    /** Return true if single player game AIs are compatible with created
      * universe and are ready to start a new game. */
    bool VerifySPGameAIs(const SinglePlayerSetupData& single_player_setup_data);

    /** Intializes multi player game universe, sends out initial game state to
      * clients, and signals clients to start first turn */
    void NewMPGameInit(const MultiplayerLobbyData& multiplayer_lobby_data);

    /** Restores saved single player gamestate and human and AI client state
      * information. */
    void LoadSPGameInit(const std::vector<PlayerSaveGameData>& player_save_game_data,
                        std::shared_ptr<ServerSaveGameData> server_save_game_data);

    /** Restores saved multiplayer gamestate and human and AI client state
      * information. */
    void LoadMPGameInit(const MultiplayerLobbyData& lobby_data,
                        const std::vector<PlayerSaveGameData>& player_save_game_data,
                        std::shared_ptr<ServerSaveGameData> server_save_game_data);

    /** Checks if \a player_name requires auth to login and fill \a roles if not. */
    [[nodiscard]] bool IsAuthRequiredOrFillRoles(const std::string& player_name,
                                                 const std::string& ip_address,
                                                 Networking::AuthRoles& roles);

    /** Checks if \a auth match \a player_name and fill \a roles if successed. */
    [[nodiscard]] bool IsAuthSuccessAndFillRoles(const std::string& player_name,
                                                 const std::string& auth,
                                                 Networking::AuthRoles& roles);

    /** Returns list of players for multiplayer quickstart*/
    [[nodiscard]] std::vector<PlayerSetupData> FillListPlayers();

    /** Adds new observing player to running game.
      * Simply sends GAME_START message so established player knows he is in the game. */
    void AddObserverPlayerIntoGame(const PlayerConnectionPtr& player_connection);

    /** Eliminate player's empire by \a player_connection. Return true if player was eliminated. */
    [[nodiscard]] bool EliminatePlayer(const PlayerConnectionPtr& player_connection);

    /** Drop link between player with \a player_id and his empire. */
    void DropPlayerEmpireLink(int planet_id);

    /** Adds new player to running game.
      * Search empire by player's name or delegation list if \a target_empire_id set and return
      * empire id if success and ALL_EMPIRES if no empire found.
      * Simply sends GAME_START message so established player knows he is in the game.
      * Notificates the player about statuses of other empires. */
    int AddPlayerIntoGame(const PlayerConnectionPtr& player_connection, int target_empire_id);

    /** Get list of players delegated by \a player_name */
    [[nodiscard]] std::vector<std::string> GetPlayerDelegation(const std::string& player_name);

    /** Sets turn to be expired. Server doesn't wait for human player turns. */
    void ExpireTurn();

    void UpdateSavePreviews(const Message& msg, PlayerConnectionPtr player_connection);

    /** Send the requested combat logs to the client.*/
    void UpdateCombatLogs(const Message& msg, PlayerConnectionPtr player_connection);

    /** Loads chat history via python script. */
    void LoadChatHistory();

    void PushChatMessage(std::string text, std::string player_name, std::array<uint8_t, 4> text_color,
                         boost::posix_time::ptime timestamp);

    [[nodiscard]] ServerNetworking& Networking() noexcept { return m_networking; };

private:
    void Run();          ///< initializes app state, then executes main event handler/render loop (Poll())

    /** Initialize the python engine if not already running.*/
    void InitializePython();

    /** Called when server process receive termination signal */
    void SignalHandler(const boost::system::error_code& error, int signal_number);


    /** Clears any old game stored orders, victors or eliminated players, ads
      * empires to turn processing list, does start-of-turn empire supply and
      * resource pool determination. */
    void NewGameInitConcurrentWithJoiners(const GalaxySetupData& galaxy_setup_data,
                                          const std::vector<PlayerSetupData>& player_setup_data);

    /** Return true if player data is consistent with starting a new game. */
    bool NewGameInitVerifyJoiners(const std::vector<PlayerSetupData>& player_setup_data);

    /** Sends out initial new game state to clients, and signals clients to start first turn. */
    void SendNewGameStartMessages();

    /** Clears any old game stored orders, victors or eliminated players, ads
      * empires to turn processing list, does start-of-turn empire supply and
      * resource pool determination, regenerates pathfinding graphc, restores
      * any UI or AI state information players had saved, and compiles info
      * about all players to send out to all other players are part of game
      * start messages. */
    void LoadGameInit(const std::vector<PlayerSaveGameData>& player_save_game_data,
                      const std::vector<std::pair<int, int>>& player_id_to_save_game_data_index,
                      std::shared_ptr<ServerSaveGameData> server_save_game_data);

    /** Calls Python universe generator script.
      * Supposed to be called to create a new universe so it can be used by content
      * scripters to customize universe generation. */
    void GenerateUniverse(std::map<int, PlayerSetupData>& player_setup_data);

    /** Calls Python turn events script.
      * Supposed to be called every turn so it can be used by content scripters to
      * implement user customizable turn events. */
    void ExecuteScriptedTurnEvents();

    void CleanupAIs();   ///< cleans up AI processes: kills the process and empties the container of AI processes

    /** Sets the priority for all AI processes */
    void  SetAIsProcessPriorityToLow(bool set_to_low);

    /** Get players info map to send it in GameStart message */
    std::map<int, PlayerInfo> GetPlayerInfoMap() const;

    /** Handles an incoming message from the server with the appropriate action
      * or response */
    void HandleMessage(const Message& msg, PlayerConnectionPtr player_connection);

    /** Checks validity of shut down message from player, then attempts to
      * cleanly shut down this server process. */
    void HandleShutdownMessage(const Message& msg, PlayerConnectionPtr player_connection);

    /** Checks validity of logger config message and then update logger and loggers of all AIs. */
    void HandleLoggerConfig(const Message& msg, PlayerConnectionPtr player_connection);

    /** When Messages arrive from connections that are not established players,
      * they arrive via a call to this function*/
    void HandleNonPlayerMessage(const Message& msg, PlayerConnectionPtr player_connection);

    /** Called by ServerNetworking when a player's TCP connection is closed*/
    void PlayerDisconnected(PlayerConnectionPtr player_connection);

    /** Handle shutdown timeout by killing all ais. */
    void ShutdownTimedoutHandler(boost::system::error_code error);

    /** Called when the host player has disconnected.  Select a new host player*/
    void SelectNewHost();

    /** Called when this server's EmpireManager changes the diplomatic status
      * between two empires. Updates those empires of the change. */
    void HandleDiplomaticStatusChange(int empire1_id, int empire2_id);

    /** Called when this sever's EmpireManager changes the diplmatic message
      * between two empires. Updates those empires of the change. */
    void HandleDiplomaticMessageChange(int empire1_id, int empire2_id);

    /**  Adds an existing empire to turn processing. The position the empire is
      * in the vector is it's position in the turn processing.*/
    void AddEmpireTurn(int empire_id, const PlayerSaveGameData& psgd);

    /** Removes an empire from turn processing. This is most likely called when
      * an empire is eliminated from the game */
    void RemoveEmpireTurn(int empire_id);

    /** Called when asyncio timer ends. Executes Python asyncio callbacks if any was generated. */
    void AsyncIOTimedoutHandler(const boost::system::error_code& error);

    /** Called when new \a turn state received by player playing \a empire_id. */
    void UpdateEmpireTurnReceived(bool success, int empire_id, int turn);

    boost::asio::io_context m_io_context;
    boost::asio::signal_set m_signals;
    boost::asio::high_resolution_timer m_timer;

    Universe         m_universe;
    EmpireManager    m_empires;
    SpeciesManager   m_species_manager;
    SupplyManager    m_supply_manager;
    ServerNetworking m_networking;

    std::unique_ptr<ServerFSM> m_fsm;

    PythonServer            m_python_server;
    std::map<int, int>      m_player_empire_ids;                ///< map from player id to empire id that the player controls.
    int                     m_current_turn = INVALID_GAME_TURN; ///< current turn number
    bool                    m_turn_expired = false;             ///< true when turn exceeds its timeout
    std::map<std::string, Process> m_ai_client_processes;       ///< AI client child processes indexed by player name
    bool                    m_single_player_game = false;       ///< true when the game being played is single-player
    GalaxySetupData         m_galaxy_setup_data;                ///< stored setup data for the game currently being played
    boost::circular_buffer<ChatHistoryEntity> m_chat_history;   ///< Stored last chat messages.


    /** Turn sequence map is used for turn processing. Each empire is added at
      * the start of a game or reload and then the map maintains OrderSets for
      * that turn.
      * The map contains pointer to orders from empire with ready state which should be true
      * to advance turn.
      * */
    std::map<int, std::unique_ptr<PlayerSaveGameData>> m_turn_sequence;

    // storage for cached costs between pre- and post-combat update steps
    void CacheCostsTimes(const ScriptingContext& context);
    std::map<int, std::vector<std::pair<std::string_view, double>>> m_cached_empire_policy_adoption_costs;
    std::map<int, std::vector<std::tuple<std::string_view, double, int>>> m_cached_empire_research_costs_times;
    std::map<int, std::vector<std::tuple<std::string_view, int, float, int>>> m_cached_empire_production_costs_times;
    std::map<int, std::vector<std::pair<int, double>>> m_cached_empire_annexation_costs;

    std::map<int, std::vector<int>> m_empire_fleet_combat_initiation_vis_overrides;

    // Give FSM and its states direct access.  We are using the FSM code as a
    // control-flow mechanism; it is all notionally part of this class.
    friend struct ServerFSM;
    friend struct Idle;
    friend struct MPLobby;
    friend struct WaitingForSPGameJoiners;
    friend struct WaitingForMPGameJoiners;
    friend struct PlayingGame;
    friend struct WaitingForTurnEnd;
    friend struct WaitingForTurnEndIdle;
    friend struct WaitingForSaveData;
    friend struct ProcessingTurn;
    friend struct ShuttingDownServer;
};


#endif