diff --git a/apps/openmw/mwlua/nearbybindings.cpp b/apps/openmw/mwlua/nearbybindings.cpp index f48c57b6db..5c2572c0e5 100644 --- a/apps/openmw/mwlua/nearbybindings.cpp +++ b/apps/openmw/mwlua/nearbybindings.cpp @@ -115,9 +115,9 @@ namespace MWLua MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); return res; }; - api["asyncCastRenderingRay"] = [context](const LuaUtil::Callback& callback, const osg::Vec3f& from, - const osg::Vec3f& to) { - context.mLuaManager->addAction([context, callback, from, to] { + api["asyncCastRenderingRay"] = [context]( + const sol::table& callback, const osg::Vec3f& from, const osg::Vec3f& to) { + context.mLuaManager->addAction([context, callback = LuaUtil::Callback::fromLua(callback), from, to] { MWPhysics::RayCastingResult res; MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false); context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res)); diff --git a/apps/openmw_test_suite/lua/test_storage.cpp b/apps/openmw_test_suite/lua/test_storage.cpp index 270012c9b6..6bba813529 100644 --- a/apps/openmw_test_suite/lua/test_storage.cpp +++ b/apps/openmw_test_suite/lua/test_storage.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include namespace @@ -24,15 +24,16 @@ namespace LuaUtil::LuaStorage storage(mLua); std::vector callbackCalls; - LuaUtil::Callback callback{ sol::make_object(mLua, - [&](const std::string& section, const sol::optional& key) { - if (key) - callbackCalls.push_back(section + "_" + *key); - else - callbackCalls.push_back(section + "_*"); - }), - sol::table(mLua, sol::create) }; - callback.mHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{}; + sol::table callbackHiddenData(mLua, sol::create); + callbackHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{}; + sol::table callback(mLua, sol::create); + callback[1] = [&](const std::string& section, const sol::optional& key) { + if (key) + callbackCalls.push_back(section + "_" + *key); + else + callbackCalls.push_back(section + "_*"); + }; + callback[2] = LuaUtil::AsyncPackageId{ nullptr, 0, callbackHiddenData }; mLua["mutable"] = storage.getMutableSection("test"); mLua["ro"] = storage.getReadOnlySection("test"); diff --git a/components/lua/asyncpackage.cpp b/components/lua/asyncpackage.cpp index bc11cee908..b60238de13 100644 --- a/components/lua/asyncpackage.cpp +++ b/components/lua/asyncpackage.cpp @@ -22,6 +22,21 @@ namespace LuaUtil std::string mName; }; + Callback Callback::fromLua(const sol::table& t) + { + return Callback{ t.raw_get(1), t.raw_get(2).mHiddenData }; + } + + bool Callback::isLuaCallback(const sol::object& t) + { + if (!t.is()) + return false; + sol::object meta = sol::table(t)[sol::metatable_key]; + if (!meta.is()) + return false; + return sol::table(meta).raw_get_or("isCallback", false); + } + sol::function getAsyncPackageInitializer( lua_State* L, std::function simulationTimeFn, std::function gameTimeFn) { @@ -53,13 +68,20 @@ namespace LuaUtil asyncId.mContainer->setupUnsavableTimer( TimerType::GAME_TIME, gameTimeFn() + delay, asyncId.mScriptId, std::move(callback)); }; - api["callback"] = [](const AsyncPackageId& asyncId, sol::main_protected_function fn) -> Callback { - return Callback{ std::move(fn), asyncId.mHiddenData }; - }; - sol::usertype callbackType = lua.new_usertype("Callback"); - callbackType[sol::meta_function::call] - = [](const Callback& callback, sol::variadic_args va) { return callback.call(sol::as_args(va)); }; + sol::table callbackMeta = sol::table::create(L); + callbackMeta[sol::meta_function::call] = [](const sol::table& callback, sol::variadic_args va) { + return Callback::fromLua(callback).call(sol::as_args(va)); + }; + callbackMeta[sol::meta_function::to_string] = [] { return "Callback"; }; + callbackMeta[sol::meta_function::metatable] = false; + callbackMeta["isCallback"] = true; + api["callback"] = [callbackMeta](const AsyncPackageId& asyncId, sol::main_protected_function fn) -> sol::table { + sol::table c = sol::table::create(fn.lua_state(), 2); + c.raw_set(1, std::move(fn), 2, asyncId); + c[sol::metatable_key] = callbackMeta; + return c; + }; auto initializer = [](sol::table hiddenData) { ScriptId id = hiddenData[ScriptsContainer::sScriptIdKey]; diff --git a/components/lua/asyncpackage.hpp b/components/lua/asyncpackage.hpp index cbf9ad9ac6..e8d5f04271 100644 --- a/components/lua/asyncpackage.hpp +++ b/components/lua/asyncpackage.hpp @@ -22,6 +22,9 @@ namespace LuaUtil sol::main_protected_function mFunc; sol::table mHiddenData; // same object as Script::mHiddenData in ScriptsContainer + static bool isLuaCallback(const sol::object&); + static Callback fromLua(const sol::table&); + bool isValid() const { return mHiddenData[ScriptsContainer::sScriptIdKey] != sol::nil; } template diff --git a/components/lua/storage.cpp b/components/lua/storage.cpp index 309a687941..3932a43280 100644 --- a/components/lua/storage.cpp +++ b/components/lua/storage.cpp @@ -111,7 +111,7 @@ namespace LuaUtil return section.mSection->get(key).getCopy(s); }; sview["asTable"] = [](const SectionView& section) { return section.mSection->asTable(); }; - sview["subscribe"] = [](const SectionView& section, const Callback& callback) { + sview["subscribe"] = [](const SectionView& section, const sol::table& callback) { std::vector& callbacks = section.mSection->mCallbacks; if (!callbacks.empty() && callbacks.size() == callbacks.capacity()) { @@ -119,7 +119,7 @@ namespace LuaUtil std::remove_if(callbacks.begin(), callbacks.end(), [&](const Callback& c) { return !c.isValid(); }), callbacks.end()); } - callbacks.push_back(callback); + callbacks.push_back(Callback::fromLua(callback)); }; sview["reset"] = [](const SectionView& section, const sol::optional& newValues) { if (section.mReadOnly) diff --git a/components/lua_ui/element.cpp b/components/lua_ui/element.cpp index 91d1acc433..3e9b1e6437 100644 --- a/components/lua_ui/element.cpp +++ b/components/lua_ui/element.cpp @@ -106,11 +106,11 @@ namespace LuaUi throw std::logic_error("The \"events\" layout field must be a table of callbacks"); auto events = eventsObj.as(); events.for_each([ext](const sol::object& name, const sol::object& callback) { - if (name.is() && callback.is()) - ext->setCallback(name.as(), callback.as()); + if (name.is() && LuaUtil::Callback::isLuaCallback(callback)) + ext->setCallback(name.as(), LuaUtil::Callback::fromLua(callback)); else if (!name.is()) Log(Debug::Warning) << "UI event key must be a string"; - else if (!callback.is()) + else Log(Debug::Warning) << "UI event handler for key \"" << name.as() << "\" must be an openmw.async.callback"; });