From d2f1eeff981892348de8f1c4a30505a93d950c01 Mon Sep 17 00:00:00 2001 From: Aussiemon <1407091-Aussiemon@users.noreply.gitlab.com> Date: Mon, 26 May 2025 01:22:10 -0600 Subject: [PATCH] Prevent hard freeze when camera receives invalid inputs from Lua --- CHANGELOG.md | 1 + apps/openmw/mwlua/camerabindings.cpp | 26 +++++++++------- components/misc/finitenumbers.hpp | 45 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 11 deletions(-) create mode 100644 components/misc/finitenumbers.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7429aa2ddd..4117e86054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -234,6 +234,7 @@ Bug #8445: Launcher crashes on exit when cell name loading thread is still running Bug #8462: Crashes when resizing the window on macOS Bug #8465: Blue screen w/ antialiasing and post-processing on macOS + Bug #8503: Camera does not handle NaN gracefully Feature #1415: Infinite fall failsafe Feature #2566: Handle NAM9 records for manual cell references Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking diff --git a/apps/openmw/mwlua/camerabindings.cpp b/apps/openmw/mwlua/camerabindings.cpp index d64110ea98..345ca9af65 100644 --- a/apps/openmw/mwlua/camerabindings.cpp +++ b/apps/openmw/mwlua/camerabindings.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include "../mwbase/environment.hpp" @@ -11,11 +12,12 @@ namespace MWLua { - using CameraMode = MWRender::Camera::Mode; sol::table initCameraPackage(sol::state_view lua) { + using FiniteFloat = Misc::FiniteFloat; + MWRender::Camera* camera = MWBase::Environment::get().getWorld()->getCamera(); MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager(); @@ -49,26 +51,27 @@ namespace MWLua api["getRoll"] = [camera]() { return -camera->getRoll(); }; api["setStaticPosition"] = [camera](const osg::Vec3f& pos) { camera->setStaticPosition(pos); }; - api["setPitch"] = [camera](float v) { + api["setPitch"] = [camera](const FiniteFloat v) { camera->setPitch(-v, true); if (camera->getMode() == CameraMode::ThirdPerson) camera->calculateDeferredRotation(); }; - api["setYaw"] = [camera](float v) { + api["setYaw"] = [camera](const FiniteFloat v) { camera->setYaw(-v, true); if (camera->getMode() == CameraMode::ThirdPerson) camera->calculateDeferredRotation(); }; - api["setRoll"] = [camera](float v) { camera->setRoll(-v); }; - api["setExtraPitch"] = [camera](float v) { camera->setExtraPitch(-v); }; - api["setExtraYaw"] = [camera](float v) { camera->setExtraYaw(-v); }; - api["setExtraRoll"] = [camera](float v) { camera->setExtraRoll(-v); }; + api["setRoll"] = [camera](const FiniteFloat v) { camera->setRoll(-v); }; + api["setExtraPitch"] = [camera](const FiniteFloat v) { camera->setExtraPitch(-v); }; + api["setExtraYaw"] = [camera](const FiniteFloat v) { camera->setExtraYaw(-v); }; + api["setExtraRoll"] = [camera](const FiniteFloat v) { camera->setExtraRoll(-v); }; api["getExtraPitch"] = [camera]() { return -camera->getExtraPitch(); }; api["getExtraYaw"] = [camera]() { return -camera->getExtraYaw(); }; api["getExtraRoll"] = [camera]() { return -camera->getExtraRoll(); }; api["getThirdPersonDistance"] = [camera]() { return camera->getCameraDistance(); }; - api["setPreferredThirdPersonDistance"] = [camera](float v) { camera->setPreferredCameraDistance(v); }; + api["setPreferredThirdPersonDistance"] + = [camera](const FiniteFloat v) { camera->setPreferredCameraDistance(v); }; api["getFirstPersonOffset"] = [camera]() { return camera->getFirstPersonOffset(); }; api["setFirstPersonOffset"] = [camera](const osg::Vec3f& v) { camera->setFirstPersonOffset(v); }; @@ -76,7 +79,7 @@ namespace MWLua api["getFocalPreferredOffset"] = [camera]() -> osg::Vec2f { return camera->getFocalPointTargetOffset(); }; api["setFocalPreferredOffset"] = [camera](const osg::Vec2f& v) { camera->setFocalPointTargetOffset(v); }; api["getFocalTransitionSpeed"] = [camera]() { return camera->getFocalPointTransitionSpeed(); }; - api["setFocalTransitionSpeed"] = [camera](float v) { camera->setFocalPointTransitionSpeed(v); }; + api["setFocalTransitionSpeed"] = [camera](const FiniteFloat v) { camera->setFocalPointTransitionSpeed(v); }; api["instantTransition"] = [camera]() { camera->instantTransition(); }; api["getCollisionType"] = [camera]() { return camera->getCollisionType(); }; @@ -86,11 +89,12 @@ namespace MWLua api["getFieldOfView"] = [renderingManager]() { return osg::DegreesToRadians(renderingManager->getFieldOfView()); }; api["setFieldOfView"] - = [renderingManager](float v) { renderingManager->setFieldOfView(osg::RadiansToDegrees(v)); }; + = [renderingManager](const FiniteFloat v) { renderingManager->setFieldOfView(osg::RadiansToDegrees(v)); }; api["getBaseViewDistance"] = [] { return Settings::camera().mViewingDistance.get(); }; api["getViewDistance"] = [renderingManager]() { return renderingManager->getViewDistance(); }; - api["setViewDistance"] = [renderingManager](float d) { renderingManager->setViewDistance(d, true); }; + api["setViewDistance"] + = [renderingManager](const FiniteFloat d) { renderingManager->setViewDistance(d, true); }; api["getViewTransform"] = [camera]() { return LuaUtil::TransformM{ camera->getViewMatrix() }; }; diff --git a/components/misc/finitenumbers.hpp b/components/misc/finitenumbers.hpp new file mode 100644 index 0000000000..3d166ee26d --- /dev/null +++ b/components/misc/finitenumbers.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_COMPONENTS_MISC_FINITENUMBERS_HPP +#define OPENMW_COMPONENTS_MISC_FINITENUMBERS_HPP + +#include + +#include +#include + +namespace Misc +{ + struct FiniteFloat + { + float mValue; + FiniteFloat(float v) + { + if (!std::isfinite(v)) + throw std::invalid_argument("Value must be a finite number"); + mValue = v; + } + operator float() const { return mValue; } + }; +} + +namespace sol +{ + using FiniteFloat = Misc::FiniteFloat; + + template + bool sol_lua_check( + sol::types, lua_State* L, int index, Handler&& handler, sol::stack::record& tracking) + { + bool success = sol::stack::check(L, lua_absindex(L, index), handler); + tracking.use(1); + return success; + } + + static FiniteFloat sol_lua_get(sol::types, lua_State* L, int index, sol::stack::record& tracking) + { + float val = sol::stack::get(L, lua_absindex(L, index)); + tracking.use(1); + return FiniteFloat(val); + } +} + +#endif