1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-02-06 10:45:33 +00:00

Execute async callbacks on the main Lua stack

This commit is contained in:
uramer 2022-10-02 12:47:33 +02:00
parent b13a4f6b01
commit a2fa1fe0a5
5 changed files with 36 additions and 32 deletions

View file

@ -29,32 +29,32 @@ namespace MWLua
using TimerType = LuaUtil::ScriptsContainer::TimerType; using TimerType = LuaUtil::ScriptsContainer::TimerType;
sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage"); sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage");
api["registerTimerCallback"] api["registerTimerCallback"]
= [](const AsyncPackageId& asyncId, std::string_view name, sol::function callback) { = [](const AsyncPackageId& asyncId, std::string_view name, sol::main_protected_function callback) {
asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback)); asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback));
return TimerCallback{ asyncId, std::string(name) }; return TimerCallback{ asyncId, std::string(name) };
}; };
api["newSimulationTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay, api["newSimulationTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg) { const TimerCallback& callback, sol::main_object callbackArg) {
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::SIMULATION_TIME, callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::SIMULATION_TIME,
world->getSimulationTime() + delay, callback.mAsyncId.mScriptId, callback.mName, world->getSimulationTime() + delay, callback.mAsyncId.mScriptId, callback.mName,
std::move(callbackArg)); std::move(callbackArg));
}; };
api["newGameTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay, api["newGameTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay,
const TimerCallback& callback, sol::object callbackArg) { const TimerCallback& callback, sol::main_object callbackArg) {
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::GAME_TIME, world->getGameTime() + delay, callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::GAME_TIME, world->getGameTime() + delay,
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg)); callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
}; };
api["newUnsavableSimulationTimer"] api["newUnsavableSimulationTimer"] = [world = context.mWorldView](const AsyncPackageId& asyncId, double delay,
= [world = context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback) { sol::main_protected_function callback) {
asyncId.mContainer->setupUnsavableTimer(TimerType::SIMULATION_TIME, asyncId.mContainer->setupUnsavableTimer(
world->getSimulationTime() + delay, asyncId.mScriptId, std::move(callback)); TimerType::SIMULATION_TIME, world->getSimulationTime() + delay, asyncId.mScriptId, std::move(callback));
}; };
api["newUnsavableGameTimer"] api["newUnsavableGameTimer"] = [world = context.mWorldView](const AsyncPackageId& asyncId, double delay,
= [world = context.mWorldView](const AsyncPackageId& asyncId, double delay, sol::function callback) { sol::main_protected_function callback) {
asyncId.mContainer->setupUnsavableTimer( asyncId.mContainer->setupUnsavableTimer(
TimerType::GAME_TIME, world->getGameTime() + delay, asyncId.mScriptId, std::move(callback)); TimerType::GAME_TIME, world->getGameTime() + delay, asyncId.mScriptId, std::move(callback));
}; };
api["callback"] = [](const AsyncPackageId& asyncId, sol::function fn) -> LuaUtil::Callback { api["callback"] = [](const AsyncPackageId& asyncId, sol::main_protected_function fn) -> LuaUtil::Callback {
return LuaUtil::Callback{ std::move(fn), asyncId.mHiddenData }; return LuaUtil::Callback{ std::move(fn), asyncId.mHiddenData };
}; };

View file

