diff --git a/apps/openmw/mwlua/actions.cpp b/apps/openmw/mwlua/actions.cpp index 92a7f915bb..7389363fe6 100644 --- a/apps/openmw/mwlua/actions.cpp +++ b/apps/openmw/mwlua/actions.cpp @@ -3,6 +3,7 @@ #include #include +#include #include "../mwworld/cellstore.hpp" #include "../mwworld/class.hpp" @@ -12,14 +13,34 @@ namespace MWLua { +#ifdef NDEBUG + Action::Action(LuaUtil::LuaState* state) {} +#else + Action::Action(LuaUtil::LuaState* state) : mCallerTraceback(state->debugTraceback()) {} +#endif + + void Action::safeApply(WorldView& w) const + { + try + { + apply(w); + } + catch (const std::exception& e) + { + Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what(); +#ifdef NDEBUG + Log(Debug::Error) << "Traceback is available only in debug builds"; +#else + Log(Debug::Error) << "Caller " << mCallerTraceback; +#endif + } + } + void TeleportAction::apply(WorldView& worldView) const { MWWorld::CellStore* cell = worldView.findCell(mCell, mPos); if (!cell) - { - Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'"; - return; - } + throw std::runtime_error(std::string("cell not found: '") + mCell + "'"); MWBase::World* world = MWBase::Environment::get().getWorld(); MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false); diff --git a/apps/openmw/mwlua/actions.hpp b/apps/openmw/mwlua/actions.hpp index 900b175320..3b71eef4a9 100644 --- a/apps/openmw/mwlua/actions.hpp +++ b/apps/openmw/mwlua/actions.hpp @@ -6,6 +6,11 @@ #include "object.hpp" #include "worldview.hpp" +namespace LuaUtil +{ + class LuaState; +} + namespace MWLua { @@ -16,17 +21,27 @@ namespace MWLua class Action { public: + Action(LuaUtil::LuaState* state); virtual ~Action() {} + + void safeApply(WorldView&) const; virtual void apply(WorldView&) const = 0; + virtual std::string toString() const = 0; + + private: +#ifndef NDEBUG + std::string mCallerTraceback; +#endif }; class TeleportAction final : public Action { public: - TeleportAction(ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot) - : mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {} + TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot) + : Action(state), mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {} void apply(WorldView&) const override; + std::string toString() const override { return "TeleportAction"; } private: ObjectId mObject; @@ -41,9 +56,11 @@ namespace MWLua using Item = std::variant; // recordId or ObjectId using Equipment = std::map; // slot to item - SetEquipmentAction(ObjectId actor, Equipment equipment) : mActor(actor), mEquipment(std::move(equipment)) {} + SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment) + : Action(state), mActor(actor), mEquipment(std::move(equipment)) {} void apply(WorldView&) const override; + std::string toString() const override { return "SetEquipmentAction"; } private: ObjectId mActor; diff --git a/apps/openmw/mwlua/localscripts.cpp b/apps/openmw/mwlua/localscripts.cpp index ee23b4b90c..98cf0a0b1a 100644 --- a/apps/openmw/mwlua/localscripts.cpp +++ b/apps/openmw/mwlua/localscripts.cpp @@ -39,7 +39,7 @@ namespace MWLua selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; }; selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; }; - selfAPI["setEquipment"] = [manager=context.mLuaManager](const SelfObject& obj, sol::table equipment) + selfAPI["setEquipment"] = [context](const SelfObject& obj, sol::table equipment) { if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) { @@ -56,7 +56,7 @@ namespace MWLua else eqp[slot] = value.as(); } - manager->addAction(std::make_unique(obj.id(), std::move(eqp))); + context.mLuaManager->addAction(std::make_unique(context.mLua, obj.id(), std::move(eqp))); }; selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional { diff --git a/apps/openmw/mwlua/luabindings.cpp b/apps/openmw/mwlua/luabindings.cpp index 07e8dcc30c..bf323351a3 100644 --- a/apps/openmw/mwlua/luabindings.cpp +++ b/apps/openmw/mwlua/luabindings.cpp @@ -28,8 +28,7 @@ namespace MWLua api["API_REVISION"] = 9; api["quit"] = [lua]() { - std::string traceback = lua->sol()["debug"]["traceback"]().get(); - Log(Debug::Warning) << "Quit requested by a Lua script.\n" << traceback; + Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback(); MWBase::Environment::get().getStateManager()->requestQuit(); }; api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) diff --git a/apps/openmw/mwlua/luamanagerimp.cpp b/apps/openmw/mwlua/luamanagerimp.cpp index 2b9f0702e2..dae11513b5 100644 --- a/apps/openmw/mwlua/luamanagerimp.cpp +++ b/apps/openmw/mwlua/luamanagerimp.cpp @@ -188,11 +188,11 @@ namespace MWLua mUIMessages.clear(); for (std::unique_ptr& action : mActionQueue) - action->apply(mWorldView); + action->safeApply(mWorldView); mActionQueue.clear(); if (mTeleportPlayerAction) - mTeleportPlayerAction->apply(mWorldView); + mTeleportPlayerAction->safeApply(mWorldView); mTeleportPlayerAction.reset(); } diff --git a/apps/openmw/mwlua/objectbindings.cpp b/apps/openmw/mwlua/objectbindings.cpp index 2eceecc061..9857170b32 100644 --- a/apps/openmw/mwlua/objectbindings.cpp +++ b/apps/openmw/mwlua/objectbindings.cpp @@ -179,16 +179,16 @@ namespace MWLua localScripts->removeScript(*scriptId); }; - objectT["teleport"] = [luaManager=context.mLuaManager](const GObject& object, std::string_view cell, - const osg::Vec3f& pos, const sol::optional& optRot) + objectT["teleport"] = [context](const GObject& object, std::string_view cell, + const osg::Vec3f& pos, const sol::optional& optRot) { MWWorld::Ptr ptr = object.ptr(); osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3(); - auto action = std::make_unique(object.id(), std::string(cell), pos, rot); + auto action = std::make_unique(context.mLua, object.id(), std::string(cell), pos, rot); if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) - luaManager->addTeleportPlayerAction(std::move(action)); + context.mLuaManager->addTeleportPlayerAction(std::move(action)); else - luaManager->addAction(std::move(action)); + context.mLuaManager->addAction(std::move(action)); }; } else @@ -352,7 +352,7 @@ namespace MWLua if constexpr (std::is_same_v) { // Only for global scripts - objectT["setEquipment"] = [manager=context.mLuaManager](const GObject& obj, sol::table equipment) + objectT["setEquipment"] = [context](const GObject& obj, sol::table equipment) { if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) { @@ -360,7 +360,8 @@ namespace MWLua throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots"); return; } - manager->addAction(std::make_unique(obj.id(), parseEquipmentTable(equipment))); + context.mLuaManager->addAction(std::make_unique( + context.mLua, obj.id(), parseEquipmentTable(equipment))); }; // TODO diff --git a/components/lua/luastate.hpp b/components/lua/luastate.hpp index b302e565f4..71cdb4f75d 100644 --- a/components/lua/luastate.hpp +++ b/components/lua/luastate.hpp @@ -35,6 +35,11 @@ namespace LuaUtil // Returns underlying sol::state. sol::state& sol() { return mLua; } + // Can be used by a C++ function that is called from Lua to get the Lua traceback. + // Makes no sense if called not from Lua code. + // Note: It is a slow function, should be used for debug purposes only. + std::string debugTraceback() { return mLua["debug"]["traceback"]().get(); } + // A shortcut to create a new Lua table. sol::table newTable() { return sol::table(mLua, sol::create); }