1 /*
2 	This file is part of Warzone 2100.
3 	Copyright (C) 2020  Warzone 2100 Project
4 
5 	Warzone 2100 is free software; you can redistribute it and/or modify
6 	it under the terms of the GNU General Public License as published by
7 	the Free Software Foundation; either version 2 of the License, or
8 	(at your option) any later version.
9 
10 	Warzone 2100 is distributed in the hope that it will be useful,
11 	but WITHOUT ANY WARRANTY; without even the implied warranty of
12 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 	GNU General Public License for more details.
14 
15 	You should have received a copy of the GNU General Public License
16 	along with Warzone 2100; if not, write to the Free Software
17 	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19 
20 #ifndef __INCLUDED_SRC_ACTIVITY_H__
21 #define __INCLUDED_SRC_ACTIVITY_H__
22 
23 #include "scores.h"
24 #include "multiplay.h"
25 #include "levels.h"
26 #include "lib/netplay/netplay.h"
27 #include <vector>
28 #include <string>
29 
30 #include <optional-lite/optional.hpp>
31 using nonstd::optional;
32 using nonstd::nullopt;
33 
34 // Subclass ActivitySink to implement a custom handler for higher-level game-state event callbacks.
35 class ActivitySink
36 {
37 public:
38 	enum class GameMode {
39 		MENUS,
40 		TUTORIAL,
41 		CAMPAIGN,
42 		CHALLENGE,
43 		SKIRMISH,
44 		HOSTING_IN_LOBBY,
45 		JOINING_IN_PROGRESS, // joined but waiting on game information from host
46 		JOINING_IN_LOBBY,
47 		MULTIPLAYER
48 	};
49 
50 	enum class GameEndReason {
51 		WON,
52 		LOST,
53 		QUIT
54 	};
55 public:
~ActivitySink()56 	virtual ~ActivitySink() { }
57 public:
58 	// campaign games
startedCampaignMission(const std::string & campaign,const std::string & levelName)59 	virtual void startedCampaignMission(const std::string& campaign, const std::string& levelName) { }
endedCampaignMission(const std::string & campaign,const std::string & levelName,GameEndReason result,END_GAME_STATS_DATA stats,bool cheatsUsed)60 	virtual void endedCampaignMission(const std::string& campaign, const std::string& levelName, GameEndReason result, END_GAME_STATS_DATA stats, bool cheatsUsed) { }
61 
62 	// challenges
startedChallenge(const std::string & challengeName)63 	virtual void startedChallenge(const std::string& challengeName) { }
endedChallenge(const std::string & challengeName,GameEndReason result,const END_GAME_STATS_DATA & stats,bool cheatsUsed)64 	virtual void endedChallenge(const std::string& challengeName, GameEndReason result, const END_GAME_STATS_DATA& stats, bool cheatsUsed) { }
65 
66 	// skirmish
67 	struct SkirmishGameInfo
68 	{
69 		MULTIPLAYERGAME game;
70 		uint8_t numAIBotPlayers = 0;
71 		size_t currentPlayerIdx = 0;			// = the selectedPlayer global for the current client (points to currently controlled player in the players array)
72 		std::vector<PLAYER> players;
73 
74 		// information on limits
75 		bool limit_no_tanks;					///< Flag for tanks disabled
76 		bool limit_no_cyborgs;					///< Flag for cyborgs disabled
77 		bool limit_no_vtols;					///< Flag for VTOLs disabled
78 		bool limit_no_uplink;					///< Flag for Satellite Uplink disabled
79 		bool limit_no_lassat;					///< Flag for Laser Satellite Command Post disabled
80 		bool force_structure_limits;			///< Flag to force structure limits
81 		std::vector<MULTISTRUCTLIMITS> structureLimits;
82 
83 		// information on alliance settings
84 		enum class AllianceOption
85 		{
86 			NO_ALLIANCES,        // FFA
87 			ALLIANCES,           // Players can make and break alliances during the game.
88 			ALLIANCES_TEAMS,     // Alliances are set before the game.
89 			ALLIANCES_UNSHARED,  // Alliances are set before the game. No shared research.
90 		};
91 		AllianceOption alliances;
92 
93 	public:
~SkirmishGameInfoSkirmishGameInfo94 		virtual ~SkirmishGameInfo() { }
95 		// some convenience functions to get data
gameNameSkirmishGameInfo96 		const char * gameName() const { return game.name; }
mapNameSkirmishGameInfo97 		const char * mapName() const { return game.map; }
numberOfPlayersSkirmishGameInfo98 		virtual uint8_t numberOfPlayers() const { return numAIBotPlayers + 1; }
hasLimitsSkirmishGameInfo99 		bool hasLimits() const { return limit_no_tanks || limit_no_cyborgs || limit_no_vtols || limit_no_uplink || limit_no_lassat || force_structure_limits; }
100 	};
startedSkirmishGame(const SkirmishGameInfo & info)101 	virtual void startedSkirmishGame(const SkirmishGameInfo& info) { }
endedSkirmishGame(const SkirmishGameInfo & info,GameEndReason result,const END_GAME_STATS_DATA & stats)102 	virtual void endedSkirmishGame(const SkirmishGameInfo& info, GameEndReason result, const END_GAME_STATS_DATA& stats) { }
103 
104 	// multiplayer
105 	struct ListeningInterfaces {
106 		bool IPv4 = false;
107 		bool IPv6 = false;
108 		unsigned int ipv4_port;
109 		unsigned int ipv6_port;
110 	};
111 	struct MultiplayerGameInfo : public SkirmishGameInfo {
112 		// host information
113 		std::string hostName;		// host player name
114 		ListeningInterfaces listeningInterfaces;
115 		std::string lobbyAddress;
116 		unsigned int lobbyPort;
117 		uint32_t lobbyGameId = 0;
118 
119 		bool isHost;	// whether the current client is the game host
120 		bool privateGame;			// whether game is password-protected
121 		uint8_t maxPlayers = 0;
122 		uint8_t numHumanPlayers = 0;
123 		uint8_t numAvailableSlots = 0;
124 	};
hostingMultiplayerGame(const MultiplayerGameInfo & info)125 	virtual void hostingMultiplayerGame(const MultiplayerGameInfo& info) { }
joinedMultiplayerGame(const MultiplayerGameInfo & info)126 	virtual void joinedMultiplayerGame(const MultiplayerGameInfo& info) { }
updateMultiplayerGameInfo(const MultiplayerGameInfo & info)127 	virtual void updateMultiplayerGameInfo(const MultiplayerGameInfo& info) { }
leftMultiplayerGameLobby(bool wasHost,LOBBY_ERROR_TYPES type)128 	virtual void leftMultiplayerGameLobby(bool wasHost, LOBBY_ERROR_TYPES type) { }
startedMultiplayerGame(const MultiplayerGameInfo & info)129 	virtual void startedMultiplayerGame(const MultiplayerGameInfo& info) { }
endedMultiplayerGame(const MultiplayerGameInfo & info,GameEndReason result,const END_GAME_STATS_DATA & stats)130 	virtual void endedMultiplayerGame(const MultiplayerGameInfo& info, GameEndReason result, const END_GAME_STATS_DATA& stats) { }
131 
132 	// changing settings
changedSetting(const std::string & settingKey,const std::string & settingValue)133 	virtual void changedSetting(const std::string& settingKey, const std::string& settingValue) { }
134 
135 	// cheats used
cheatUsed(const std::string & cheatName)136 	virtual void cheatUsed(const std::string& cheatName) { }
137 
138 public:
139 	// Helper Functions
140 	static std::string getTeamDescription(const ActivitySink::SkirmishGameInfo& info);
141 };
142 
143 std::string to_string(const ActivitySink::GameEndReason& reason);
144 std::string to_string(const END_GAME_STATS_DATA& stats);
145 
146 // ActivityManager accepts numerous event callbacks from the core game and synthesizes
147 // a (more) sensible stream of higher-level event callbacks to subscribed ActivitySinks.
148 //
149 // To access the single, global instance of ActivityManager, use ActivityManager::instance()
150 //
151 class ActivityManager
152 {
153 public:
154 	void startingGame();
155 	void startingSavedGame();
156 	void loadedLevel(LEVEL_TYPE type, const std::string& levelName);
157 	void completedMission(bool result, END_GAME_STATS_DATA stats, bool cheatsUsed);
158 	void quitGame(END_GAME_STATS_DATA stats, bool cheatsUsed);
159 	void preSystemShutdown();
160 
161 	// changing settings
162 	void beginLoadingSettings();
163 	void changedSetting(const std::string& settingKey, const std::string& settingValue);
164 	void endLoadingSettings();
165 
166 	// cheats used
167 	void cheatUsed(const std::string& cheatName);
168 
169 	// called when a joinable multiplayer game is hosted
170 	// lobbyGameId is 0 if the lobby can't be contacted / the game is not registered with the lobby
171 	void hostGame(const char *SessionName, const char *PlayerName, const char* lobbyAddress, unsigned int lobbyPort, const ActivitySink::ListeningInterfaces& listeningInterfaces, uint32_t lobbyGameId = 0);
172 	void hostGameLobbyServerDisconnect();
173 	void hostLobbyQuit();
174 	// called when attempting to join a lobby game
175 	void willAttemptToJoinLobbyGame(const std::string& lobbyAddress, unsigned int lobbyPort, uint32_t lobbyGameId, const std::vector<JoinConnectionDescription>& connections);
176 	// called when an attempt to join fails
177 	void joinGameFailed(const std::vector<JoinConnectionDescription>& connection_list);
178 	// called when joining a multiplayer game
179 	void joinGameSucceeded(const char *host, uint32_t port);
180 	void joinedLobbyQuit();
181 	// for skirmish / multiplayer, provide additional data / state
182 	void updateMultiplayGameData(const MULTIPLAYERGAME& game, const MULTIPLAYERINGAME& ingame, optional<bool> privateGame);
183 	// called on the host when the host kicks a player
184 	void hostKickPlayer(const PLAYER& player, LOBBY_ERROR_TYPES kick_type, const std::string& reason);
185 	// called on the kicked player when they are kicked by another player
186 	void wasKickedByPlayer(const PLAYER& kicker, LOBBY_ERROR_TYPES kick_type, const std::string& reason);
187 public:
188 	static ActivityManager& instance();
189 	bool initialize();
190 	void shutdown();
191 	void addActivitySink(std::shared_ptr<ActivitySink> sink);
192 	void removeActivitySink(const std::shared_ptr<ActivitySink>& sink);
193 	ActivitySink::GameMode getCurrentGameMode() const;
194 private:
195 	void _endedMission(ActivitySink::GameEndReason result, END_GAME_STATS_DATA stats, bool cheatsUsed);
196 private:
197 	std::vector<std::shared_ptr<ActivitySink>> activitySinks;
198 
199 	// storing current game state, to aide in synthesizing events
200 	bool bIsLoadingConfiguration = false;
201 	ActivitySink::GameMode currentMode = ActivitySink::GameMode::MENUS;
202 	bool bEndedCurrentMission = false;
203 	ActivitySink::MultiplayerGameInfo currentMultiplayGameInfo;
204 
205 	struct LoadedLevelEvent {
206 		LEVEL_TYPE type;
207 		std::string levelName;
208 	};
209 	LoadedLevelEvent* cachedLoadedLevelEvent = nullptr;
210 
211 	LoadedLevelEvent lastLoadedLevelEvent;
212 
213 	struct FoundLobbyGameDetails
214 	{
215 		std::string lobbyAddress;
216 		unsigned int lobbyPort;
217 		uint32_t lobbyGameId;
218 		std::vector<JoinConnectionDescription> connections;
219 
clearFoundLobbyGameDetails220 		void clear()
221 		{
222 			lobbyAddress.clear();
223 			lobbyPort = 0;
224 			lobbyGameId = 0;
225 			connections.clear();
226 		}
227 	};
228 	FoundLobbyGameDetails lastLobbyGameJoinAttempt;
229 };
230 
231 #endif // __INCLUDED_SRC_ACTIVITY_H__
232