1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-01-19 19:53:53 +00:00

Merge branch 'lua' into 'master'

LuaManager refactoring

See merge request OpenMW/openmw!2852
This commit is contained in:
psi29a 2023-03-26 11:12:51 +00:00
commit 76cba95a44
33 changed files with 435 additions and 300 deletions

View file

@ -59,8 +59,8 @@ add_openmw_dir (mwscript
) )
add_openmw_dir (mwlua add_openmw_dir (mwlua
luamanagerimp object worldview userdataserializer eventqueue objectvariant luamanagerimp object worldview userdataserializer luaevents engineevents objectvariant
luabindings localscripts playerscripts objectbindings cellbindings context globalscripts localscripts playerscripts luabindings objectbindings cellbindings
camerabindings uibindings inputbindings nearbybindings postprocessingbindings stats debugbindings 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 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 worker

View file

@ -23,6 +23,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -23,6 +23,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -86,6 +86,11 @@ namespace MWClass
return !name.empty() ? name : ref->mBase->mId.getRefIdString(); return !name.empty() ? name : ref->mBase->mId.getRefIdString();
} }
bool Light::isItem(const MWWorld::ConstPtr& ptr) const
{
return ptr.get<ESM::Light>()->mBase->mData.mFlags & ESM::Light::Carry;
}
std::unique_ptr<MWWorld::Action> Light::activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const std::unique_ptr<MWWorld::Action> Light::activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const
{ {
if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory)) if (!MWBase::Environment::get().getWindowManager()->isAllowed(MWGui::GW_Inventory))

View file

