Remove LuaManager::Action

This commit is contained in:
Petr Mikheev 2023-04-22 14:24:48 +02:00
parent 4562b8c06b
commit 003f611bdb
6 changed files with 132 additions and 272 deletions

View file

@ -206,12 +206,12 @@ namespace MWLua
windowManager->printToConsole(msg, "#" + color.toHex()); windowManager->printToConsole(msg, "#" + color.toHex());
mInGameConsoleMessages.clear(); mInGameConsoleMessages.clear();
for (std::unique_ptr<Action>& action : mActionQueue) for (DelayedAction& action : mActionQueue)
action->safeApply(); action.apply();
mActionQueue.clear(); mActionQueue.clear();
if (mTeleportPlayerAction) if (mTeleportPlayerAction)
mTeleportPlayerAction->safeApply(); mTeleportPlayerAction->apply();
mTeleportPlayerAction.reset(); mTeleportPlayerAction.reset();
} }
@ -465,22 +465,24 @@ namespace MWLua
"No Lua handlers for console\n", MWBase::WindowManager::sConsoleColor_Error); "No Lua handlers for console\n", MWBase::WindowManager::sConsoleColor_Error);
} }
LuaManager::Action::Action(LuaUtil::LuaState* state) LuaManager::DelayedAction::DelayedAction(LuaUtil::LuaState* state, std::function<void()> fn, std::string_view name)
: mFn(std::move(fn))
, mName(name)
{ {
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua"); static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
if (luaDebug) if (luaDebug)
mCallerTraceback = state->debugTraceback(); mCallerTraceback = state->debugTraceback();
} }
void LuaManager::Action::safeApply() const void LuaManager::DelayedAction::apply() const
{ {
try try
{ {
apply(); mFn();
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
Log(Debug::Error) << "Error in " << this->toString() << ": " << e.what(); Log(Debug::Error) << "Error in DelayedAction " << mName << ": " << e.what();
if (mCallerTraceback.empty()) if (mCallerTraceback.empty())
Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks"; Log(Debug::Error) << "Set 'lua_debug=true' in settings.cfg to enable action tracebacks";
@ -489,35 +491,14 @@ namespace MWLua
} }
} }
namespace
{
class FunctionAction final : public LuaManager::Action
{
public:
FunctionAction(LuaUtil::LuaState* state, std::function<void()> fn, std::string_view name)
: Action(state)
, mFn(std::move(fn))
, mName(name)
{
}
void apply() const override { mFn(); }
std::string toString() const override { return "FunctionAction " + mName; }
private:
std::function<void()> mFn;
std::string mName;
};
}
void LuaManager::addAction(std::function<void()> action, std::string_view name) void LuaManager::addAction(std::function<void()> action, std::string_view name)
{ {
mActionQueue.push_back(std::make_unique<FunctionAction>(&mLua, std::move(action), name)); mActionQueue.emplace_back(&mLua, std::move(action), name);
} }
void LuaManager::addTeleportPlayerAction(std::function<void()> action) void LuaManager::addTeleportPlayerAction(std::function<void()> action)
{ {
mTeleportPlayerAction = std::make_unique<FunctionAction>(&mLua, std::move(action), "TeleportPlayer"); mTeleportPlayerAction = DelayedAction(&mLua, std::move(action), "TeleportPlayer");
} }
void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const void LuaManager::reportStats(unsigned int frameNumber, osg::Stats& stats) const

View file