@ -103,7 +103,7 @@ namespace MWLua
const std::string& consoleMode, const std::string& command, const MWWorld::Ptr& selectedPtr) override; const std::string& consoleMode, const std::string& command, const MWWorld::Ptr& selectedPtr) override;
// Used to call Lua callbacks from C++ // Used to call Lua callbacks from C++
void queueCallback(LuaUtil::Callback callback, sol::object arg) void queueCallback(LuaUtil::Callback callback, sol::main_object arg)
{ {
mQueuedCallbacks.push_back({ std::move(callback), std::move(arg) }); mQueuedCallbacks.push_back({ std::move(callback), std::move(arg) });
} }
@ -114,7 +114,8 @@ namespace MWLua
template <class Arg> template <class Arg>
std::function<void(Arg)> wrapLuaCallback(const LuaUtil::Callback& c) std::function<void(Arg)> wrapLuaCallback(const LuaUtil::Callback& c)
{ {
return [this, c](Arg arg) { this->queueCallback(c, sol::make_object(c.mFunc.lua_state(), arg)); }; return
[this, c](Arg arg) { this->queueCallback(c, sol::main_object(this->mLua.sol(), sol::in_place, arg)); };
} }
LuaUi::ResourceManager* uiResourceManager() { return &mUiResourceManager; } LuaUi::ResourceManager* uiResourceManager() { return &mUiResourceManager; }
@ -167,7 +168,7 @@ namespace MWLua
struct CallbackWithData struct CallbackWithData
{ {
LuaUtil::Callback mCallback; LuaUtil::Callback mCallback;
sol::object mArg; sol::main_object mArg;
}; };
std::vector<CallbackWithData> mQueuedCallbacks; std::vector<CallbackWithData> mQueuedCallbacks;

View file

@ -115,12 +115,12 @@ namespace MWLua
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false);
return res; return res;
}; };
api["asyncCastRenderingRay"] = [manager = context.mLuaManager](const LuaUtil::Callback& callback, api["asyncCastRenderingRay"] = [context](const LuaUtil::Callback& callback, const osg::Vec3f& from,
const osg::Vec3f& from, const osg::Vec3f& to) { const osg::Vec3f& to) {
manager->addAction([manager, callback, from, to] { context.mLuaManager->addAction([context, callback, from, to] {
MWPhysics::RayCastingResult res; MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false);
manager->queueCallback(callback, sol::make_object(callback.mFunc.lua_state(), res)); context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res));
}); });
}; };

View file

