mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-28 03:40:04 +00:00
Lua callbacks
This commit is contained in:
parent
0bd1c22e24
commit
fb3917fc1a
@ -1,5 +1,7 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
@ -48,11 +50,16 @@ namespace MWLua
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimeUnit::HOURS, world->getGameTimeInHours() + delay, asyncId.mScript, std::move(callback));
|
||||
};
|
||||
api["callback"] = [](const AsyncPackageId& asyncId, sol::function fn)
|
||||
{
|
||||
return Callback{std::move(fn), asyncId.mHiddenData};
|
||||
};
|
||||
|
||||
auto initializer = [](sol::table hiddenData)
|
||||
{
|
||||
LuaUtil::ScriptsContainer::ScriptId id = hiddenData[LuaUtil::ScriptsContainer::ScriptId::KEY];
|
||||
return AsyncPackageId{id.mContainer, id.mPath};
|
||||
hiddenData[Callback::SCRIPT_NAME_KEY] = id.toString();
|
||||
return AsyncPackageId{id.mContainer, id.mPath, hiddenData};
|
||||
};
|
||||
return sol::make_object(context.mLua->sol(), initializer);
|
||||
}
|
||||
|
@ -47,9 +47,9 @@ namespace MWLua
|
||||
// Implemented in asyncbindings.cpp
|
||||
struct AsyncPackageId
|
||||
{
|
||||
// TODO: add ObjectId mLocalObject;
|
||||
LuaUtil::ScriptsContainer* mContainer;
|
||||
std::string mScript;
|
||||
sol::table mHiddenData;
|
||||
};
|
||||
sol::function getAsyncPackageInitializer(const Context&);
|
||||
|
||||
|
@ -74,6 +74,16 @@ namespace MWLua
|
||||
mInitialized = true;
|
||||
}
|
||||
|
||||
void Callback::operator()(sol::object arg) const
|
||||
{
|
||||
if (mHiddenData[LuaUtil::ScriptsContainer::ScriptId::KEY] != sol::nil)
|
||||
LuaUtil::call(mFunc, std::move(arg));
|
||||
else
|
||||
{
|
||||
Log(Debug::Debug) << "Ignored callback to removed script " << mHiddenData.get<std::string>(SCRIPT_NAME_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
void LuaManager::update(bool paused, float dt)
|
||||
{
|
||||
ObjectRegistry* objectRegistry = mWorldView.getObjectRegistry();
|
||||
@ -126,6 +136,11 @@ namespace MWLua
|
||||
<< ". Object not found or has no attached scripts";
|
||||
}
|
||||
|
||||
// Run queued callbacks
|
||||
for (CallbackWithData& c : mQueuedCallbacks)
|
||||
c.mCallback(c.mArg);
|
||||
mQueuedCallbacks.clear();
|
||||
|
||||
// Engine handlers in local scripts
|
||||
PlayerScripts* playerScripts = dynamic_cast<PlayerScripts*>(mPlayer.getRefData().getLuaScripts());
|
||||
if (playerScripts)
|
||||
|
@ -19,6 +19,19 @@
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
// Wrapper for a single-argument Lua function.
|
||||
// Holds information about the script the function belongs to.
|
||||
// Needed to prevent callback calls if the script was removed.
|
||||
struct Callback
|
||||
{
|
||||
static constexpr std::string_view SCRIPT_NAME_KEY = "name";
|
||||
|
||||
sol::function mFunc;
|
||||
sol::table mHiddenData;
|
||||
|
||||
void operator()(sol::object arg) const;
|
||||
};
|
||||
|
||||
class LuaManager : public MWBase::LuaManager
|
||||
{
|
||||
public:
|
||||
@ -67,6 +80,18 @@ namespace MWLua
|
||||
// Drops script cache and reloads all scripts. Calls `onSave` and `onLoad` for every script.
|
||||
void reloadAllScripts() override;
|
||||
|
||||
// Used to call Lua callbacks from C++
|
||||
void queueCallback(Callback callback, sol::object arg) { mQueuedCallbacks.push_back({std::move(callback), std::move(arg)}); }
|
||||
|
||||
// Wraps Lua callback into an std::function.
|
||||
// NOTE: Resulted function is not thread safe. Can not be used while LuaManager::update() or
|
||||
// any other Lua-related function is running.
|
||||
template <class Arg>
|
||||
std::function<void(Arg)> wrapLuaCallback(const Callback& c)
|
||||
{
|
||||
return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); };
|
||||
}
|
||||
|
||||
private:
|
||||
LocalScripts* createLocalScripts(const MWWorld::Ptr& ptr);
|
||||
|
||||
@ -100,6 +125,13 @@ namespace MWLua
|
||||
std::vector<MWBase::LuaManager::InputEvent> mInputEvents;
|
||||
std::vector<ObjectId> mActorAddedEvents;
|
||||
|
||||
struct CallbackWithData
|
||||
{
|
||||
Callback mCallback;
|
||||
sol::object mArg;
|
||||
};
|
||||
std::vector<CallbackWithData> mQueuedCallbacks;
|
||||
|
||||
struct LocalEngineEvent
|
||||
{
|
||||
ObjectId mDest;
|
||||
|
@ -79,6 +79,20 @@ namespace MWLua
|
||||
return rayCasting->castSphere(from, to, radius, collisionType);
|
||||
}
|
||||
};
|
||||
// TODO: async raycasting
|
||||
/*api["asyncCastRay"] = [luaManager = context.mLuaManager, defaultCollisionType](
|
||||
const Callback& luaCallback, const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options)
|
||||
{
|
||||
std::function<void(MWPhysics::RayCastingResult)> callback =
|
||||
luaManager->wrapLuaCallback<MWPhysics::RayCastingResult>(luaCallback);
|
||||
MWPhysics::RayCastingInterface* rayCasting = MWBase::Environment::get().getWorld()->getRayCasting();
|
||||
|
||||
// Handle options the same way as in `castRay`.
|
||||
|
||||
// NOTE: `callback` is not thread safe. If MWPhysics works in separate thread, it must put results to a queue
|
||||
// and use this callback from the main thread at the beginning of the next frame processing.
|
||||
rayCasting->asyncCastRay(callback, from, to, ignore, std::vector<MWWorld::Ptr>(), collisionType);
|
||||
};*/
|
||||
|
||||
api["activators"] = LObjectList{worldView->getActivatorsInScene()};
|
||||
api["actors"] = LObjectList{worldView->getActorsInScene()};
|
||||
|
@ -16,6 +16,15 @@ namespace LuaUtil
|
||||
static constexpr std::string_view REGISTERED_TIMER_CALLBACKS = "_timers";
|
||||
static constexpr std::string_view TEMPORARY_TIMER_CALLBACKS = "_temp_timers";
|
||||
|
||||
std::string ScriptsContainer::ScriptId::toString() const
|
||||
{
|
||||
std::string res = mContainer->mNamePrefix;
|
||||
res.push_back('[');
|
||||
res.append(mPath);
|
||||
res.push_back(']');
|
||||
return res;
|
||||
}
|
||||
|
||||
ScriptsContainer::ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix) : mNamePrefix(namePrefix), mLua(*lua)
|
||||
{
|
||||
registerEngineHandlers({&mUpdateHandlers});
|
||||
@ -81,6 +90,7 @@ namespace LuaUtil
|
||||
auto scriptIter = mScripts.find(path);
|
||||
if (scriptIter == mScripts.end())
|
||||
return false; // no such script
|
||||
scriptIter->second.mHiddenData[ScriptId::KEY] = sol::nil;
|
||||
sol::object& script = scriptIter->second.mInterface;
|
||||
if (getFieldOrNil(script, INTERFACE_NAME) != sol::nil)
|
||||
{
|
||||
@ -320,6 +330,8 @@ namespace LuaUtil
|
||||
|
||||
void ScriptsContainer::removeAllScripts()
|
||||
{
|
||||
for (auto& [_, script] : mScripts)
|
||||
script.mHiddenData[ScriptId::KEY] = sol::nil;
|
||||
mScripts.clear();
|
||||
mScriptOrder.clear();
|
||||
for (auto& [_, handlers] : mEngineHandlers)
|
||||
|
@ -66,6 +66,8 @@ namespace LuaUtil
|
||||
|
||||
ScriptsContainer* mContainer;
|
||||
std::string mPath;
|
||||
|
||||
std::string toString() const;
|
||||
};
|
||||
using TimeUnit = ESM::LuaTimer::TimeUnit;
|
||||
|
||||
@ -73,7 +75,7 @@ namespace LuaUtil
|
||||
ScriptsContainer(LuaUtil::LuaState* lua, std::string_view namePrefix);
|
||||
ScriptsContainer(const ScriptsContainer&) = delete;
|
||||
ScriptsContainer(ScriptsContainer&&) = delete;
|
||||
virtual ~ScriptsContainer() {}
|
||||
virtual ~ScriptsContainer() { removeAllScripts(); }
|
||||
|
||||
// Adds package that will be available (via `require`) for all scripts in the container.
|
||||
// Automatically applies LuaUtil::makeReadOnly to the package.
|
||||
|
@ -48,5 +48,12 @@
|
||||
-- @param #number delay
|
||||
-- @param #function func
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Wraps Lua function with `Callback` object that can be used in async API calls.
|
||||
-- @function [parent=#async] callback
|
||||
-- @param self
|
||||
-- @param #function func
|
||||
-- @return #Callback
|
||||
|
||||
return nil
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user