@ -36,6 +36,8 @@ namespace MWClass
bool showsInInventory(const MWWorld::ConstPtr& ptr) const override; bool showsInInventory(const MWWorld::ConstPtr& ptr) const override;
bool isItem(const MWWorld::ConstPtr&) const override;
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -26,6 +26,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -21,6 +21,8 @@ namespace MWClass
std::string_view getName(const MWWorld::ConstPtr& ptr) const override; std::string_view getName(const MWWorld::ConstPtr& ptr) const override;
///< \return name or ID; can return an empty string. ///< \return name or ID; can return an empty string.
bool isItem(const MWWorld::ConstPtr&) const override { return true; }
std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override; std::unique_ptr<MWWorld::Action> activate(const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const override;
///< Generate action for activation ///< Generate action for activation

View file

@ -1,8 +1,6 @@
#ifndef MWLUA_CONTEXT_H #ifndef MWLUA_CONTEXT_H
#define MWLUA_CONTEXT_H #define MWLUA_CONTEXT_H
#include "eventqueue.hpp"
namespace LuaUtil namespace LuaUtil
{ {
class LuaState; class LuaState;
@ -11,6 +9,7 @@ namespace LuaUtil
namespace MWLua namespace MWLua
{ {
class LuaEvents;
class LuaManager; class LuaManager;
class WorldView; class WorldView;
@ -21,8 +20,7 @@ namespace MWLua
LuaUtil::LuaState* mLua; LuaUtil::LuaState* mLua;
LuaUtil::UserdataSerializer* mSerializer; LuaUtil::UserdataSerializer* mSerializer;
WorldView* mWorldView; WorldView* mWorldView;
LocalEventQueue* mLocalEventQueue; LuaEvents* mLuaEvents;
GlobalEventQueue* mGlobalEventQueue;
}; };
} }

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

@ -1,64 +0,0 @@
#include "eventqueue.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm/luascripts.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/lua/serialization.hpp>
namespace MWLua
{
template <typename Event>
void saveEvent(ESM::ESMWriter& esm, const ObjectId& dest, const Event& event)
{
esm.writeHNString("LUAE", event.mEventName);
dest.save(esm, true);
if (!event.mEventData.empty())
saveLuaBinaryData(esm, event.mEventData);
}
void loadEvents(sol::state_view& lua, ESM::ESMReader& esm, GlobalEventQueue& globalEvents,
LocalEventQueue& localEvents, const std::map<int, int>& contentFileMapping,
const LuaUtil::UserdataSerializer* serializer)
{
while (esm.isNextSub("LUAE"))
{
std::string name = esm.getHString();
ObjectId dest;
dest.load(esm, true);
std::string data = loadLuaBinaryData(esm);
try
{
data = LuaUtil::serialize(LuaUtil::deserialize(lua, data, serializer), serializer);
}
catch (std::exception& e)
{
Log(Debug::Error) << "loadEvent: invalid event data: " << e.what();
}
if (dest.isSet())
{
auto it = contentFileMapping.find(dest.mContentFile);
if (it != contentFileMapping.end())
dest.mContentFile = it->second;
localEvents.push_back({ dest, std::move(name), std::move(data) });
}
else
globalEvents.push_back({ std::move(name), std::move(data) });
}
}
void saveEvents(ESM::ESMWriter& esm, const GlobalEventQueue& globalEvents, const LocalEventQueue& localEvents)
{
// Used as a marker of a global event.
constexpr ObjectId globalId;
for (const GlobalEvent& e : globalEvents)
saveEvent(esm, globalId, e);
for (const LocalEvent& e : localEvents)
saveEvent(esm, e.mDest, e);
}
}

View file

@ -1,43 +0,0 @@
#ifndef MWLUA_EVENTQUEUE_H
#define MWLUA_EVENTQUEUE_H
#include "object.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace LuaUtil
{
class UserdataSerializer;
}
namespace sol
{
class state;
}
namespace MWLua
{
struct GlobalEvent
{
std::string mEventName;
std::string mEventData;
};
struct LocalEvent
{
ObjectId mDest;
std::string mEventName;
std::string mEventData;
};
using GlobalEventQueue = std::vector<GlobalEvent>;
using LocalEventQueue = std::vector<LocalEvent>;
void loadEvents(sol::state_view& lua, ESM::ESMReader& esm, GlobalEventQueue&, LocalEventQueue&,
const std::map<int, int>& contentFileMapping, const LuaUtil::UserdataSerializer* serializer);
void saveEvents(ESM::ESMWriter& esm, const GlobalEventQueue&, const LocalEventQueue&);
}
#endif // MWLUA_EVENTQUEUE_H

View file

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

View file

@ -23,11 +23,6 @@ namespace MWLua
public: public:
using Setter = void (*)(int, std::string_view, const MWWorld::Ptr&, const sol::object&); 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) CachedStat(Setter setter, int index, std::string_view prop)
: mSetter(setter) : mSetter(setter)
, mIndex(index) , mIndex(index)
@ -44,6 +39,11 @@ namespace MWLua
{ {
return std::tie(mSetter, mIndex, mProp) < std::tie(other.mSetter, other.mIndex, other.mProp); 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) SelfObject(const LObject& obj)
@ -65,23 +65,9 @@ namespace MWLua
MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; } MWBase::LuaManager::ActorControls* getActorControls() { return &mData.mControls; }
const MWWorld::Ptr& getPtr() const { return mData.ptr(); } const MWWorld::Ptr& getPtr() const { return mData.ptr(); }
struct OnActive void setActive(bool active);
{ void onConsume(const LObject& consumable) { callEngineHandlers(mOnConsumeHandlers, consumable); }
}; void onActivated(const LObject& actor) { callEngineHandlers(mOnActivatedHandlers, actor); }
struct OnInactive
{
};
struct OnActivated
{
LObject mActivatingActor;
};
struct OnConsume
{
LObject mConsumable;
};
using EngineEvent = std::variant<OnActive, OnInactive, OnConsume, OnActivated>;
void receiveEngineEvent(const EngineEvent&);
void applyStatsCache(); void applyStatsCache();

View file

