Merge branch 'lua_action_errors' into 'master'

Add error checking in MWLua::Action

See merge request OpenMW/openmw!1401
pull/3224/head
psi29a 3 years ago
commit b20a413865

@ -3,6 +3,7 @@
#include <cstring> #include <cstring>
#include <components/debug/debuglog.hpp> #include <components/debug/debuglog.hpp>
#include <components/lua/luastate.hpp>
#include "../mwworld/cellstore.hpp" #include "../mwworld/cellstore.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
@ -12,14 +13,34 @@
namespace MWLua 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 void TeleportAction::apply(WorldView& worldView) const
{ {
MWWorld::CellStore* cell = worldView.findCell(mCell, mPos); MWWorld::CellStore* cell = worldView.findCell(mCell, mPos);
if (!cell) if (!cell)
{ throw std::runtime_error(std::string("cell not found: '") + mCell + "'");
Log(Debug::Error) << "LuaManager::applyTeleport -> cell not found: '" << mCell << "'";
return;
}
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false); MWWorld::Ptr obj = worldView.getObjectRegistry()->getPtr(mObject, false);

@ -6,6 +6,11 @@
#include "object.hpp" #include "object.hpp"
#include "worldview.hpp" #include "worldview.hpp"
namespace LuaUtil
{
class LuaState;
}
namespace MWLua namespace MWLua
{ {
@ -16,17 +21,27 @@ namespace MWLua
class Action class Action
{ {
public: public:
Action(LuaUtil::LuaState* state);
virtual ~Action() {} virtual ~Action() {}
void safeApply(WorldView&) const;
virtual void apply(WorldView&) const = 0; virtual void apply(WorldView&) const = 0;
virtual std::string toString() const = 0;
private:
#ifndef NDEBUG
std::string mCallerTraceback;
#endif
}; };
class TeleportAction final : public Action class TeleportAction final : public Action
{ {
public: public:
TeleportAction(ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot) TeleportAction(LuaUtil::LuaState* state, ObjectId object, std::string cell, const osg::Vec3f& pos, const osg::Vec3f& rot)
: mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {} : Action(state), mObject(object), mCell(std::move(cell)), mPos(pos), mRot(rot) {}
void apply(WorldView&) const override; void apply(WorldView&) const override;
std::string toString() const override { return "TeleportAction"; }
private: private:
ObjectId mObject; ObjectId mObject;
@ -41,9 +56,11 @@ namespace MWLua
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
using Equipment = std::map<int, Item>; // slot to item using Equipment = std::map<int, Item>; // 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; void apply(WorldView&) const override;
std::string toString() const override { return "SetEquipmentAction"; }
private: private:
ObjectId mActor; ObjectId mActor;

@ -39,7 +39,7 @@ namespace MWLua
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; }); selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; }; selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; }; 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())) if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{ {
@ -56,7 +56,7 @@ namespace MWLua
else else
eqp[slot] = value.as<std::string>(); eqp[slot] = value.as<std::string>();
} }
manager->addAction(std::make_unique<SetEquipmentAction>(obj.id(), std::move(eqp))); context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp)));
}; };
selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional<LObject> selfAPI["getCombatTarget"] = [worldView=context.mWorldView](SelfObject& self) -> sol::optional<LObject>
{ {

@ -28,8 +28,7 @@ namespace MWLua
api["API_REVISION"] = 9; api["API_REVISION"] = 9;
api["quit"] = [lua]() api["quit"] = [lua]()
{ {
std::string traceback = lua->sol()["debug"]["traceback"]().get<std::string>(); Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << traceback;
MWBase::Environment::get().getStateManager()->requestQuit(); MWBase::Environment::get().getStateManager()->requestQuit();
}; };
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData) api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData)

@ -188,11 +188,11 @@ namespace MWLua
mUIMessages.clear(); mUIMessages.clear();
for (std::unique_ptr<Action>& action : mActionQueue) for (std::unique_ptr<Action>& action : mActionQueue)
action->apply(mWorldView); action->safeApply(mWorldView);
mActionQueue.clear(); mActionQueue.clear();
if (mTeleportPlayerAction) if (mTeleportPlayerAction)
mTeleportPlayerAction->apply(mWorldView); mTeleportPlayerAction->safeApply(mWorldView);
mTeleportPlayerAction.reset(); mTeleportPlayerAction.reset();
} }

@ -179,16 +179,16 @@ namespace MWLua
localScripts->removeScript(*scriptId); localScripts->removeScript(*scriptId);
}; };
objectT["teleport"] = [luaManager=context.mLuaManager](const GObject& object, std::string_view cell, objectT["teleport"] = [context](const GObject& object, std::string_view cell,
const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot) const osg::Vec3f& pos, const sol::optional<osg::Vec3f>& optRot)
{ {
MWWorld::Ptr ptr = object.ptr(); MWWorld::Ptr ptr = object.ptr();
osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3(); osg::Vec3f rot = optRot ? *optRot : ptr.getRefData().getPosition().asRotationVec3();
auto action = std::make_unique<TeleportAction>(object.id(), std::string(cell), pos, rot); auto action = std::make_unique<TeleportAction>(context.mLua, object.id(), std::string(cell), pos, rot);
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr()) if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
luaManager->addTeleportPlayerAction(std::move(action)); context.mLuaManager->addTeleportPlayerAction(std::move(action));
else else
luaManager->addAction(std::move(action)); context.mLuaManager->addAction(std::move(action));
}; };
} }
else else
@ -352,7 +352,7 @@ namespace MWLua
if constexpr (std::is_same_v<ObjectT, GObject>) if constexpr (std::is_same_v<ObjectT, GObject>)
{ // Only for global scripts { // 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())) if (!obj.ptr().getClass().hasInventoryStore(obj.ptr()))
{ {
@ -360,7 +360,8 @@ namespace MWLua
throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots"); throw std::runtime_error(ptrToString(obj.ptr()) + " has no equipment slots");
return; return;
} }
manager->addAction(std::make_unique<SetEquipmentAction>(obj.id(), parseEquipmentTable(equipment))); context.mLuaManager->addAction(std::make_unique<SetEquipmentAction>(
context.mLua, obj.id(), parseEquipmentTable(equipment)));
}; };
// TODO // TODO

@ -35,6 +35,11 @@ namespace LuaUtil
// Returns underlying sol::state. // Returns underlying sol::state.
sol::state& sol() { return mLua; } 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<std::string>(); }
// A shortcut to create a new Lua table. // A shortcut to create a new Lua table.
sol::table newTable() { return sol::table(mLua, sol::create); } sol::table newTable() { return sol::table(mLua, sol::create); }

Loading…
Cancel
Save