@ -86,24 +86,8 @@ namespace MWLua
} }
// Some changes to the game world can not be done from the scripting thread (because it runs in parallel with // Some changes to the game world can not be done from the scripting thread (because it runs in parallel with
// OSG Cull), so we need to queue it and apply from the main thread. All such changes should be implemented as // OSG Cull), so we need to queue it and apply from the main thread.
// classes inherited from MWLua::Action.
class Action
{
public:
Action(LuaUtil::LuaState* state);
virtual ~Action() {}
void safeApply() const;
virtual void apply() const = 0;
virtual std::string toString() const = 0;
private:
std::string mCallerTraceback;
};
void addAction(std::function<void()> action, std::string_view name = ""); void addAction(std::function<void()> action, std::string_view name = "");
void addAction(std::unique_ptr<Action>&& action) { mActionQueue.push_back(std::move(action)); }
void addTeleportPlayerAction(std::function<void()> action); void addTeleportPlayerAction(std::function<void()> action);
// Saving // Saving
@ -183,8 +167,19 @@ namespace MWLua
std::vector<CallbackWithData> mQueuedCallbacks; std::vector<CallbackWithData> mQueuedCallbacks;
// Queued actions that should be done in main thread. Processed by applyQueuedChanges(). // Queued actions that should be done in main thread. Processed by applyQueuedChanges().
std::vector<std::unique_ptr<Action>> mActionQueue; class DelayedAction
std::unique_ptr<Action> mTeleportPlayerAction; {
public:
DelayedAction(LuaUtil::LuaState* state, std::function<void()> fn, std::string_view name);
void apply() const;
private:
std::string mCallerTraceback;
std::function<void()> mFn;
std::string mName;
};
std::vector<DelayedAction> mActionQueue;
std::optional<DelayedAction> mTeleportPlayerAction;
std::vector<std::string> mUIMessages; std::vector<std::string> mUIMessages;
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages; std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;

View file

@ -5,39 +5,6 @@
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
namespace
{
template <class T>
class SetUniformShaderAction final : public MWLua::LuaManager::Action
{
public:
SetUniformShaderAction(
LuaUtil::LuaState* state, std::shared_ptr<fx::Technique> shader, const std::string& name, const T& value)
: MWLua::LuaManager::Action(state)
, mShader(std::move(shader))
, mName(name)
, mValue(value)
{
}
void apply() const override
{
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(mShader, mName, mValue);
}
std::string toString() const override
{
return std::string("SetUniformShaderAction shader=") + (mShader ? mShader->getName() : "nil")
+ std::string("uniform=") + (mShader ? mName : "nil");
}
private:
std::shared_ptr<fx::Technique> mShader;
std::string mName;
T mValue;
};
}
namespace MWLua namespace MWLua
{ {
struct Shader; struct Shader;
@ -84,7 +51,10 @@ namespace MWLua
{ {
return [context](const Shader& shader, const std::string& name, const T& value) { return [context](const Shader& shader, const std::string& name, const T& value) {
context.mLuaManager->addAction( context.mLuaManager->addAction(
std::make_unique<SetUniformShaderAction<T>>(context.mLua, shader.mShader, name, value)); [&] {
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, value);
},
"SetUniformShaderAction");
}; };
} }
@ -114,7 +84,10 @@ namespace MWLua
} }
context.mLuaManager->addAction( context.mLuaManager->addAction(
std::make_unique<SetUniformShaderAction<std::vector<T>>>(context.mLua, shader.mShader, name, values)); [&] {
MWBase::Environment::get().getWorld()->getPostProcessor()->setUniform(shader.mShader, name, values);
},
"SetUniformShaderAction");
}; };
} }

View file