@ -13,7 +13,7 @@
#include "../mwworld/scene.hpp" #include "../mwworld/scene.hpp"
#include "../mwworld/store.hpp" #include "../mwworld/store.hpp"
#include "eventqueue.hpp" #include "luaevents.hpp"
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "worldview.hpp" #include "worldview.hpp"
@ -57,7 +57,7 @@ namespace MWLua
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) {
context.mGlobalEventQueue->push_back( context.mLuaEvents->addGlobalEvent(
{ std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) }); { std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
}; };
addTimeBindings(api, context, false); addTimeBindings(api, context, false);

View file

@ -0,0 +1,108 @@
#include "luaevents.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm/luascripts.hpp>
#include <components/esm3/esmreader.hpp>
#include <components/esm3/esmwriter.hpp>
#include <components/lua/serialization.hpp>
#include "../mwbase/environment.hpp"
#include "../mwworld/worldmodel.hpp"
#include "globalscripts.hpp"
#include "localscripts.hpp"
namespace MWLua
{
void LuaEvents::clear()
{
mGlobalEventBatch.clear();
mLocalEventBatch.clear();
mNewGlobalEventBatch.clear();
mNewLocalEventBatch.clear();
}
void LuaEvents::finalizeEventBatch()
{
mNewGlobalEventBatch.swap(mGlobalEventBatch);
mNewLocalEventBatch.swap(mLocalEventBatch);
mNewGlobalEventBatch.clear();
mNewLocalEventBatch.clear();
}
void LuaEvents::callEventHandlers()
{
for (const Global& e : mGlobalEventBatch)
mGlobalScripts.receiveEvent(e.mEventName, e.mEventData);
mGlobalEventBatch.clear();
for (const Local& e : mLocalEventBatch)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorldModel()->getPtr(e.mDest);
LocalScripts* scripts = ptr.isEmpty() ? nullptr : ptr.getRefData().getLuaScripts();
if (scripts)
scripts->receiveEvent(e.mEventName, e.mEventData);
else
Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << e.mDest.toString()
<< ". Object not found or has no attached scripts";
}
mLocalEventBatch.clear();
}
template <typename Event>
static void saveEvent(ESM::ESMWriter& esm, const ESM::RefNum& dest, const Event& event)
{
esm.writeHNString("LUAE", event.mEventName);
dest.save(esm, true);
if (!event.mEventData.empty())
saveLuaBinaryData(esm, event.mEventData);
}
void LuaEvents::load(lua_State* lua, ESM::ESMReader& esm, const std::map<int, int>& contentFileMapping,
const LuaUtil::UserdataSerializer* serializer)
{
clear();
while (esm.isNextSub("LUAE"))
{
std::string name = esm.getHString();
ESM::RefNum dest;
dest.load(esm, true);
std::string data = loadLuaBinaryData(esm);
try
{
data = LuaUtil::serialize(LuaUtil::deserialize(lua, data, serializer), serializer);
}
catch (std::exception& e)
{
Log(Debug::Error) << "loadEvent: invalid event data: " << e.what();
}
if (dest.isSet())
{
auto it = contentFileMapping.find(dest.mContentFile);
if (it != contentFileMapping.end())
dest.mContentFile = it->second;
mLocalEventBatch.push_back({ dest, std::move(name), std::move(data) });
}
else
mGlobalEventBatch.push_back({ std::move(name), std::move(data) });
}
}
void LuaEvents::save(ESM::ESMWriter& esm) const
{
// Used as a marker of a global event.
constexpr ESM::RefNum globalId;
for (const Global& e : mGlobalEventBatch)
saveEvent(esm, globalId, e);
for (const Global& e : mNewGlobalEventBatch)
saveEvent(esm, globalId, e);
for (const Local& e : mLocalEventBatch)
saveEvent(esm, e.mDest, e);
for (const Local& e : mNewLocalEventBatch)
saveEvent(esm, e.mDest, e);
}
}

View file

