File: GameServer.h

package info (click to toggle)
spring 106.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 55,260 kB
  • sloc: cpp: 543,946; ansic: 44,800; python: 12,575; java: 12,201; awk: 5,889; sh: 1,796; asm: 1,546; xml: 655; perl: 405; php: 211; objc: 194; makefile: 76; sed: 2
file content (310 lines) | stat: -rw-r--r-- 9,132 bytes parent folder | download | duplicates (3)
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
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */

#ifndef _GAME_SERVER_H
#define _GAME_SERVER_H

// #include <asio/ip/udp.hpp>

#include <atomic>
#include <memory>
#include <string>
#include <array>
#include <deque>
#include <map>
#include <set>
#include <vector>

#include "Game/GameData.h"
#include "Sim/Misc/GlobalConstants.h"
#include "Sim/Misc/TeamBase.h"
#include "System/float3.h"
#include "System/GlobalRNG.h"
#include "System/Misc/SpringTime.h"
#include "System/Threading/SpringThreading.h"

/**
 * "player" number for GameServer-generated messages
 */
#define SERVER_PLAYER 255

namespace netcode
{
	class RawPacket;
	class CConnection;
	class UDPListener;
}
class CDemoReader;
class Action;
class CDemoRecorder;
class AutohostInterface;
class ClientSetup;
class CGameSetup;
class ChatMessage;
class GameParticipant;
class GameSkirmishAI;

class GameTeam : public TeamBase
{
public:
	GameTeam() : active(false) {}
	GameTeam& operator=(const TeamBase& base) { TeamBase::operator=(base); return *this; }

	void SetActive(bool b) { active = b; }
	bool IsActive() const { return active; }

private:
	bool active;
};

/**
 * @brief Server class for game handling
 * This class represents a gameserver. It is responsible for recieving,
 * checking and forwarding gamedata to the clients. It keeps track of the sync,
 * cpu and other stats and informs all clients about events.
 */
class CGameServer
{
	friend class CCregLoadSaveHandler; // For initializing server state after load
public:
	CGameServer(
		const std::shared_ptr<const ClientSetup> newClientSetup,
		const std::shared_ptr<const    GameData> newGameData,
		const std::shared_ptr<const  CGameSetup> newGameSetup
	);

	CGameServer(const CGameServer&) = delete; // no-copy
	~CGameServer();

	static void Reload(const std::shared_ptr<const CGameSetup> newGameSetup);

	void AddLocalClient(const std::string& myName, const std::string& myVersion, const std::string& myPlatform);
	void AddAutohostInterface(const std::string& autohostIP, const int autohostPort);

	void Initialize();
	/**
	 * @brief Set frame after loading
	 * WARNING! No checks are done, so be carefull
	 */
	void PostLoad(int serverFrameNum);

	void CreateNewFrame(bool fromServerThread, bool fixedFrameTime);

	void SetGamePausable(const bool arg);
	void SetReloading(const bool arg) { reloadingServer = arg; }

	bool PreSimFrame() const { return (serverFrameNum == -1); }
	bool HasStarted() const { return gameHasStarted; }
	bool HasGameID() const { return generatedGameID; }
	bool HasLocalClient() const { return (localClientNumber != -1u); }
	/// Is the server still running?
	bool HasFinished() const;

	void UpdateSpeedControl(int speedCtrl);
	static std::string SpeedControlToString(int speedCtrl);

	static bool IsServerCommand(const std::string& cmd) {
		const auto pred = [](const std::string& a, const std::string& b) { return (a < b); };
		const auto iter = std::lower_bound(commandBlacklist.begin(), commandBlacklist.end(), cmd, pred);
		return (iter != commandBlacklist.end() && *iter == cmd);
	}

	std::string GetPlayerNames(const std::vector<int>& indices) const;

	const std::shared_ptr<const ClientSetup> GetClientSetup() const { return myClientSetup; }
	const std::shared_ptr<const    GameData> GetGameData() const { return myGameData; }
	const std::shared_ptr<const  CGameSetup> GetGameSetup() const { return myGameSetup; }

	const std::unique_ptr<CDemoReader>& GetDemoReader() const { return demoReader; }
	const std::unique_ptr<CDemoRecorder>& GetDemoRecorder() const { return demoRecorder; }

private:
	/**
	 * @brief relay chat messages to players / autohost
	 */
	void GotChatMessage(const ChatMessage& msg);

	/// Execute textual messages received from clients
	void PushAction(const Action& action, bool fromAutoHost);

	void StripGameSetupText(GameData* gameData);

	/**
	 * @brief kick the specified player from the battle
	 */
	void KickPlayer(int playerNum);
	/**
	 * @brief force the specified player to spectate
	 */
	void SpecPlayer(int playerNum);
	/**
	 * @brief drops chat or draw messages for given playerNum
	 */
	void MutePlayer(int playerNum, bool muteChat, bool muteDraw);
	void ResignPlayer(int playerNum);

	bool CheckPlayerPassword(const int playerNum, const std::string& pw) const;

	unsigned BindConnection(
		std::shared_ptr<netcode::CConnection> clientLink,
		std::string clientName,
		const std::string& clientPassword,
		const std::string& clientVersion,
		const std::string& clientPlatform,
		bool isLocal,
		bool reconnect = false,
		int netloss = 0
	);

