From a5c8ff920ffbb663d126405ddd9b2462564f85a6 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sun, 23 Feb 2020 02:00:22 +0000 Subject: [PATCH] Add game mutable protection --- src/openrct2-ui/scripting/CustomMenu.h | 2 +- src/openrct2-ui/scripting/CustomWindow.cpp | 2 +- src/openrct2-ui/scripting/ScViewport.hpp | 3 ++- src/openrct2/GameState.cpp | 4 ++-- src/openrct2/network/Network.cpp | 2 +- src/openrct2/scripting/HookEngine.cpp | 12 +++++----- src/openrct2/scripting/HookEngine.h | 6 ++--- src/openrct2/scripting/ScDate.hpp | 2 ++ src/openrct2/scripting/ScPark.hpp | 6 +++++ src/openrct2/scripting/ScRide.hpp | 5 +++++ src/openrct2/scripting/ScThing.hpp | 11 ++++++--- src/openrct2/scripting/ScTile.hpp | 4 ++++ src/openrct2/scripting/ScriptEngine.cpp | 26 ++++++++++++++++++---- src/openrct2/scripting/ScriptEngine.h | 16 +++++++++++-- 14 files changed, 77 insertions(+), 24 deletions(-) diff --git a/src/openrct2-ui/scripting/CustomMenu.h b/src/openrct2-ui/scripting/CustomMenu.h index 4a26fb1a56..74f1a009bd 100644 --- a/src/openrct2-ui/scripting/CustomMenu.h +++ b/src/openrct2-ui/scripting/CustomMenu.h @@ -38,7 +38,7 @@ namespace OpenRCT2::Scripting auto& execInfo = scriptEngine.GetExecInfo(); auto ctx = scriptEngine.GetContext(); - ScriptExecutionInfo::PluginScope scope(execInfo, Owner); + ScriptExecutionInfo::PluginScope scope(execInfo, Owner, false); Callback.push(); duk_pcall(ctx, 0); duk_pop(ctx); diff --git a/src/openrct2-ui/scripting/CustomWindow.cpp b/src/openrct2-ui/scripting/CustomWindow.cpp index 7a55c621cd..d93b512d13 100644 --- a/src/openrct2-ui/scripting/CustomWindow.cpp +++ b/src/openrct2-ui/scripting/CustomWindow.cpp @@ -648,7 +648,7 @@ namespace OpenRCT2::Ui::Windows auto& scriptEngine = GetContext()->GetScriptEngine(); auto& execInfo = scriptEngine.GetExecInfo(); { - ScriptExecutionInfo::PluginScope scope(execInfo, owner); + ScriptExecutionInfo::PluginScope scope(execInfo, owner, false); dukHandler.push(); for (const auto& arg : args) { diff --git a/src/openrct2-ui/scripting/ScViewport.hpp b/src/openrct2-ui/scripting/ScViewport.hpp index 17e5e99169..c56cfccec5 100644 --- a/src/openrct2-ui/scripting/ScViewport.hpp +++ b/src/openrct2-ui/scripting/ScViewport.hpp @@ -232,7 +232,8 @@ namespace OpenRCT2::Scripting dukglue_register_property(ctx, &ScViewport::bottom_get, &ScViewport::bottom_set, "bottom"); dukglue_register_property(ctx, &ScViewport::rotation_get, &ScViewport::rotation_set, "rotation"); dukglue_register_property(ctx, &ScViewport::zoom_get, &ScViewport::zoom_set, "zoom"); - dukglue_register_property(ctx, &ScViewport::visibilityFlags_get, &ScViewport::visibilityFlags_set, "visibilityFlags"); + dukglue_register_property( + ctx, &ScViewport::visibilityFlags_get, &ScViewport::visibilityFlags_set, "visibilityFlags"); dukglue_register_method(ctx, &ScViewport::getCentrePosition, "getCentrePosition"); dukglue_register_method(ctx, &ScViewport::moveTo, "moveTo"); dukglue_register_method(ctx, &ScViewport::scrollTo, "scrollTo"); diff --git a/src/openrct2/GameState.cpp b/src/openrct2/GameState.cpp index 2523a03dac..568fdb2183 100644 --- a/src/openrct2/GameState.cpp +++ b/src/openrct2/GameState.cpp @@ -270,7 +270,7 @@ void GameState::UpdateLogic() } auto& hookEngine = GetContext()->GetScriptEngine().GetHookEngine(); - hookEngine.Call(HOOK_TYPE::INTERVAL_TICK); + hookEngine.Call(HOOK_TYPE::INTERVAL_TICK, true); auto day = _date.GetDay(); @@ -279,7 +279,7 @@ void GameState::UpdateLogic() if (day != _date.GetDay()) { - hookEngine.Call(HOOK_TYPE::INTERVAL_DAY); + hookEngine.Call(HOOK_TYPE::INTERVAL_DAY, true); } scenario_update(); diff --git a/src/openrct2/network/Network.cpp b/src/openrct2/network/Network.cpp index 182c6c2153..10f36d33a6 100644 --- a/src/openrct2/network/Network.cpp +++ b/src/openrct2/network/Network.cpp @@ -2905,7 +2905,7 @@ void Network::Server_Handle_CHAT(NetworkConnection& connection, NetworkPacket& p auto e = DukValue::take_from_stack(ctx); // Call the subscriptions - hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_CHAT, e); + hookEngine.Call(OpenRCT2::Scripting::HOOK_TYPE::NETWORK_CHAT, e, false); // Update text from object if subscriptions changed it if (e["message"].type() != DukValue::Type::STRING) diff --git a/src/openrct2/scripting/HookEngine.cpp b/src/openrct2/scripting/HookEngine.cpp index 695586b9cb..be79ffe3b3 100644 --- a/src/openrct2/scripting/HookEngine.cpp +++ b/src/openrct2/scripting/HookEngine.cpp @@ -93,12 +93,12 @@ bool HookEngine::HasSubscriptions(HOOK_TYPE type) const return !hookList.Hooks.empty(); } -void HookEngine::Call(HOOK_TYPE type) +void HookEngine::Call(HOOK_TYPE type, bool isGameStateMutable) { auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner); + ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner, false); const auto& function = hook.Function; function.push(); @@ -107,12 +107,12 @@ void HookEngine::Call(HOOK_TYPE type) } } -void HookEngine::Call(HOOK_TYPE type, DukValue args) +void HookEngine::Call(HOOK_TYPE type, DukValue args, bool isGameStateMutable) { auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner); + ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner, false); const auto& function = hook.Function; auto ctx = function.context(); @@ -124,12 +124,12 @@ void HookEngine::Call(HOOK_TYPE type, DukValue args) } } -void HookEngine::Call(HOOK_TYPE type, const std::initializer_list>& args) +void HookEngine::Call(HOOK_TYPE type, const std::initializer_list>& args, bool isGameStateMutable) { auto& hookList = GetHookList(type); for (auto& hook : hookList.Hooks) { - ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner); + ScriptExecutionInfo::PluginScope scope(_execInfo, hook.Owner, false); const auto& function = hook.Function; auto ctx = function.context(); diff --git a/src/openrct2/scripting/HookEngine.h b/src/openrct2/scripting/HookEngine.h index e2dde251b8..9efa3d4a74 100644 --- a/src/openrct2/scripting/HookEngine.h +++ b/src/openrct2/scripting/HookEngine.h @@ -81,9 +81,9 @@ namespace OpenRCT2::Scripting void UnsubscribeAll(std::shared_ptr owner); void UnsubscribeAll(); bool HasSubscriptions(HOOK_TYPE type) const; - void Call(HOOK_TYPE type); - void Call(HOOK_TYPE type, DukValue args); - void Call(HOOK_TYPE type, const std::initializer_list>& args); + void Call(HOOK_TYPE type, bool isGameStateMutable); + void Call(HOOK_TYPE type, DukValue args, bool isGameStateMutable); + void Call(HOOK_TYPE type, const std::initializer_list>& args, bool isGameStateMutable); private: HookList& GetHookList(HOOK_TYPE type); diff --git a/src/openrct2/scripting/ScDate.hpp b/src/openrct2/scripting/ScDate.hpp index 03bb89c715..96838e5c90 100644 --- a/src/openrct2/scripting/ScDate.hpp +++ b/src/openrct2/scripting/ScDate.hpp @@ -42,6 +42,7 @@ namespace OpenRCT2::Scripting void monthsElapsed_set(uint32_t value) { + ThrowIfGameStateNotMutable(); gDateMonthsElapsed = value; } @@ -53,6 +54,7 @@ namespace OpenRCT2::Scripting void monthProgress_set(int32_t value) { + ThrowIfGameStateNotMutable(); gDateMonthTicks = value; } diff --git a/src/openrct2/scripting/ScPark.hpp b/src/openrct2/scripting/ScPark.hpp index 57d133f57e..6d073df32c 100644 --- a/src/openrct2/scripting/ScPark.hpp +++ b/src/openrct2/scripting/ScPark.hpp @@ -16,6 +16,7 @@ #include "../windows/Intent.h" #include "../world/Park.h" #include "Duktape.hpp" +#include "ScriptEngine.h" #include @@ -30,6 +31,7 @@ namespace OpenRCT2::Scripting } void cash_set(money32 value) { + ThrowIfGameStateNotMutable(); gCash = value; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); context_broadcast_intent(&intent); @@ -41,6 +43,7 @@ namespace OpenRCT2::Scripting } void rating_set(int32_t value) { + ThrowIfGameStateNotMutable(); gParkRating = std::min(std::max(0, value), 999); auto intent = Intent(INTENT_ACTION_UPDATE_PARK_RATING); context_broadcast_intent(&intent); @@ -52,6 +55,7 @@ namespace OpenRCT2::Scripting } void bankLoan_set(money32 value) { + ThrowIfGameStateNotMutable(); gBankLoan = value; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); context_broadcast_intent(&intent); @@ -63,6 +67,7 @@ namespace OpenRCT2::Scripting } void maxBankLoan_set(money32 value) { + ThrowIfGameStateNotMutable(); gMaxBankLoan = value; auto intent = Intent(INTENT_ACTION_UPDATE_CASH); context_broadcast_intent(&intent); @@ -70,6 +75,7 @@ namespace OpenRCT2::Scripting void postMessage(DukValue message) { + ThrowIfGameStateNotMutable(); try { uint8_t type = NEWS_ITEM_BLANK; diff --git a/src/openrct2/scripting/ScRide.hpp b/src/openrct2/scripting/ScRide.hpp index f316bf906c..1e75fb7d3b 100644 --- a/src/openrct2/scripting/ScRide.hpp +++ b/src/openrct2/scripting/ScRide.hpp @@ -114,6 +114,7 @@ namespace OpenRCT2::Scripting } void name_set(std::string value) { + ThrowIfGameStateNotMutable(); _ride->custom_name = value; } @@ -123,6 +124,7 @@ namespace OpenRCT2::Scripting } void excitement_set(int32_t value) { + ThrowIfGameStateNotMutable(); _ride->excitement = value; } @@ -132,6 +134,7 @@ namespace OpenRCT2::Scripting } void intensity_set(int32_t value) { + ThrowIfGameStateNotMutable(); _ride->intensity = value; } @@ -141,6 +144,7 @@ namespace OpenRCT2::Scripting } void nausea_set(int32_t value) { + ThrowIfGameStateNotMutable(); _ride->nausea = value; } @@ -150,6 +154,7 @@ namespace OpenRCT2::Scripting } void totalCustomers_set(int32_t value) { + ThrowIfGameStateNotMutable(); _ride->total_customers = value; } diff --git a/src/openrct2/scripting/ScThing.hpp b/src/openrct2/scripting/ScThing.hpp index 41496e1199..e67aa60b24 100644 --- a/src/openrct2/scripting/ScThing.hpp +++ b/src/openrct2/scripting/ScThing.hpp @@ -42,7 +42,8 @@ namespace OpenRCT2::Scripting } void x_set(int32_t value) { - _sprite->generic.x = value; + ThrowIfGameStateNotMutable(); + sprite_move(value, _sprite->generic.y, _sprite->generic.z, &_sprite->generic); } // y getter and setter @@ -52,7 +53,8 @@ namespace OpenRCT2::Scripting } void y_set(int32_t value) { - _sprite->generic.y = value; + ThrowIfGameStateNotMutable(); + sprite_move(_sprite->generic.x, value, _sprite->generic.z, &_sprite->generic); } // z getter and setter @@ -62,7 +64,8 @@ namespace OpenRCT2::Scripting } void z_set(int16_t value) { - _sprite->generic.z = value; + ThrowIfGameStateNotMutable(); + sprite_move(_sprite->generic.x, _sprite->generic.y, value, &_sprite->generic); } uint8_t tshirtColour_get() @@ -71,6 +74,7 @@ namespace OpenRCT2::Scripting } void tshirtColour_set(uint8_t value) { + ThrowIfGameStateNotMutable(); _sprite->peep.tshirt_colour = value; } uint8_t trousersColour_get() @@ -79,6 +83,7 @@ namespace OpenRCT2::Scripting } void trousersColour_set(uint8_t value) { + ThrowIfGameStateNotMutable(); _sprite->peep.trousers_colour = value; } diff --git a/src/openrct2/scripting/ScTile.hpp b/src/openrct2/scripting/ScTile.hpp index 82593fe111..d65e9ed3ac 100644 --- a/src/openrct2/scripting/ScTile.hpp +++ b/src/openrct2/scripting/ScTile.hpp @@ -69,6 +69,7 @@ namespace OpenRCT2::Scripting } void broken_set(bool value) { + ThrowIfGameStateNotMutable(); if (value) { _element->flags |= TILE_ELEMENT_FLAG_BROKEN; @@ -85,6 +86,7 @@ namespace OpenRCT2::Scripting } void baseHeight_set(uint8_t newBaseHeight) { + ThrowIfGameStateNotMutable(); _element->base_height = newBaseHeight; } @@ -94,6 +96,7 @@ namespace OpenRCT2::Scripting } void clearanceHeight_set(uint8_t newClearanceHeight) { + ThrowIfGameStateNotMutable(); _element->clearance_height = newClearanceHeight; } @@ -106,6 +109,7 @@ namespace OpenRCT2::Scripting } void grassLength_set(uint8_t value) { + ThrowIfGameStateNotMutable(); auto el = _element->AsSurface(); if (el != nullptr) { diff --git a/src/openrct2/scripting/ScriptEngine.cpp b/src/openrct2/scripting/ScriptEngine.cpp index 163b8e5b74..e067791114 100644 --- a/src/openrct2/scripting/ScriptEngine.cpp +++ b/src/openrct2/scripting/ScriptEngine.cpp @@ -132,7 +132,7 @@ void ScriptEngine::LoadPlugin(std::shared_ptr& plugin) { try { - ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); + ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); plugin->Load(); auto metadata = plugin->GetMetadata(); @@ -162,7 +162,7 @@ void ScriptEngine::StopPlugin(std::shared_ptr plugin) callback(plugin); } - ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); + ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); try { plugin->Stop(); @@ -214,7 +214,7 @@ void ScriptEngine::AutoReloadPlugins() { StopPlugin(plugin); - ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); + ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); plugin->Load(); LogPluginInfo(plugin, "Reloaded"); plugin->Start(); @@ -247,7 +247,7 @@ void ScriptEngine::StartPlugins() { if (!plugin->HasStarted() && ShouldStartPlugin(plugin)) { - ScriptExecutionInfo::PluginScope scope(_execInfo, plugin); + ScriptExecutionInfo::PluginScope scope(_execInfo, plugin, false); try { LogPluginInfo(plugin, "Started"); @@ -380,3 +380,21 @@ static std::string Stringify(duk_context* ctx, duk_idx_t idx) return duk_safe_to_string(ctx, idx); } } + +bool OpenRCT2::Scripting::IsGameStateMutable() +{ + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto& execInfo = scriptEngine.GetExecInfo(); + return execInfo.IsGameStateMutable(); +} + +void OpenRCT2::Scripting::ThrowIfGameStateNotMutable() +{ + auto& scriptEngine = GetContext()->GetScriptEngine(); + auto& execInfo = scriptEngine.GetExecInfo(); + if (!execInfo.IsGameStateMutable()) + { + auto ctx = scriptEngine.GetContext(); + duk_error(ctx, DUK_ERR_ERROR, "Game state is not mutable in this context."); + } +} diff --git a/src/openrct2/scripting/ScriptEngine.h b/src/openrct2/scripting/ScriptEngine.h index 4156c13825..da6517f699 100644 --- a/src/openrct2/scripting/ScriptEngine.h +++ b/src/openrct2/scripting/ScriptEngine.h @@ -39,6 +39,7 @@ namespace OpenRCT2::Scripting { private: std::shared_ptr _plugin; + bool _isGameStateMutable{}; public: class PluginScope @@ -48,16 +49,18 @@ namespace OpenRCT2::Scripting std::shared_ptr _plugin; public: - PluginScope(ScriptExecutionInfo& execInfo, std::shared_ptr plugin) + PluginScope(ScriptExecutionInfo& execInfo, std::shared_ptr plugin, bool isGameStateMutable) : _execInfo(execInfo) , _plugin(plugin) { _execInfo._plugin = plugin; + _execInfo._isGameStateMutable = isGameStateMutable; } PluginScope(const PluginScope&) = delete; ~PluginScope() { _execInfo._plugin = nullptr; + _execInfo._isGameStateMutable = false; } }; @@ -65,6 +68,11 @@ namespace OpenRCT2::Scripting { return _plugin; } + + bool IsGameStateMutable() + { + return _isGameStateMutable; + } }; class DukContext @@ -150,9 +158,13 @@ namespace OpenRCT2::Scripting void LoadPlugin(std::shared_ptr& plugin); void StopPlugin(std::shared_ptr plugin); bool ShouldLoadScript(const std::string& path); - bool ShouldStartPlugin(const std::shared_ptr &plugin); + bool ShouldStartPlugin(const std::shared_ptr& plugin); void SetupHotReloading(); void AutoReloadPlugins(); void ProcessREPL(); }; + + bool IsGameStateMutable(); + void ThrowIfGameStateNotMutable(); + } // namespace OpenRCT2::Scripting