@ -0,0 +1,68 @@
#ifndef MWLUA_LUAEVENTS_H
#define MWLUA_LUAEVENTS_H
#include <map>
#include <string>
#include <components/esm3/cellref.hpp> // defines RefNum that is used as a unique id
struct lua_State;
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace LuaUtil
{
class UserdataSerializer;
}
namespace MWLua
{
class GlobalScripts;
class LuaEvents
{
public:
explicit LuaEvents(GlobalScripts& globalScripts)
: mGlobalScripts(globalScripts)
{
}
struct Global
{
std::string mEventName;
std::string mEventData;
};
struct Local
{
ESM::RefNum mDest;
std::string mEventName;
std::string mEventData;
};
void addGlobalEvent(Global event) { mNewGlobalEventBatch.push_back(std::move(event)); }
void addLocalEvent(Local event) { mNewLocalEventBatch.push_back(std::move(event)); }
void clear();
void finalizeEventBatch();
void callEventHandlers();
void load(lua_State* lua, ESM::ESMReader& esm, const std::map<int, int>& contentFileMapping,
const LuaUtil::UserdataSerializer* serializer);
void save(ESM::ESMWriter& esm) const;
private:
GlobalScripts& mGlobalScripts;
std::vector<Global> mNewGlobalEventBatch;
std::vector<Local> mNewLocalEventBatch;
std::vector<Global> mGlobalEventBatch;
std::vector<Local> mLocalEventBatch;
};
}
#endif // MWLUA_LUAEVENTS_H

View file

