mirror of https://github.com/OpenMW/openmw.git
Merge branch 'fix_lua_48' into 'openmw-48'
Merge !2661, !2687, !2733, !2770, !2774 to openmw-48 (fixes #7128) See merge request OpenMW/openmw!2778backport_gl_clamp_removal
commit
2f6a809d18
@ -1,69 +0,0 @@
|
||||
#include "luabindings.hpp"
|
||||
|
||||
#include "luamanagerimp.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<MWLua::AsyncPackageId> : std::false_type {};
|
||||
|
||||
template <>
|
||||
struct is_automagical<LuaUtil::Callback> : std::false_type {};
|
||||
}
|
||||
|
||||
namespace MWLua
|
||||
{
|
||||
|
||||
struct TimerCallback
|
||||
{
|
||||
AsyncPackageId mAsyncId;
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
sol::function getAsyncPackageInitializer(const Context& context)
|
||||
{
|
||||
using TimerType = LuaUtil::ScriptsContainer::TimerType;
|
||||
sol::usertype<AsyncPackageId> api = context.mLua->sol().new_usertype<AsyncPackageId>("AsyncPackage");
|
||||
api["registerTimerCallback"]
|
||||
= [](const AsyncPackageId& asyncId, std::string_view name, sol::main_protected_function callback) {
|
||||
asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback));
|
||||
return TimerCallback{ asyncId, std::string(name) };
|
||||
};
|
||||
api["newSimulationTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::main_object callbackArg) {
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::SIMULATION_TIME,
|
||||
world->getSimulationTime() + delay, callback.mAsyncId.mScriptId, callback.mName,
|
||||
std::move(callbackArg));
|
||||
};
|
||||
api["newGameTimer"] = [world = context.mWorldView](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::main_object callbackArg) {
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::GAME_TIME, world->getGameTime() + delay,
|
||||
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
|
||||
};
|
||||
api["newUnsavableSimulationTimer"] = [world = context.mWorldView](const AsyncPackageId& asyncId, double delay,
|
||||
sol::main_protected_function callback) {
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimerType::SIMULATION_TIME, world->getSimulationTime() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
api["newUnsavableGameTimer"] = [world = context.mWorldView](const AsyncPackageId& asyncId, double delay,
|
||||
sol::main_protected_function callback) {
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimerType::GAME_TIME, world->getGameTime() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
api["callback"] = [](const AsyncPackageId& asyncId, sol::main_protected_function fn) -> LuaUtil::Callback {
|
||||
return LuaUtil::Callback{ std::move(fn), asyncId.mHiddenData };
|
||||
};
|
||||
|
||||
sol::usertype<LuaUtil::Callback> callbackType = context.mLua->sol().new_usertype<LuaUtil::Callback>("Callback");
|
||||
callbackType[sol::meta_function::call] =
|
||||
[](const LuaUtil::Callback& callback, sol::variadic_args va) { return callback.call(sol::as_args(va)); };
|
||||
|
||||
auto initializer = [](sol::table hiddenData)
|
||||
{
|
||||
LuaUtil::ScriptsContainer::ScriptId id = hiddenData[LuaUtil::ScriptsContainer::sScriptIdKey];
|
||||
return AsyncPackageId{id.mContainer, id.mIndex, hiddenData};
|
||||
};
|
||||
return sol::make_object(context.mLua->sol(), initializer);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
#include "asyncpackage.hpp"
|
||||
|
||||
namespace sol
|
||||
{
|
||||
template <>
|
||||
struct is_automagical<LuaUtil::AsyncPackageId> : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <>
|
||||
struct is_automagical<LuaUtil::Callback> : std::false_type
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
|
||||
struct TimerCallback
|
||||
{
|
||||
AsyncPackageId mAsyncId;
|
||||
std::string mName;
|
||||
};
|
||||
|
||||
Callback Callback::fromLua(const sol::table& t)
|
||||
{
|
||||
return Callback{ t.raw_get<sol::main_protected_function>(1), t.raw_get<AsyncPackageId>(2).mHiddenData };
|
||||
}
|
||||
|
||||
bool Callback::isLuaCallback(const sol::object& t)
|
||||
{
|
||||
if (!t.is<sol::table>())
|
||||
return false;
|
||||
sol::object meta = sol::table(t)[sol::metatable_key];
|
||||
if (!meta.is<sol::table>())
|
||||
return false;
|
||||
return sol::table(meta).raw_get_or<bool, std::string_view, bool>("isCallback", false);
|
||||
}
|
||||
|
||||
sol::function getAsyncPackageInitializer(
|
||||
lua_State* L, std::function<double()> simulationTimeFn, std::function<double()> gameTimeFn)
|
||||
{
|
||||
sol::state_view lua(L);
|
||||
using TimerType = ScriptsContainer::TimerType;
|
||||
sol::usertype<AsyncPackageId> api = lua.new_usertype<AsyncPackageId>("AsyncPackage");
|
||||
api["registerTimerCallback"]
|
||||
= [](const AsyncPackageId& asyncId, std::string_view name, sol::main_protected_function callback) {
|
||||
asyncId.mContainer->registerTimerCallback(asyncId.mScriptId, name, std::move(callback));
|
||||
return TimerCallback{ asyncId, std::string(name) };
|
||||
};
|
||||
api["newSimulationTimer"] = [simulationTimeFn](const AsyncPackageId&, double delay,
|
||||
const TimerCallback& callback, sol::main_object callbackArg) {
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::SIMULATION_TIME, simulationTimeFn() + delay,
|
||||
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
|
||||
};
|
||||
api["newGameTimer"] = [gameTimeFn](const AsyncPackageId&, double delay, const TimerCallback& callback,
|
||||
sol::main_object callbackArg) {
|
||||
callback.mAsyncId.mContainer->setupSerializableTimer(TimerType::GAME_TIME, gameTimeFn() + delay,
|
||||
callback.mAsyncId.mScriptId, callback.mName, std::move(callbackArg));
|
||||
};
|
||||
api["newUnsavableSimulationTimer"]
|
||||
= [simulationTimeFn](const AsyncPackageId& asyncId, double delay, sol::main_protected_function callback) {
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimerType::SIMULATION_TIME, simulationTimeFn() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
api["newUnsavableGameTimer"]
|
||||
= [gameTimeFn](const AsyncPackageId& asyncId, double delay, sol::main_protected_function callback) {
|
||||
asyncId.mContainer->setupUnsavableTimer(
|
||||
TimerType::GAME_TIME, gameTimeFn() + delay, asyncId.mScriptId, std::move(callback));
|
||||
};
|
||||
|
||||
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) {
|
||||
ScriptsContainer::ScriptId id = hiddenData[ScriptsContainer::sScriptIdKey];
|
||||
return AsyncPackageId{ id.mContainer, id.mIndex, hiddenData };
|
||||
};
|
||||
return sol::make_object(lua, initializer);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
#ifndef COMPONENTS_LUA_ASYNCPACKAGE_H
|
||||
#define COMPONENTS_LUA_ASYNCPACKAGE_H
|
||||
|
||||
#include "scriptscontainer.hpp"
|
||||
|
||||
namespace LuaUtil
|
||||
{
|
||||
struct AsyncPackageId
|
||||
{
|
||||
ScriptsContainer* mContainer;
|
||||
int mScriptId;
|
||||
sol::table mHiddenData;
|
||||
};
|
||||
sol::function getAsyncPackageInitializer(
|
||||
lua_State* L, std::function<double()> simulationTimeFn, std::function<double()> gameTimeFn);
|
||||
|
||||
// Wrapper for a Lua function.
|
||||
// Holds information about the script the function belongs to.
|
||||
// Needed to prevent callback calls if the script was removed.
|
||||
struct Callback
|
||||
{
|
||||
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 <typename... Args>
|
||||
sol::object call(Args&&... args) const
|
||||
{
|
||||
if (isValid())
|
||||
return LuaUtil::call(mFunc, std::forward<Args>(args)...);
|
||||
else
|
||||
Log(Debug::Debug) << "Ignored callback to the removed script "
|
||||
<< mHiddenData.get<std::string>(ScriptsContainer::sScriptDebugNameKey);
|
||||
return sol::nil;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void tryCall(Args&&... args) const
|
||||
{
|
||||
try
|
||||
{
|
||||
this->call(std::forward<Args>(args)...);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Log(Debug::Error) << "Error in callback: " << e.what();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // COMPONENTS_LUA_ASYNCPACKAGE_H
|
@ -0,0 +1,140 @@
|
||||
local M = {}
|
||||
M.__Content = true
|
||||
M.new = function(source)
|
||||
local result = {}
|
||||
result.__nameIndex = {}
|
||||
for i, v in ipairs(source) do
|
||||
if type(v) ~= 'table' then
|
||||
error('Content can only contain tables')
|
||||
end
|
||||
result[i] = v
|
||||
if type(v.name) == 'string' then
|
||||
result.__nameIndex[v.name] = i
|
||||
end
|
||||
end
|
||||
return setmetatable(result, M)
|
||||
end
|
||||
local function validateIndex(self, index)
|
||||
if type(index) ~= 'number' then
|
||||
error('Unexpected Content key: ' .. tostring(index))
|
||||
end
|
||||
if index < 1 or (#self + 1) < index then
|
||||
error('Invalid Content index: ' .. tostring(index))
|
||||
end
|
||||
end
|
||||
|
||||
local function getIndexFromKey(self, key)
|
||||
local index = key
|
||||
if type(key) == 'string' then
|
||||
index = self.__nameIndex[key]
|
||||
if not index then
|
||||
error('Unexpected content key:' .. key)
|
||||
end
|
||||
end
|
||||
validateIndex(self, index)
|
||||
return index
|
||||
end
|
||||
|
||||
local methods = {
|
||||
insert = function(self, index, value)
|
||||
validateIndex(self, index)
|
||||
if type(value) ~= 'table' then
|
||||
error('Content can only contain tables')
|
||||
end
|
||||
for i = #self, index, -1 do
|
||||
rawset(self, i + 1, rawget(self, i))
|
||||
local name = rawget(self, i + 1)
|
||||
if name then
|
||||
self.__nameIndex[name] = i + 1
|
||||
end
|
||||
end
|
||||
rawset(self, index, value)
|
||||
if value.name then
|
||||
self.__nameIndex[value.name] = index
|
||||
end
|
||||
end,
|
||||
indexOf = function(self, value)
|
||||
if type(value) == 'string' then
|
||||
return self.__nameIndex[value]
|
||||
elseif type(value) == 'table' then
|
||||
for i = 1, #self do
|
||||
if rawget(self, i) == value then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
add = function(self, value)
|
||||
self:insert(#self + 1, value)
|
||||
return #self
|
||||
end,
|
||||
}
|
||||
M.__index = function(self, key)
|
||||
if methods[key] then return methods[key] end
|
||||
local index = getIndexFromKey(self, key)
|
||||
return rawget(self, index)
|
||||
end
|
||||
local function nameAt(self, index)
|
||||
local v = rawget(self, index)
|
||||
return v and type(v.name) == 'string' and v.name
|
||||
end
|
||||
|
||||
local function remove(self, index)
|
||||
local oldName = nameAt(self, index)
|
||||
if oldName then
|
||||
self.__nameIndex[oldName] = nil
|
||||
end
|
||||
if index > #self then
|
||||
error('Invalid Content index:' .. tostring(index))
|
||||
end
|
||||
for i = index, #self - 1 do
|
||||
local v = rawget(self, i + 1)
|
||||
rawset(self, i, v)
|
||||
if type(v.name) == 'string' then
|
||||
self.__nameIndex[v.name] = i
|
||||
end
|
||||
end
|
||||
rawset(self, #self, nil)
|
||||
end
|
||||
|
||||
local function assign(self, index, value)
|
||||
local oldName = nameAt(self, index)
|
||||
if oldName then
|
||||
self.__nameIndex[oldName] = nil
|
||||
end
|
||||
rawset(self, index, value)
|
||||
if value.name then
|
||||
self.__nameIndex[value.name] = index
|
||||
end
|
||||
end
|
||||
|
||||
M.__newindex = function(self, key, value)
|
||||
local index = getIndexFromKey(self, key)
|
||||
if value == nil then
|
||||
remove(self, index)
|
||||
elseif type(value) == 'table' then
|
||||
assign(self, index, value)
|
||||
else
|
||||
error('Content can only contain tables')
|
||||
end
|
||||
end
|
||||
M.__tostring = function(self)
|
||||
return ('UiContent{%d layouts}'):format(#self)
|
||||
end
|
||||
local function next(self, index)
|
||||
local v = rawget(self, index)
|
||||
if v then
|
||||
return index + 1, v
|
||||
else
|
||||
return nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
M.__pairs = function(self)
|
||||
return next, self, 1
|
||||
end
|
||||
M.__ipairs = M.__pairs
|
||||
M.__metatable = false
|
||||
|
||||
return M
|
Loading…
Reference in New Issue