@ -55,29 +55,17 @@ namespace
namespace MWLua namespace MWLua
{ {
namespace static void addStatUpdateAction(MWLua::LuaManager* manager, const SelfObject& obj)
{ {
class StatUpdateAction final : public LuaManager::Action if (!obj.mStatsCache.empty())
{ return; // was already added before
ObjectId mId; manager->addAction(
[obj = Object(obj)] {
public:
StatUpdateAction(LuaUtil::LuaState* state, ObjectId id)
: Action(state)
, mId(id)
{
}
void apply() const override
{
LObject obj(mId);
LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts(); LocalScripts* scripts = obj.ptr().getRefData().getLuaScripts();
if (scripts) if (scripts)
scripts->applyStatsCache(); scripts->applyStatsCache();
} },
"StatUpdateAction");
std::string toString() const override { return "StatUpdateAction"; }
};
} }
class LevelStat class LevelStat
@ -99,8 +87,7 @@ namespace MWLua
void setCurrent(const Context& context, const sol::object& value) const void setCurrent(const Context& context, const sol::object& value) const
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
if (obj->mStatsCache.empty()) addStatUpdateAction(context.mLuaManager, *obj);
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value; obj->mStatsCache[SelfObject::CachedStat{ &LevelStat::setValue, 0, "current" }] = value;
} }
@ -158,8 +145,7 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const void cache(const Context& context, std::string_view prop, const sol::object& value) const
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
if (obj->mStatsCache.empty()) addStatUpdateAction(context.mLuaManager, *obj);
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value;
} }
@ -217,8 +203,7 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const void cache(const Context& context, std::string_view prop, const sol::object& value) const
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
if (obj->mStatsCache.empty()) addStatUpdateAction(context.mLuaManager, *obj);
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mIndex, prop }] = value;
} }
@ -302,8 +287,7 @@ namespace MWLua
void cache(const Context& context, std::string_view prop, const sol::object& value) const void cache(const Context& context, std::string_view prop, const sol::object& value) const
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
if (obj->mStatsCache.empty()) addStatUpdateAction(context.mLuaManager, *obj);
context.mLuaManager->addAction(std::make_unique<StatUpdateAction>(context.mLua, obj->id()));
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value; obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mIndex, prop }] = value;
} }

View file

