1 /* Copyright (C) 2017 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 #ifndef INCLUDED_TURNMANAGER 19 #define INCLUDED_TURNMANAGER 20 21 #include "simulation2/helpers/SimulationCommand.h" 22 23 #include <list> 24 #include <map> 25 #include <vector> 26 27 extern const u32 DEFAULT_TURN_LENGTH_SP; 28 extern const u32 DEFAULT_TURN_LENGTH_MP; 29 30 extern const int COMMAND_DELAY; 31 32 class CSimulationMessage; 33 class CSimulation2; 34 class IReplayLogger; 35 36 /** 37 * This file defines the base class of the turn managers for clients, local games and replays. 38 * The basic idea of our turn managing system across a network is as in this article: 39 * http://www.gamasutra.com/view/feature/3094/1500_archers_on_a_288_network_.php?print=1 40 * 41 * Each player performs the simulation for turn N. 42 * User input is translated into commands scheduled for execution in turn N+2 which are 43 * distributed to all other clients. 44 * After a while, a client wants to perform the simulation for turn N+1, 45 * which first requires that it has all the other clients' commands for turn N+1. 46 * In that case, it does the simulation and tells all the other clients (via the server) 47 * it has finished sending commands for turn N+2, and it starts sending commands for turn N+3. 48 * 49 * Commands are redistributed immediately by the server. 50 * To ensure a consistent execution of commands, they are each associated with a 51 * client session ID (which is globally unique and consistent), which is used to sort them. 52 */ 53 54 /** 55 * Common turn system (used by clients and offline games). 56 */ 57 class CTurnManager 58 { 59 NONCOPYABLE(CTurnManager); 60 public: 61 /** 62 * Construct for a given network session ID. 63 */ 64 CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay); 65 ~CTurnManager()66 virtual ~CTurnManager() { } 67 68 void ResetState(u32 newCurrentTurn, u32 newReadyTurn); 69 70 /** 71 * Set the current user's player ID, which will be added into command messages. 72 */ 73 void SetPlayerID(int playerId); 74 75 /** 76 * Advance the simulation by a certain time. If this brings us past the current 77 * turn length, the next turns are processed and the function returns true. 78 * Otherwise, nothing happens and it returns false. 79 * 80 * @param simFrameLength Length of the previous frame, in simulation seconds 81 * @param maxTurns Maximum number of turns to simulate at once 82 */ 83 bool Update(float simFrameLength, size_t maxTurns); 84 85 /** 86 * Advance the simulation by as much as possible. Intended for catching up 87 * over a small number of turns when rejoining a multiplayer match. 88 * Returns true if it advanced by at least one turn. 89 */ 90 bool UpdateFastForward(); 91 92 /** 93 * Returns whether Update(simFrameLength, ...) will process at least one new turn. 94 * @param simFrameLength Length of the previous frame, in simulation seconds 95 */ 96 bool WillUpdate(float simFrameLength) const; 97 98 /** 99 * Advance the graphics by a certain time. 100 * @param simFrameLength Length of the previous frame, in simulation seconds 101 * @param realFrameLength Length of the previous frame, in real time seconds 102 */ 103 void Interpolate(float simFrameLength, float realFrameLength); 104 105 /** 106 * Called by networking code when a simulation message is received. 107 */ 108 virtual void OnSimulationMessage(CSimulationMessage* msg) = 0; 109 110 /** 111 * Called by simulation code, to add a new command to be distributed to all clients and executed soon. 112 */ 113 virtual void PostCommand(JS::HandleValue data) = 0; 114 115 /** 116 * Called when all commands for a given turn have been received. 117 * This allows Update to progress to that turn. 118 */ 119 void FinishedAllCommands(u32 turn, u32 turnLength); 120 121 /** 122 * Enables the recording of state snapshots every @p numTurns, 123 * which can be jumped back to via RewindTimeWarp(). 124 * If @p numTurns is 0 then recording is disabled. 125 */ 126 void EnableTimeWarpRecording(size_t numTurns); 127 128 /** 129 * Jumps back to the latest recorded state snapshot (if any). 130 */ 131 void RewindTimeWarp(); 132 133 void QuickSave(); 134 void QuickLoad(); 135 GetCurrentTurn()136 u32 GetCurrentTurn() { return m_CurrentTurn; } 137 138 protected: 139 /** 140 * Store a command to be executed at a given turn. 141 */ 142 void AddCommand(int client, int player, JS::HandleValue data, u32 turn); 143 144 /** 145 * Called when this client has finished sending all its commands scheduled for the given turn. 146 */ 147 virtual void NotifyFinishedOwnCommands(u32 turn) = 0; 148 149 /** 150 * Called when this client has finished a simulation update. 151 */ 152 virtual void NotifyFinishedUpdate(u32 turn) = 0; 153 154 /** 155 * Returns whether we should compute a complete state hash for the given turn, 156 * instead of a quick less-complete hash. 157 */ 158 bool TurnNeedsFullHash(u32 turn) const; 159 160 CSimulation2& m_Simulation2; 161 162 /// The turn that we have most recently executed 163 u32 m_CurrentTurn; 164 165 /// The latest turn for which we have received all commands from all clients 166 u32 m_ReadyTurn; 167 168 // Current turn length 169 u32 m_TurnLength; 170 171 /// Commands queued at each turn (index 0 is for m_CurrentTurn+1) 172 std::deque<std::map<u32, std::vector<SimulationCommand>>> m_QueuedCommands; 173 174 int m_PlayerId; 175 uint m_ClientId; 176 177 /// Simulation time remaining until we ought to execute the next turn (as a negative value to 178 /// add elapsed time increments to until we reach 0). 179 float m_DeltaSimTime; 180 181 bool m_HasSyncError; 182 183 IReplayLogger& m_Replay; 184 185 // The number of the last turn that is allowed to be executed (used for replays) 186 u32 m_FinalTurn; 187 188 private: 189 size_t m_TimeWarpNumTurns; // 0 if disabled 190 std::list<std::string> m_TimeWarpStates; 191 std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system 192 std::string m_QuickSaveMetadata; 193 }; 194 195 #endif // INCLUDED_TURNMANAGER 196