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