mirror of https://github.com/OpenRCT2/OpenRCT2.git
Refactor game action binding for plugins (#11528)
* Refactor game action binding for plugins Adds parameter visiting for game actions to reduce code needed for binding game actions to JavaScript objects * Apply suggestions from code review Co-Authored-By: Tulio Leao <tupaschoal@gmail.com> Co-authored-by: Tulio Leao <tupaschoal@gmail.com>
This commit is contained in:
parent
a8f4a1e114
commit
bbe7ff0d80
|
@ -259,6 +259,22 @@ declare global {
|
||||||
result: RideCreateGameActionResult;
|
result: RideCreateGameActionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface SmallSceneryPlaceEventArgs extends GameActionEventArgs {
|
||||||
|
readonly x: number;
|
||||||
|
readonly y: number;
|
||||||
|
readonly z: number;
|
||||||
|
readonly direction: number;
|
||||||
|
readonly quadrant: number;
|
||||||
|
readonly object: number;
|
||||||
|
readonly primaryColour: number;
|
||||||
|
readonly secondaryColour: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GuestSetNameActionEventArgs extends GameActionEventArgs {
|
||||||
|
readonly id: number;
|
||||||
|
readonly name: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface NetworkEventArgs {
|
interface NetworkEventArgs {
|
||||||
readonly player: number;
|
readonly player: number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -140,6 +140,39 @@ public:
|
||||||
std::string GetErrorMessage() const;
|
std::string GetErrorMessage() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class GameActionParameterVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~GameActionParameterVisitor() = default;
|
||||||
|
|
||||||
|
virtual void Visit(const std::string_view& name, int32_t& param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Visit(const std::string_view& name, std::string& param)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(CoordsXYZD& param)
|
||||||
|
{
|
||||||
|
Visit("x", param.x);
|
||||||
|
Visit("y", param.y);
|
||||||
|
Visit("z", param.z);
|
||||||
|
Visit("direction", param.direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> void Visit(const std::string_view& name, T& param)
|
||||||
|
{
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
|
||||||
|
auto value = static_cast<int32_t>(param);
|
||||||
|
Visit(name, value);
|
||||||
|
param = static_cast<T>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct GameAction
|
struct GameAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -164,6 +197,10 @@ public:
|
||||||
|
|
||||||
virtual const char* GetName() const = 0;
|
virtual const char* GetName() const = 0;
|
||||||
|
|
||||||
|
virtual void AcceptParameters(GameActionParameterVisitor&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
NetworkPlayerId_t GetPlayer() const
|
NetworkPlayerId_t GetPlayer() const
|
||||||
{
|
{
|
||||||
return _playerId;
|
return _playerId;
|
||||||
|
|
|
@ -37,6 +37,22 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AcceptParameters(GameActionParameterVisitor & visitor) override
|
||||||
|
{
|
||||||
|
visitor.Visit("id", _spriteIndex);
|
||||||
|
visitor.Visit("name", _name);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t GetSpriteIndex() const
|
||||||
|
{
|
||||||
|
return _spriteIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetGuestName() const
|
||||||
|
{
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t GetActionFlags() const override
|
uint16_t GetActionFlags() const override
|
||||||
{
|
{
|
||||||
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
|
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
|
||||||
|
|
|
@ -37,6 +37,11 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AcceptParameters(GameActionParameterVisitor & visitor) override
|
||||||
|
{
|
||||||
|
visitor.Visit("name", _name);
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t GetActionFlags() const override
|
uint16_t GetActionFlags() const override
|
||||||
{
|
{
|
||||||
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
|
return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED;
|
||||||
|
|
|
@ -60,6 +60,14 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AcceptParameters(GameActionParameterVisitor & visitor) override
|
||||||
|
{
|
||||||
|
visitor.Visit("rideType", _rideType);
|
||||||
|
visitor.Visit("rideObject", _subType);
|
||||||
|
visitor.Visit("colour1", _colour1);
|
||||||
|
visitor.Visit("colour2", _colour2);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t GetRideType() const
|
int32_t GetRideType() const
|
||||||
{
|
{
|
||||||
return _rideType;
|
return _rideType;
|
||||||
|
|
|
@ -73,6 +73,15 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AcceptParameters(GameActionParameterVisitor & visitor) override
|
||||||
|
{
|
||||||
|
visitor.Visit(_loc);
|
||||||
|
visitor.Visit("quadrant", _quadrant);
|
||||||
|
visitor.Visit("object", _sceneryType);
|
||||||
|
visitor.Visit("primaryColour", _primaryColour);
|
||||||
|
visitor.Visit("secondaryColour", _secondaryColour);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t GetCooldownTime() const override
|
uint32_t GetCooldownTime() const override
|
||||||
{
|
{
|
||||||
return 20;
|
return 20;
|
||||||
|
|
|
@ -11,9 +11,7 @@
|
||||||
|
|
||||||
#ifdef ENABLE_SCRIPTING
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
|
||||||
# include "../actions/CustomAction.hpp"
|
# include "../actions/GameAction.h"
|
||||||
# include "../actions/ParkSetNameAction.hpp"
|
|
||||||
# include "../actions/SmallSceneryPlaceAction.hpp"
|
|
||||||
# include "../object/ObjectManager.h"
|
# include "../object/ObjectManager.h"
|
||||||
# include "../scenario/Scenario.h"
|
# include "../scenario/Scenario.h"
|
||||||
# include "Duktape.hpp"
|
# include "Duktape.hpp"
|
||||||
|
@ -161,7 +159,7 @@ namespace OpenRCT2::Scripting
|
||||||
auto ctx = scriptEngine.GetContext();
|
auto ctx = scriptEngine.GetContext();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto action = CreateGameAction(actionid, args);
|
auto action = scriptEngine.CreateGameAction(actionid, args);
|
||||||
if (action != nullptr)
|
if (action != nullptr)
|
||||||
{
|
{
|
||||||
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
auto plugin = scriptEngine.GetExecInfo().GetCurrentPlugin();
|
||||||
|
@ -189,45 +187,6 @@ namespace OpenRCT2::Scripting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GameAction> CreateGameAction(const std::string& actionid, const DukValue& args)
|
|
||||||
{
|
|
||||||
if (actionid == "parksetname")
|
|
||||||
{
|
|
||||||
auto name = args["name"].as_string();
|
|
||||||
return std::make_unique<ParkSetNameAction>(name);
|
|
||||||
}
|
|
||||||
else if (actionid == "smallsceneryplace")
|
|
||||||
{
|
|
||||||
CoordsXYZD loc;
|
|
||||||
loc.x = args["x"].as_int();
|
|
||||||
loc.y = args["y"].as_int();
|
|
||||||
loc.z = args["z"].as_int();
|
|
||||||
loc.direction = args["direction"].as_int();
|
|
||||||
uint8_t quadrant = args["quadrant"].as_int();
|
|
||||||
uint8_t sceneryType = args["object"].as_int();
|
|
||||||
uint8_t primaryColour = args["primaryColour"].as_int();
|
|
||||||
uint8_t secondaryColour = args["secondaryColour"].as_int();
|
|
||||||
return std::make_unique<SmallSceneryPlaceAction>(loc, quadrant, sceneryType, primaryColour, secondaryColour);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Serialise args to json so that it can be sent
|
|
||||||
auto ctx = args.context();
|
|
||||||
if (args.type() == DukValue::Type::OBJECT)
|
|
||||||
{
|
|
||||||
args.push();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
duk_push_object(ctx);
|
|
||||||
}
|
|
||||||
auto jsonz = duk_json_encode(ctx, -1);
|
|
||||||
auto json = std::string(jsonz);
|
|
||||||
duk_pop(ctx);
|
|
||||||
return std::make_unique<CustomAction>(actionid, json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleGameActionResult(
|
void HandleGameActionResult(
|
||||||
const std::shared_ptr<Plugin>& plugin, const GameActionResult& res, const DukValue& callback)
|
const std::shared_ptr<Plugin>& plugin, const GameActionResult& res, const DukValue& callback)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
# include "ScriptEngine.h"
|
# include "ScriptEngine.h"
|
||||||
|
|
||||||
# include "../PlatformEnvironment.h"
|
# include "../PlatformEnvironment.h"
|
||||||
|
# include "../actions/CustomAction.hpp"
|
||||||
# include "../actions/GameAction.h"
|
# include "../actions/GameAction.h"
|
||||||
# include "../actions/RideCreateAction.hpp"
|
# include "../actions/RideCreateAction.hpp"
|
||||||
# include "../config/Config.h"
|
# include "../config/Config.h"
|
||||||
|
@ -811,7 +812,7 @@ bool ScriptEngine::RegisterCustomAction(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomAction customAction;
|
CustomActionInfo customAction;
|
||||||
customAction.Owner = plugin;
|
customAction.Owner = plugin;
|
||||||
customAction.Name = std::move(actionz);
|
customAction.Name = std::move(actionz);
|
||||||
customAction.Query = query;
|
customAction.Query = query;
|
||||||
|
@ -835,6 +836,52 @@ void ScriptEngine::RemoveCustomGameActions(const std::shared_ptr<Plugin>& plugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DukToGameActionParameterVisitor : public GameActionParameterVisitor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DukValue _dukValue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DukToGameActionParameterVisitor(DukValue&& dukValue)
|
||||||
|
: _dukValue(dukValue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(const std::string_view& name, int32_t& param) override
|
||||||
|
{
|
||||||
|
param = _dukValue[name].as_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(const std::string_view& name, std::string& param) override
|
||||||
|
{
|
||||||
|
param = _dukValue[name].as_string();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class DukFromGameActionParameterVisitor : public GameActionParameterVisitor
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DukObject& _dukObject;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DukFromGameActionParameterVisitor(DukObject& dukObject)
|
||||||
|
: _dukObject(dukObject)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(const std::string_view& name, int32_t& param) override
|
||||||
|
{
|
||||||
|
std::string szName(name);
|
||||||
|
_dukObject.Set(szName.c_str(), param);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Visit(const std::string_view& name, std::string& param) override
|
||||||
|
{
|
||||||
|
std::string szName(name);
|
||||||
|
_dukObject.Set(szName.c_str(), param);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr<GameActionResult>& result, bool isExecute)
|
void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr<GameActionResult>& result, bool isExecute)
|
||||||
{
|
{
|
||||||
DukStackFrame frame(_context);
|
DukStackFrame frame(_context);
|
||||||
|
@ -848,12 +895,8 @@ void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr<
|
||||||
auto flags = action.GetActionFlags();
|
auto flags = action.GetActionFlags();
|
||||||
obj.Set("isClientOnly", (flags & GA_FLAGS::CLIENT_ONLY) != 0);
|
obj.Set("isClientOnly", (flags & GA_FLAGS::CLIENT_ONLY) != 0);
|
||||||
|
|
||||||
if (action.GetType() == GAME_COMMAND_CREATE_RIDE)
|
DukFromGameActionParameterVisitor visitor(obj);
|
||||||
{
|
const_cast<GameAction&>(action).AcceptParameters(visitor);
|
||||||
auto& rideCreateAction = static_cast<const RideCreateAction&>(action);
|
|
||||||
obj.Set("rideType", rideCreateAction.GetRideType());
|
|
||||||
obj.Set("rideObject", rideCreateAction.GetRideObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.Set("result", GameActionResultToDuk(action, result));
|
obj.Set("result", GameActionResultToDuk(action, result));
|
||||||
auto dukEventArgs = obj.Take();
|
auto dukEventArgs = obj.Take();
|
||||||
|
@ -873,6 +916,51 @@ void ScriptEngine::RunGameActionHooks(const GameAction& action, std::unique_ptr<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<GameAction> CreateGameActionFromActionId(const std::string& actionid)
|
||||||
|
{
|
||||||
|
const static std::unordered_map<std::string, uint32_t> ActionNameToType = {
|
||||||
|
{ "parksetname", GAME_COMMAND_SET_PARK_NAME },
|
||||||
|
{ "smallsceneryplace", GAME_COMMAND_PLACE_SCENERY },
|
||||||
|
{ "guestsetname", GAME_COMMAND_SET_GUEST_NAME },
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = ActionNameToType.find(actionid);
|
||||||
|
if (result != ActionNameToType.end())
|
||||||
|
{
|
||||||
|
return GameActions::Create(result->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<GameAction> ScriptEngine::CreateGameAction(const std::string& actionid, const DukValue& args)
|
||||||
|
{
|
||||||
|
auto action = CreateGameActionFromActionId(actionid);
|
||||||
|
if (action != nullptr)
|
||||||
|
{
|
||||||
|
DukValue argsCopy = args;
|
||||||
|
DukToGameActionParameterVisitor visitor(std::move(argsCopy));
|
||||||
|
action->AcceptParameters(visitor);
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Serialise args to json so that it can be sent
|
||||||
|
auto ctx = args.context();
|
||||||
|
if (args.type() == DukValue::Type::OBJECT)
|
||||||
|
{
|
||||||
|
args.push();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
duk_push_object(ctx);
|
||||||
|
}
|
||||||
|
auto jsonz = duk_json_encode(ctx, -1);
|
||||||
|
auto json = std::string(jsonz);
|
||||||
|
duk_pop(ctx);
|
||||||
|
return std::make_unique<CustomAction>(actionid, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ScriptEngine::InitSharedStorage()
|
void ScriptEngine::InitSharedStorage()
|
||||||
{
|
{
|
||||||
duk_push_object(_context);
|
duk_push_object(_context);
|
||||||
|
|
|
@ -123,7 +123,7 @@ namespace OpenRCT2::Scripting
|
||||||
std::mutex _changedPluginFilesMutex;
|
std::mutex _changedPluginFilesMutex;
|
||||||
std::vector<std::function<void(std::shared_ptr<Plugin>)>> _pluginStoppedSubscriptions;
|
std::vector<std::function<void(std::shared_ptr<Plugin>)>> _pluginStoppedSubscriptions;
|
||||||
|
|
||||||
struct CustomAction
|
struct CustomActionInfo
|
||||||
{
|
{
|
||||||
std::shared_ptr<Plugin> Owner;
|
std::shared_ptr<Plugin> Owner;
|
||||||
std::string Name;
|
std::string Name;
|
||||||
|
@ -131,7 +131,7 @@ namespace OpenRCT2::Scripting
|
||||||
DukValue Execute;
|
DukValue Execute;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unordered_map<std::string, CustomAction> _customActions;
|
std::unordered_map<std::string, CustomActionInfo> _customActions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
|
ScriptEngine(InteractiveConsole& console, IPlatformEnvironment& env);
|
||||||
|
@ -181,6 +181,7 @@ namespace OpenRCT2::Scripting
|
||||||
const std::shared_ptr<Plugin>& plugin, const std::string_view& action, const DukValue& query,
|
const std::shared_ptr<Plugin>& plugin, const std::string_view& action, const DukValue& query,
|
||||||
const DukValue& execute);
|
const DukValue& execute);
|
||||||
void RunGameActionHooks(const GameAction& action, std::unique_ptr<GameActionResult>& result, bool isExecute);
|
void RunGameActionHooks(const GameAction& action, std::unique_ptr<GameActionResult>& result, bool isExecute);
|
||||||
|
std::unique_ptr<GameAction> CreateGameAction(const std::string& actionid, const DukValue& args);
|
||||||
|
|
||||||
void SaveSharedStorage();
|
void SaveSharedStorage();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue