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