@ -438,7 +438,8 @@ namespace LuaUtil
try try
{ {
timer.mArg = deserialize(mLua.sol(), savedTimer.mCallbackArgument, mSavedDataDeserializer); timer.mArg = sol::main_object(
deserialize(mLua.sol(), savedTimer.mCallbackArgument, mSavedDataDeserializer));
// It is important if the order of content files was changed. The deserialize-serialize procedure // It is important if the order of content files was changed. The deserialize-serialize procedure
// updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument. // updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument.
timer.mSerializedArg = serialize(timer.mArg, mSerializer); timer.mSerializedArg = serialize(timer.mArg, mSerializer);
@ -488,7 +489,8 @@ namespace LuaUtil
return it->second; return it->second;
} }
void ScriptsContainer::registerTimerCallback(int scriptId, std::string_view callbackName, sol::function callback) void ScriptsContainer::registerTimerCallback(
int scriptId, std::string_view callbackName, sol::main_protected_function callback)
{ {
getScript(scriptId).mRegisteredCallbacks.emplace(std::string(callbackName), std::move(callback)); getScript(scriptId).mRegisteredCallbacks.emplace(std::string(callbackName), std::move(callback));
} }
@ -500,7 +502,7 @@ namespace LuaUtil
} }
void ScriptsContainer::setupSerializableTimer( void ScriptsContainer::setupSerializableTimer(
TimerType type, double time, int scriptId, std::string_view callbackName, sol::object callbackArg) TimerType type, double time, int scriptId, std::string_view callbackName, sol::main_object callbackArg)
{ {
Timer t; Timer t;
t.mCallback = std::string(callbackName); t.mCallback = std::string(callbackName);
@ -512,7 +514,8 @@ namespace LuaUtil
insertTimer(type == TimerType::GAME_TIME ? mGameTimersQueue : mSimulationTimersQueue, std::move(t)); insertTimer(type == TimerType::GAME_TIME ? mGameTimersQueue : mSimulationTimersQueue, std::move(t));
} }
void ScriptsContainer::setupUnsavableTimer(TimerType type, double time, int scriptId, sol::function callback) void ScriptsContainer::setupUnsavableTimer(
TimerType type, double time, int scriptId, sol::main_protected_function callback)
{ {
Timer t; Timer t;
t.mScriptId = scriptId; t.mScriptId = scriptId;

View file

@ -136,7 +136,7 @@ namespace LuaUtil
// Callbacks for serializable timers should be registered in advance. // Callbacks for serializable timers should be registered in advance.
// The script with the given path should already present in the container. // The script with the given path should already present in the container.
void registerTimerCallback(int scriptId, std::string_view callbackName, sol::function callback); void registerTimerCallback(int scriptId, std::string_view callbackName, sol::main_protected_function callback);
// Sets up a timer, that can be automatically saved and loaded. // Sets up a timer, that can be automatically saved and loaded.
// type - the type of timer, either SIMULATION_TIME or GAME_TIME. // type - the type of timer, either SIMULATION_TIME or GAME_TIME.
@ -145,11 +145,11 @@ namespace LuaUtil
// in the container. callbackName - callback (should be registered in advance) for this timer. callbackArg - // in the container. callbackName - callback (should be registered in advance) for this timer. callbackArg -
// parameter for the callback (should be serializable). // parameter for the callback (should be serializable).
void setupSerializableTimer( void setupSerializableTimer(
TimerType type, double time, int scriptId, std::string_view callbackName, sol::object callbackArg); TimerType type, double time, int scriptId, std::string_view callbackName, sol::main_object callbackArg);
// Creates a timer. `callback` is an arbitrary Lua function. These timers are called "unsavable" // Creates a timer. `callback` is an arbitrary Lua function. These timers are called "unsavable"
// because they can not be stored in saves. I.e. loading a saved game will not fully restore the state. // because they can not be stored in saves. I.e. loading a saved game will not fully restore the state.
void setupUnsavableTimer(TimerType type, double time, int scriptId, sol::function callback); void setupUnsavableTimer(TimerType type, double time, int scriptId, sol::main_protected_function callback);
protected: protected:
struct Handler struct Handler
@ -203,8 +203,8 @@ namespace LuaUtil
std::optional<sol::table> mInterface; std::optional<sol::table> mInterface;
std::string mInterfaceName; std::string mInterfaceName;
sol::table mHiddenData; sol::table mHiddenData;
std::map<std::string, sol::function> mRegisteredCallbacks; std::map<std::string, sol::main_protected_function> mRegisteredCallbacks;
std::map<int64_t, sol::function> mTemporaryCallbacks; std::map<int64_t, sol::main_protected_function> mTemporaryCallbacks;
std::string mPath; std::string mPath;
}; };
struct Timer struct Timer
@ -213,7 +213,7 @@ namespace LuaUtil
bool mSerializable; bool mSerializable;
int mScriptId; int mScriptId;
std::variant<std::string, int64_t> mCallback; // string if serializable, integer otherwise std::variant<std::string, int64_t> mCallback; // string if serializable, integer otherwise
sol::object mArg; sol::main_object mArg;
std::string mSerializedArg; std::string mSerializedArg;
bool operator<(const Timer& t) const { return mTime > t.mTime; } bool operator<(const Timer& t) const { return mTime > t.mTime; }
@ -259,7 +259,7 @@ namespace LuaUtil
// Needed to prevent callback calls if the script was removed. // Needed to prevent callback calls if the script was removed.
struct Callback struct Callback
{ {
sol::function mFunc; sol::main_protected_function mFunc;
sol::table mHiddenData; // same object as Script::mHiddenData in ScriptsContainer sol::table mHiddenData; // same object as Script::mHiddenData in ScriptsContainer
bool isValid() const { return mHiddenData[ScriptsContainer::sScriptIdKey] != sol::nil; } bool isValid() const { return mHiddenData[ScriptsContainer::sScriptIdKey] != sol::nil; }