1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-06 06:32:56 +01:00

Fix #14190: Game crash likely related to plug-in hotkeys

Do not cache references to RegisteredShortcut as they can be invalidated when new shortcuts are registered / removed.
Use a map to improve query performance of shortcut by ID.
Store a separate list of strings for the map to use as a key.
This commit is contained in:
Ted John
2021-02-28 01:13:43 +00:00
parent 314faa69ca
commit 17a29dfe87
5 changed files with 30 additions and 33 deletions

View File

@@ -291,21 +291,17 @@ void InputManager::ProcessHoldEvents()
auto& shortcutManager = GetShortcutManager();
if (!shortcutManager.IsPendingShortcutChange())
{
ProcessViewScrollEvent(ShortcutId::ViewScrollUp, _scrollUpShortcut, { 0, -1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollDown, _scrollDownShortcut, { 0, 1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollLeft, _scrollLeftShortcut, { -1, 0 });
ProcessViewScrollEvent(ShortcutId::ViewScrollRight, _scrollRightShortcut, { 1, 0 });
ProcessViewScrollEvent(ShortcutId::ViewScrollUp, { 0, -1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollDown, { 0, 1 });
ProcessViewScrollEvent(ShortcutId::ViewScrollLeft, { -1, 0 });
ProcessViewScrollEvent(ShortcutId::ViewScrollRight, { 1, 0 });
}
}
void InputManager::ProcessViewScrollEvent(
std::string_view shortcutId, RegisteredShortcut*& shortcut, const ScreenCoordsXY& delta)
void InputManager::ProcessViewScrollEvent(std::string_view shortcutId, const ScreenCoordsXY& delta)
{
if (shortcut == nullptr)
{
auto& shortcutManager = GetShortcutManager();
shortcut = shortcutManager.GetShortcut(shortcutId);
}
auto& shortcutManager = GetShortcutManager();
auto shortcut = shortcutManager.GetShortcut(shortcutId);
if (shortcut != nullptr && GetState(*shortcut))
{
_viewScroll.x += delta.x;

View File

@@ -53,11 +53,6 @@ namespace OpenRCT2::Ui
uint32_t _mouseState;
std::vector<uint8_t> _keyboardState;
RegisteredShortcut* _scrollLeftShortcut{};
RegisteredShortcut* _scrollUpShortcut{};
RegisteredShortcut* _scrollRightShortcut{};
RegisteredShortcut* _scrollDownShortcut{};
void CheckJoysticks();
void HandleViewScrolling();
@@ -67,7 +62,7 @@ namespace OpenRCT2::Ui
void ProcessInGameConsole(const InputEvent& e);
void ProcessChat(const InputEvent& e);
void ProcessHoldEvents();
void ProcessViewScrollEvent(std::string_view shortcutId, RegisteredShortcut*& shortcut, const ScreenCoordsXY& delta);
void ProcessViewScrollEvent(std::string_view shortcutId, const ScreenCoordsXY& delta);
bool GetState(const RegisteredShortcut& shortcut) const;
bool GetState(const ShortcutInput& shortcut) const;

View File

@@ -119,22 +119,24 @@ void ShortcutManager::RegisterShortcut(RegisteredShortcut&& shortcut)
{
if (!shortcut.Id.empty() && GetShortcut(shortcut.Id) == nullptr)
{
Shortcuts.push_back(shortcut);
auto id = std::make_unique<std::string>(shortcut.Id);
auto idView = std::string_view(*id);
_ids.push_back(std::move(id));
Shortcuts[idView] = shortcut;
}
}
RegisteredShortcut* ShortcutManager::GetShortcut(std::string_view id)
{
auto result = std::find_if(Shortcuts.begin(), Shortcuts.end(), [id](const RegisteredShortcut& s) { return s.Id == id; });
return result == Shortcuts.end() ? nullptr : &(*result);
auto result = Shortcuts.find(id);
return result == Shortcuts.end() ? nullptr : &result->second;
}
void ShortcutManager::RemoveShortcut(std::string_view id)
{
Shortcuts.erase(
std::remove_if(
Shortcuts.begin(), Shortcuts.end(), [id](const RegisteredShortcut& shortcut) { return shortcut.Id == id; }),
Shortcuts.end());
Shortcuts.erase(id);
_ids.erase(
std::remove_if(_ids.begin(), _ids.end(), [id](const std::unique_ptr<std::string>& x) { return *x == id; }), _ids.end());
}
bool ShortcutManager::IsPendingShortcutChange() const
@@ -153,9 +155,9 @@ void ShortcutManager::ProcessEvent(const InputEvent& e)
{
for (const auto& shortcut : Shortcuts)
{
if (shortcut.Matches(e))
if (shortcut.second.Matches(e))
{
shortcut.Action();
shortcut.second.Action();
}
}
}
@@ -334,15 +336,15 @@ void ShortcutManager::SaveUserBindings(const fs::path& path)
for (const auto& shortcut : Shortcuts)
{
auto& jShortcut = root[shortcut.Id];
if (shortcut.Current.size() == 1)
auto& jShortcut = root[shortcut.second.Id];
if (shortcut.second.Current.size() == 1)
{
jShortcut = shortcut.Current[0].ToString();
jShortcut = shortcut.second.Current[0].ToString();
}
else
{
jShortcut = nlohmann::json::array();
for (const auto& binding : shortcut.Current)
for (const auto& binding : shortcut.second.Current)
{
jShortcut.push_back(binding.ToString());
}

View File

@@ -18,6 +18,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
@@ -118,8 +119,11 @@ namespace OpenRCT2::Ui
void LoadUserBindings(const fs::path& path);
void SaveUserBindings(const fs::path& path);
// We store the IDs separately so that we can safely use them for string_view in the map
std::vector<std::unique_ptr<std::string>> _ids;
public:
std::vector<RegisteredShortcut> Shortcuts;
std::unordered_map<std::string_view, RegisteredShortcut> Shortcuts;
ShortcutManager(const std::shared_ptr<IPlatformEnvironment>& env);
ShortcutManager(const ShortcutManager&) = delete;

View File

@@ -403,9 +403,9 @@ private:
auto& shortcutManager = GetShortcutManager();
for (const auto& shortcut : shortcutManager.Shortcuts)
{
if (IsInCurrentTab(shortcut))
if (IsInCurrentTab(shortcut.second))
{
result.push_back(&shortcut);
result.push_back(&shortcut.second);
}
}
return result;