1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-02-20 06:40:09 +00:00

Extract engine handlers processing from LuaManager to a new class EngineEvents

This commit is contained in:
Petr Mikheev 2023-03-22 01:27:22 +01:00
parent 7ef759c78b
commit 8d1e52ed51
9 changed files with 209 additions and 154 deletions

View File

@ -59,8 +59,8 @@ add_openmw_dir (mwscript
)
add_openmw_dir (mwlua
luamanagerimp object worldview userdataserializer luaevents objectvariant
luabindings localscripts playerscripts objectbindings cellbindings
luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant
context globalscripts localscripts playerscripts luabindings objectbindings cellbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings
types/types types/door types/actor types/container types/weapon types/npc types/creature types/activator types/book types/lockpick types/probe types/apparatus types/potion types/ingredient types/misc types/repair types/armor types/light types/static types/clothing
worker

View File

@ -0,0 +1,105 @@
#include "engineevents.hpp"
#include <components/debug/debuglog.hpp>
#include <components/settings/settings.hpp>
#include "../mwbase/environment.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/worldmodel.hpp"
#include "globalscripts.hpp"
#include "localscripts.hpp"
#include "object.hpp"
namespace MWLua
{
class EngineEvents::Visitor
{
public:
explicit Visitor(GlobalScripts* globalScripts)
: mGlobalScripts(globalScripts)
{
}
void operator()(const OnNewGame&) const { mGlobalScripts->newGameStarted(); }
void operator()(const OnActive& event) const
{
MWWorld::Ptr ptr = getPtr(event.mObject);
if (ptr.isEmpty())
return;
if (ptr.getCellRef().getRefId() == "player")
mGlobalScripts->playerAdded(GObject(ptr));
else
{
mGlobalScripts->objectActive(GObject(ptr));
const MWWorld::Class& objClass = ptr.getClass();
if (objClass.isActor())
mGlobalScripts->actorActive(GObject(ptr));
if (objClass.isItem(ptr))
mGlobalScripts->itemActive(GObject(ptr));
}
if (auto* scripts = getLocalScripts(ptr))
scripts->setActive(true);
}
void operator()(const OnInactive& event) const
{
if (auto* scripts = getLocalScripts(event.mObject))
scripts->setActive(false);
}
void operator()(const OnActivate& event) const
{
MWWorld::Ptr obj = getPtr(event.mObject);
MWWorld::Ptr actor = getPtr(event.mActor);
if (actor.isEmpty() || obj.isEmpty())
return;
if (auto* scripts = getLocalScripts(obj))
scripts->onActivated(LObject(actor));
}
void operator()(const OnConsume& event) const
{
MWWorld::Ptr actor = getPtr(event.mActor);
MWWorld::Ptr consumable = getPtr(event.mConsumable);
if (actor.isEmpty() || consumable.isEmpty())
return;
if (auto* scripts = getLocalScripts(actor))
scripts->onConsume(LObject(consumable));
}
private:
MWWorld::Ptr getPtr(const ESM::RefNum& id) const
{
MWWorld::Ptr res = mWorldModel->getPtr(id);
if (res.isEmpty() && mLuaDebug)
Log(Debug::Verbose) << "Can not find object" << id.toString() << " when calling engine hanglers";
return res;
}
LocalScripts* getLocalScripts(const MWWorld::Ptr& ptr) const
{
if (ptr.isEmpty())
return nullptr;
else
return ptr.getRefData().getLuaScripts();
}
LocalScripts* getLocalScripts(const ESM::RefNum& id) const { return getLocalScripts(getPtr(id)); }
GlobalScripts* mGlobalScripts;
bool mLuaDebug = Settings::Manager::getBool("lua debug", "Lua");
MWWorld::WorldModel* mWorldModel = MWBase::Environment::get().getWorldModel();
};
void EngineEvents::callEngineHandlers()
{
Visitor vis(mGlobalScripts);
for (const Event& event : mQueue)
std::visit(vis, event);
mQueue.clear();
}
}

View File

@ -0,0 +1,56 @@
#ifndef MWLUA_ENGINEEVENTS_H
#define MWLUA_ENGINEEVENTS_H
#include <variant>
#include <components/esm3/cellref.hpp> // defines RefNum that is used as a unique id
namespace MWLua
{
class GlobalScripts;
class EngineEvents
{
public:
explicit EngineEvents(GlobalScripts* globalScripts)
: mGlobalScripts(globalScripts)
{
}
struct OnNewGame
{
};
struct OnActive
{
ESM::RefNum mObject;
};
struct OnInactive
{
ESM::RefNum mObject;
};
struct OnActivate
{
ESM::RefNum mActor;
ESM::RefNum mObject;
};
struct OnConsume
{
ESM::RefNum mActor;
ESM::RefNum mConsumable;
};
using Event = std::variant<OnNewGame, OnActive, OnInactive, OnConsume, OnActivate>;
void clear() { mQueue.clear(); }
void addToQueue(Event e) { mQueue.push_back(std::move(e)); }
void callEngineHandlers();
private:
class Visitor;
GlobalScripts* mGlobalScripts;
std::vector<Event> mQueue;
};
}
#endif // MWLUA_ENGINEEVENTS_H

View File

@ -206,32 +206,13 @@ namespace MWLua
{ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers });
}
void LocalScripts::receiveEngineEvent(const EngineEvent& event)
void LocalScripts::setActive(bool active)
{
std::visit(
[this](auto&& arg) {
using EventT = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<EventT, OnActive>)
{
mData.mIsActive = true;
callEngineHandlers(mOnActiveHandlers);
}
else if constexpr (std::is_same_v<EventT, OnInactive>)
{
mData.mIsActive = false;
callEngineHandlers(mOnInactiveHandlers);
}
else if constexpr (std::is_same_v<EventT, OnActivated>)
{
callEngineHandlers(mOnActivatedHandlers, arg.mActivatingActor);
}
else
{
static_assert(std::is_same_v<EventT, OnConsume>);
callEngineHandlers(mOnConsumeHandlers, arg.mConsumable);
}
},
event);
mData.mIsActive = active;
if (active)
callEngineHandlers(mOnActiveHandlers);
else
callEngineHandlers(mOnInactiveHandlers);
}
void LocalScripts::applyStatsCache()

