mirror of https://github.com/OpenRCT2/OpenRCT2.git
[Plugin] Wrap callback arguments for custom game actions in event arguments object, fix issue with unloading multiplayer plugins (#19091)
* Wrap custom game action arguments in event args object * Update Typescript declaration, documentation and changelog * Pass custom game action by value and remove log messages
This commit is contained in:
parent
f2b4d9dcf6
commit
b15a6e843a
|
@ -13,11 +13,13 @@
|
||||||
- Improved: [#18975] Add lift sprites for 60 steep hills on the wooden roller coaster.
|
- Improved: [#18975] Add lift sprites for 60 steep hills on the wooden roller coaster.
|
||||||
- Improved: [#19044] Added special thanks to RMC and Wiegand to the About page.
|
- Improved: [#19044] Added special thanks to RMC and Wiegand to the About page.
|
||||||
- Change: [#19018] Renamed actions to fit the naming scheme.
|
- Change: [#19018] Renamed actions to fit the naming scheme.
|
||||||
|
- Change: [#19091] [Plugin] Add game action information to callback arguments of custom actions.
|
||||||
- Fix: [#18467] “Selected only” Object Selection filter is active in Track Designs Manager, and cannot be toggled.
|
- Fix: [#18467] “Selected only” Object Selection filter is active in Track Designs Manager, and cannot be toggled.
|
||||||
- Fix: [#18905] Ride Construction window theme is not applied correctly.
|
- Fix: [#18905] Ride Construction window theme is not applied correctly.
|
||||||
- Fix: [#18911] Mini Golf station does not draw correctly from all angles.
|
- Fix: [#18911] Mini Golf station does not draw correctly from all angles.
|
||||||
- Fix: [#18971] New Game does not prompt for save before quitting.
|
- Fix: [#18971] New Game does not prompt for save before quitting.
|
||||||
- Fix: [#19026] Park loan is clamped to a 32-bit integer.
|
- Fix: [#19026] Park loan is clamped to a 32-bit integer.
|
||||||
|
- Fix: [#19091] [Plugin] Remote plugins in multiplayer servers do not unload properly.
|
||||||
- Fix: [#19112] Clearing the last character in the Object Selection filter does not properly reset it.
|
- Fix: [#19112] Clearing the last character in the Object Selection filter does not properly reset it.
|
||||||
- Fix: [#19114] [Plugin] GameActionResult does not comply to API specification.
|
- Fix: [#19114] [Plugin] GameActionResult does not comply to API specification.
|
||||||
- Fix: [#19136] SV6 saves with experimental RCT1 paths not imported correctly.
|
- Fix: [#19136] SV6 saves with experimental RCT1 paths not imported correctly.
|
||||||
|
|
|
@ -265,10 +265,10 @@ declare global {
|
||||||
* @param execute Logic for validating and executing the action.
|
* @param execute Logic for validating and executing the action.
|
||||||
* @throws An error if the action has already been registered by this or another plugin.
|
* @throws An error if the action has already been registered by this or another plugin.
|
||||||
*/
|
*/
|
||||||
registerAction(
|
registerAction<T = object>(
|
||||||
action: string,
|
action: string,
|
||||||
query: (args: object) => GameActionResult,
|
query: (args: GameActionEventArgs<T>) => GameActionResult,
|
||||||
execute: (args: object) => GameActionResult): void;
|
execute: (args: GameActionEventArgs<T>) => GameActionResult): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query the result of running a game action. This allows you to check the outcome and validity of
|
* Query the result of running a game action. This allows you to check the outcome and validity of
|
||||||
|
@ -1278,12 +1278,12 @@ declare global {
|
||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GameActionEventArgs {
|
interface GameActionEventArgs<T = object> {
|
||||||
readonly player: number;
|
readonly player: number;
|
||||||
readonly type: number;
|
readonly type: number;
|
||||||
readonly action: string;
|
readonly action: string;
|
||||||
readonly isClientOnly: boolean;
|
readonly isClientOnly: boolean;
|
||||||
readonly args: object;
|
readonly args: T;
|
||||||
result: GameActionResult;
|
result: GameActionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,8 +56,9 @@ The hot reload feature can be enabled by editing your `config.ini` file and sett
|
||||||
## Breaking changes
|
## Breaking changes
|
||||||
As of version 34 there are breaking Api changes.
|
As of version 34 there are breaking Api changes.
|
||||||
|
|
||||||
> Version 34
|
- **Version 34:** `Entity.type` will now return `"guest"` or `"staff"` instead of `"peep"`.
|
||||||
```Entity.type will now return "guest" or "staff" instead of "peep"```
|
- **Version 63:** Accessing G2 sprites by id directly is now deprecated in favor of a future-proof implementation using `IconName` and/or `context.getIcon()`.
|
||||||
|
- **Version 68:** Custom game actions registered through `context.registerAction()` now wrap the callback arguments in a `GameActionEventArgs`, similar to `context.subscribe()` callbacks.
|
||||||
|
|
||||||
## Frequently Asked Questions
|
## Frequently Asked Questions
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,13 @@ void CustomAction::Serialise(DataSerialiser& stream)
|
||||||
GameActions::Result CustomAction::Query() const
|
GameActions::Result CustomAction::Query() const
|
||||||
{
|
{
|
||||||
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
|
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
|
||||||
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, false);
|
return scriptingEngine.QueryOrExecuteCustomGameAction(*this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameActions::Result CustomAction::Execute() const
|
GameActions::Result CustomAction::Execute() const
|
||||||
{
|
{
|
||||||
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
|
auto& scriptingEngine = OpenRCT2::GetContext()->GetScriptEngine();
|
||||||
return scriptingEngine.QueryOrExecuteCustomGameAction(_id, _json, true);
|
return scriptingEngine.QueryOrExecuteCustomGameAction(*this, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
// It is used for making sure only compatible builds get connected, even within
|
// It is used for making sure only compatible builds get connected, even within
|
||||||
// single OpenRCT2 version.
|
// single OpenRCT2 version.
|
||||||
|
|
||||||
#define NETWORK_STREAM_VERSION "1"
|
#define NETWORK_STREAM_VERSION "2"
|
||||||
|
|
||||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||||
|
|
||||||
|
|
|
@ -1019,8 +1019,13 @@ void ScriptEngine::RemoveNetworkPlugins()
|
||||||
auto it = _plugins.begin();
|
auto it = _plugins.begin();
|
||||||
while (it != _plugins.end())
|
while (it != _plugins.end())
|
||||||
{
|
{
|
||||||
if (!(*it)->HasPath())
|
auto plugin = (*it);
|
||||||
|
if (!plugin->HasPath())
|
||||||
{
|
{
|
||||||
|
StopPlugin(plugin);
|
||||||
|
UnloadPlugin(plugin);
|
||||||
|
LogPluginInfo(plugin, "Unregistered network plugin");
|
||||||
|
|
||||||
it = _plugins.erase(it);
|
it = _plugins.erase(it);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1030,16 +1035,16 @@ void ScriptEngine::RemoveNetworkPlugins()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(std::string_view id, std::string_view args, bool isExecute)
|
GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(const CustomAction& customAction, bool isExecute)
|
||||||
{
|
{
|
||||||
std::string actionz = std::string(id);
|
std::string actionz = customAction.GetId();
|
||||||
auto kvp = _customActions.find(actionz);
|
auto kvp = _customActions.find(actionz);
|
||||||
if (kvp != _customActions.end())
|
if (kvp != _customActions.end())
|
||||||
{
|
{
|
||||||
const auto& customAction = kvp->second;
|
const auto& customActionInfo = kvp->second;
|
||||||
|
|
||||||
// Deserialise the JSON args
|
// Deserialise the JSON args
|
||||||
std::string argsz(args);
|
std::string argsz = customAction.GetJson();
|
||||||
|
|
||||||
auto dukArgs = DuktapeTryParseJson(_context, argsz);
|
auto dukArgs = DuktapeTryParseJson(_context, argsz);
|
||||||
if (!dukArgs)
|
if (!dukArgs)
|
||||||
|
@ -1050,15 +1055,33 @@ GameActions::Result ScriptEngine::QueryOrExecuteCustomGameAction(std::string_vie
|
||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<DukValue> pluginCallArgs;
|
||||||
|
if (GetTargetAPIVersion() <= API_VERSION_68_CUSTOM_ACTION_ARGS)
|
||||||
|
{
|
||||||
|
pluginCallArgs = { *dukArgs };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DukObject obj(_context);
|
||||||
|
obj.Set("action", actionz);
|
||||||
|
obj.Set("args", *dukArgs);
|
||||||
|
obj.Set("player", customAction.GetPlayer());
|
||||||
|
obj.Set("type", EnumValue(customAction.GetType()));
|
||||||
|
|
||||||
|
auto flags = customAction.GetActionFlags();
|
||||||
|
obj.Set("isClientOnly", (flags & GameActions::Flags::ClientOnly) != 0);
|
||||||
|
pluginCallArgs = { obj.Take() };
|
||||||
|
}
|
||||||
|
|
||||||
// Ready to call plugin handler
|
// Ready to call plugin handler
|
||||||
DukValue dukResult;
|
DukValue dukResult;
|
||||||
if (!isExecute)
|
if (!isExecute)
|
||||||
{
|
{
|
||||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Query, { *dukArgs }, false);
|
dukResult = ExecutePluginCall(customActionInfo.Owner, customActionInfo.Query, pluginCallArgs, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dukResult = ExecutePluginCall(customAction.Owner, customAction.Execute, { *dukArgs }, true);
|
dukResult = ExecutePluginCall(customActionInfo.Owner, customActionInfo.Execute, pluginCallArgs, true);
|
||||||
}
|
}
|
||||||
return DukToGameActionResult(dukResult);
|
return DukToGameActionResult(dukResult);
|
||||||
}
|
}
|
||||||
|
@ -1469,7 +1492,13 @@ std::unique_ptr<GameAction> ScriptEngine::CreateGameAction(const std::string& ac
|
||||||
auto jsonz = duk_json_encode(ctx, -1);
|
auto jsonz = duk_json_encode(ctx, -1);
|
||||||
auto json = std::string(jsonz);
|
auto json = std::string(jsonz);
|
||||||
duk_pop(ctx);
|
duk_pop(ctx);
|
||||||
return std::make_unique<CustomAction>(actionid, json);
|
auto customAction = std::make_unique<CustomAction>(actionid, json);
|
||||||
|
|
||||||
|
if (customAction->GetPlayer() == -1 && network_get_mode() != NETWORK_MODE_NONE)
|
||||||
|
{
|
||||||
|
customAction->SetPlayer(network_get_current_player_id());
|
||||||
|
}
|
||||||
|
return customAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScriptEngine::InitSharedStorage()
|
void ScriptEngine::InitSharedStorage()
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#ifdef ENABLE_SCRIPTING
|
#ifdef ENABLE_SCRIPTING
|
||||||
|
|
||||||
|
# include "../actions/CustomAction.h"
|
||||||
# include "../common.h"
|
# include "../common.h"
|
||||||
# include "../core/FileWatcher.h"
|
# include "../core/FileWatcher.h"
|
||||||
# include "../management/Finance.h"
|
# include "../management/Finance.h"
|
||||||
|
@ -46,11 +47,12 @@ namespace OpenRCT2
|
||||||
|
|
||||||
namespace OpenRCT2::Scripting
|
namespace OpenRCT2::Scripting
|
||||||
{
|
{
|
||||||
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 68;
|
static constexpr int32_t OPENRCT2_PLUGIN_API_VERSION = 69;
|
||||||
|
|
||||||
// Versions marking breaking changes.
|
// Versions marking breaking changes.
|
||||||
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
|
static constexpr int32_t API_VERSION_33_PEEP_DEPRECATION = 33;
|
||||||
static constexpr int32_t API_VERSION_63_G2_REORDER = 63;
|
static constexpr int32_t API_VERSION_63_G2_REORDER = 63;
|
||||||
|
static constexpr int32_t API_VERSION_68_CUSTOM_ACTION_ARGS = 68;
|
||||||
|
|
||||||
# ifndef DISABLE_NETWORK
|
# ifndef DISABLE_NETWORK
|
||||||
class ScSocketBase;
|
class ScSocketBase;
|
||||||
|
@ -236,8 +238,7 @@ namespace OpenRCT2::Scripting
|
||||||
void AddNetworkPlugin(std::string_view code);
|
void AddNetworkPlugin(std::string_view code);
|
||||||
void RemoveNetworkPlugins();
|
void RemoveNetworkPlugins();
|
||||||
|
|
||||||
[[nodiscard]] GameActions::Result QueryOrExecuteCustomGameAction(
|
[[nodiscard]] GameActions::Result QueryOrExecuteCustomGameAction(const CustomAction& action, bool isExecute);
|
||||||
std::string_view id, std::string_view args, bool isExecute);
|
|
||||||
bool RegisterCustomAction(
|
bool RegisterCustomAction(
|
||||||
const std::shared_ptr<Plugin>& plugin, std::string_view action, const DukValue& query, const DukValue& execute);
|
const std::shared_ptr<Plugin>& plugin, std::string_view action, const DukValue& query, const DukValue& execute);
|
||||||
void RunGameActionHooks(const GameAction& action, GameActions::Result& result, bool isExecute);
|
void RunGameActionHooks(const GameAction& action, GameActions::Result& result, bool isExecute);
|
||||||
|
|
Loading…
Reference in New Issue