@ -81,8 +81,7 @@ namespace MWLua
context.mLuaManager = this; context.mLuaManager = this;
context.mLua = &mLua; context.mLua = &mLua;
context.mWorldView = &mWorldView; context.mWorldView = &mWorldView;
context.mLocalEventQueue = &mLocalEvents; context.mLuaEvents = &mLuaEvents;
context.mGlobalEventQueue = &mGlobalEvents;
context.mSerializer = mGlobalSerializer.get(); context.mSerializer = mGlobalSerializer.get();
Context localContext = context; Context localContext = context;
@ -137,7 +136,6 @@ namespace MWLua
void LuaManager::update() 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"); static const int gcStepCount = Settings::Manager::getInt("gc steps per frame", "Lua");
if (gcStepCount > 0) if (gcStepCount > 0)
lua_gc(mLua.sol(), LUA_GCSTEP, gcStepCount); lua_gc(mLua.sol(), LUA_GCSTEP, gcStepCount);
@ -165,10 +163,7 @@ namespace MWLua
for (LocalScripts* scripts : mActiveLocalScripts) for (LocalScripts* scripts : mActiveLocalScripts)
scripts->statsNextFrame(); scripts->statsNextFrame();
std::vector<GlobalEvent> globalEvents = std::move(mGlobalEvents); mLuaEvents.finalizeEventBatch();
std::vector<LocalEvent> localEvents = std::move(mLocalEvents);
mGlobalEvents = std::vector<GlobalEvent>();
mLocalEvents = std::vector<LocalEvent>();
if (!mWorldView.isPaused()) if (!mWorldView.isPaused())
{ // Update time and process timers { // Update time and process timers
@ -181,84 +176,23 @@ namespace MWLua
scripts->processTimers(simulationTime, gameTime); scripts->processTimers(simulationTime, gameTime);
} }
// Receive events // Run event handlers for events that were sent before `finalizeEventBatch`.
for (GlobalEvent& e : globalEvents) mLuaEvents.callEventHandlers();
mGlobalScripts.receiveEvent(e.mEventName, e.mEventData);
for (LocalEvent& e : localEvents)
{
LObject obj(e.mDest);
const MWWorld::Ptr& ptr = obj.ptrOrNull();
LocalScripts* scripts = ptr.isEmpty() ? nullptr : ptr.getRefData().getLuaScripts();
if (scripts)
scripts->receiveEvent(e.mEventName, e.mEventData);
else
Log(Debug::Debug) << "Ignored event " << e.mEventName << " to L" << e.mDest.toString()
<< ". Object not found or has no attached scripts";
}
// Run queued callbacks // Run queued callbacks
for (CallbackWithData& c : mQueuedCallbacks) for (CallbackWithData& c : mQueuedCallbacks)
c.mCallback.tryCall(c.mArg); c.mCallback.tryCall(c.mArg);
mQueuedCallbacks.clear(); mQueuedCallbacks.clear();
// Engine handlers in local scripts // Run engine handlers
for (const LocalEngineEvent& e : mLocalEngineEvents) mEngineEvents.callEngineHandlers();
{
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();
if (!mWorldView.isPaused()) if (!mWorldView.isPaused())
{ {
for (LocalScripts* scripts : mActiveLocalScripts) for (LocalScripts* scripts : mActiveLocalScripts)
scripts->update(frameDuration); 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 (mWorldView.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); mGlobalScripts.update(frameDuration);
} }
}
void LuaManager::synchronizedUpdate() void LuaManager::synchronizedUpdate()
{ {
@ -302,13 +236,9 @@ namespace MWLua
MWBase::Environment::get().getWindowManager()->setConsoleMode(""); MWBase::Environment::get().getWindowManager()->setConsoleMode("");
MWBase::Environment::get().getWorld()->getPostProcessor()->disableDynamicShaders(); MWBase::Environment::get().getWorld()->getPostProcessor()->disableDynamicShaders();
mActiveLocalScripts.clear(); mActiveLocalScripts.clear();
mLocalEvents.clear(); mLuaEvents.clear();
mGlobalEvents.clear(); mEngineEvents.clear();
mInputEvents.clear(); mInputEvents.clear();
mObjectAddedEvents.clear();
mLocalEngineEvents.clear();
mNewGameStarted = false;
mPlayerChanged = false;
mWorldView.clear(); mWorldView.clear();
mGlobalScripts.removeAllScripts(); mGlobalScripts.removeAllScripts();
mGlobalScriptsStarted = false; mGlobalScriptsStarted = false;
@ -339,16 +269,15 @@ namespace MWLua
localScripts->addAutoStartedScripts(); localScripts->addAutoStartedScripts();
} }
mActiveLocalScripts.insert(localScripts); mActiveLocalScripts.insert(localScripts);
mLocalEngineEvents.push_back({ getId(ptr), LocalScripts::OnActive{} }); mEngineEvents.addToQueue(EngineEvents::OnActive{ getId(ptr) });
mPlayerChanged = true;
} }
void LuaManager::newGameStarted() void LuaManager::newGameStarted()
{ {
mNewGameStarted = true;
mInputEvents.clear(); mInputEvents.clear();
mGlobalScripts.addAutoStartedScripts(); mGlobalScripts.addAutoStartedScripts();
mGlobalScriptsStarted = true; mGlobalScriptsStarted = true;
mEngineEvents.addToQueue(EngineEvents::OnNewGame{});
} }
void LuaManager::gameLoaded() void LuaManager::gameLoaded()
@ -361,27 +290,17 @@ namespace MWLua
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr) void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
{ {
mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet. mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
mEngineEvents.addToQueue(EngineEvents::OnActive{ getId(ptr) });
LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); if (!ptr.getRefData().getLuaScripts())
if (!localScripts)
{ {
LuaUtil::ScriptIdsWithInitializationData autoStartConf LuaUtil::ScriptIdsWithInitializationData autoStartConf
= mConfiguration.getLocalConf(getLiveCellRefType(ptr.mRef), ptr.getCellRef().getRefId(), getId(ptr)); = mConfiguration.getLocalConf(getLiveCellRefType(ptr.mRef), ptr.getCellRef().getRefId(), getId(ptr));
// TODO: put to a queue and apply `addAutoStartedScripts` on next `update()`
if (!autoStartConf.empty()) if (!autoStartConf.empty())
{ createLocalScripts(ptr, std::move(autoStartConf))->addAutoStartedScripts();
localScripts = createLocalScripts(ptr, std::move(autoStartConf));
localScripts->addAutoStartedScripts(); // TODO: put to a queue and apply on next `update()`
} }
} }
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) void LuaManager::objectRemovedFromScene(const MWWorld::Ptr& ptr)
{ {
@ -391,21 +310,10 @@ namespace MWLua
{ {
mActiveLocalScripts.erase(localScripts); mActiveLocalScripts.erase(localScripts);
if (!MWBase::Environment::get().getWorldModel()->getPtr(getId(ptr)).isEmpty()) 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 MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const
{ {
LocalScripts* localScripts = ptr.getRefData().getLuaScripts(); LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
@ -470,7 +378,7 @@ namespace MWLua
ESM::LuaScripts globalScripts; ESM::LuaScripts globalScripts;
mGlobalScripts.save(globalScripts); mGlobalScripts.save(globalScripts);
globalScripts.save(writer); globalScripts.save(writer);
saveEvents(writer, mGlobalEvents, mLocalEvents); mLuaEvents.save(writer);
writer.endRecord(ESM::REC_LUAM); writer.endRecord(ESM::REC_LUAM);
} }
@ -483,7 +391,7 @@ namespace MWLua
mWorldView.load(reader); mWorldView.load(reader);
ESM::LuaScripts globalScripts; ESM::LuaScripts globalScripts;
globalScripts.load(reader); globalScripts.load(reader);
loadEvents(mLua.sol(), reader, mGlobalEvents, mLocalEvents, mContentFileMapping, mGlobalLoader.get()); mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get());
mGlobalScripts.setSavedDataDeserializer(mGlobalLoader.get()); mGlobalScripts.setSavedDataDeserializer(mGlobalLoader.get());
mGlobalScripts.load(globalScripts); mGlobalScripts.load(globalScripts);
@ -547,7 +455,7 @@ namespace MWLua
scripts->load(data); scripts->load(data);
} }
for (LocalScripts* scripts : mActiveLocalScripts) for (LocalScripts* scripts : mActiveLocalScripts)
scripts->receiveEngineEvent(LocalScripts::OnActive()); scripts->setActive(true);
} }
void LuaManager::handleConsoleCommand( void LuaManager::handleConsoleCommand(

View file

@ -14,9 +14,10 @@
#include "../mwbase/luamanager.hpp" #include "../mwbase/luamanager.hpp"
#include "eventqueue.hpp" #include "engineevents.hpp"
#include "globalscripts.hpp" #include "globalscripts.hpp"
#include "localscripts.hpp" #include "localscripts.hpp"
#include "luaevents.hpp"
#include "object.hpp" #include "object.hpp"
#include "worldview.hpp" #include "worldview.hpp"
@ -31,6 +32,8 @@ namespace MWLua
{ {
public: public:
LuaManager(const VFS::Manager* vfs, const std::filesystem::path& libsDir); LuaManager(const VFS::Manager* vfs, const std::filesystem::path& libsDir);
LuaManager(const LuaManager&) = delete;
LuaManager(LuaManager&&) = delete;
// Called by engine.cpp when the environment is fully initialized. // Called by engine.cpp when the environment is fully initialized.
void init(); void init();
@ -62,8 +65,14 @@ namespace MWLua
void objectAddedToScene(const MWWorld::Ptr& ptr) override; void objectAddedToScene(const MWWorld::Ptr& ptr) override;
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override; void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); } void inputEvent(const InputEvent& event) override { mInputEvents.push_back(event); }
void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override; void itemConsumed(const MWWorld::Ptr& consumable, const MWWorld::Ptr& actor) override
void objectActivated(const MWWorld::Ptr& object, 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; MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override;
@ -161,12 +170,11 @@ namespace MWLua
std::set<LocalScripts*> mActiveLocalScripts; std::set<LocalScripts*> mActiveLocalScripts;
WorldView mWorldView; WorldView mWorldView;
bool mPlayerChanged = false;
bool mNewGameStarted = false;
MWWorld::Ptr mPlayer; MWWorld::Ptr mPlayer;
GlobalEventQueue mGlobalEvents; LuaEvents mLuaEvents{ mGlobalScripts };
LocalEventQueue mLocalEvents; EngineEvents mEngineEvents{ mGlobalScripts };
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer; std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer;
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer; std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer;
@ -175,9 +183,6 @@ namespace MWLua
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalLoader; std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalLoader;
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalLoader; std::unique_ptr<LuaUtil::UserdataSerializer> mLocalLoader;
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
std::vector<ObjectId> mObjectAddedEvents;
struct CallbackWithData struct CallbackWithData
{ {
LuaUtil::Callback mCallback; LuaUtil::Callback mCallback;
@ -185,13 +190,6 @@ namespace MWLua
}; };
std::vector<CallbackWithData> mQueuedCallbacks; 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(). // Queued actions that should be done in main thread. Processed by applyQueuedChanges().
std::vector<std::unique_ptr<Action>> mActionQueue; std::vector<std::unique_ptr<Action>> mActionQueue;
std::unique_ptr<Action> mTeleportPlayerAction; std::unique_ptr<Action> mTeleportPlayerAction;

View file

@ -11,7 +11,7 @@
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "eventqueue.hpp" #include "luaevents.hpp"
#include "luamanagerimp.hpp" #include "luamanagerimp.hpp"
#include "types/types.hpp" #include "types/types.hpp"
@ -188,7 +188,7 @@ namespace MWLua
objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); }; objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); };
objectT[sol::meta_function::to_string] = &ObjectT::toString; objectT[sol::meta_function::to_string] = &ObjectT::toString;
objectT["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData) { objectT["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData) {
context.mLocalEventQueue->push_back( context.mLuaEvents->addLocalEvent(
{ dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) }); { dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer) });
}; };