@ -18,30 +18,17 @@
namespace MWLua namespace MWLua
{ {
namespace using EquipmentItem = std::variant<std::string, ObjectId>;
{ using Equipment = std::map<int, EquipmentItem>;
class SetEquipmentAction final : public LuaManager::Action
{
public:
using Item = std::variant<std::string, ObjectId>; // recordId or ObjectId
using Equipment = std::map<int, Item>; // slot to item
SetEquipmentAction(LuaUtil::LuaState* state, ObjectId actor, Equipment equipment) static void setEquipment(const MWWorld::Ptr& actor, const Equipment& equipment)
: Action(state)
, mActor(actor)
, mEquipment(std::move(equipment))
{ {
}
void apply() const override
{
MWWorld::Ptr actor = MWBase::Environment::get().getWorldModel()->getPtr(mActor);
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor); MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
std::array<bool, MWWorld::InventoryStore::Slots> usedSlots; std::array<bool, MWWorld::InventoryStore::Slots> usedSlots;
std::fill(usedSlots.begin(), usedSlots.end(), false); std::fill(usedSlots.begin(), usedSlots.end(), false);
static constexpr int anySlot = -1; static constexpr int anySlot = -1;
auto tryEquipToSlot = [&store, &usedSlots](int slot, const Item& item) -> bool { auto tryEquipToSlot = [&store, &usedSlots](int slot, const EquipmentItem& item) -> bool {
auto old_it = slot != anySlot ? store.getSlot(slot) : store.end(); auto old_it = slot != anySlot ? store.getSlot(slot) : store.end();
MWWorld::Ptr itemPtr; MWWorld::Ptr itemPtr;
if (std::holds_alternative<ObjectId>(item)) if (std::holds_alternative<ObjectId>(item))
@ -52,8 +39,7 @@ namespace MWLua
if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0 if (itemPtr.isEmpty() || itemPtr.getRefData().getCount() == 0
|| itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store)) || itemPtr.getContainerStore() != static_cast<const MWWorld::ContainerStore*>(&store))
{ {
Log(Debug::Warning) Log(Debug::Warning) << "Object" << std::get<ObjectId>(item).toString() << " is not in inventory";
<< "Object" << std::get<ObjectId>(item).toString() << " is not in inventory";
return false; return false;
} }
} }
@ -75,8 +61,8 @@ namespace MWLua
= std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end(); = std::find(allowedSlots.begin(), allowedSlots.end(), slot) != allowedSlots.end();
if (!requestedSlotIsAllowed) if (!requestedSlotIsAllowed)
{ {
auto firstAllowed = std::find_if( auto firstAllowed
allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; }); = std::find_if(allowedSlots.begin(), allowedSlots.end(), [&](int s) { return !usedSlots[s]; });
if (firstAllowed == allowedSlots.end()) if (firstAllowed == allowedSlots.end())
{ {
Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString(); Log(Debug::Warning) << "No suitable slot for " << itemPtr.toString();
@ -91,15 +77,14 @@ namespace MWLua
throw std::logic_error("Item not found in container"); throw std::logic_error("Item not found in container");
store.equip(slot, it); store.equip(slot, it);
return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was return requestedSlotIsAllowed; // return true if equipped to requested slot and false if slot was changed
// changed
}; };
for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot) for (int slot = 0; slot < MWWorld::InventoryStore::Slots; ++slot)
{ {
auto old_it = store.getSlot(slot); auto old_it = store.getSlot(slot);
auto new_it = mEquipment.find(slot); auto new_it = equipment.find(slot);
if (new_it == mEquipment.end()) if (new_it == equipment.end())
{ {
if (old_it != store.end()) if (old_it != store.end())
store.unequipSlot(slot); store.unequipSlot(slot);
@ -108,19 +93,11 @@ namespace MWLua
if (tryEquipToSlot(slot, new_it->second)) if (tryEquipToSlot(slot, new_it->second))
usedSlots[slot] = true; usedSlots[slot] = true;
} }
for (const auto& [slot, item] : mEquipment) for (const auto& [slot, item] : equipment)
if (slot >= MWWorld::InventoryStore::Slots) if (slot >= MWWorld::InventoryStore::Slots)
tryEquipToSlot(anySlot, item); tryEquipToSlot(anySlot, item);
} }
std::string toString() const override { return "SetEquipmentAction"; }
private:
ObjectId mActor;
Equipment mEquipment;
};
}
void addActorBindings(sol::table actor, const Context& context) void addActorBindings(sol::table actor, const Context& context)
{ {
actor["STANCE"] actor["STANCE"]
@ -263,13 +240,14 @@ namespace MWLua
return store.isEquipped(item.ptr()); return store.isEquipped(item.ptr());
}; };
actor["setEquipment"] = [context](const SelfObject& obj, const sol::table& equipment) { actor["setEquipment"] = [context](const SelfObject& obj, const sol::table& equipment) {
if (!obj.ptr().getClass().hasInventoryStore(obj.ptr())) const MWWorld::Ptr& ptr = obj.ptr();
if (!ptr.getClass().hasInventoryStore(ptr))
{ {
if (!equipment.empty()) if (!equipment.empty())
throw std::runtime_error(obj.toString() + " has no equipment slots"); throw std::runtime_error(obj.toString() + " has no equipment slots");
return; return;
} }
SetEquipmentAction::Equipment eqp; Equipment eqp;
for (auto& [key, value] : equipment) for (auto& [key, value] : equipment)
{ {
int slot = key.as<int>(); int slot = key.as<int>();
@ -279,7 +257,7 @@ namespace MWLua
eqp[slot] = value.as<std::string>(); eqp[slot] = value.as<std::string>();
} }
context.mLuaManager->addAction( context.mLuaManager->addAction(
std::make_unique<SetEquipmentAction>(context.mLua, obj.id(), std::move(eqp))); [obj = Object(ptr), eqp = std::move(eqp)] { setEquipment(obj.ptr(), eqp); }, "SetEquipmentAction");
}; };
actor["getPathfindingAgentBounds"] = [](sol::this_state lua, const LObject& o) { actor["getPathfindingAgentBounds"] = [](sol::this_state lua, const LObject& o) {
const DetourNavigator::AgentBounds agentBounds const DetourNavigator::AgentBounds agentBounds

View file

@ -20,72 +20,21 @@ namespace MWLua
{ {
namespace namespace
{ {
class UiAction final : public LuaManager::Action template <typename Fn>
{ void wrapAction(const std::shared_ptr<LuaUi::Element>& element, Fn&& fn)
public:
enum Type
{
CREATE = 0,
UPDATE,
DESTROY,
};
UiAction(Type type, std::shared_ptr<LuaUi::Element> element, LuaUtil::LuaState* state)
: Action(state)
, mType{ type }
, mElement{ std::move(element) }
{
}
void apply() const override
{ {
try try
{ {
switch (mType) fn();
{
case CREATE:
mElement->create();
break;
case UPDATE:
mElement->update();
break;
case DESTROY:
mElement->destroy();
break;
} }
} catch (...)
catch (std::exception&)
{ {
// prevent any actions on a potentially corrupted widget // prevent any actions on a potentially corrupted widget
mElement->mRoot = nullptr; element->mRoot = nullptr;
throw; throw;
} }
} }
std::string toString() const override
{
std::string result;
switch (mType)
{
case CREATE:
result += "Create";
break;
case UPDATE:
result += "Update";
break;
case DESTROY:
result += "Destroy";
break;
}
result += " UI";
return result;
}
private:
Type mType;
std::shared_ptr<LuaUi::Element> mElement;
};
// Lua arrays index from 1 // Lua arrays index from 1
inline size_t fromLuaIndex(size_t i) inline size_t fromLuaIndex(size_t i)
{ {
@ -102,17 +51,17 @@ namespace MWLua
auto element = context.mLua->sol().new_usertype<LuaUi::Element>("Element"); auto element = context.mLua->sol().new_usertype<LuaUi::Element>("Element");
element["layout"] = sol::property([](LuaUi::Element& element) { return element.mLayout; }, element["layout"] = sol::property([](LuaUi::Element& element) { return element.mLayout; },
[](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; }); [](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; });
element["update"] = [context](const std::shared_ptr<LuaUi::Element>& element) { element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mDestroy || element->mUpdate) if (element->mDestroy || element->mUpdate)
return; return;
element->mUpdate = true; element->mUpdate = true;
context.mLuaManager->addAction(std::make_unique<UiAction>(UiAction::UPDATE, element, context.mLua)); luaManager->addAction([element] { wrapAction(element, [&] { element->update(); }); }, "Update UI");
}; };
element["destroy"] = [context](const std::shared_ptr<LuaUi::Element>& element) { element["destroy"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mDestroy) if (element->mDestroy)
return; return;
element->mDestroy = true; element->mDestroy = true;
context.mLuaManager->addAction(std::make_unique<UiAction>(UiAction::DESTROY, element, context.mLua)); luaManager->addAction([element] { wrapAction(element, [&] { element->destroy(); }); }, "Destroy UI");
}; };
sol::table api = context.mLua->newTable(); sol::table api = context.mLua->newTable();
@ -144,9 +93,9 @@ namespace MWLua
} }
}; };
api["content"] = LuaUi::loadContentConstructor(context.mLua); api["content"] = LuaUi::loadContentConstructor(context.mLua);
api["create"] = [context](const sol::table& layout) { api["create"] = [luaManager = context.mLuaManager](const sol::table& layout) {
auto element = LuaUi::Element::make(layout); auto element = LuaUi::Element::make(layout);
context.mLuaManager->addAction(std::make_unique<UiAction>(UiAction::CREATE, element, context.mLua)); luaManager->addAction([element] { wrapAction(element, [&] { element->create(); }); }, "Create UI");
return element; return element;
}; };
api["updateAll"] = [context]() { api["updateAll"] = [context]() {