mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-01-31 15:32:45 +00:00
Add apps/openmw/mwlua
This commit is contained in:
parent
7df500c385
commit
3d7e306064
@ -55,6 +55,11 @@ add_openmw_dir (mwscript
|
||||
animationextensions transformationextensions consoleextensions userextensions
|
||||
)
|
||||
|
||||
add_openmw_dir (mwlua
|
||||
luamanagerimp localscripts object worldview luabindings userdataserializer
|
||||
objectbindings
|
||||
)
|
||||
|
||||
add_openmw_dir (mwsound
|
||||
soundmanagerimp openal_output ffmpeg_decoder sound sound_buffer sound_decoder sound_output
|
||||
loudness movieaudiofactory alext efx efx-presets regionsoundselector watersoundupdater volumesettings
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "engine.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
@ -46,6 +47,8 @@
|
||||
|
||||
#include "mwgui/windowmanagerimp.hpp"
|
||||
|
||||
#include "mwlua/luamanagerimp.hpp"
|
||||
|
||||
#include "mwscript/scriptmanagerimp.hpp"
|
||||
#include "mwscript/interpretercontext.hpp"
|
||||
|
||||
@ -101,6 +104,7 @@ namespace
|
||||
PhysicsWorker,
|
||||
World,
|
||||
Gui,
|
||||
Lua,
|
||||
|
||||
Number,
|
||||
};
|
||||
@ -138,6 +142,9 @@ namespace
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Gui>::sValue {"Gui", "gui"};
|
||||
|
||||
template <>
|
||||
const UserStats UserStatsValue<UserStatsType::Lua>::sValue {"Lua", "lua"};
|
||||
|
||||
template <UserStatsType type>
|
||||
struct ForEachUserStatsValue
|
||||
{
|
||||
@ -700,6 +707,9 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||
|
||||
mViewer->addEventHandler(mScreenCaptureHandler);
|
||||
|
||||
mLuaManager = new MWLua::LuaManager(mVFS.get());
|
||||
mEnvironment.setLuaManager(mLuaManager);
|
||||
|
||||
// Create input and UI first to set up a bootstrapping environment for
|
||||
// showing a loading screen and keeping the window responsive while doing so
|
||||
|
||||
@ -811,6 +821,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||
<< 100*static_cast<double> (result.second)/result.first
|
||||
<< "%)";
|
||||
}
|
||||
|
||||
mLuaManager->init();
|
||||
}
|
||||
|
||||
// Initialise and enter main loop.
|
||||
@ -895,6 +907,28 @@ void OMW::Engine::go()
|
||||
mEnvironment.getWindowManager()->executeInConsole(mStartupScript);
|
||||
}
|
||||
|
||||
// Start Lua scripting thread
|
||||
std::atomic_bool luaUpdateRequest;
|
||||
double luaDt = 0;
|
||||
std::thread scriptingThread([&]() {
|
||||
const osg::Timer* const timer = osg::Timer::instance();
|
||||
osg::Stats* const stats = mViewer->getViewerStats();
|
||||
while (!mViewer->done() && !mEnvironment.getStateManager()->hasQuitRequest())
|
||||
{
|
||||
while (!luaUpdateRequest)
|
||||
std::this_thread::yield();
|
||||
|
||||
{
|
||||
const osg::Timer_t frameStart = mViewer->getStartTick();
|
||||
const unsigned int frameNumber = mViewer->getFrameStamp()->getFrameNumber();
|
||||
ScopedProfile<UserStatsType::Lua> profile(frameStart, frameNumber, *timer, *stats);
|
||||
|
||||
mLuaManager->update(mEnvironment.getWindowManager()->isGuiMode(), luaDt);
|
||||
}
|
||||
luaUpdateRequest = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Start the main rendering loop
|
||||
double simulationTime = 0.0;
|
||||
Misc::FrameRateLimiter frameRateLimiter = Misc::makeFrameRateLimiter(mEnvironment.getFrameRateLimit());
|
||||
@ -920,8 +954,17 @@ void OMW::Engine::go()
|
||||
|
||||
mEnvironment.getWorld()->updateWindowManager();
|
||||
|
||||
// scriptingThread starts processing Lua scripts
|
||||
luaDt = dt;
|
||||
luaUpdateRequest = true;
|
||||
|
||||
mViewer->renderingTraversals();
|
||||
|
||||
// wait for scriptingThread to finish
|
||||
while (luaUpdateRequest)
|
||||
std::this_thread::yield();
|
||||
mLuaManager->applyQueuedChanges();
|
||||
|
||||
bool guiActive = mEnvironment.getWindowManager()->isGuiMode();
|
||||
if (!guiActive)
|
||||
simulationTime += dt;
|
||||
@ -943,6 +986,8 @@ void OMW::Engine::go()
|
||||
frameRateLimiter.limit();
|
||||
}
|
||||
|
||||
scriptingThread.join();
|
||||
|
||||
// Save user settings
|
||||
settings.saveUser(settingspath);
|
||||
|
||||
|
@ -33,6 +33,11 @@ namespace Compiler
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
class LuaManager;
|
||||
}
|
||||
|
||||
namespace Files
|
||||
{
|
||||
struct ConfigurationManager;
|
||||
@ -85,6 +90,8 @@ namespace OMW
|
||||
Compiler::Extensions mExtensions;
|
||||
Compiler::Context *mScriptContext;
|
||||
|
||||
MWLua::LuaManager* mLuaManager;
|
||||
|
||||
Files::Collections mFileCollections;
|
||||
bool mFSStrict;
|
||||
Translation::Storage mTranslationDataStorage;
|
||||
|
@ -13,13 +13,14 @@
|
||||
#include "inputmanager.hpp"
|
||||
#include "windowmanager.hpp"
|
||||
#include "statemanager.hpp"
|
||||
#include "luamanager.hpp"
|
||||
|
||||
MWBase::Environment *MWBase::Environment::sThis = nullptr;
|
||||
|
||||
MWBase::Environment::Environment()
|
||||
: mWorld (nullptr), mSoundManager (nullptr), mScriptManager (nullptr), mWindowManager (nullptr),
|
||||
mMechanicsManager (nullptr), mDialogueManager (nullptr), mJournal (nullptr), mInputManager (nullptr),
|
||||
mStateManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f)
|
||||
mStateManager (nullptr), mLuaManager (nullptr), mResourceSystem (nullptr), mFrameDuration (0), mFrameRateLimit(0.f)
|
||||
{
|
||||
assert (!sThis);
|
||||
sThis = this;
|
||||
@ -76,6 +77,11 @@ void MWBase::Environment::setStateManager (StateManager *stateManager)
|
||||
mStateManager = stateManager;
|
||||
}
|
||||
|
||||
void MWBase::Environment::setLuaManager (LuaManager *luaManager)
|
||||
{
|
||||
mLuaManager = luaManager;
|
||||
}
|
||||
|
||||
void MWBase::Environment::setResourceSystem (Resource::ResourceSystem *resourceSystem)
|
||||
{
|
||||
mResourceSystem = resourceSystem;
|
||||
@ -150,6 +156,12 @@ MWBase::StateManager *MWBase::Environment::getStateManager() const
|
||||
return mStateManager;
|
||||
}
|
||||
|
||||
MWBase::LuaManager *MWBase::Environment::getLuaManager() const
|
||||
{
|
||||
assert (mLuaManager);
|
||||
return mLuaManager;
|
||||
}
|
||||
|
||||
Resource::ResourceSystem *MWBase::Environment::getResourceSystem() const
|
||||
{
|
||||
return mResourceSystem;
|
||||
@ -188,6 +200,9 @@ void MWBase::Environment::cleanup()
|
||||
|
||||
delete mStateManager;
|
||||
mStateManager = nullptr;
|
||||
|
||||
delete mLuaManager;
|
||||
mLuaManager = nullptr;
|
||||
}
|
||||
|
||||
const MWBase::Environment& MWBase::Environment::get()
|
||||
|
@ -22,6 +22,7 @@ namespace MWBase
|
||||
class InputManager;
|
||||
class WindowManager;
|
||||
class StateManager;
|
||||
class LuaManager;
|
||||
|
||||
/// \brief Central hub for mw-subsystems
|
||||
///
|
||||
@ -42,6 +43,7 @@ namespace MWBase
|
||||
Journal *mJournal;
|
||||
InputManager *mInputManager;
|
||||
StateManager *mStateManager;
|
||||
LuaManager *mLuaManager;
|
||||
Resource::ResourceSystem *mResourceSystem;
|
||||
float mFrameDuration;
|
||||
float mFrameRateLimit;
|
||||
@ -76,6 +78,8 @@ namespace MWBase
|
||||
|
||||
void setStateManager (StateManager *stateManager);
|
||||
|
||||
void setLuaManager (LuaManager *luaManager);
|
||||
|
||||
void setResourceSystem (Resource::ResourceSystem *resourceSystem);
|
||||
|
||||
void setFrameDuration (float duration);
|
||||
@ -102,6 +106,8 @@ namespace MWBase
|
||||
|
||||
StateManager *getStateManager() const;
|
||||
|
||||
LuaManager *getLuaManager() const;
|
||||
|
||||
Resource::ResourceSystem *getResourceSystem() const;
|
||||
|
||||
float getFrameDuration() const;
|
||||
|
42
apps/openmw/mwbase/luamanager.hpp
Normal file
42
apps/openmw/mwbase/luamanager.hpp
Normal file
@ -0,0 +1,42 @@
|
||||
#ifndef GAME_MWBASE_LUAMANAGER_H
|
||||
#define GAME_MWBASE_LUAMANAGER_H
|
||||
|
||||
#include <SDL_events.h>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
}
|
||||
|
||||
namespace MWBase
|
||||
{
|
||||
|
||||
class LuaManager
|
||||
{
|
||||
public:
|
||||
virtual ~LuaManager() = default;
|
||||
|
||||
virtual void newGameStarted() = 0;
|
||||
virtual void objectAddedToScene(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void objectRemovedFromScene(const MWWorld::Ptr& ptr) = 0;
|
||||
virtual void keyPressed(const SDL_KeyboardEvent &arg) = 0;
|
||||
|
||||
struct ActorControls {
|
||||
bool controlledFromLua;
|
||||
|
||||
bool jump;
|
||||
bool run;
|
||||
float movement;
|
||||
float sideMovement;
|
||||
float turn;
|
||||
};
|
||||
|
||||
virtual const ActorControls* getActorControls(const MWWorld::Ptr&) const = 0;
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual void setupPlayer(const MWWorld::Ptr&) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // GAME_MWBASE_LUAMANAGER_H
|
23
apps/openmw/mwlua/eventqueue.hpp
Normal file
23
apps/openmw/mwlua/eventqueue.hpp
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef MWLUA_EVENTQUEUE_H
|
||||
#define MWLUA_EVENTQUEUE_H
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
struct GlobalEvent
|
||||
{
|
||||
std::string eventName;
|
||||
std::string eventData;
|
||||
};
|
||||
struct LocalEvent
|
||||
{
|
||||
ObjectId dest;
|
||||
std::string eventName;
|
||||
std::string eventData;
|
||||
};
|
||||
using GlobalEventQueue = std::vector<GlobalEvent>;
|
||||
using LocalEventQueue = std::vector<LocalEvent>;
|
||||
}
|
||||
|
||||
#endif // MWLUA_EVENTQUEUE_H
|
36
apps/openmw/mwlua/globalscripts.hpp
Normal file
36
apps/openmw/mwlua/globalscripts.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef MWLUA_GLOBALSCRIPTS_H
|
||||
#define MWLUA_GLOBALSCRIPTS_H
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/scriptscontainer.hpp>
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
class GlobalScripts : public LuaUtil::ScriptsContainer
|
||||
{
|
||||
public:
|
||||
GlobalScripts(LuaUtil::LuaState* lua) : LuaUtil::ScriptsContainer(lua, "Global")
|
||||
{
|
||||
registerEngineHandlers({&mActorActiveHandlers, &mNewGameHandlers, &mPlayerAddedHandlers});
|
||||
}
|
||||
|
||||
void newGameStarted() { callEngineHandlers(mNewGameHandlers); }
|
||||
void actorActive(const GObject& obj) { callEngineHandlers(mActorActiveHandlers, obj); }
|
||||
void playerAdded(const GObject& obj) { callEngineHandlers(mPlayerAddedHandlers, obj); }
|
||||
|
||||
private:
|
||||
EngineHandlerList mActorActiveHandlers{"onActorActive"};
|
||||
EngineHandlerList mNewGameHandlers{"onNewGame"};
|
||||
EngineHandlerList mPlayerAddedHandlers{"onPlayerAdded"};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_GLOBALSCRIPTS_H
|
44
apps/openmw/mwlua/localscripts.cpp
Normal file
44
apps/openmw/mwlua/localscripts.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "localscripts.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWBase::LuaManager::ActorControls> : std::false_type {};
|
||||
template <>
|
||||
struct is_automagical<MWLua::LocalScripts::SelfObject> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
void LocalScripts::initializeSelfPackage(const Context& context)
|
||||
{
|
||||
using ActorControls = MWBase::LuaManager::ActorControls;
|
||||
sol::usertype<ActorControls> controls = context.mLua->sol().new_usertype<ActorControls>("ActorControls");
|
||||
controls["movement"] = &ActorControls::movement;
|
||||
controls["sideMovement"] = &ActorControls::sideMovement;
|
||||
controls["turn"] = &ActorControls::turn;
|
||||
controls["run"] = &ActorControls::run;
|
||||
controls["jump"] = &ActorControls::jump;
|
||||
|
||||
sol::usertype<SelfObject> selfAPI =
|
||||
context.mLua->sol().new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject>());
|
||||
selfAPI[sol::meta_function::to_string] = [](SelfObject& self) { return "openmw.self[" + self.toString() + "]"; };
|
||||
selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
|
||||
selfAPI["controls"] = sol::readonly_property([](SelfObject& self) { return &self.mControls; });
|
||||
selfAPI["setDirectControl"] = [](SelfObject& self, bool v) { self.mControls.controlledFromLua = v; };
|
||||
}
|
||||
|
||||
std::unique_ptr<LocalScripts> LocalScripts::create(LuaUtil::LuaState* lua, const LObject& obj)
|
||||
{
|
||||
return std::unique_ptr<LocalScripts>(new LocalScripts(lua, obj));
|
||||
}
|
||||
|
||||
LocalScripts::LocalScripts(LuaUtil::LuaState* lua, const LObject& obj)
|
||||
: LuaUtil::ScriptsContainer(lua, "L" + idToString(obj.id())), mData(obj)
|
||||
{
|
||||
mData.mControls.controlledFromLua = false;
|
||||
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
|
||||
}
|
||||
|
||||
}
|
39
apps/openmw/mwlua/localscripts.hpp
Normal file
39
apps/openmw/mwlua/localscripts.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef MWLUA_LOCALSCRIPTS_H
|
||||
#define MWLUA_LOCALSCRIPTS_H
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/scriptscontainer.hpp>
|
||||
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
#include "luabindings.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
class LocalScripts : public LuaUtil::ScriptsContainer
|
||||
{
|
||||
public:
|
||||
static std::unique_ptr<LocalScripts> create(LuaUtil::LuaState* lua, const LObject& obj);
|
||||
static void initializeSelfPackage(const Context&);
|
||||
|
||||
const MWBase::LuaManager::ActorControls* getActorControls() const { return &mData.mControls; }
|
||||
|
||||
struct SelfObject : public LObject
|
||||
{
|
||||
SelfObject(const LObject& obj) : LObject(obj) {}
|
||||
MWBase::LuaManager::ActorControls mControls;
|
||||
};
|
||||
protected:
|
||||
LocalScripts(LuaUtil::LuaState* lua, const LObject& obj);
|
||||
SelfObject mData;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_LOCALSCRIPTS_H
|
39
apps/openmw/mwlua/luabindings.cpp
Normal file
39
apps/openmw/mwlua/luabindings.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
sol::table initCorePackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
api["sendGlobalEvent"] = [context](std::string eventName, const sol::object& eventData)
|
||||
{
|
||||
context.mGlobalEventQueue->push_back({std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||
};
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
sol::table initWorldPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
WorldView* worldView = context.mWorldView;
|
||||
api["activeActors"] = GObjectList{worldView->getActorsInScene()};
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
sol::table initNearbyPackage(const Context& context)
|
||||
{
|
||||
sol::table api(context.mLua->sol(), sol::create);
|
||||
WorldView* worldView = context.mWorldView;
|
||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||
api["items"] = LObjectList{worldView->getItemsInScene()};
|
||||
return context.mLua->makeReadOnly(api);
|
||||
}
|
||||
|
||||
}
|
||||
|
36
apps/openmw/mwlua/luabindings.hpp
Normal file
36
apps/openmw/mwlua/luabindings.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef MWLUA_LUABINDINGS_H
|
||||
#define MWLUA_LUABINDINGS_H
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
#include <components/lua/serialization.hpp>
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "object.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
class LuaManager;
|
||||
|
||||
struct Context
|
||||
{
|
||||
LuaManager* mLuaManager;
|
||||
LuaUtil::LuaState* mLua;
|
||||
LuaUtil::UserdataSerializer* mSerializer;
|
||||
WorldView* mWorldView;
|
||||
LocalEventQueue* mLocalEventQueue;
|
||||
GlobalEventQueue* mGlobalEventQueue;
|
||||
};
|
||||
|
||||
sol::table initCorePackage(const Context&);
|
||||
sol::table initWorldPackage(const Context&);
|
||||
sol::table initNearbyPackage(const Context&);
|
||||
|
||||
// Implemented in objectbindings.cpp
|
||||
void initObjectBindingsForLocalScripts(const Context&);
|
||||
void initObjectBindingsForGlobalScripts(const Context&);
|
||||
|
||||
// openmw.self package is implemented in localscripts.cpp
|
||||
}
|
||||
|
||||
#endif // MWLUA_LUABINDINGS_H
|
202
apps/openmw/mwlua/luamanagerimp.cpp
Normal file
202
apps/openmw/mwlua/luamanagerimp.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
#include <components/lua/utilpackage.hpp>
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
#include "luabindings.hpp"
|
||||
#include "userdataserializer.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
LuaManager::LuaManager(const VFS::Manager* vfs) : mLua(vfs)
|
||||
{
|
||||
Log(Debug::Info) << "Lua version: " << LuaUtil::getLuaVersion();
|
||||
mGlobalSerializer = createUserdataSerializer(false, mWorldView.getObjectRegistry());
|
||||
mLocalSerializer = createUserdataSerializer(true, mWorldView.getObjectRegistry());
|
||||
mGlobalScripts.setSerializer(mGlobalSerializer.get());
|
||||
|
||||
Context context;
|
||||
context.mLuaManager = this;
|
||||
context.mLua = &mLua;
|
||||
context.mWorldView = &mWorldView;
|
||||
context.mLocalEventQueue = &mLocalEvents;
|
||||
context.mGlobalEventQueue = &mGlobalEvents;
|
||||
context.mSerializer = mGlobalSerializer.get();
|
||||
|
||||
Context localContext = context;
|
||||
localContext.mSerializer = mLocalSerializer.get();
|
||||
|
||||
initObjectBindingsForGlobalScripts(context);
|
||||
initObjectBindingsForLocalScripts(localContext);
|
||||
LocalScripts::initializeSelfPackage(localContext);
|
||||
|
||||
mLua.addCommonPackage("openmw.util", LuaUtil::initUtilPackage(mLua.sol()));
|
||||
mLua.addCommonPackage("openmw.core", initCorePackage(context));
|
||||
mGlobalScripts.addPackage("openmw.world", initWorldPackage(context));
|
||||
mNearbyPackage = initNearbyPackage(localContext);
|
||||
}
|
||||
|
||||
void LuaManager::init()
|
||||
{
|
||||
mKeyPressEvents.clear();
|
||||
if (mGlobalScripts.addNewScript("test.lua"))
|
||||
Log(Debug::Info) << "Global script started: test.lua";
|
||||
}
|
||||
|
||||
void LuaManager::update(bool paused, float dt)
|
||||
{
|
||||
mWorldView.update();
|
||||
|
||||
if (paused)
|
||||
{
|
||||
mKeyPressEvents.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<GlobalEvent> globalEvents = std::move(mGlobalEvents);
|
||||
std::vector<LocalEvent> localEvents = std::move(mLocalEvents);
|
||||
mGlobalEvents = std::vector<GlobalEvent>();
|
||||
mLocalEvents = std::vector<LocalEvent>();
|
||||
|
||||
for (GlobalEvent& e : globalEvents)
|
||||
mGlobalScripts.receiveEvent(e.eventName, e.eventData);
|
||||
for (LocalEvent& e : localEvents)
|
||||
{
|
||||
LObject obj(e.dest, mWorldView.getObjectRegistry());
|
||||
LocalScripts* scripts = obj.isValid() ? obj.ptr().getRefData().getLuaScripts() : nullptr;
|
||||
if (scripts)
|
||||
scripts->receiveEvent(e.eventName, e.eventData);
|
||||
else
|
||||
Log(Debug::Debug) << "Ignored event " << e.eventName << " to L" << idToString(e.dest)
|
||||
<< ". Object not found or has no attached scripts";
|
||||
}
|
||||
|
||||
if (mPlayerChanged)
|
||||
{
|
||||
mPlayerChanged = false;
|
||||
mGlobalScripts.playerAdded(GObject(getId(mPlayer), mWorldView.getObjectRegistry()));
|
||||
}
|
||||
|
||||
if (mPlayerScripts)
|
||||
{
|
||||
for (const SDL_Keysym key : mKeyPressEvents)
|
||||
mPlayerScripts->keyPress(key.sym, key.mod);
|
||||
}
|
||||
mKeyPressEvents.clear();
|
||||
|
||||
for (ObjectId id : mActorAddedEvents)
|
||||
mGlobalScripts.actorActive(GObject(id, mWorldView.getObjectRegistry()));
|
||||
mActorAddedEvents.clear();
|
||||
|
||||
mGlobalScripts.update(dt);
|
||||
for (LocalScripts* scripts : mActiveLocalScripts)
|
||||
scripts->update(dt);
|
||||
}
|
||||
|
||||
void LuaManager::applyQueuedChanges()
|
||||
{
|
||||
}
|
||||
|
||||
void LuaManager::clear()
|
||||
{
|
||||
mActiveLocalScripts.clear();
|
||||
mLocalEvents.clear();
|
||||
mGlobalEvents.clear();
|
||||
mKeyPressEvents.clear();
|
||||
mActorAddedEvents.clear();
|
||||
mPlayerChanged = false;
|
||||
mPlayerScripts = nullptr;
|
||||
mWorldView.clear();
|
||||
if (!mPlayer.isEmpty())
|
||||
{
|
||||
mPlayer.getCellRef().unsetRefNum();
|
||||
mPlayer.getRefData().setLuaScripts(nullptr);
|
||||
mPlayer = MWWorld::Ptr();
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mWorldView.objectAddedToScene(ptr); // assigns generated RefNum if it is not set yet.
|
||||
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (localScripts)
|
||||
mActiveLocalScripts.insert(localScripts);
|
||||
|
||||
if (ptr.getClass().isActor() && ptr != mPlayer)
|
||||
mActorAddedEvents.push_back(getId(ptr));
|
||||
}
|
||||
|
||||
void LuaManager::setupPlayer(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (!mPlayer.isEmpty())
|
||||
throw std::logic_error("Player is initialized twice");
|
||||
mWorldView.objectAddedToScene(ptr);
|
||||
mPlayer = ptr;
|
||||
MWWorld::RefData& refData = ptr.getRefData();
|
||||
if (!refData.getLuaScripts())
|
||||
createLocalScripts(ptr);
|
||||
if (!mPlayerScripts)
|
||||
throw std::logic_error("mPlayerScripts not initialized");
|
||||
mActiveLocalScripts.insert(mPlayerScripts);
|
||||
mPlayerChanged = true;
|
||||
}
|
||||
|
||||
void LuaManager::objectRemovedFromScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
mWorldView.objectRemovedFromScene(ptr);
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (localScripts)
|
||||
mActiveLocalScripts.erase(localScripts);
|
||||
|
||||
// TODO: call mWorldView.objectUnloaded if object is unloaded from memory (does it ever happen?) and ptr becomes invalid.
|
||||
}
|
||||
|
||||
void LuaManager::keyPressed(const SDL_KeyboardEvent& arg)
|
||||
{
|
||||
mKeyPressEvents.push_back(arg.keysym);
|
||||
}
|
||||
|
||||
const MWBase::LuaManager::ActorControls* LuaManager::getActorControls(const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
LocalScripts* localScripts = ptr.getRefData().getLuaScripts();
|
||||
if (!localScripts)
|
||||
return nullptr;
|
||||
return localScripts->getActorControls();
|
||||
}
|
||||
|
||||
void LuaManager::addLocalScript(const MWWorld::Ptr& ptr, const std::string& scriptPath)
|
||||
{
|
||||
MWWorld::RefData& refData = ptr.getRefData();
|
||||
if (!refData.getLuaScripts())
|
||||
mActiveLocalScripts.insert(createLocalScripts(ptr));
|
||||
refData.getLuaScripts()->addNewScript(scriptPath);
|
||||
}
|
||||
|
||||
LocalScripts* LuaManager::createLocalScripts(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
std::unique_ptr<LocalScripts> scripts;
|
||||
// When loading a game, it can be called before LuaManager::setPlayer,
|
||||
// so we can't just check ptr == mPlayer here.
|
||||
if (*ptr.getCellRef().getRefIdPtr() == "player")
|
||||
{
|
||||
mPlayerScripts = new PlayerScripts(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
|
||||
scripts = std::unique_ptr<LocalScripts>(mPlayerScripts);
|
||||
// TODO: scripts->addPackage("openmw.ui", ...);
|
||||
// TODO: scripts->addPackage("openmw.camera", ...);
|
||||
}
|
||||
else
|
||||
scripts = LocalScripts::create(&mLua, LObject(getId(ptr), mWorldView.getObjectRegistry()));
|
||||
scripts->addPackage("openmw.nearby", mNearbyPackage);
|
||||
scripts->setSerializer(mLocalSerializer.get());
|
||||
|
||||
MWWorld::RefData& refData = ptr.getRefData();
|
||||
refData.setLuaScripts(std::move(scripts));
|
||||
return refData.getLuaScripts();
|
||||
}
|
||||
|
||||
}
|
78
apps/openmw/mwlua/luamanagerimp.hpp
Normal file
78
apps/openmw/mwlua/luamanagerimp.hpp
Normal file
@ -0,0 +1,78 @@
|
||||
#ifndef MWLUA_LUAMANAGERIMP_H
|
||||
#define MWLUA_LUAMANAGERIMP_H
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "../mwbase/luamanager.hpp"
|
||||
|
||||
#include "object.hpp"
|
||||
#include "eventqueue.hpp"
|
||||
#include "globalscripts.hpp"
|
||||
#include "localscripts.hpp"
|
||||
#include "playerscripts.hpp"
|
||||
#include "worldview.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
class LuaManager : public MWBase::LuaManager
|
||||
{
|
||||
public:
|
||||
LuaManager(const VFS::Manager* vfs);
|
||||
~LuaManager() {}
|
||||
|
||||
// Called by engine.cpp when environment is fully initialized.
|
||||
void init();
|
||||
|
||||
// Called by engine.cpp every frame. For performance reasons it works in a separate
|
||||
// thread (in parallel with osg Cull). Can not use scene graph.
|
||||
void update(bool paused, float dt);
|
||||
|
||||
// Called by engine.cpp from the main thread. Can use scene graph.
|
||||
void applyQueuedChanges();
|
||||
|
||||
// Available everywhere through the MWBase::LuaManager interface.
|
||||
// LuaManager queues these events and propagates to scripts on the next `update` call.
|
||||
void newGameStarted() override { mGlobalScripts.newGameStarted(); }
|
||||
void objectAddedToScene(const MWWorld::Ptr& ptr) override;
|
||||
void objectRemovedFromScene(const MWWorld::Ptr& ptr) override;
|
||||
void keyPressed(const SDL_KeyboardEvent &arg) override;
|
||||
|
||||
const MWBase::LuaManager::ActorControls* getActorControls(const MWWorld::Ptr&) const override;
|
||||
|
||||
void clear() override; // should be called before loading game or starting a new game to reset internal state.
|
||||
void setupPlayer(const MWWorld::Ptr& ptr) override; // Should be called once after each "clear".
|
||||
|
||||
// Used only in luabindings.cpp
|
||||
void addLocalScript(const MWWorld::Ptr&, const std::string& scriptPath);
|
||||
|
||||
private:
|
||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
||||
|
||||
LuaUtil::LuaState mLua;
|
||||
sol::table mNearbyPackage;
|
||||
|
||||
GlobalScripts mGlobalScripts{&mLua};
|
||||
std::set<LocalScripts*> mActiveLocalScripts;
|
||||
WorldView mWorldView;
|
||||
|
||||
bool mPlayerChanged = false;
|
||||
MWWorld::Ptr mPlayer;
|
||||
PlayerScripts* mPlayerScripts = nullptr;
|
||||
|
||||
GlobalEventQueue mGlobalEvents;
|
||||
LocalEventQueue mLocalEvents;
|
||||
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> mGlobalSerializer;
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> mLocalSerializer;
|
||||
|
||||
std::vector<SDL_Keysym> mKeyPressEvents;
|
||||
std::vector<ObjectId> mActorAddedEvents;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_LUAMANAGERIMP_H
|
111
apps/openmw/mwlua/object.cpp
Normal file
111
apps/openmw/mwlua/object.cpp
Normal file
@ -0,0 +1,111 @@
|
||||
#include "object.hpp"
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
#include <components/esm/loadcrea.hpp>
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
std::string idToString(const ObjectId& id)
|
||||
{
|
||||
return std::to_string(id.mIndex) + "_" + std::to_string(id.mContentFile);
|
||||
}
|
||||
|
||||
std::string Object::toString() const
|
||||
{
|
||||
std::string res = idToString(mId);
|
||||
if (isValid())
|
||||
{
|
||||
res.append(" (");
|
||||
res.append(type());
|
||||
res.append(", ");
|
||||
res.append(*ptr().getCellRef().getRefIdPtr());
|
||||
res.append(")");
|
||||
}
|
||||
else
|
||||
res.append(" (not found)");
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string_view Object::type() const
|
||||
{
|
||||
if (*ptr().getCellRef().getRefIdPtr() == "player")
|
||||
return "Player";
|
||||
const std::string& typeName = ptr().getTypeName();
|
||||
if (typeName == typeid(ESM::NPC).name())
|
||||
return "NPC";
|
||||
else if (typeName == typeid(ESM::Creature).name())
|
||||
return "Creature";
|
||||
else
|
||||
return typeName;
|
||||
}
|
||||
|
||||
bool Object::isValid() const
|
||||
{
|
||||
if (mLastUpdate < mObjectRegistry->mUpdateCounter)
|
||||
{
|
||||
updatePtr();
|
||||
mLastUpdate = mObjectRegistry->mUpdateCounter;
|
||||
}
|
||||
return !mPtr.isEmpty();
|
||||
}
|
||||
|
||||
const MWWorld::Ptr& Object::ptr() const
|
||||
{
|
||||
if (!isValid())
|
||||
throw std::runtime_error("Object is not available: " + idToString(mId));
|
||||
return mPtr;
|
||||
}
|
||||
|
||||
void ObjectRegistry::update()
|
||||
{
|
||||
if (mChanged)
|
||||
{
|
||||
mUpdateCounter++;
|
||||
mChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectRegistry::clear()
|
||||
{
|
||||
mObjectMapping.clear();
|
||||
mChanged = false;
|
||||
mUpdateCounter = 0;
|
||||
mLastAssignedId.unset();
|
||||
}
|
||||
|
||||
MWWorld::Ptr ObjectRegistry::getPtr(ObjectId id, bool onlyActive)
|
||||
{
|
||||
MWWorld::Ptr ptr;
|
||||
auto it = mObjectMapping.find(id);
|
||||
if (it != mObjectMapping.end())
|
||||
ptr = it->second;
|
||||
if (onlyActive)
|
||||
{
|
||||
// TODO: add flag `isActive` to LiveCellRefBase. Return empty Ptr if the flag is not set.
|
||||
// Needed because in multiplayer mode inactive objects will not be synchronized, so will likely be out of date.
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: If Ptr is empty then try to load the object from esp/esm.
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
ObjectId ObjectRegistry::registerPtr(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
ObjectId id = ptr.getCellRef().getOrAssignRefNum(mLastAssignedId);
|
||||
mChanged = true;
|
||||
mObjectMapping[id] = ptr;
|
||||
return id;
|
||||
}
|
||||
|
||||
ObjectId ObjectRegistry::deregisterPtr(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
ObjectId id = getId(ptr);
|
||||
mChanged = true;
|
||||
mObjectMapping.erase(id);
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
103
apps/openmw/mwlua/object.hpp
Normal file
103
apps/openmw/mwlua/object.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
#ifndef MWLUA_OBJECT_H
|
||||
#define MWLUA_OBJECT_H
|
||||
|
||||
#include <components/esm/cellref.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
// ObjectId is a unique identifier of a game object.
|
||||
// It can change only if the order of content files was change.
|
||||
using ObjectId = ESM::RefNum;
|
||||
inline const ObjectId& getId(const MWWorld::Ptr& ptr) { return ptr.getCellRef().getRefNum(); }
|
||||
std::string idToString(const ObjectId& id);
|
||||
|
||||
// Holds a mapping ObjectId -> MWWord::Ptr.
|
||||
class ObjectRegistry
|
||||
{
|
||||
public:
|
||||
ObjectRegistry() { mLastAssignedId.unset(); }
|
||||
|
||||
void update(); // Should be called every frame.
|
||||
void clear(); // Should be called before starting or loading a new game.
|
||||
|
||||
ObjectId registerPtr(const MWWorld::Ptr& ptr);
|
||||
ObjectId deregisterPtr(const MWWorld::Ptr& ptr);
|
||||
|
||||
// Returns Ptr by id. If object is not found, returns empty Ptr.
|
||||
// If onlyActive = true, returns non-empty ptr only if it is registered and is in an active cell.
|
||||
// If onlyActive = false, tries to load and register the object if it is not loaded yet.
|
||||
// NOTE: `onlyActive` logic is not yet implemented.
|
||||
MWWorld::Ptr getPtr(ObjectId id, bool onlyActive);
|
||||
|
||||
// Needed only for saving/loading.
|
||||
const ObjectId& getLastAssignedId() const { return mLastAssignedId; }
|
||||
void setLastAssignedId(ObjectId id) { mLastAssignedId = id; }
|
||||
|
||||
private:
|
||||
friend class Object;
|
||||
friend class GObject;
|
||||
friend class LObject;
|
||||
|
||||
bool mChanged = false;
|
||||
int64_t mUpdateCounter = 0;
|
||||
std::map<ObjectId, MWWorld::Ptr> mObjectMapping;
|
||||
ObjectId mLastAssignedId;
|
||||
};
|
||||
|
||||
// Lua scripts can't use MWWorld::Ptr directly, because lifetime of a script can be longer than lifetime of Ptr.
|
||||
// `GObject` and `LObject` are intended to be passed to Lua as a userdata.
|
||||
// It automatically updates the underlying Ptr when needed.
|
||||
class Object
|
||||
{
|
||||
public:
|
||||
Object(ObjectId id, ObjectRegistry* reg) : mId(id), mObjectRegistry(reg) {}
|
||||
virtual ~Object() {}
|
||||
ObjectId id() const { return mId; }
|
||||
|
||||
std::string toString() const;
|
||||
std::string_view type() const;
|
||||
|
||||
// Updates and returns the underlying Ptr. Throws an exception if object is not available.
|
||||
const MWWorld::Ptr& ptr() const;
|
||||
|
||||
// Returns `true` if calling `ptr()` is safe.
|
||||
bool isValid() const;
|
||||
|
||||
protected:
|
||||
virtual void updatePtr() const = 0;
|
||||
|
||||
const ObjectId mId;
|
||||
ObjectRegistry* mObjectRegistry;
|
||||
|
||||
mutable MWWorld::Ptr mPtr;
|
||||
mutable int64_t mLastUpdate = -1;
|
||||
};
|
||||
|
||||
// Used only in local scripts
|
||||
class LObject : public Object
|
||||
{
|
||||
using Object::Object;
|
||||
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, true); }
|
||||
};
|
||||
|
||||
// Used only in global scripts
|
||||
class GObject : public Object
|
||||
{
|
||||
using Object::Object;
|
||||
void updatePtr() const final { mPtr = mObjectRegistry->getPtr(mId, false); }
|
||||
};
|
||||
|
||||
using ObjectIdList = std::shared_ptr<std::vector<ObjectId>>;
|
||||
template <typename Obj>
|
||||
struct ObjectList { ObjectIdList mIds; };
|
||||
using GObjectList = ObjectList<GObject>;
|
||||
using LObjectList = ObjectList<LObject>;
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_OBJECT_H
|
106
apps/openmw/mwlua/objectbindings.cpp
Normal file
106
apps/openmw/mwlua/objectbindings.cpp
Normal file
@ -0,0 +1,106 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include <components/lua/luastate.hpp>
|
||||
|
||||
#include "eventqueue.hpp"
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWLua::LObject> : std::false_type {};
|
||||
template <>
|
||||
struct is_automagical<MWLua::GObject> : std::false_type {};
|
||||
template <>
|
||||
struct is_automagical<MWLua::LObjectList> : std::false_type {};
|
||||
template <>
|
||||
struct is_automagical<MWLua::GObjectList> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
template <class ObjectT>
|
||||
static void registerObjectList(const std::string& prefix, const Context& context)
|
||||
{
|
||||
using ListT = ObjectList<ObjectT>;
|
||||
sol::state& lua = context.mLua->sol();
|
||||
ObjectRegistry* registry = context.mWorldView->getObjectRegistry();
|
||||
sol::usertype<ListT> listT = lua.new_usertype<ListT>(prefix + "ObjectList");
|
||||
listT[sol::meta_function::to_string] =
|
||||
[](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; };
|
||||
listT[sol::meta_function::length] = [](const ListT& list) { return list.mIds->size(); };
|
||||
listT[sol::meta_function::index] = [registry](const ListT& list, size_t index)
|
||||
{
|
||||
if (index > 0 && index <= list.mIds->size())
|
||||
return ObjectT((*list.mIds)[index - 1], registry);
|
||||
else
|
||||
throw std::runtime_error("Index out of range");
|
||||
};
|
||||
listT["ipairs"] = [registry](const ListT& list)
|
||||
{
|
||||
auto iter = [registry](const ListT& l, int64_t i) -> sol::optional<std::tuple<int64_t, ObjectT>>
|
||||
{
|
||||
if (i >= 0 && i < static_cast<int64_t>(l.mIds->size()))
|
||||
return std::make_tuple(i + 1, ObjectT((*l.mIds)[i], registry));
|
||||
else
|
||||
return sol::nullopt;
|
||||
};
|
||||
return std::make_tuple(iter, list, 0);
|
||||
};
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void addBasicBindings(sol::usertype<ObjectT>& objectT, const Context& context)
|
||||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return o.isValid(); };
|
||||
objectT["recordId"] = sol::readonly_property([](const ObjectT& o) -> std::string
|
||||
{
|
||||
return o.ptr().getCellRef().getRefId();
|
||||
});
|
||||
objectT["position"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return o.ptr().getRefData().getPosition().asVec3();
|
||||
});
|
||||
objectT["rotation"] = sol::readonly_property([](const ObjectT& o) -> osg::Vec3f
|
||||
{
|
||||
return o.ptr().getRefData().getPosition().asRotationVec3();
|
||||
});
|
||||
objectT["type"] = sol::readonly_property(&ObjectT::type);
|
||||
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["sendEvent"] = [context](const ObjectT& dest, std::string eventName, const sol::object& eventData)
|
||||
{
|
||||
context.mLocalEventQueue->push_back({dest.id(), std::move(eventName), LuaUtil::serialize(eventData, context.mSerializer)});
|
||||
};
|
||||
|
||||
if constexpr (std::is_same_v<ObjectT, GObject>)
|
||||
{ // Only for global scripts
|
||||
objectT["addScript"] = [luaManager=context.mLuaManager](const GObject& object, const std::string& path)
|
||||
{
|
||||
luaManager->addLocalScript(object.ptr(), path);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <class ObjectT>
|
||||
static void initObjectBindings(const std::string& prefix, const Context& context)
|
||||
{
|
||||
sol::usertype<ObjectT> objectT = context.mLua->sol().new_usertype<ObjectT>(prefix + "Object");
|
||||
addBasicBindings<ObjectT>(objectT, context);
|
||||
|
||||
registerObjectList<ObjectT>(prefix, context);
|
||||
}
|
||||
|
||||
void initObjectBindingsForLocalScripts(const Context& context)
|
||||
{
|
||||
initObjectBindings<LObject>("L", context);
|
||||
}
|
||||
|
||||
void initObjectBindingsForGlobalScripts(const Context& context)
|
||||
{
|
||||
initObjectBindings<GObject>("G", context);
|
||||
}
|
||||
|
||||
}
|
||||
|
25
apps/openmw/mwlua/playerscripts.hpp
Normal file
25
apps/openmw/mwlua/playerscripts.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef MWLUA_PLAYERSCRIPTS_H
|
||||
#define MWLUA_PLAYERSCRIPTS_H
|
||||
|
||||
#include "localscripts.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
class PlayerScripts : public LocalScripts
|
||||
{
|
||||
public:
|
||||
PlayerScripts(LuaUtil::LuaState* lua, const LObject& obj) : LocalScripts(lua, obj)
|
||||
{
|
||||
registerEngineHandlers({&mKeyPressHandlers});
|
||||
}
|
||||
|
||||
void keyPress(int sym, int mod) { callEngineHandlers(mKeyPressHandlers, sym, mod); }
|
||||
|
||||
private:
|
||||
EngineHandlerList mKeyPressHandlers{"onKeyPress"};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_PLAYERSCRIPTS_H
|
64
apps/openmw/mwlua/userdataserializer.cpp
Normal file
64
apps/openmw/mwlua/userdataserializer.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "userdataserializer.hpp"
|
||||
|
||||
#include <components/lua/serialization.hpp>
|
||||
#include <components/misc/endianness.hpp>
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
class Serializer final : public LuaUtil::UserdataSerializer
|
||||
{
|
||||
public:
|
||||
explicit Serializer(bool localSerializer, ObjectRegistry* registry)
|
||||
: mLocalSerializer(localSerializer), mObjectRegistry(registry) {}
|
||||
|
||||
private:
|
||||
// Appends serialized sol::userdata to the end of BinaryData.
|
||||
// Returns false if this type of userdata is not supported by this serializer.
|
||||
bool serialize(LuaUtil::BinaryData& out, const sol::userdata& data) const override
|
||||
{
|
||||
if (data.is<GObject>() || data.is<LObject>())
|
||||
{
|
||||
ObjectId id = data.as<Object>().id();
|
||||
static_assert(sizeof(ObjectId) == 8);
|
||||
id.mIndex = Misc::toLittleEndian(id.mIndex);
|
||||
id.mContentFile = Misc::toLittleEndian(id.mContentFile);
|
||||
append(out, "o", &id, sizeof(ObjectId));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deserializes userdata of type "typeName" from binaryData. Should push the result on stack using sol::stack::push.
|
||||
// Returns false if this type is not supported by this serializer.
|
||||
bool deserialize(std::string_view typeName, std::string_view binaryData, sol::state& lua) const override
|
||||
{
|
||||
if (typeName == "o")
|
||||
{
|
||||
if (binaryData.size() != sizeof(ObjectId))
|
||||
throw std::runtime_error("Incorrect serialization format. Size of ObjectId doesn't match.");
|
||||
ObjectId id;
|
||||
std::memcpy(&id, binaryData.data(), sizeof(ObjectId));
|
||||
id.mIndex = Misc::fromLittleEndian(id.mIndex);
|
||||
id.mContentFile = Misc::fromLittleEndian(id.mContentFile);
|
||||
if (mLocalSerializer)
|
||||
sol::stack::push<LObject>(lua, LObject(id, mObjectRegistry));
|
||||
else
|
||||
sol::stack::push<GObject>(lua, GObject(id, mObjectRegistry));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool mLocalSerializer;
|
||||
ObjectRegistry* mObjectRegistry;
|
||||
};
|
||||
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry)
|
||||
{
|
||||
return std::make_unique<Serializer>(local, registry);
|
||||
}
|
||||
|
||||
}
|
19
apps/openmw/mwlua/userdataserializer.hpp
Normal file
19
apps/openmw/mwlua/userdataserializer.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef MWLUA_USERDATASERIALIZER_H
|
||||
#define MWLUA_USERDATASERIALIZER_H
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
class UserdataSerializer;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
// UserdataSerializer is an extension for components/lua/serialization.hpp
|
||||
// Needed to serialize references to objects.
|
||||
// If local=true, then during deserialization creates LObject, otherwise creates GObject.
|
||||
std::unique_ptr<LuaUtil::UserdataSerializer> createUserdataSerializer(bool local, ObjectRegistry* registry);
|
||||
}
|
||||
|
||||
#endif // MWLUA_USERDATASERIALIZER_H
|
68
apps/openmw/mwlua/worldview.cpp
Normal file
68
apps/openmw/mwlua/worldview.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "worldview.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
void WorldView::update()
|
||||
{
|
||||
mObjectRegistry.update();
|
||||
mActorsInScene.updateList();
|
||||
mItemsInScene.updateList();
|
||||
}
|
||||
|
||||
void WorldView::clear()
|
||||
{
|
||||
mObjectRegistry.clear();
|
||||
mActorsInScene.clear();
|
||||
mItemsInScene.clear();
|
||||
}
|
||||
|
||||
void WorldView::objectAddedToScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
addToGroup(mActorsInScene, ptr);
|
||||
else
|
||||
addToGroup(mItemsInScene, ptr);
|
||||
}
|
||||
|
||||
void WorldView::objectRemovedFromScene(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
if (ptr.getClass().isActor())
|
||||
removeFromGroup(mActorsInScene, ptr);
|
||||
else
|
||||
removeFromGroup(mItemsInScene, ptr);
|
||||
}
|
||||
|
||||
void WorldView::ObjectGroup::updateList()
|
||||
{
|
||||
if (mChanged)
|
||||
{
|
||||
mList->clear();
|
||||
for (const ObjectId& id : mSet)
|
||||
mList->push_back(id);
|
||||
mChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldView::ObjectGroup::clear()
|
||||
{
|
||||
mChanged = false;
|
||||
mList->clear();
|
||||
mSet.clear();
|
||||
}
|
||||
|
||||
void WorldView::addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
group.mSet.insert(getId(ptr));
|
||||
group.mChanged = true;
|
||||
}
|
||||
|
||||
void WorldView::removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr)
|
||||
{
|
||||
group.mSet.erase(getId(ptr));
|
||||
group.mChanged = true;
|
||||
}
|
||||
|
||||
}
|
47
apps/openmw/mwlua/worldview.hpp
Normal file
47
apps/openmw/mwlua/worldview.hpp
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef MWLUA_WORLDVIEW_H
|
||||
#define MWLUA_WORLDVIEW_H
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
// Tracks all used game objects.
|
||||
class WorldView
|
||||
{
|
||||
public:
|
||||
void update(); // Should be called every frame.
|
||||
void clear(); // Should be called every time before starting or loading a new game.
|
||||
|
||||
ObjectIdList getActorsInScene() const { return mActorsInScene.mList; }
|
||||
ObjectIdList getItemsInScene() const { return mItemsInScene.mList; }
|
||||
|
||||
ObjectRegistry* getObjectRegistry() { return &mObjectRegistry; }
|
||||
|
||||
void objectUnloaded(const MWWorld::Ptr& ptr) { mObjectRegistry.deregisterPtr(ptr); }
|
||||
|
||||
void objectAddedToScene(const MWWorld::Ptr& ptr);
|
||||
void objectRemovedFromScene(const MWWorld::Ptr& ptr);
|
||||
|
||||
private:
|
||||
struct ObjectGroup
|
||||
{
|
||||
void updateList();
|
||||
void clear();
|
||||
|
||||
bool mChanged = false;
|
||||
ObjectIdList mList = std::make_shared<std::vector<ObjectId>>();
|
||||
std::set<ObjectId> mSet;
|
||||
};
|
||||
|
||||
void addToGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||
void removeFromGroup(ObjectGroup& group, const MWWorld::Ptr& ptr);
|
||||
|
||||
ObjectRegistry mObjectRegistry;
|
||||
ObjectGroup mActorsInScene;
|
||||
ObjectGroup mItemsInScene;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MWLUA_WORLDVIEW_H
|
@ -8,6 +8,8 @@
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwlua/localscripts.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum RefDataFlags
|
||||
@ -21,6 +23,12 @@ enum RefDataFlags
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
void RefData::setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&& scripts)
|
||||
{
|
||||
mChanged = true;
|
||||
mLuaScripts = std::move(scripts);
|
||||
}
|
||||
|
||||
void RefData::copy (const RefData& refData)
|
||||
{
|
||||
mBaseNode = refData.mBaseNode;
|
||||
@ -36,12 +44,14 @@ namespace MWWorld
|
||||
mAnimationState = refData.mAnimationState;
|
||||
|
||||
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : nullptr;
|
||||
mLuaScripts = refData.mLuaScripts;
|
||||
}
|
||||
|
||||
void RefData::cleanup()
|
||||
{
|
||||
mBaseNode = nullptr;
|
||||
mCustomData = nullptr;
|
||||
mLuaScripts = nullptr;
|
||||
}
|
||||
|
||||
RefData::RefData()
|
||||
|
@ -22,6 +22,11 @@ namespace ESM
|
||||
struct ObjectState;
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
class LocalScripts;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
@ -32,6 +37,7 @@ namespace MWWorld
|
||||
SceneUtil::PositionAttitudeTransform* mBaseNode;
|
||||
|
||||
MWScript::Locals mLocals;
|
||||
std::shared_ptr<MWLua::LocalScripts> mLuaScripts;
|
||||
|
||||
/// separate delete flag used for deletion by a content file
|
||||
/// @note not stored in the save game file.
|
||||
@ -96,6 +102,9 @@ namespace MWWorld
|
||||
|
||||
void setLocals (const ESM::Script& script);
|
||||
|
||||
MWLua::LocalScripts* getLuaScripts() { return mLuaScripts.get(); }
|
||||
void setLuaScripts(std::unique_ptr<MWLua::LocalScripts>&&);
|
||||
|
||||
void setCount (int count);
|
||||
///< Set object count (an object pile is a simple object with a count >1).
|
||||
///
|
||||
|
Loading…
x
Reference in New Issue
Block a user