View file

@ -11,6 +11,7 @@
#include "apps/openmw/mwworld/store.hpp" #include "apps/openmw/mwworld/store.hpp"
#include "../context.hpp" #include "../context.hpp"
#include "../object.hpp"
namespace MWLua namespace MWLua
{ {

View file

@ -52,7 +52,7 @@ namespace MWLua
return &mDoorsInScene; return &mDoorsInScene;
if (typeid(cls) == typeid(MWClass::Container)) if (typeid(cls) == typeid(MWClass::Container))
return &mContainersInScene; return &mContainersInScene;
if (cls.hasToolTip(ptr)) if (cls.isItem(ptr))
return &mItemsInScene; return &mItemsInScene;
return nullptr; return nullptr;
} }

View file

@ -58,9 +58,6 @@ namespace MWLua
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
void save(ESM::ESMWriter& esm) const; void save(ESM::ESMWriter& esm) const;
// TODO: move this functionality to MWClass
bool isItem(const MWWorld::Ptr& ptr) { return chooseGroup(ptr) == &mItemsInScene; }
private: private:
struct ObjectGroup struct ObjectGroup
{ {

View file

@ -323,6 +323,9 @@ namespace MWWorld
virtual bool isDoor() const { return false; } virtual bool isDoor() const { return false; }
// True if it is an item that can be picked up.
virtual bool isItem(const MWWorld::ConstPtr& ptr) const { return false; }
virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const; virtual bool isBipedal(const MWWorld::ConstPtr& ptr) const;
virtual bool canFly(const MWWorld::ConstPtr& ptr) const; virtual bool canFly(const MWWorld::ConstPtr& ptr) const;
virtual bool canSwim(const MWWorld::ConstPtr& ptr) const; virtual bool canSwim(const MWWorld::ConstPtr& ptr) const;

View file

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

View file

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