View File

@ -23,11 +23,6 @@ namespace MWLua
public:
using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&);
private:
Setter mSetter; // Function that updates a stat's property
int mIndex; // Optional index to disambiguate the stat
std::string_view mProp; // Name of the stat's property
public:
CachedStat(Setter setter, int index, std::string_view prop)
: mSetter(setter)
, mIndex(index)
@ -44,6 +39,11 @@ namespace MWLua
{
return std::tie(mSetter, mIndex, mProp) < std::tie(other.mSetter, other.mIndex, other.mProp);
}
private:
Setter mSetter; // Function that updates a stat's property
int mIndex; // Optional index to disambiguate the stat
std::string_view mProp; // Name of the stat's property
};
SelfObject(const LObject& obj)
@ -65,23 +65,9 @@ namespace MWLua
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
struct OnActive
{
};
struct OnInactive
{
};
struct OnActivated
{
LObject mActivatingActor;
};
struct OnConsume
{
LObject mConsumable;
};
using EngineEvent = std::variant<OnActive, OnInactive, OnConsume, OnActivated>;
void receiveEngineEvent(const EngineEvent&);
void setActive(bool active);
void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); }
void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); }
void applyStatsCache();

View File

@ -136,7 +136,6 @@ namespace MWLua
void LuaManager::update()
{
static const bool luaDebug = Settings::Manager::getBool("lua debug", "Lua");
static const int gcStepCount = Settings::Manager::getInt("gc steps per frame", "Lua");
if (gcStepCount > 0)
lua_gc(mLua.sol(), LUA_GCSTEP, gcStepCount);
@ -177,6 +176,7 @@ namespace MWLua
scripts->processTimers(simulationTime, gameTime);
}
// Run event handlers for events that were sent before `finalizeEventBatch`.
mLuaEvents.callEventHandlers();
// Run queued callbacks
@ -184,63 +184,14 @@ namespace MWLua
c.mCallback.tryCall(c.mArg);
mQueuedCallbacks.clear();
// Engine handlers in local scripts
for (const LocalEngineEvent& e : mLocalEngineEvents)
{
LObject obj(e.mDest);
const MWWorld::Ptr& ptr = obj.ptrOrNull();
if (ptr.isEmpty())
{
if (luaDebug)
Log(Debug::Verbose) << "Can not call engine handlers: object" << e.mDest.toString()
<< " is not found";
continue;
}
LocalScripts* scripts = ptr.getRefData().getLuaScripts();
if (scripts)
scripts->receiveEngineEvent(e.mEvent);
}
mLocalEngineEvents.clear();
// Run engine handlers
mEngineEvents.callEngineHandlers();
if (!mWorldView.isPaused())
{
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->update(frameDuration);
}
// Engine handlers in global scripts
if (mPlayerChanged)
{
mPlayerChanged = false;
mGlobalScripts.playerAdded(GObject(getId(mPlayer)));
}
if (mNewGameStarted)
{
mNewGameStarted = false;
mGlobalScripts.newGameStarted();
}
for (ObjectId id : mObjectAddedEvents)
{
GObject obj(id);
const MWWorld::Ptr& ptr = obj.ptrOrNull();
if (!ptr.isEmpty())
{
mGlobalScripts.objectActive(obj);
const MWWorld::Class& objClass = ptr.getClass();
if (objClass.isActor())
mGlobalScripts.actorActive(obj);
if (objClass.isItem(ptr))
mGlobalScripts.itemActive(obj);
}
else if (luaDebug)
Log(Debug::Verbose) << "Could not resolve a Lua object added event: object" << id.toString()
<< " is already removed";
}
mObjectAddedEvents.clear();
if (!mWorldView.isPaused())
mGlobalScripts.update(frameDuration);
}
}
void LuaManager::synchronizedUpdate()
@ -286,11 +237,8 @@ namespace MWLua
MWBase::Environment::get().getWorld()->getPostProcessor()->disableDynamicShaders();
mActiveLocalScripts.clear();
mLuaEvents.clear();
mEngineEvents.clear();
mInputEvents.clear();
mObjectAddedEvents.clear();
mLocalEngineEvents.clear();
mNewGameStarted = false;
mPlayerChanged = false;
mWorldView.clear();
mGlobalScripts.removeAllScripts();
mGlobalScriptsStarted = false;
@ -321,16 +269,15 @@ namespace MWLua
localScripts->addAutoStartedScripts();
}
mActiveLocalScripts.insert(localScripts);
mLocalEngineEvents.push_back({ getId(ptr), LocalScripts::OnActive{} });
mPlayerChanged = true;
mEngineEvents.addToQueue(EngineEvents::OnActive{ getId(ptr) });
}
void LuaManager::newGameStarted()
{
mNewGameStarted = true;
mInputEvents.clear();
mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true;
mEngineEvents.addToQueue(EngineEvents::OnNewGame{});
}
void LuaManager::gameLoaded()
@ -343,26 +290,16 @@ namespace MWLua
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
{
mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
mEngineEvents.addToQueue(EngineEvents::OnActive{ getId(ptr) });
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
if (!localScripts)
if (!ptr.getRefData().getLuaScripts())
{
LuaUtil::ScriptIdsWithInitializationData autoStartConf
= mConfiguration.getLocalConf(getLiveCellRefType(ptr.mRef), ptr.getCellRef().getRefId(), getId(ptr));
// TODO: put to a queue and apply `addAutoStartedScripts` on next `update()`
if (!autoStartConf.empty())
{
localScripts = createLocalScripts(ptr, std::move(autoStartConf));
localScripts->addAutoStartedScripts(); // TODO: put to a queue and apply on next `update()`
}
createLocalScripts(ptr, std::move(autoStartConf))->addAutoStartedScripts();
}
if (localScripts)
{
mActiveLocalScripts.insert(localScripts);
mLocalEngineEvents.push_back({ getId(ptr), LocalScripts::OnActive{} });
}
if (ptr != mPlayer)
mObjectAddedEvents.push_back(getId(ptr));
}
void LuaManager::objectRemovedFromScene(const MWWorld::Ptr& ptr)
@ -373,21 +310,10 @@ namespace MWLua
{
mActiveLocalScripts.erase(localScripts);
if (!MWBase::Environment::get().getWorldModel()->getPtr(getId(ptr)).isEmpty())
mLocalEngineEvents.push_back({ getId(ptr), LocalScripts::OnInactive{} });
mEngineEvents.addToQueue(EngineEvents::OnInactive{ getId(ptr) });
}
}
void LuaManager::itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor)
{
MWBase::Environment::get().getWorldModel()->registerPtr(consumable);
mLocalEngineEvents.push_back({ getId(actor), LocalScripts::OnConsume{ LObject(getId(consumable)) } });
}
void LuaManager::objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor)
{
mLocalEngineEvents.push_back({ getId(object), LocalScripts::OnActivated{ LObject(getId(actor)) } });
}
MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const
{
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
@ -529,7 +455,7 @@ namespace MWLua
scripts->load(data);
}
for (LocalScripts* scripts : mActiveLocalScripts)
scripts->receiveEngineEvent(LocalScripts::OnActive());
scripts->setActive(true);
}
void LuaManager::handleConsoleCommand(

View File

@ -14,6 +14,7 @@
#include "../mwbase/luamanager.hpp"
#include "engineevents.hpp"
#include "globalscripts.hpp"
#include "localscripts.hpp"
#include "luaevents.hpp"
@ -64,8 +65,14 @@ namespace MWLua
void objectAddedToScene(const MWWorld::Ptr& ptr) override;
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); }
void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override;
void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override;
void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override
{
mEngineEvents.addToQueue(EngineEvents::OnConsume{ getId(actor), getId(consumable) });
}
void objectActivated(const MWWorld::Ptr& object, const MWWorld::Ptr& actor) override
{
mEngineEvents.addToQueue(EngineEvents::OnActivate{ getId(actor), getId(object) });
}
MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override;
@ -163,11 +170,11 @@ namespace MWLua
std::set<LocalScripts*> mActiveLocalScripts;
WorldView mWorldView;
bool mPlayerChanged = false;
bool mNewGameStarted = false;
MWWorld::Ptr mPlayer;
LuaEvents mLuaEvents{ &mGlobalScripts };
EngineEvents mEngineEvents{ &mGlobalScripts };
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer;
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer;
@ -176,9 +183,6 @@ namespace MWLua
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalLoader;
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalLoader;
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
std::vector<ObjectId> mObjectAddedEvents;
struct CallbackWithData
{
LuaUtil::Callback mCallback;
@ -186,13 +190,6 @@ namespace MWLua
};
std::vector<CallbackWithData> mQueuedCallbacks;
struct LocalEngineEvent
{
ObjectId mDest;
LocalScripts::EngineEvent mEvent;
};
std::vector<LocalEngineEvent> mLocalEngineEvents;
// Queued actions that should be done in main thread. Processed by applyQueuedChanges().
std::vector<std::unique_ptr<Action>> mActionQueue;
std::unique_ptr<Action> mTeleportPlayerAction;

View File

@ -21,6 +21,13 @@ namespace MWWorld
return res;
}
SafePtr::SafePtr(const Ptr& ptr)
: mId(ptr.getCellRef().getRefNum())
, mPtr(ptr)
, mLastUpdate(MWBase::Environment::get().getWorldModel()->getPtrIndexUpdateCounter())
{
}
std::string SafePtr::toString() const
{
update();

View File

@ -159,10 +159,7 @@ namespace MWWorld
: mId(id)
{
}
explicit SafePtr(const Ptr& ptr)
: SafePtr(ptr.getCellRef().getRefNum())
{
}
explicit SafePtr(const Ptr& ptr);
virtual ~SafePtr() = default;
Id id() const { return mId; }