1 /***************************************************************************** 2 * Copyright (c) 2014-2020 OpenRCT2 developers 3 * 4 * For a complete list of all authors, please refer to contributors.md 5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 6 * 7 * OpenRCT2 is licensed under the GNU General Public License version 3. 8 *****************************************************************************/ 9 10 #pragma once 11 12 #include "../Game.h" 13 #include "../common.h" 14 #include "../core/DataSerialiser.h" 15 #include "../localisation/StringIds.h" 16 #include "GameActionResult.h" 17 18 #include <array> 19 #include <functional> 20 #include <memory> 21 #include <utility> 22 23 namespace GameActions 24 { 25 namespace Flags 26 { 27 constexpr uint16_t AllowWhilePaused = 1 << 0; 28 constexpr uint16_t ClientOnly = 1 << 1; 29 constexpr uint16_t EditorOnly = 1 << 2; 30 } // namespace Flags 31 32 } // namespace GameActions 33 34 #ifdef __WARN_SUGGEST_FINAL_METHODS__ 35 # pragma GCC diagnostic push 36 # pragma GCC diagnostic ignored "-Wsuggest-final-methods" 37 # pragma GCC diagnostic ignored "-Wsuggest-final-types" 38 #endif 39 40 /** 41 * 42 */ 43 class GameActionParameterVisitor 44 { 45 public: 46 virtual ~GameActionParameterVisitor() = default; 47 Visit(std::string_view name,bool & param)48 virtual void Visit(std::string_view name, bool& param) 49 { 50 } 51 Visit(std::string_view name,int32_t & param)52 virtual void Visit(std::string_view name, int32_t& param) 53 { 54 } 55 Visit(std::string_view name,std::string & param)56 virtual void Visit(std::string_view name, std::string& param) 57 { 58 } 59 Visit(CoordsXY & param)60 void Visit(CoordsXY& param) 61 { 62 Visit("x", param.x); 63 Visit("y", param.y); 64 } 65 Visit(CoordsXYZ & param)66 void Visit(CoordsXYZ& param) 67 { 68 Visit("x", param.x); 69 Visit("y", param.y); 70 Visit("z", param.z); 71 } 72 Visit(CoordsXYZD & param)73 void Visit(CoordsXYZD& param) 74 { 75 Visit("x", param.x); 76 Visit("y", param.y); 77 Visit("z", param.z); 78 Visit("direction", param.direction); 79 } 80 Visit(std::string_view name,T & param)81 template<typename T> void Visit(std::string_view name, T& param) 82 { 83 static_assert(std::is_arithmetic_v<T> || std::is_enum_v<T>, "Not an arithmetic type"); 84 auto value = static_cast<int32_t>(param); 85 Visit(name, value); 86 param = static_cast<T>(value); 87 } 88 Visit(std::string_view name,NetworkObjectId_t<T,_TypeID> & param)89 template<typename T, size_t _TypeID> void Visit(std::string_view name, NetworkObjectId_t<T, _TypeID>& param) 90 { 91 Visit(name, param.id); 92 } 93 }; 94 95 class GameAction 96 { 97 public: 98 using Ptr = std::unique_ptr<GameAction>; 99 using Callback_t = std::function<void(const class GameAction*, const GameActions::Result*)>; 100 101 private: 102 GameCommand const _type; 103 104 NetworkPlayerId_t _playerId = { -1 }; // Callee 105 uint32_t _flags = 0; // GAME_COMMAND_FLAGS 106 uint32_t _networkId = 0; 107 Callback_t _callback; 108 109 public: GameAction(GameCommand type)110 GameAction(GameCommand type) 111 : _type(type) 112 { 113 } 114 115 virtual ~GameAction() = default; 116 117 const char* GetName() const; 118 AcceptParameters(GameActionParameterVisitor &)119 virtual void AcceptParameters(GameActionParameterVisitor&) 120 { 121 } 122 AcceptFlags(GameActionParameterVisitor & visitor)123 void AcceptFlags(GameActionParameterVisitor& visitor) 124 { 125 visitor.Visit("flags", _flags); 126 } 127 GetPlayer()128 NetworkPlayerId_t GetPlayer() const 129 { 130 return _playerId; 131 } 132 SetPlayer(NetworkPlayerId_t playerId)133 void SetPlayer(NetworkPlayerId_t playerId) 134 { 135 _playerId = playerId; 136 } 137 138 /** 139 * Gets the GameActions::Flags flags that are enabled for this game action. 140 */ GetActionFlags()141 virtual uint16_t GetActionFlags() const 142 { 143 // Make sure we execute some things only on the client. 144 uint16_t flags = 0; 145 146 if ((GetFlags() & GAME_COMMAND_FLAG_GHOST) != 0 || (GetFlags() & GAME_COMMAND_FLAG_NO_SPEND) != 0) 147 { 148 flags |= GameActions::Flags::ClientOnly; 149 } 150 151 if (GetFlags() & GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED) 152 { 153 flags |= GameActions::Flags::AllowWhilePaused; 154 } 155 156 return flags; 157 } 158 159 /** 160 * Currently used for GAME_COMMAND_FLAGS, needs refactoring once everything is replaced. 161 */ GetFlags()162 uint32_t GetFlags() const 163 { 164 return _flags; 165 } 166 SetFlags(uint32_t flags)167 uint32_t SetFlags(uint32_t flags) 168 { 169 return _flags = flags; 170 } 171 GetType()172 GameCommand GetType() const 173 { 174 return _type; 175 } 176 SetCallback(Callback_t cb)177 void SetCallback(Callback_t cb) 178 { 179 _callback = cb; 180 } 181 GetCallback()182 const Callback_t& GetCallback() const 183 { 184 return _callback; 185 } 186 SetNetworkId(uint32_t id)187 void SetNetworkId(uint32_t id) 188 { 189 _networkId = id; 190 } 191 GetNetworkId()192 uint32_t GetNetworkId() const 193 { 194 return _networkId; 195 } 196 Serialise(DataSerialiser & stream)197 virtual void Serialise(DataSerialiser& stream) 198 { 199 stream << DS_TAG(_networkId) << DS_TAG(_flags) << DS_TAG(_playerId); 200 } 201 202 // Helper function, allows const Objects to still serialize into DataSerialiser while being const. Serialise(DataSerialiser & stream)203 void Serialise(DataSerialiser& stream) const 204 { 205 return const_cast<GameAction&>(*this).Serialise(stream); 206 } 207 208 /** 209 * Override this to specify the wait time in milliseconds the player is required to wait before 210 * being able to execute it again. 211 */ GetCooldownTime()212 virtual uint32_t GetCooldownTime() const 213 { 214 return 0; 215 } 216 217 /** 218 * Query the result of the game action without changing the game state. 219 */ 220 virtual GameActions::Result::Ptr Query() const abstract; 221 222 /** 223 * Apply the game action and change the game state. 224 */ 225 virtual GameActions::Result::Ptr Execute() const abstract; 226 227 bool LocationValid(const CoordsXY& coords) const; 228 }; 229 230 #ifdef __WARN_SUGGEST_FINAL_METHODS__ 231 # pragma GCC diagnostic pop 232 #endif 233 234 template<GameCommand TId> struct GameActionNameQuery 235 { 236 }; 237 238 template<GameCommand TType> struct GameActionBase : GameAction 239 { 240 public: 241 static constexpr GameCommand TYPE = TType; 242 GameActionBaseGameActionBase243 GameActionBase() 244 : GameAction(TYPE) 245 { 246 } 247 248 protected: MakeResultGameActionBase249 template<class... TTypes> static std::unique_ptr<GameActions::Result> MakeResult(TTypes&&... args) 250 { 251 return std::make_unique<GameActions::Result>(std::forward<TTypes>(args)...); 252 } 253 }; 254 255 namespace GameActions 256 { 257 using GameActionFactory = GameAction* (*)(); 258 259 bool IsValidId(uint32_t id); 260 const char* GetName(GameCommand id); 261 262 // Halts the queue processing until ResumeQueue is called, any calls to ProcessQueue 263 // will have no effect during suspension. It has no effect of actions that will not 264 // cross the network. 265 void SuspendQueue(); 266 267 // Resumes queue processing. 268 void ResumeQueue(); 269 270 void Enqueue(const GameAction* ga, uint32_t tick); 271 void Enqueue(GameAction::Ptr&& ga, uint32_t tick); 272 void ProcessQueue(); 273 void ClearQueue(); 274 275 GameAction::Ptr Create(GameCommand id); 276 GameAction::Ptr Clone(const GameAction* action); 277 278 // This should be used if a round trip is to be expected. 279 GameActions::Result::Ptr Query(const GameAction* action); 280 GameActions::Result::Ptr Execute(const GameAction* action); 281 282 // This should be used from within game actions. 283 GameActions::Result::Ptr QueryNested(const GameAction* action); 284 GameActions::Result::Ptr ExecuteNested(const GameAction* action); 285 286 } // namespace GameActions 287