	void CheckForGameStart(bool forced = false);
	void StartGame(bool forced);
	void UpdateLoop();
	void Update();
	void ProcessPacket(const unsigned playerNum, std::shared_ptr<const netcode::RawPacket> packet);
	void CheckSync();
	void HandleConnectionAttempts();
	void ServerReadNet();

	void LagProtection();

	/** @brief Generate a unique game identifier and send it to all clients. */
	void GenerateAndSendGameID();

	void WriteDemoData();
	/// read data from demo and send it to clients
	bool SendDemoData(int targetFrameNum);

	void Broadcast(std::shared_ptr<const netcode::RawPacket> packet);

	/**
	 * @brief skip frames
	 *
	 * If you are watching a demo, this will push out all data until
	 * targetFrame to all clients
	 */
	void SkipTo(int targetFrameNum);

	void Message(const std::string& message, bool broadcast = true, bool internal = false);
	void PrivateMessage(int playerNum, const std::string& message);

	float GetDemoTime() const;

private:
	///////////////// internal stuff //////////////////
	void InternalSpeedChange(float newSpeed);
	void UserSpeedChange(float newSpeed, int player);

	void AddAdditionalUser( const std::string& name, const std::string& passwd, bool fromDemo = false, bool spectator = true, int team = 0, int playerNum = -1);

	uint8_t ReserveSkirmishAIId();

private:
	/////////////////// game settings ///////////////////
	std::shared_ptr<const ClientSetup> myClientSetup;
	std::shared_ptr<const    GameData> myGameData;
	std::shared_ptr<const  CGameSetup> myGameSetup;


	std::vector< std::pair<bool, GameSkirmishAI> > skirmishAIs;
	std::vector<uint8_t> freeSkirmishAIs;

	std::vector<GameParticipant> players;
	std::vector<GameTeam> teams;
	std::vector<unsigned char> winningAllyTeams;

	std::array<           spring_time           , MAX_PLAYERS> netPingTimings; // throttles NETMSG_PING
	std::array< std::pair<spring_time, uint32_t>, MAX_PLAYERS> mapDrawTimings; // throttles NETMSG_MAPDRAW
	std::array< std::pair<       bool,     bool>, MAX_PLAYERS> chatMutedFlags; // blocks NETMSG_{CHAT,DRAW}
	std::array<                            bool , MAX_PLAYERS> aiControlFlags; // blocks NETMSG_AI_CREATED (aicontrol)

	// std::map<asio::ip::udp::endpoint, int> rejectedConnections;
	std::map<std::string, int> rejectedConnections;

	std::pair<std::string, std::string> refClientVersion;

	std::deque< std::shared_ptr<const netcode::RawPacket> > packetCache;

	/////////////////// sync stuff ///////////////////
#ifdef SYNCCHECK
	std::set<int> outstandingSyncFrames;
#endif

	/////////////////// game status variables ///////////////////
	spring_time serverStartTime = spring_gettime();
	spring_time readyTime = spring_notime;

	spring_time lastNewFrameTick = spring_notime;
	spring_time lastPlayerInfo = spring_notime;
	spring_time lastUpdate = spring_notime;
	spring_time lastBandwidthUpdate = spring_notime;

	float modGameTime = 0.0f;
	float gameTime = 0.0f;
	float startTime = 0.0f;
	float frameTimeLeft = 0.0f;

	float userSpeedFactor = 1.0f;
	float internalSpeed = 1.0f;

	float medianCpu = 0.0f;
	int medianPing = 0;
	int curSpeedCtrl = 0;
	int loopSleepTime = 0;


	int serverFrameNum = -1;

	int syncErrorFrame = 0;
	int syncWarningFrame = 0;

	int linkMinPacketSize = 1;

	unsigned localClientNumber = -1u;


	/// The maximum speed users are allowed to set
	float maxUserSpeed = 1.0f;
	/// The minimum speed users are allowed to set (actual speed can be lower due to high cpu usage)
	float minUserSpeed = 1.0f;

	bool isPaused = false;
	/// whether the game is pausable for others than the host
	bool gamePausable = true;

	bool cheating = false;
	bool noHelperAIs = false;
	bool canReconnect = false;
	bool allowSpecDraw = true;
	bool allowSpecJoin = false;
	bool whiteListAdditionalPlayers = false;

	bool logInfoMessages = false;
	bool logDebugMessages = false;


	/// If the server receives a command, it will forward it to clients if it is not in this set
	static std::array<std::string, 25> commandBlacklist;

	std::unique_ptr<netcode::UDPListener> udpListener;
	std::unique_ptr<CDemoReader> demoReader;
	std::unique_ptr<CDemoRecorder> demoRecorder;
	std::unique_ptr<AutohostInterface> hostif;

	CGlobalUnsyncedRNG rng;
	spring::thread thread;

	mutable spring::recursive_mutex gameServerMutex;

	std::atomic<bool> gameHasStarted{false};
	std::atomic<bool> generatedGameID{false};
	std::atomic<bool> reloadingServer{false};
	std::atomic<bool> quitServer{false};

	union {
		unsigned char charArray[16];
		unsigned int intArray[4];
	} gameID;
};

extern CGameServer* gameServer;

#endif // _GAME_SERVER_H