From 4e4379e6ef8f881893a7ed6cdece5bb30c8120e7 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 1 Mar 2020 21:56:49 +0000 Subject: [PATCH] Start work on custom game actions --- src/openrct2/Game.h | 1 + src/openrct2/actions/CustomAction.hpp | 56 +++++++++++++++++++ .../actions/GameActionRegistration.cpp | 4 ++ src/openrct2/network/Network.cpp | 13 +++-- src/openrct2/scripting/ScContext.hpp | 14 ++++- src/openrct2/scripting/ScriptEngine.cpp | 24 ++++++++ src/openrct2/scripting/ScriptEngine.h | 3 + 7 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 src/openrct2/actions/CustomAction.hpp diff --git a/src/openrct2/Game.h b/src/openrct2/Game.h index a1167563f2..b29f5a77ff 100644 --- a/src/openrct2/Game.h +++ b/src/openrct2/Game.h @@ -98,6 +98,7 @@ enum GAME_COMMAND GAME_COMMAND_REMOVE_FOOTPATH_SCENERY, // GA GAME_COMMAND_GUEST_SET_FLAGS, // GA GAME_COMMAND_SET_DATE, // GA + GAME_COMMAND_CUSTOM, // GA GAME_COMMAND_COUNT, }; diff --git a/src/openrct2/actions/CustomAction.hpp b/src/openrct2/actions/CustomAction.hpp new file mode 100644 index 0000000000..e220598159 --- /dev/null +++ b/src/openrct2/actions/CustomAction.hpp @@ -0,0 +1,56 @@ +/***************************************************************************** + * Copyright (c) 2014-2020 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ + +#pragma once + +#ifdef __ENABLE_SCRIPTING__ + +# include "../Context.h" +# include "../scripting/ScriptEngine.h" +# include "GameAction.h" + +DEFINE_GAME_ACTION(CustomAction, GAME_COMMAND_CUSTOM, GameActionResult) +{ +private: + std::string _id; + std::string _json; + +public: + CustomAction() = default; + CustomAction(const std::string& id, const std::string& json) + : _id(id) + , _json(json) + { + } + + uint16_t GetActionFlags() const override + { + return GameAction::GetActionFlags() | GA_FLAGS::ALLOW_WHILE_PAUSED; + } + + void Serialise(DataSerialiser & stream) override + { + GameAction::Serialise(stream); + stream << DS_TAG(_id) << DS_TAG(_json); + } + + GameActionResult::Ptr Query() const override + { + auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine(); + return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, false); + } + + GameActionResult::Ptr Execute() const override + { + auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine(); + return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, true); + } +}; + +#endif diff --git a/src/openrct2/actions/GameActionRegistration.cpp b/src/openrct2/actions/GameActionRegistration.cpp index c693d892c6..8f2638cd8d 100644 --- a/src/openrct2/actions/GameActionRegistration.cpp +++ b/src/openrct2/actions/GameActionRegistration.cpp @@ -15,6 +15,7 @@ #include "BannerSetStyleAction.hpp" #include "ClearAction.hpp" #include "ClimateSetAction.hpp" +#include "CustomAction.hpp" #include "FootpathPlaceAction.hpp" #include "FootpathPlaceFromTrackAction.hpp" #include "FootpathRemoveAction.hpp" @@ -171,5 +172,8 @@ namespace GameActions Register(); Register(); Register(); +#ifdef __ENABLE_SCRIPTING__ + Register(); +#endif } } // namespace GameActions diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index e603eda448..fec0573264 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -2997,12 +2997,15 @@ void Network::Server_Handle_GAME_ACTION(NetworkConnection& connection, NetworkPa return; } - // Check if player's group permission allows command to run - NetworkGroup* group = GetGroupByID(connection.Player->Group); - if (group == nullptr || group->CanPerformCommand(actionType) == false) + if (actionType != GAME_COMMAND_CUSTOM) { - Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED); - return; + // Check if player's group permission allows command to run + NetworkGroup* group = GetGroupByID(connection.Player->Group); + if (group == nullptr || group->CanPerformCommand(actionType) == false) + { + Server_Send_SHOWERROR(connection, STR_CANT_DO_THIS, STR_PERMISSION_DENIED); + return; + } } // Create and enqueue the action. diff --git a/src/openrct2/scripting/ScContext.hpp b/src/openrct2/scripting/ScContext.hpp index 87ba11772c..8c7893ac1a 100644 --- a/src/openrct2/scripting/ScContext.hpp +++ b/src/openrct2/scripting/ScContext.hpp @@ -11,6 +11,7 @@ #ifdef __ENABLE_SCRIPTING__ +# include "../actions/CustomAction.hpp" # include "../actions/ParkSetNameAction.hpp" # include "../actions/SmallSceneryPlaceAction.hpp" # include "Duktape.hpp" @@ -91,7 +92,7 @@ namespace OpenRCT2::Scripting if (isExecute) { action->SetCallback( - [this, &plugin, &callback](const GameAction*, const GameActionResult* res) -> void { + [this, plugin, callback](const GameAction*, const GameActionResult* res) -> void { HandleGameActionResult(plugin, *res, callback); }); GameActions::Execute(action.get()); @@ -143,7 +144,16 @@ namespace OpenRCT2::Scripting uint8_t secondaryColour = args["secondaryColour"].as_int(); return std::make_unique(loc, quadrant, sceneryType, primaryColour, secondaryColour); } - return {}; + else + { + // Serialise args to json so that it can be sent + auto ctx = args.context(); + args.push(); + auto jsonz = duk_json_encode(ctx, -1); + auto json = std::string(jsonz); + duk_pop(ctx); + return std::make_unique(actionid, json); + } } void HandleGameActionResult( diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index f7d72ed029..47792c4c42 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -694,6 +694,30 @@ void ScriptEngine::AddNetworkPlugin(const std::string_view& code) LoadPlugin(plugin); } +std::unique_ptr ScriptEngine::QueryOrExecuteCustomGameAction( + const std::string_view& id, + const std::string_view& args, + bool isExecute) +{ + // Deserialise the JSON args + std::string argsz(args); + duk_push_string(_context, argsz.c_str()); + duk_json_decode(_context, -1); + auto dukArgs = DukValue::take_from_stack(_context); + + // Ready to call plugin handler + if (isExecute) + { + std::printf("EXECUTE: %s(%s)\n", std::string(id).c_str(), std::string(args).c_str()); + } + else + { + std::printf("QUERY: %s(%s)\n", std::string(id).c_str(), std::string(args).c_str()); + } + + return std::make_unique(); +} + std::string OpenRCT2::Scripting::Stringify(const DukValue& val) { return ExpressionStringifier::StringifyExpression(val); diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index cf7f62acfa..a309f1faa1 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -27,6 +27,7 @@ struct duk_hthread; typedef struct duk_hthread duk_context; +class GameActionResult; class FileWatcher; class InteractiveConsole; @@ -155,6 +156,8 @@ namespace OpenRCT2::Scripting void AddNetworkPlugin(const std::string_view& code); + std::unique_ptr QueryOrExecuteCustomGameAction(const std::string_view& id, const std::string_view& args, bool isExecute); + private: void Initialise(); void StartPlugins();