Merge branch 'pcallallthethings' into 'master'

Wrap all memory allocating Lua functions in protected calls

Closes #8099

See merge request OpenMW/openmw!4336
pull/3236/head
psi29a 4 months ago
commit b3677d07fd

@ -13,12 +13,14 @@ namespace
{
void SetUp() override
{
mLua.sol()["callback"] = [&](sol::protected_function fn) -> LuaUtil::Callback {
sol::table hiddenData(mLua.sol(), sol::create);
hiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{};
return LuaUtil::Callback{ std::move(fn), hiddenData };
};
mLua.sol()["pass"] = [this](LuaUtil::Callback callback) { mCb = callback; };
mLua.protectedCall([&](LuaUtil::LuaView& view) {
view.sol()["callback"] = [](sol::this_state state, sol::protected_function fn) -> LuaUtil::Callback {
sol::table hiddenData(state, sol::create);
hiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{};
return LuaUtil::Callback{ std::move(fn), hiddenData };
};
view.sol()["pass"] = [&](LuaUtil::Callback callback) { mCb = callback; };
});
}
LuaUtil::LuaState mLua{ nullptr, nullptr };
@ -28,25 +30,29 @@ namespace
TEST_F(LuaCoroutineCallbackTest, CoroutineCallbacks)
{
internal::CaptureStdout();
mLua.sol().safe_script(R"X(
local s = 'test'
coroutine.wrap(function()
pass(callback(function(v) print(s) end))
end)()
)X");
mLua.sol().collect_garbage();
mCb.call();
mLua.protectedCall([&](LuaUtil::LuaView& view) {
view.sol().safe_script(R"X(
local s = 'test'
coroutine.wrap(function()
pass(callback(function(v) print(s) end))
end)()
)X");
view.sol().collect_garbage();
mCb.call();
});
EXPECT_THAT(internal::GetCapturedStdout(), "test\n");
}
TEST_F(LuaCoroutineCallbackTest, ErrorInCoroutineCallbacks)
{
mLua.sol().safe_script(R"X(
coroutine.wrap(function()
pass(callback(function() error('COROUTINE CALLBACK') end))
end)()
)X");
mLua.sol().collect_garbage();
mLua.protectedCall([&](LuaUtil::LuaView& view) {
view.sol().safe_script(R"X(
coroutine.wrap(function()
pass(callback(function() error('COROUTINE CALLBACK') end))
end)()
)X");
view.sol().collect_garbage();
});
EXPECT_ERROR(mCb.call(), "COROUTINE CALLBACK");
}
}

@ -81,84 +81,87 @@ you_have_arrows: "Arrows count: {count}"
TEST_F(LuaL10nTest, L10n)
{
LuaUtil::LuaState lua{ mVFS.get(), &mCfg };
sol::state_view& l = lua.sol();
internal::CaptureStdout();
l10n::Manager l10nManager(mVFS.get());
l10nManager.setPreferredLocales({ "de", "en" });
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
l["l10n"] = LuaUtil::initL10nLoader(l, &l10nManager);
internal::CaptureStdout();
l.safe_script("t1 = l10n('Test1')");
EXPECT_THAT(internal::GetCapturedStdout(),
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n");
internal::CaptureStdout();
l.safe_script("t2 = l10n('Test2')");
{
std::string output = internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Language file \"l10n/Test2/en.yaml\" is enabled"));
}
EXPECT_EQ(get<std::string>(l, "t1('good_morning')"), "Guten Morgen.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "Du hast ein Pfeil.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "Du hast 5 Pfeile.");
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
internal::CaptureStdout();
l10nManager.setPreferredLocales({ "en", "de" });
EXPECT_THAT(internal::GetCapturedStdout(),
"Preferred locales: gmst en de\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n"
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n");
EXPECT_EQ(get<std::string>(l, "t1('good_morning')"), "Good morning.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "You have one arrow.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "You have 5 arrows.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"male\"})"), "He is coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"female\"})"), "She is coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"blah\"})"), "They are coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"other\"})"), "They are coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('quest_completion', {done=0.1})"), "The quest is 10% complete.");
EXPECT_EQ(get<std::string>(l, "t1('quest_completion', {done=1})"), "The quest is 100% complete.");
EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=1})"), "You came in 1st place.");
EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=100})"), "You came in 100th place.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=1})"), "There is one thing.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=100})"), "There are one hundred things.");
EXPECT_EQ(get<std::string>(l, "t1('duration', {num=100})"), "It took 1:40");
EXPECT_EQ(get<std::string>(l, "t1('numbers', {int=123, double=123.456})"),
"123 and 123 are integers, but 123.456 is a double");
EXPECT_EQ(get<std::string>(l, "t1('rounding', {value=123.456789})"), "123.46");
// Check that failed messages display the key instead of an empty string
EXPECT_EQ(get<std::string>(l, "t1('{mismatched_braces')"), "{mismatched_braces");
EXPECT_EQ(get<std::string>(l, "t1('{unknown_arg}')"), "{unknown_arg}");
EXPECT_EQ(get<std::string>(l, "t1('{num, integer}', {num=1})"), "{num, integer}");
// Doesn't give a valid currency symbol with `en`. Not that openmw is designed for real world currency.
l10nManager.setPreferredLocales({ "en-US", "de" });
EXPECT_EQ(get<std::string>(l, "t1('currency', {money=10000.10})"), "You have $10,000.10");
// Note: Not defined in English localisation file, so we fall back to the German before falling back to the key
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
// Test that locales with variants and country codes fall back to more generic locales
internal::CaptureStdout();
l10nManager.setPreferredLocales({ "en-GB-oed", "de" });
EXPECT_THAT(internal::GetCapturedStdout(),
"Preferred locales: gmst en_GB_OED de\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n"
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
// Test setting fallback language
l.safe_script("t3 = l10n('Test3', 'de')");
l10nManager.setPreferredLocales({ "en" });
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
lua.protectedCall([&](LuaUtil::LuaView& view) {
sol::state_view& l = view.sol();
internal::CaptureStdout();
l10n::Manager l10nManager(mVFS.get());
l10nManager.setPreferredLocales({ "de", "en" });
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
l["l10n"] = LuaUtil::initL10nLoader(l, &l10nManager);
internal::CaptureStdout();
l.safe_script("t1 = l10n('Test1')");
EXPECT_THAT(internal::GetCapturedStdout(),
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n");
internal::CaptureStdout();
l.safe_script("t2 = l10n('Test2')");
{
std::string output = internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Language file \"l10n/Test2/en.yaml\" is enabled"));
}
EXPECT_EQ(get<std::string>(l, "t1('good_morning')"), "Guten Morgen.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "Du hast ein Pfeil.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "Du hast 5 Pfeile.");
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
internal::CaptureStdout();
l10nManager.setPreferredLocales({ "en", "de" });
EXPECT_THAT(internal::GetCapturedStdout(),
"Preferred locales: gmst en de\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n"
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n");
EXPECT_EQ(get<std::string>(l, "t1('good_morning')"), "Good morning.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "You have one arrow.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "You have 5 arrows.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"male\"})"), "He is coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"female\"})"), "She is coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"blah\"})"), "They are coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"other\"})"), "They are coming with us.");
EXPECT_EQ(get<std::string>(l, "t1('quest_completion', {done=0.1})"), "The quest is 10% complete.");
EXPECT_EQ(get<std::string>(l, "t1('quest_completion', {done=1})"), "The quest is 100% complete.");
EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=1})"), "You came in 1st place.");
EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=100})"), "You came in 100th place.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=1})"), "There is one thing.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=100})"), "There are one hundred things.");
EXPECT_EQ(get<std::string>(l, "t1('duration', {num=100})"), "It took 1:40");
EXPECT_EQ(get<std::string>(l, "t1('numbers', {int=123, double=123.456})"),
"123 and 123 are integers, but 123.456 is a double");
EXPECT_EQ(get<std::string>(l, "t1('rounding', {value=123.456789})"), "123.46");
// Check that failed messages display the key instead of an empty string
EXPECT_EQ(get<std::string>(l, "t1('{mismatched_braces')"), "{mismatched_braces");
EXPECT_EQ(get<std::string>(l, "t1('{unknown_arg}')"), "{unknown_arg}");
EXPECT_EQ(get<std::string>(l, "t1('{num, integer}', {num=1})"), "{num, integer}");
// Doesn't give a valid currency symbol with `en`. Not that openmw is designed for real world currency.
l10nManager.setPreferredLocales({ "en-US", "de" });
EXPECT_EQ(get<std::string>(l, "t1('currency', {money=10000.10})"), "You have $10,000.10");
// Note: Not defined in English localisation file, so we fall back to the German before falling back to the
// key
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
// Test that locales with variants and country codes fall back to more generic locales
internal::CaptureStdout();
l10nManager.setPreferredLocales({ "en-GB-oed", "de" });
EXPECT_THAT(internal::GetCapturedStdout(),
"Preferred locales: gmst en_GB_OED de\n"
"Language file \"l10n/Test1/en.yaml\" is enabled\n"
"Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
// Test setting fallback language
l.safe_script("t3 = l10n('Test3', 'de')");
l10nManager.setPreferredLocales({ "en" });
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!");
});
}
}

@ -97,26 +97,26 @@ return {
TEST_F(LuaStateTest, ToString)
{
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), 3.14)), "3.14");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), true)), "true");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.unsafeState(), 3.14)), "3.14");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.unsafeState(), true)), "true");
EXPECT_EQ(LuaUtil::toString(sol::nil), "nil");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), "something")), "\"something\"");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.unsafeState(), "something")), "\"something\"");
}
TEST_F(LuaStateTest, Cast)
{
EXPECT_EQ(LuaUtil::cast<int>(sol::make_object(mLua.sol(), 3.14)), 3);
EXPECT_ERROR(
LuaUtil::cast<int>(sol::make_object(mLua.sol(), "3.14")), "Value \"\"3.14\"\" can not be casted to int");
EXPECT_ERROR(LuaUtil::cast<std::string_view>(sol::make_object(mLua.sol(), sol::nil)),
EXPECT_EQ(LuaUtil::cast<int>(sol::make_object(mLua.unsafeState(), 3.14)), 3);
EXPECT_ERROR(LuaUtil::cast<int>(sol::make_object(mLua.unsafeState(), "3.14")),
"Value \"\"3.14\"\" can not be casted to int");
EXPECT_ERROR(LuaUtil::cast<std::string_view>(sol::make_object(mLua.unsafeState(), sol::nil)),
"Value \"nil\" can not be casted to string");
EXPECT_ERROR(LuaUtil::cast<std::string>(sol::make_object(mLua.sol(), sol::nil)),
EXPECT_ERROR(LuaUtil::cast<std::string>(sol::make_object(mLua.unsafeState(), sol::nil)),
"Value \"nil\" can not be casted to string");
EXPECT_ERROR(LuaUtil::cast<sol::table>(sol::make_object(mLua.sol(), sol::nil)),
EXPECT_ERROR(LuaUtil::cast<sol::table>(sol::make_object(mLua.unsafeState(), sol::nil)),
"Value \"nil\" can not be casted to sol::table");
EXPECT_ERROR(LuaUtil::cast<sol::function>(sol::make_object(mLua.sol(), "3.14")),
EXPECT_ERROR(LuaUtil::cast<sol::function>(sol::make_object(mLua.unsafeState(), "3.14")),
"Value \"\"3.14\"\" can not be casted to sol::function");
EXPECT_ERROR(LuaUtil::cast<sol::protected_function>(sol::make_object(mLua.sol(), "3.14")),
EXPECT_ERROR(LuaUtil::cast<sol::protected_function>(sol::make_object(mLua.unsafeState(), "3.14")),
"Value \"\"3.14\"\" can not be casted to sol::function");
}
@ -186,21 +186,22 @@ return {
TEST_F(LuaStateTest, ProvideAPI)
{
LuaUtil::LuaState lua(mVFS.get(), &mCfg);
lua.protectedCall([&](LuaUtil::LuaView& view) {
sol::table api1 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api1"));
sol::table api2 = LuaUtil::makeReadOnly(view.sol().create_table_with("name", "api2"));
sol::table api1 = LuaUtil::makeReadOnly(lua.sol().create_table_with("name", "api1"));
sol::table api2 = LuaUtil::makeReadOnly(lua.sol().create_table_with("name", "api2"));
sol::table script1 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api1 } });
sol::table script1 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api1 } });
lua.addCommonPackage("sqrlib", lua.sol().create_table_with("sqr", [](int x) { return x * x; }));
lua.addCommonPackage("sqrlib", view.sol().create_table_with("sqr", [](int x) { return x * x; }));
sol::table script2 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api2 } });
sol::table script2 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api2 } });
EXPECT_ERROR(LuaUtil::call(script1["sqr"], 3), "module not found: sqrlib");
EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9);
EXPECT_ERROR(LuaUtil::call(script1["sqr"], 3), "module not found: sqrlib");
EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9);
EXPECT_EQ(LuaUtil::call(script1["apiName"]).get<std::string>(), "api1");
EXPECT_EQ(LuaUtil::call(script2["apiName"]).get<std::string>(), "api2");
EXPECT_EQ(LuaUtil::call(script1["apiName"]).get<std::string>(), "api1");
EXPECT_EQ(LuaUtil::call(script2["apiName"]).get<std::string>(), "api2");
});
}
TEST_F(LuaStateTest, GetLuaVersion)
@ -212,7 +213,7 @@ return {
{
auto getMem = [&] {
for (int i = 0; i < 5; ++i)
lua_gc(mLua.sol(), LUA_GCCOLLECT, 0);
lua_gc(mLua.unsafeState(), LUA_GCCOLLECT, 0);
return mLua.getTotalMemoryUsage();
};
int64_t memWithScript;

@ -198,8 +198,9 @@ CUSTOM, PLAYER: useInterface.lua
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
std::string X0 = LuaUtil::serialize(mLua.sol().create_table_with("x", 0.5));
std::string X1 = LuaUtil::serialize(mLua.sol().create_table_with("x", 1.5));
sol::state_view sol = mLua.unsafeState();
std::string X0 = LuaUtil::serialize(sol.create_table_with("x", 0.5));
std::string X1 = LuaUtil::serialize(sol.create_table_with("x", 1.5));
{
testing::internal::CaptureStdout();
@ -243,7 +244,8 @@ CUSTOM, PLAYER: useInterface.lua
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test1.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
std::string X = LuaUtil::serialize(mLua.sol().create_table_with("x", 0.5));
sol::state_view sol = mLua.unsafeState();
std::string X = LuaUtil::serialize(sol.create_table_with("x", 0.5));
{
testing::internal::CaptureStdout();
@ -334,8 +336,9 @@ CUSTOM, PLAYER: useInterface.lua
scripts1.addAutoStartedScripts();
EXPECT_TRUE(scripts1.addCustomScript(*mCfg.findId("test1.lua")));
scripts1.receiveEvent("Set", LuaUtil::serialize(mLua.sol().create_table_with("n", 1, "x", 0.5, "y", 3.5)));
scripts1.receiveEvent("Set", LuaUtil::serialize(mLua.sol().create_table_with("n", 2, "x", 2.5, "y", 1.5)));
sol::state_view sol = mLua.unsafeState();
scripts1.receiveEvent("Set", LuaUtil::serialize(sol.create_table_with("n", 1, "x", 0.5, "y", 3.5)));
scripts1.receiveEvent("Set", LuaUtil::serialize(sol.create_table_with("n", 2, "x", 2.5, "y", 1.5)));
ESM::LuaScripts data;
scripts1.save(data);
@ -379,10 +382,10 @@ CUSTOM, PLAYER: useInterface.lua
EXPECT_EQ(internal::GetCapturedStdout(), "");
int counter1 = 0, counter2 = 0, counter3 = 0, counter4 = 0;
sol::function fn1 = sol::make_object(mLua.sol(), [&]() { counter1++; });
sol::function fn2 = sol::make_object(mLua.sol(), [&]() { counter2++; });
sol::function fn3 = sol::make_object(mLua.sol(), [&](int d) { counter3 += d; });
sol::function fn4 = sol::make_object(mLua.sol(), [&](int d) { counter4 += d; });
sol::function fn1 = sol::make_object(mLua.unsafeState(), [&]() { counter1++; });
sol::function fn2 = sol::make_object(mLua.unsafeState(), [&]() { counter2++; });
sol::function fn3 = sol::make_object(mLua.unsafeState(), [&](int d) { counter3 += d; });
sol::function fn4 = sol::make_object(mLua.unsafeState(), [&](int d) { counter4 += d; });
scripts.registerTimerCallback(test1Id, "A", fn3);
scripts.registerTimerCallback(test1Id, "B", fn4);
@ -391,12 +394,16 @@ CUSTOM, PLAYER: useInterface.lua
scripts.processTimers(1, 2);
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 10, test1Id, "B", sol::make_object(mLua.sol(), 3));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 10, test2Id, "B", sol::make_object(mLua.sol(), 4));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 5, test1Id, "A", sol::make_object(mLua.sol(), 1));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 5, test2Id, "A", sol::make_object(mLua.sol(), 2));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "A", sol::make_object(mLua.sol(), 10));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "B", sol::make_object(mLua.sol(), 20));
scripts.setupSerializableTimer(
TimerType::SIMULATION_TIME, 10, test1Id, "B", sol::make_object(mLua.unsafeState(), 3));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 10, test2Id, "B", sol::make_object(mLua.unsafeState(), 4));
scripts.setupSerializableTimer(
TimerType::SIMULATION_TIME, 5, test1Id, "A", sol::make_object(mLua.unsafeState(), 1));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 5, test2Id, "A", sol::make_object(mLua.unsafeState(), 2));
scripts.setupSerializableTimer(
TimerType::SIMULATION_TIME, 15, test1Id, "A", sol::make_object(mLua.unsafeState(), 10));
scripts.setupSerializableTimer(
TimerType::SIMULATION_TIME, 15, test1Id, "B", sol::make_object(mLua.unsafeState(), 20));
scripts.setupUnsavableTimer(TimerType::SIMULATION_TIME, 10, test2Id, fn2);
scripts.setupUnsavableTimer(TimerType::GAME_TIME, 10, test1Id, fn2);
@ -446,7 +453,8 @@ CUSTOM, PLAYER: useInterface.lua
TEST_F(LuaScriptsContainerTest, CallbackWrapper)
{
LuaUtil::Callback callback{ mLua.sol()["print"], mLua.newTable() };
sol::state_view view = mLua.unsafeState();
LuaUtil::Callback callback{ view["print"], sol::table(view, sol::create) };
callback.mHiddenData[LuaUtil::ScriptsContainer::sScriptDebugNameKey] = "some_script.lua";
callback.mHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{ nullptr, 0 };

@ -19,102 +19,110 @@ namespace
{
// Note: LuaUtil::Callback can be used only if Lua is initialized via LuaUtil::LuaState
LuaUtil::LuaState luaState{ nullptr, nullptr };
sol::state_view& mLua = luaState.sol();
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
sol::table callbackHiddenData(mLua, sol::create);
callbackHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{};
LuaUtil::getAsyncPackageInitializer(
mLua.lua_state(), []() { return 0.0; }, []() { return 0.0; })(callbackHiddenData);
mLua["async"] = LuaUtil::AsyncPackageId{ nullptr, 0, callbackHiddenData };
mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test");
mLua.safe_script(R"(
callbackCalls = {}
ro:subscribe(async:callback(function(section, key)
table.insert(callbackCalls, section .. '_' .. (key or '*'))
end))
)");
mLua.safe_script("mutable:set('x', 5)");
EXPECT_EQ(get<int>(mLua, "mutable:get('x')"), 5);
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 5);
EXPECT_THROW(mLua.safe_script("ro:set('y', 3)"), std::exception);
mLua.safe_script("t1 = mutable:asTable()");
mLua.safe_script("t2 = ro:asTable()");
EXPECT_EQ(get<int>(mLua, "t1.x"), 5);
EXPECT_EQ(get<int>(mLua, "t2.x"), 5);
mLua.safe_script("mutable:reset()");
EXPECT_TRUE(get<bool>(mLua, "ro:get('x') == nil"));
mLua.safe_script("mutable:reset({x=4, y=7})");
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 4);
EXPECT_EQ(get<int>(mLua, "ro:get('y')"), 7);
EXPECT_THAT(get<std::string>(mLua, "table.concat(callbackCalls, ', ')"), "test_x, test_*, test_*");
luaState.protectedCall([](LuaUtil::LuaView& view) {
sol::state_view& lua = view.sol();
LuaUtil::LuaStorage::initLuaBindings(view);
LuaUtil::LuaStorage storage;
storage.setActive(true);
sol::table callbackHiddenData(lua, sol::create);
callbackHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{};
LuaUtil::getAsyncPackageInitializer(
lua.lua_state(), []() { return 0.0; }, []() { return 0.0; })(callbackHiddenData);
lua["async"] = LuaUtil::AsyncPackageId{ nullptr, 0, callbackHiddenData };
lua["mutable"] = storage.getMutableSection(lua, "test");
lua["ro"] = storage.getReadOnlySection(lua, "test");
lua.safe_script(R"(
callbackCalls = {}
ro:subscribe(async:callback(function(section, key)
table.insert(callbackCalls, section .. '_' .. (key or '*'))
end))
)");
lua.safe_script("mutable:set('x', 5)");
EXPECT_EQ(get<int>(lua, "mutable:get('x')"), 5);
EXPECT_EQ(get<int>(lua, "ro:get('x')"), 5);
EXPECT_THROW(lua.safe_script("ro:set('y', 3)"), std::exception);
lua.safe_script("t1 = mutable:asTable()");
lua.safe_script("t2 = ro:asTable()");
EXPECT_EQ(get<int>(lua, "t1.x"), 5);
EXPECT_EQ(get<int>(lua, "t2.x"), 5);
lua.safe_script("mutable:reset()");
EXPECT_TRUE(get<bool>(lua, "ro:get('x') == nil"));
lua.safe_script("mutable:reset({x=4, y=7})");
EXPECT_EQ(get<int>(lua, "ro:get('x')"), 4);
EXPECT_EQ(get<int>(lua, "ro:get('y')"), 7);
EXPECT_THAT(get<std::string>(lua, "table.concat(callbackCalls, ', ')"), "test_x, test_*, test_*");
});
}
TEST(LuaUtilStorageTest, Table)
{
sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test");
mLua.safe_script("mutable:set('x', { y = 'abc', z = 7 })");
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7);
EXPECT_THROW(mLua.safe_script("mutable:get('x').z = 3"), std::exception);
EXPECT_NO_THROW(mLua.safe_script("mutable:getCopy('x').z = 3"));
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7);
EXPECT_EQ(get<int>(mLua, "ro:get('x').z"), 7);
EXPECT_EQ(get<std::string>(mLua, "ro:get('x').y"), "abc");
LuaUtil::LuaState luaState{ nullptr, nullptr };
luaState.protectedCall([](LuaUtil::LuaView& view) {
LuaUtil::LuaStorage::initLuaBindings(view);
LuaUtil::LuaStorage storage;
auto& lua = view.sol();
storage.setActive(true);
lua["mutable"] = storage.getMutableSection(lua, "test");
lua["ro"] = storage.getReadOnlySection(lua, "test");
lua.safe_script("mutable:set('x', { y = 'abc', z = 7 })");
EXPECT_EQ(get<int>(lua, "mutable:get('x').z"), 7);
EXPECT_THROW(lua.safe_script("mutable:get('x').z = 3"), std::exception);
EXPECT_NO_THROW(lua.safe_script("mutable:getCopy('x').z = 3"));
EXPECT_EQ(get<int>(lua, "mutable:get('x').z"), 7);
EXPECT_EQ(get<int>(lua, "ro:get('x').z"), 7);
EXPECT_EQ(get<std::string>(lua, "ro:get('x').y"), "abc");
});
}
TEST(LuaUtilStorageTest, Saving)
{
sol::state mLua;
LuaUtil::LuaStorage::initLuaBindings(mLua);
LuaUtil::LuaStorage storage(mLua);
storage.setActive(true);
mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary");
mLua.safe_script("temporary:removeOnExit()");
mLua.safe_script("permanent:set('x', 1)");
mLua.safe_script("temporary:set('y', 2)");
const auto tmpFile = std::filesystem::temp_directory_path() / "test_storage.bin";
storage.save(tmpFile);
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_EQ(get<int>(mLua, "temporary:get('y')"), 2);
storage.clearTemporaryAndRemoveCallbacks();
mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary");
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil"));
mLua.safe_script("permanent:set('x', 3)");
mLua.safe_script("permanent:set('z', 4)");
LuaUtil::LuaStorage storage2(mLua);
storage2.setActive(true);
storage2.load(tmpFile);
mLua["permanent"] = storage2.getMutableSection("permanent");
mLua["temporary"] = storage2.getMutableSection("temporary");
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(mLua, "permanent:get('z') == nil"));
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil"));
LuaUtil::LuaState luaState{ nullptr, nullptr };
luaState.protectedCall([](LuaUtil::LuaView& view) {
LuaUtil::LuaStorage::initLuaBindings(view);
LuaUtil::LuaStorage storage;
auto& lua = view.sol();
storage.setActive(true);
lua["permanent"] = storage.getMutableSection(lua, "permanent");
lua["temporary"] = storage.getMutableSection(lua, "temporary");
lua.safe_script("temporary:removeOnExit()");
lua.safe_script("permanent:set('x', 1)");
lua.safe_script("temporary:set('y', 2)");
const auto tmpFile = std::filesystem::temp_directory_path() / "test_storage.bin";
storage.save(lua, tmpFile);
EXPECT_EQ(get<int>(lua, "permanent:get('x')"), 1);
EXPECT_EQ(get<int>(lua, "temporary:get('y')"), 2);
storage.clearTemporaryAndRemoveCallbacks();
lua["permanent"] = storage.getMutableSection(lua, "permanent");
lua["temporary"] = storage.getMutableSection(lua, "temporary");
EXPECT_EQ(get<int>(lua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(lua, "temporary:get('y') == nil"));
lua.safe_script("permanent:set('x', 3)");
lua.safe_script("permanent:set('z', 4)");
LuaUtil::LuaStorage storage2;
storage2.setActive(true);
storage2.load(lua, tmpFile);
lua["permanent"] = storage2.getMutableSection(lua, "permanent");
lua["temporary"] = storage2.getMutableSection(lua, "temporary");
EXPECT_EQ(get<int>(lua, "permanent:get('x')"), 1);
EXPECT_TRUE(get<bool>(lua, "permanent:get('z') == nil"));
EXPECT_TRUE(get<bool>(lua, "temporary:get('y') == nil"));
});
}
}

@ -27,7 +27,7 @@ namespace
return LuaUi::ContentView(result.get<sol::table>());
}
sol::table makeTable() { return sol::table(mLuaState.sol(), sol::create); }
sol::table makeTable() { return sol::table(mLuaState.unsafeState(), sol::create); }
sol::table makeTable(std::string name)
{
@ -39,13 +39,14 @@ namespace
TEST_F(LuaUiContentTest, ProtectedMetatable)
{
mLuaState.sol()["makeContent"] = mNew;
mLuaState.sol()["M"] = makeContent(makeTable()).getMetatable();
sol::state_view sol = mLuaState.unsafeState();
sol["makeContent"] = mNew;
sol["M"] = makeContent(makeTable()).getMetatable();
std::string testScript = R"(
assert(not pcall(function() setmetatable(makeContent{}, {}) end), 'Metatable is not protected')
assert(getmetatable(makeContent{}) == false, 'Metatable is not protected')
)";
EXPECT_NO_THROW(mLuaState.sol().safe_script(testScript));
EXPECT_NO_THROW(sol.safe_script(testScript));
}
TEST_F(LuaUiContentTest, Create)

@ -99,45 +99,48 @@ namespace MWLua
sol::table initAnimationPackage(const Context& context)
{
auto* lua = context.mLua;
auto view = context.sol();
auto mechanics = MWBase::Environment::get().getMechanicsManager();
auto world = MWBase::Environment::get().getWorld();
sol::table api(lua->sol(), sol::create);
sol::table api(view, sol::create);
api["PRIORITY"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWMechanics::Priority>({
{ "Default", MWMechanics::Priority::Priority_Default },
{ "WeaponLowerBody", MWMechanics::Priority::Priority_WeaponLowerBody },
{ "SneakIdleLowerBody", MWMechanics::Priority::Priority_SneakIdleLowerBody },
{ "SwimIdle", MWMechanics::Priority::Priority_SwimIdle },
{ "Jump", MWMechanics::Priority::Priority_Jump },
{ "Movement", MWMechanics::Priority::Priority_Movement },
{ "Hit", MWMechanics::Priority::Priority_Hit },
{ "Weapon", MWMechanics::Priority::Priority_Weapon },
{ "Block", MWMechanics::Priority::Priority_Block },
{ "Knockdown", MWMechanics::Priority::Priority_Knockdown },
{ "Torch", MWMechanics::Priority::Priority_Torch },
{ "Storm", MWMechanics::Priority::Priority_Storm },
{ "Death", MWMechanics::Priority::Priority_Death },
{ "Scripted", MWMechanics::Priority::Priority_Scripted },
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWMechanics::Priority>(view,
{
{ "Default", MWMechanics::Priority::Priority_Default },
{ "WeaponLowerBody", MWMechanics::Priority::Priority_WeaponLowerBody },
{ "SneakIdleLowerBody", MWMechanics::Priority::Priority_SneakIdleLowerBody },
{ "SwimIdle", MWMechanics::Priority::Priority_SwimIdle },
{ "Jump", MWMechanics::Priority::Priority_Jump },
{ "Movement", MWMechanics::Priority::Priority_Movement },
{ "Hit", MWMechanics::Priority::Priority_Hit },
{ "Weapon", MWMechanics::Priority::Priority_Weapon },
{ "Block", MWMechanics::Priority::Priority_Block },
{ "Knockdown", MWMechanics::Priority::Priority_Knockdown },
{ "Torch", MWMechanics::Priority::Priority_Torch },
{ "Storm", MWMechanics::Priority::Priority_Storm },
{ "Death", MWMechanics::Priority::Priority_Death },
{ "Scripted", MWMechanics::Priority::Priority_Scripted },
}));
api["BLEND_MASK"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, BlendMask>(view,
{
{ "LowerBody", BlendMask::BlendMask_LowerBody },
{ "Torso", BlendMask::BlendMask_Torso },
{ "LeftArm", BlendMask::BlendMask_LeftArm },
{ "RightArm", BlendMask::BlendMask_RightArm },
{ "UpperBody", BlendMask::BlendMask_UpperBody },
{ "All", BlendMask::BlendMask_All },
}));
api["BLEND_MASK"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, BlendMask>({
{ "LowerBody", BlendMask::BlendMask_LowerBody },
{ "Torso", BlendMask::BlendMask_Torso },
{ "LeftArm", BlendMask::BlendMask_LeftArm },
{ "RightArm", BlendMask::BlendMask_RightArm },
{ "UpperBody", BlendMask::BlendMask_UpperBody },
{ "All", BlendMask::BlendMask_All },
}));
api["BONE_GROUP"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, BoneGroup>({
{ "LowerBody", BoneGroup::BoneGroup_LowerBody },
{ "Torso", BoneGroup::BoneGroup_Torso },
{ "LeftArm", BoneGroup::BoneGroup_LeftArm },
{ "RightArm", BoneGroup::BoneGroup_RightArm },
}));
api["BONE_GROUP"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, BoneGroup>(view,
{
{ "LowerBody", BoneGroup::BoneGroup_LowerBody },
{ "Torso", BoneGroup::BoneGroup_Torso },
{ "LeftArm", BoneGroup::BoneGroup_LeftArm },
{ "RightArm", BoneGroup::BoneGroup_RightArm },
}));
api["hasAnimation"] = [world](const sol::object& object) -> bool {
return world->getAnimation(getPtrOrThrow(ObjectVariant(object))) != nullptr;
@ -305,8 +308,7 @@ namespace MWLua
sol::table initWorldVfxBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table api(lua, sol::create);
sol::table api(context.mLua->unsafeState(), sol::create);
auto world = MWBase::Environment::get().getWorld();
api["spawn"]

@ -22,8 +22,8 @@ namespace MWLua
{
sol::table initBirthSignRecordBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table birthSigns(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table birthSigns(lua, sol::create);
addRecordFunctionBinding<ESM::BirthSign>(birthSigns, context);
auto signT = lua.new_usertype<ESM::BirthSign>("ESM3_BirthSign");

@ -14,7 +14,7 @@ namespace MWLua
using CameraMode = MWRender::Camera::Mode;
sol::table initCameraPackage(sol::state_view& lua)
sol::table initCameraPackage(sol::state_view lua)
{
MWRender::Camera* camera = MWBase::Environment::get().getWorld()->getCamera();
MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager();

@ -5,7 +5,7 @@
namespace MWLua
{
sol::table initCameraPackage(sol::state_view& lua);
sol::table initCameraPackage(sol::state_view lua);
}
#endif // MWLUA_CAMERABINDINGS_H

@ -69,7 +69,8 @@ namespace MWLua
template <class CellT, class ObjectT>
static void initCellBindings(const std::string& prefix, const Context& context)
{
sol::usertype<CellT> cellT = context.mLua->sol().new_usertype<CellT>(prefix + "Cell");
auto view = context.sol();
sol::usertype<CellT> cellT = view.new_usertype<CellT>(prefix + "Cell");
cellT[sol::meta_function::equal_to] = [](const CellT& a, const CellT& b) { return a.mStore == b.mStore; };
cellT[sol::meta_function::to_string] = [](const CellT& c) {
@ -127,8 +128,7 @@ namespace MWLua
if constexpr (std::is_same_v<CellT, GCell>)
{ // only for global scripts
cellT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())](
const CellT& cell, sol::optional<sol::table> type) {
cellT["getAll"] = [ids = getPackageToTypeTable(view)](const CellT& cell, sol::optional<sol::table> type) {
if (cell.mStore->getState() != MWWorld::CellStore::State_Loaded)
cell.mStore->load();
ObjectIdList res = std::make_shared<std::vector<ObjectId>>();

@ -19,8 +19,8 @@ namespace MWLua
sol::table initClassRecordBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table classes(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table classes(lua, sol::create);
addRecordFunctionBinding<ESM::Class>(classes, context);
auto classT = lua.new_usertype<ESM::Class>("ESM3_Class");

@ -48,7 +48,7 @@ namespace MWLua
template <class... Str>
sol::object getCachedPackage(std::string_view first, const Str&... str) const
{
sol::object package = mLua->sol()[first];
sol::object package = sol()[first];
if constexpr (sizeof...(str) == 0)
return package;
else
@ -58,7 +58,7 @@ namespace MWLua
template <class... Str>
const sol::object& setCachedPackage(const sol::object& value, std::string_view first, const Str&... str) const
{
sol::state_view& lua = mLua->sol();
sol::state_view lua = sol();
if constexpr (sizeof...(str) == 0)
lua[first] = value;
else
@ -90,10 +90,17 @@ namespace MWLua
bool initializeOnce(std::string_view key) const
{
sol::object flag = mLua->sol()[key];
mLua->sol()[key] = sol::make_object(mLua->sol(), true);
auto view = sol();
sol::object flag = view[key];
view[key] = sol::make_object(view, true);
return flag == sol::nil;
}
sol::state_view sol() const
{
// Bindings are initialized in a safe context
return mLua->unsafeState();
}
};
}

@ -70,19 +70,18 @@ namespace MWLua
sol::table initCorePackage(const Context& context)
{
auto* lua = context.mLua;
auto lua = context.sol();
sol::object cached = context.getTypePackage("openmw_core");
if (cached != sol::nil)
return cached;
sol::table api(lua->sol(), sol::create);
sol::table api(lua, sol::create);
api["API_REVISION"] = Version::getLuaApiRevision(); // specified in CMakeLists.txt
api["quit"] = [lua]() {
api["quit"] = [lua = context.mLua]() {
Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit();
};
api["contentFiles"] = initContentFilesBindings(lua->sol());
api["contentFiles"] = initContentFilesBindings(lua);
api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
for (size_t i = 0; i < contentList.size(); ++i)
@ -103,10 +102,10 @@ namespace MWLua
api["dialogue"]
= context.cachePackage("openmw_core_dialogue", [context]() { return initCoreDialogueBindings(context); });
api["l10n"] = context.cachePackage("openmw_core_l10n",
[lua]() { return LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager()); });
[lua]() { return LuaUtil::initL10nLoader(lua, MWBase::Environment::get().getL10nManager()); });
const MWWorld::Store<ESM::GameSetting>* gmstStore
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
api["getGMST"] = [lua = context.mLua, gmstStore](const std::string& setting) -> sol::object {
api["getGMST"] = [lua, gmstStore](const std::string& setting) -> sol::object {
const ESM::GameSetting* gmst = gmstStore->search(setting);
if (gmst == nullptr)
return sol::nil;
@ -114,13 +113,13 @@ namespace MWLua
switch (value.getType())
{
case ESM::VT_Float:
return sol::make_object<float>(lua->sol(), value.getFloat());
return sol::make_object<float>(lua, value.getFloat());
case ESM::VT_Short:
case ESM::VT_Long:
case ESM::VT_Int:
return sol::make_object<int>(lua->sol(), value.getInteger());
return sol::make_object<int>(lua, value.getInteger());
case ESM::VT_String:
return sol::make_object<std::string>(lua->sol(), value.getString());
return sol::make_object<std::string>(lua, value.getString());
case ESM::VT_Unknown:
case ESM::VT_None:
break;

@ -20,19 +20,21 @@ namespace MWLua
{
sol::table initDebugPackage(const Context& context)
{
sol::table api = context.mLua->newTable();
auto view = context.sol();
sol::table api(view, sol::create);
api["RENDER_MODE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWRender::RenderMode>({
{ "CollisionDebug", MWRender::Render_CollisionDebug },
{ "Wireframe", MWRender::Render_Wireframe },
{ "Pathgrid", MWRender::Render_Pathgrid },
{ "Water", MWRender::Render_Water },
{ "Scene", MWRender::Render_Scene },
{ "NavMesh", MWRender::Render_NavMesh },
{ "ActorsPaths", MWRender::Render_ActorsPaths },
{ "RecastMesh", MWRender::Render_RecastMesh },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWRender::RenderMode>(view,
{
{ "CollisionDebug", MWRender::Render_CollisionDebug },
{ "Wireframe", MWRender::Render_Wireframe },
{ "Pathgrid", MWRender::Render_Pathgrid },
{ "Water", MWRender::Render_Water },
{ "Scene", MWRender::Render_Scene },
{ "NavMesh", MWRender::Render_NavMesh },
{ "ActorsPaths", MWRender::Render_ActorsPaths },
{ "RecastMesh", MWRender::Render_RecastMesh },
}));
api["toggleRenderMode"] = [context](MWRender::RenderMode value) {
context.mLuaManager->addAction([value] { MWBase::Environment::get().getWorld()->toggleRenderMode(value); });
@ -56,10 +58,11 @@ namespace MWLua
api["reloadLua"] = []() { MWBase::Environment::get().getLuaManager()->reloadAllScripts(); };
api["NAV_MESH_RENDER_MODE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, Settings::NavMeshRenderMode>({
{ "AreaType", Settings::NavMeshRenderMode::AreaType },
{ "UpdateFrequency", Settings::NavMeshRenderMode::UpdateFrequency },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, Settings::NavMeshRenderMode>(view,
{
{ "AreaType", Settings::NavMeshRenderMode::AreaType },
{ "UpdateFrequency", Settings::NavMeshRenderMode::UpdateFrequency },
}));
api["setNavMeshRenderMode"] = [context](Settings::NavMeshRenderMode value) {
context.mLuaManager->addAction(

@ -59,7 +59,7 @@ namespace
{
using StoreT = FilteredDialogueStore<filter>;
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::usertype<StoreT> storeBindingsClass
= lua.new_usertype<StoreT>("ESM3_Dialogue_Type" + std::to_string(filter) + " Store");
storeBindingsClass[sol::meta_function::to_string] = [](const StoreT& store) {
@ -317,7 +317,7 @@ namespace MWLua
{
sol::table initCoreDialogueBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
sol::table journalTable(lua, sol::create);

@ -46,7 +46,7 @@ namespace MWLua
{
sol::table initCoreFactionBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table factions(lua, sol::create);
addRecordFunctionBinding<ESM::Faction>(factions, context);
// Faction record

@ -38,13 +38,13 @@ namespace MWLua
sol::table initInputPackage(const Context& context)
{
sol::state_view lua = context.sol();
{
sol::state_view& lua = context.mLua->sol();
if (lua["openmw_input"] != sol::nil)
return lua["openmw_input"];
}
sol::usertype<SDL_Keysym> keyEvent = context.mLua->sol().new_usertype<SDL_Keysym>("KeyEvent");
sol::usertype<SDL_Keysym> keyEvent = lua.new_usertype<SDL_Keysym>("KeyEvent");
keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e) {
if (e.sym > 0 && e.sym <= 255)
return std::string(1, static_cast<char>(e.sym));
@ -57,7 +57,7 @@ namespace MWLua
keyEvent["withAlt"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_ALT; });
keyEvent["withSuper"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_GUI; });
auto touchpadEvent = context.mLua->sol().new_usertype<SDLUtil::TouchEvent>("TouchpadEvent");
auto touchpadEvent = lua.new_usertype<SDLUtil::TouchEvent>("TouchpadEvent");
touchpadEvent["device"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mDevice; });
touchpadEvent["finger"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
@ -66,7 +66,7 @@ namespace MWLua
touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; });
auto inputActions = context.mLua->sol().new_usertype<LuaUtil::InputAction::Registry>("InputActions");
auto inputActions = lua.new_usertype<LuaUtil::InputAction::Registry>("InputActions");
inputActions[sol::meta_function::index]
= [](LuaUtil::InputAction::Registry& registry, std::string_view key) { return registry[key]; };
{
@ -85,7 +85,7 @@ namespace MWLua
inputActions[sol::meta_function::pairs] = pairs;
}
auto actionInfo = context.mLua->sol().new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
auto actionInfo = lua.new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
actionInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
actionInfo["name"] = sol::readonly_property(
@ -98,7 +98,7 @@ namespace MWLua
actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
auto inputTriggers = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
auto inputTriggers = lua.new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
inputTriggers[sol::meta_function::index]
= [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) { return registry[key]; };
{
@ -117,7 +117,7 @@ namespace MWLua
inputTriggers[sol::meta_function::pairs] = pairs;
}
auto triggerInfo = context.mLua->sol().new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
auto triggerInfo = lua.new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
triggerInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
triggerInfo["name"] = sol::readonly_property(
@ -128,14 +128,15 @@ namespace MWLua
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
sol::table api(context.mLua->sol(), sol::create);
sol::table api(lua, sol::create);
api["ACTION_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, LuaUtil::InputAction::Type>({
{ "Boolean", LuaUtil::InputAction::Type::Boolean },
{ "Number", LuaUtil::InputAction::Type::Number },
{ "Range", LuaUtil::InputAction::Type::Range },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, LuaUtil::InputAction::Type>(lua,
{
{ "Boolean", LuaUtil::InputAction::Type::Boolean },
{ "Number", LuaUtil::InputAction::Type::Number },
{ "Range", LuaUtil::InputAction::Type::Range },
}));
api["actions"] = std::ref(context.mLuaManager->inputActions());
api["registerAction"] = [manager = context.mLuaManager](sol::table options) {
@ -231,215 +232,219 @@ namespace MWLua
api["getKeyName"] = [](SDL_Scancode code) { return SDL_GetScancodeName(code); };
api["ACTION"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWInput::Actions>({
{ "GameMenu", MWInput::A_GameMenu },
{ "Screenshot", MWInput::A_Screenshot },
{ "Inventory", MWInput::A_Inventory },
{ "Console", MWInput::A_Console },
{ "MoveLeft", MWInput::A_MoveLeft },
{ "MoveRight", MWInput::A_MoveRight },
{ "MoveForward", MWInput::A_MoveForward },
{ "MoveBackward", MWInput::A_MoveBackward },
{ "Activate", MWInput::A_Activate },
{ "Use", MWInput::A_Use },
{ "Jump", MWInput::A_Jump },
{ "AutoMove", MWInput::A_AutoMove },
{ "Rest", MWInput::A_Rest },
{ "Journal", MWInput::A_Journal },
{ "Run", MWInput::A_Run },
{ "CycleSpellLeft", MWInput::A_CycleSpellLeft },
{ "CycleSpellRight", MWInput::A_CycleSpellRight },
{ "CycleWeaponLeft", MWInput::A_CycleWeaponLeft },
{ "CycleWeaponRight", MWInput::A_CycleWeaponRight },
{ "AlwaysRun", MWInput::A_AlwaysRun },
{ "Sneak", MWInput::A_Sneak },
{ "QuickSave", MWInput::A_QuickSave },
{ "QuickLoad", MWInput::A_QuickLoad },
{ "QuickMenu", MWInput::A_QuickMenu },
{ "ToggleWeapon", MWInput::A_ToggleWeapon },
{ "ToggleSpell", MWInput::A_ToggleSpell },
{ "TogglePOV", MWInput::A_TogglePOV },
{ "QuickKey1", MWInput::A_QuickKey1 },
{ "QuickKey2", MWInput::A_QuickKey2 },
{ "QuickKey3", MWInput::A_QuickKey3 },
{ "QuickKey4", MWInput::A_QuickKey4 },
{ "QuickKey5", MWInput::A_QuickKey5 },
{ "QuickKey6", MWInput::A_QuickKey6 },
{ "QuickKey7", MWInput::A_QuickKey7 },
{ "QuickKey8", MWInput::A_QuickKey8 },
{ "QuickKey9", MWInput::A_QuickKey9 },
{ "QuickKey10", MWInput::A_QuickKey10 },
{ "QuickKeysMenu", MWInput::A_QuickKeysMenu },
{ "ToggleHUD", MWInput::A_ToggleHUD },
{ "ToggleDebug", MWInput::A_ToggleDebug },
{ "TogglePostProcessorHUD", MWInput::A_TogglePostProcessorHUD },
{ "ZoomIn", MWInput::A_ZoomIn },
{ "ZoomOut", MWInput::A_ZoomOut },
}));
api["ACTION"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWInput::Actions>(lua,
{
{ "GameMenu", MWInput::A_GameMenu },
{ "Screenshot", MWInput::A_Screenshot },
{ "Inventory", MWInput::A_Inventory },
{ "Console", MWInput::A_Console },
{ "MoveLeft", MWInput::A_MoveLeft },
{ "MoveRight", MWInput::A_MoveRight },
{ "MoveForward", MWInput::A_MoveForward },
{ "MoveBackward", MWInput::A_MoveBackward },
{ "Activate", MWInput::A_Activate },
{ "Use", MWInput::A_Use },
{ "Jump", MWInput::A_Jump },
{ "AutoMove", MWInput::A_AutoMove },
{ "Rest", MWInput::A_Rest },
{ "Journal", MWInput::A_Journal },
{ "Run", MWInput::A_Run },
{ "CycleSpellLeft", MWInput::A_CycleSpellLeft },
{ "CycleSpellRight", MWInput::A_CycleSpellRight },
{ "CycleWeaponLeft", MWInput::A_CycleWeaponLeft },
{ "CycleWeaponRight", MWInput::A_CycleWeaponRight },
{ "AlwaysRun", MWInput::A_AlwaysRun },
{ "Sneak", MWInput::A_Sneak },
{ "QuickSave", MWInput::A_QuickSave },
{ "QuickLoad", MWInput::A_QuickLoad },
{ "QuickMenu", MWInput::A_QuickMenu },
{ "ToggleWeapon", MWInput::A_ToggleWeapon },
{ "ToggleSpell", MWInput::A_ToggleSpell },
{ "TogglePOV", MWInput::A_TogglePOV },
{ "QuickKey1", MWInput::A_QuickKey1 },
{ "QuickKey2", MWInput::A_QuickKey2 },
{ "QuickKey3", MWInput::A_QuickKey3 },
{ "QuickKey4", MWInput::A_QuickKey4 },
{ "QuickKey5", MWInput::A_QuickKey5 },
{ "QuickKey6", MWInput::A_QuickKey6 },
{ "QuickKey7", MWInput::A_QuickKey7 },
{ "QuickKey8", MWInput::A_QuickKey8 },
{ "QuickKey9", MWInput::A_QuickKey9 },
{ "QuickKey10", MWInput::A_QuickKey10 },
{ "QuickKeysMenu", MWInput::A_QuickKeysMenu },
{ "ToggleHUD", MWInput::A_ToggleHUD },
{ "ToggleDebug", MWInput::A_ToggleDebug },
{ "TogglePostProcessorHUD", MWInput::A_TogglePostProcessorHUD },
{ "ZoomIn", MWInput::A_ZoomIn },
{ "ZoomOut", MWInput::A_ZoomOut },
}));
// input.CONTROL_SWITCH is deprecated, remove after releasing 0.49
api["CONTROL_SWITCH"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, std::string_view>({
{ "Controls", "playercontrols" },
{ "Fighting", "playerfighting" },
{ "Jumping", "playerjumping" },
{ "Looking", "playerlooking" },
{ "Magic", "playermagic" },
{ "ViewMode", "playerviewswitch" },
{ "VanityMode", "vanitymode" },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, std::string_view>(lua,
{
{ "Controls", "playercontrols" },
{ "Fighting", "playerfighting" },
{ "Jumping", "playerjumping" },
{ "Looking", "playerlooking" },
{ "Magic", "playermagic" },
{ "ViewMode", "playerviewswitch" },
{ "VanityMode", "vanitymode" },
}));
api["CONTROLLER_BUTTON"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, SDL_GameControllerButton>({
{ "A", SDL_CONTROLLER_BUTTON_A },
{ "B", SDL_CONTROLLER_BUTTON_B },
{ "X", SDL_CONTROLLER_BUTTON_X },
{ "Y", SDL_CONTROLLER_BUTTON_Y },
{ "Back", SDL_CONTROLLER_BUTTON_BACK },
{ "Guide", SDL_CONTROLLER_BUTTON_GUIDE },
{ "Start", SDL_CONTROLLER_BUTTON_START },
{ "LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK },
{ "RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK },
{ "LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER },
{ "RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER },
{ "DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP },
{ "DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN },
{ "DPadLeft", SDL_CONTROLLER_BUTTON_DPAD_LEFT },
{ "DPadRight", SDL_CONTROLLER_BUTTON_DPAD_RIGHT },
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, SDL_GameControllerButton>(lua,
{
{ "A", SDL_CONTROLLER_BUTTON_A },
{ "B", SDL_CONTROLLER_BUTTON_B },
{ "X", SDL_CONTROLLER_BUTTON_X },
{ "Y", SDL_CONTROLLER_BUTTON_Y },
{ "Back", SDL_CONTROLLER_BUTTON_BACK },
{ "Guide", SDL_CONTROLLER_BUTTON_GUIDE },
{ "Start", SDL_CONTROLLER_BUTTON_START },
{ "LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK },
{ "RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK },
{ "LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER },
{ "RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER },
{ "DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP },
{ "DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN },
{ "DPadLeft", SDL_CONTROLLER_BUTTON_DPAD_LEFT },
{ "DPadRight", SDL_CONTROLLER_BUTTON_DPAD_RIGHT },
}));
api["CONTROLLER_AXIS"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "LeftX", SDL_CONTROLLER_AXIS_LEFTX },
{ "LeftY", SDL_CONTROLLER_AXIS_LEFTY },
{ "RightX", SDL_CONTROLLER_AXIS_RIGHTX },
{ "RightY", SDL_CONTROLLER_AXIS_RIGHTY },
{ "TriggerLeft", SDL_CONTROLLER_AXIS_TRIGGERLEFT },
{ "TriggerRight", SDL_CONTROLLER_AXIS_TRIGGERRIGHT },
{ "LookUpDown", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_LookUpDown) },
{ "LookLeftRight", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_LookLeftRight) },
{ "MoveForwardBackward", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_MoveForwardBackward) },
{ "MoveLeftRight", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_MoveLeftRight) },
}));
api["KEY"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, SDL_Scancode>(lua,
{
{ "_0", SDL_SCANCODE_0 },
{ "_1", SDL_SCANCODE_1 },
{ "_2", SDL_SCANCODE_2 },
{ "_3", SDL_SCANCODE_3 },
{ "_4", SDL_SCANCODE_4 },
{ "_5", SDL_SCANCODE_5 },
{ "_6", SDL_SCANCODE_6 },
{ "_7", SDL_SCANCODE_7 },
{ "_8", SDL_SCANCODE_8 },
{ "_9", SDL_SCANCODE_9 },
{ "NP_0", SDL_SCANCODE_KP_0 },
{ "NP_1", SDL_SCANCODE_KP_1 },
{ "NP_2", SDL_SCANCODE_KP_2 },
{ "NP_3", SDL_SCANCODE_KP_3 },
{ "NP_4", SDL_SCANCODE_KP_4 },
{ "NP_5", SDL_SCANCODE_KP_5 },
{ "NP_6", SDL_SCANCODE_KP_6 },
{ "NP_7", SDL_SCANCODE_KP_7 },
{ "NP_8", SDL_SCANCODE_KP_8 },
{ "NP_9", SDL_SCANCODE_KP_9 },
{ "NP_Divide", SDL_SCANCODE_KP_DIVIDE },
{ "NP_Enter", SDL_SCANCODE_KP_ENTER },
{ "NP_Minus", SDL_SCANCODE_KP_MINUS },
{ "NP_Multiply", SDL_SCANCODE_KP_MULTIPLY },
{ "NP_Delete", SDL_SCANCODE_KP_PERIOD },
{ "NP_Plus", SDL_SCANCODE_KP_PLUS },
{ "F1", SDL_SCANCODE_F1 },
{ "F2", SDL_SCANCODE_F2 },
{ "F3", SDL_SCANCODE_F3 },
{ "F4", SDL_SCANCODE_F4 },
{ "F5", SDL_SCANCODE_F5 },
{ "F6", SDL_SCANCODE_F6 },
{ "F7", SDL_SCANCODE_F7 },
{ "F8", SDL_SCANCODE_F8 },
{ "F9", SDL_SCANCODE_F9 },
{ "F10", SDL_SCANCODE_F10 },
{ "F11", SDL_SCANCODE_F11 },
{ "F12", SDL_SCANCODE_F12 },
{ "A", SDL_SCANCODE_A },
{ "B", SDL_SCANCODE_B },
{ "C", SDL_SCANCODE_C },
{ "D", SDL_SCANCODE_D },
{ "E", SDL_SCANCODE_E },
{ "F", SDL_SCANCODE_F },
{ "G", SDL_SCANCODE_G },
{ "H", SDL_SCANCODE_H },
{ "I", SDL_SCANCODE_I },
{ "J", SDL_SCANCODE_J },
{ "K", SDL_SCANCODE_K },
{ "L", SDL_SCANCODE_L },
{ "M", SDL_SCANCODE_M },
{ "N", SDL_SCANCODE_N },
{ "O", SDL_SCANCODE_O },
{ "P", SDL_SCANCODE_P },
{ "Q", SDL_SCANCODE_Q },
{ "R", SDL_SCANCODE_R },
{ "S", SDL_SCANCODE_S },
{ "T", SDL_SCANCODE_T },
{ "U", SDL_SCANCODE_U },
{ "V", SDL_SCANCODE_V },
{ "W", SDL_SCANCODE_W },
{ "X", SDL_SCANCODE_X },
{ "Y", SDL_SCANCODE_Y },
{ "Z", SDL_SCANCODE_Z },
{ "LeftArrow", SDL_SCANCODE_LEFT },
{ "RightArrow", SDL_SCANCODE_RIGHT },
{ "UpArrow", SDL_SCANCODE_UP },
{ "DownArrow", SDL_SCANCODE_DOWN },
{ "LeftAlt", SDL_SCANCODE_LALT },
{ "LeftCtrl", SDL_SCANCODE_LCTRL },
{ "LeftBracket", SDL_SCANCODE_LEFTBRACKET },
{ "LeftSuper", SDL_SCANCODE_LGUI },
{ "LeftShift", SDL_SCANCODE_LSHIFT },
{ "RightAlt", SDL_SCANCODE_RALT },
{ "RightCtrl", SDL_SCANCODE_RCTRL },
{ "RightSuper", SDL_SCANCODE_RGUI },
{ "RightBracket", SDL_SCANCODE_RIGHTBRACKET },
{ "RightShift", SDL_SCANCODE_RSHIFT },
{ "Apostrophe", SDL_SCANCODE_APOSTROPHE },
{ "BackSlash", SDL_SCANCODE_BACKSLASH },
{ "Backspace", SDL_SCANCODE_BACKSPACE },
{ "CapsLock", SDL_SCANCODE_CAPSLOCK },
{ "Comma", SDL_SCANCODE_COMMA },
{ "Delete", SDL_SCANCODE_DELETE },
{ "End", SDL_SCANCODE_END },
{ "Enter", SDL_SCANCODE_RETURN },
{ "Equals", SDL_SCANCODE_EQUALS },
{ "Escape", SDL_SCANCODE_ESCAPE },
{ "Home", SDL_SCANCODE_HOME },
{ "Insert", SDL_SCANCODE_INSERT },
{ "Minus", SDL_SCANCODE_MINUS },
{ "NumLock", SDL_SCANCODE_NUMLOCKCLEAR },
{ "PageDown", SDL_SCANCODE_PAGEDOWN },
{ "PageUp", SDL_SCANCODE_PAGEUP },
{ "Period", SDL_SCANCODE_PERIOD },
{ "Pause", SDL_SCANCODE_PAUSE },
{ "PrintScreen", SDL_SCANCODE_PRINTSCREEN },
{ "ScrollLock", SDL_SCANCODE_SCROLLLOCK },
{ "Semicolon", SDL_SCANCODE_SEMICOLON },
{ "Slash", SDL_SCANCODE_SLASH },
{ "Space", SDL_SCANCODE_SPACE },
{ "Tab", SDL_SCANCODE_TAB },
}));
api["CONTROLLER_AXIS"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "LeftX", SDL_CONTROLLER_AXIS_LEFTX },
{ "LeftY", SDL_CONTROLLER_AXIS_LEFTY },
{ "RightX", SDL_CONTROLLER_AXIS_RIGHTX },
{ "RightY", SDL_CONTROLLER_AXIS_RIGHTY },
{ "TriggerLeft", SDL_CONTROLLER_AXIS_TRIGGERLEFT },
{ "TriggerRight", SDL_CONTROLLER_AXIS_TRIGGERRIGHT },
{ "LookUpDown", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_LookUpDown) },
{ "LookLeftRight", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_LookLeftRight) },
{ "MoveForwardBackward", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_MoveForwardBackward) },
{ "MoveLeftRight", SDL_CONTROLLER_AXIS_MAX + static_cast<int>(MWInput::A_MoveLeftRight) },
}));
api["KEY"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, SDL_Scancode>({
{ "_0", SDL_SCANCODE_0 },
{ "_1", SDL_SCANCODE_1 },
{ "_2", SDL_SCANCODE_2 },
{ "_3", SDL_SCANCODE_3 },
{ "_4", SDL_SCANCODE_4 },
{ "_5", SDL_SCANCODE_5 },
{ "_6", SDL_SCANCODE_6 },
{ "_7", SDL_SCANCODE_7 },
{ "_8", SDL_SCANCODE_8 },
{ "_9", SDL_SCANCODE_9 },
{ "NP_0", SDL_SCANCODE_KP_0 },
{ "NP_1", SDL_SCANCODE_KP_1 },
{ "NP_2", SDL_SCANCODE_KP_2 },
{ "NP_3", SDL_SCANCODE_KP_3 },
{ "NP_4", SDL_SCANCODE_KP_4 },
{ "NP_5", SDL_SCANCODE_KP_5 },
{ "NP_6", SDL_SCANCODE_KP_6 },
{ "NP_7", SDL_SCANCODE_KP_7 },
{ "NP_8", SDL_SCANCODE_KP_8 },
{ "NP_9", SDL_SCANCODE_KP_9 },
{ "NP_Divide", SDL_SCANCODE_KP_DIVIDE },
{ "NP_Enter", SDL_SCANCODE_KP_ENTER },
{ "NP_Minus", SDL_SCANCODE_KP_MINUS },
{ "NP_Multiply", SDL_SCANCODE_KP_MULTIPLY },
{ "NP_Delete", SDL_SCANCODE_KP_PERIOD },
{ "NP_Plus", SDL_SCANCODE_KP_PLUS },
{ "F1", SDL_SCANCODE_F1 },
{ "F2", SDL_SCANCODE_F2 },
{ "F3", SDL_SCANCODE_F3 },
{ "F4", SDL_SCANCODE_F4 },
{ "F5", SDL_SCANCODE_F5 },
{ "F6", SDL_SCANCODE_F6 },
{ "F7", SDL_SCANCODE_F7 },
{ "F8", SDL_SCANCODE_F8 },
{ "F9", SDL_SCANCODE_F9 },
{ "F10", SDL_SCANCODE_F10 },
{ "F11", SDL_SCANCODE_F11 },
{ "F12", SDL_SCANCODE_F12 },
{ "A", SDL_SCANCODE_A },
{ "B", SDL_SCANCODE_B },
{ "C", SDL_SCANCODE_C },
{ "D", SDL_SCANCODE_D },
{ "E", SDL_SCANCODE_E },
{ "F", SDL_SCANCODE_F },
{ "G", SDL_SCANCODE_G },
{ "H", SDL_SCANCODE_H },
{ "I", SDL_SCANCODE_I },
{ "J", SDL_SCANCODE_J },
{ "K", SDL_SCANCODE_K },
{ "L", SDL_SCANCODE_L },
{ "M", SDL_SCANCODE_M },
{ "N", SDL_SCANCODE_N },
{ "O", SDL_SCANCODE_O },
{ "P", SDL_SCANCODE_P },
{ "Q", SDL_SCANCODE_Q },
{ "R", SDL_SCANCODE_R },
{ "S", SDL_SCANCODE_S },
{ "T", SDL_SCANCODE_T },
{ "U", SDL_SCANCODE_U },
{ "V", SDL_SCANCODE_V },
{ "W", SDL_SCANCODE_W },
{ "X", SDL_SCANCODE_X },
{ "Y", SDL_SCANCODE_Y },
{ "Z", SDL_SCANCODE_Z },
{ "LeftArrow", SDL_SCANCODE_LEFT },
{ "RightArrow", SDL_SCANCODE_RIGHT },
{ "UpArrow", SDL_SCANCODE_UP },
{ "DownArrow", SDL_SCANCODE_DOWN },
{ "LeftAlt", SDL_SCANCODE_LALT },
{ "LeftCtrl", SDL_SCANCODE_LCTRL },
{ "LeftBracket", SDL_SCANCODE_LEFTBRACKET },
{ "LeftSuper", SDL_SCANCODE_LGUI },
{ "LeftShift", SDL_SCANCODE_LSHIFT },
{ "RightAlt", SDL_SCANCODE_RALT },
{ "RightCtrl", SDL_SCANCODE_RCTRL },
{ "RightSuper", SDL_SCANCODE_RGUI },
{ "RightBracket", SDL_SCANCODE_RIGHTBRACKET },
{ "RightShift", SDL_SCANCODE_RSHIFT },
{ "Apostrophe", SDL_SCANCODE_APOSTROPHE },
{ "BackSlash", SDL_SCANCODE_BACKSLASH },
{ "Backspace", SDL_SCANCODE_BACKSPACE },
{ "CapsLock", SDL_SCANCODE_CAPSLOCK },
{ "Comma", SDL_SCANCODE_COMMA },
{ "Delete", SDL_SCANCODE_DELETE },
{ "End", SDL_SCANCODE_END },
{ "Enter", SDL_SCANCODE_RETURN },
{ "Equals", SDL_SCANCODE_EQUALS },
{ "Escape", SDL_SCANCODE_ESCAPE },
{ "Home", SDL_SCANCODE_HOME },
{ "Insert", SDL_SCANCODE_INSERT },
{ "Minus", SDL_SCANCODE_MINUS },
{ "NumLock", SDL_SCANCODE_NUMLOCKCLEAR },
{ "PageDown", SDL_SCANCODE_PAGEDOWN },
{ "PageUp", SDL_SCANCODE_PAGEUP },
{ "Period", SDL_SCANCODE_PERIOD },
{ "Pause", SDL_SCANCODE_PAUSE },
{ "PrintScreen", SDL_SCANCODE_PRINTSCREEN },
{ "ScrollLock", SDL_SCANCODE_SCROLLLOCK },
{ "Semicolon", SDL_SCANCODE_SEMICOLON },
{ "Slash", SDL_SCANCODE_SLASH },
{ "Space", SDL_SCANCODE_SPACE },
{ "Tab", SDL_SCANCODE_TAB },
}));
sol::state_view& lua = context.mLua->sol();
lua["openmw_input"] = LuaUtil::makeReadOnly(api);
return lua["openmw_input"];
}

@ -60,7 +60,7 @@ namespace MWLua
if (it != self->mStatsCache.end())
return it->second;
}
return sol::make_object(context.mLua->sol(), getValue(context, prop, mObject.ptr()));
return sol::make_object(context.mLua->unsafeState(), getValue(context, prop, mObject.ptr()));
}
void set(const Context& context, std::string_view prop, const sol::object& value) const
@ -82,9 +82,9 @@ namespace MWLua
if (prop == "condition")
{
if (ptr.mRef->getType() == ESM::REC_LIGH)
return sol::make_object(context.mLua->sol(), ptr.getClass().getRemainingUsageTime(ptr));
return sol::make_object(context.mLua->unsafeState(), ptr.getClass().getRemainingUsageTime(ptr));
else if (ptr.getClass().hasItemHealth(ptr))
return sol::make_object(context.mLua->sol(),
return sol::make_object(context.mLua->unsafeState(),
ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder());
}
else if (prop == "enchantmentCharge")
@ -99,9 +99,10 @@ namespace MWLua
const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
if (charge == -1) // return the full charge
return sol::make_object(context.mLua->sol(), MWMechanics::getEnchantmentCharge(*enchantment));
return sol::make_object(
context.mLua->unsafeState(), MWMechanics::getEnchantmentCharge(*enchantment));
return sol::make_object(context.mLua->sol(), charge);
return sol::make_object(context.mLua->unsafeState(), charge);
}
else if (prop == "soul")
{
@ -109,7 +110,7 @@ namespace MWLua
if (soul.empty())
return sol::lua_nil;
return sol::make_object(context.mLua->sol(), soul.serializeText());
return sol::make_object(context.mLua->unsafeState(), soul.serializeText());
}
return sol::lua_nil;
@ -185,7 +186,7 @@ namespace MWLua
return {};
};
sol::usertype<ItemData> itemData = context.mLua->sol().new_usertype<ItemData>("ItemData");
sol::usertype<ItemData> itemData = context.sol().new_usertype<ItemData>("ItemData");
itemData[sol::meta_function::new_index] = [](const ItemData& stat, const sol::variadic_args args) {
throw std::runtime_error("Unknown ItemData property '" + args.get<std::string>() + "'");
};

@ -38,8 +38,9 @@ namespace MWLua
void LocalScripts::initializeSelfPackage(const Context& context)
{
auto lua = context.sol();
using ActorControls = MWBase::LuaManager::ActorControls;
sol::usertype<ActorControls> controls = context.mLua->sol().new_usertype<ActorControls>("ActorControls");
sol::usertype<ActorControls> controls = lua.new_usertype<ActorControls>("ActorControls");
#define CONTROL(TYPE, FIELD) \
sol::property([](const ActorControls& c) { return c.FIELD; }, \
@ -57,8 +58,8 @@ namespace MWLua
controls["use"] = CONTROL(int, mUse);
#undef CONTROL
sol::usertype<SelfObject> selfAPI = context.mLua->sol().new_usertype<SelfObject>(
"SelfObject", sol::base_classes, sol::bases<LObject, Object>());
sol::usertype<SelfObject> selfAPI
= lua.new_usertype<SelfObject>("SelfObject", sol::base_classes, sol::bases<LObject, Object>());
selfAPI[sol::meta_function::to_string]
= [](SelfObject& self) { return "openmw.self[" + self.toString() + "]"; };
selfAPI["object"] = sol::readonly_property([](SelfObject& self) -> LObject { return LObject(self); });
@ -66,13 +67,13 @@ namespace MWLua
selfAPI["isActive"] = [](SelfObject& self) { return &self.mIsActive; };
selfAPI["enableAI"] = [](SelfObject& self, bool v) { self.mControls.mDisableAI = !v; };
selfAPI["ATTACK_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWMechanics::AttackType>(
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWMechanics::AttackType>(lua,
{ { "NoAttack", MWMechanics::AttackType::NoAttack }, { "Any", MWMechanics::AttackType::Any },
{ "Chop", MWMechanics::AttackType::Chop }, { "Slash", MWMechanics::AttackType::Slash },
{ "Thrust", MWMechanics::AttackType::Thrust } }));
using AiPackage = MWMechanics::AiPackage;
sol::usertype<AiPackage> aiPackage = context.mLua->sol().new_usertype<AiPackage>("AiPackage");
sol::usertype<AiPackage> aiPackage = lua.new_usertype<AiPackage>("AiPackage");
aiPackage["type"] = sol::readonly_property([](const AiPackage& p) -> std::string_view {
switch (p.getTypeId())
{
@ -113,23 +114,24 @@ namespace MWLua
aiPackage["destPosition"] = sol::readonly_property([](const AiPackage& p) { return p.getDestination(); });
aiPackage["distance"] = sol::readonly_property([](const AiPackage& p) { return p.getDistance(); });
aiPackage["duration"] = sol::readonly_property([](const AiPackage& p) { return p.getDuration(); });
aiPackage["idle"] = sol::readonly_property([context](const AiPackage& p) -> sol::optional<sol::table> {
if (p.getTypeId() == MWMechanics::AiPackageTypeId::Wander)
{
sol::table idles(context.mLua->sol(), sol::create);
const std::vector<unsigned char>& idle = static_cast<const MWMechanics::AiWander&>(p).getIdle();
if (!idle.empty())
{
for (size_t i = 0; i < idle.size(); ++i)
{
std::string_view groupName = MWMechanics::AiWander::getIdleGroupName(i);
idles[groupName] = idle[i];
}
return idles;
}
}
return sol::nullopt;
});
aiPackage["idle"]
= sol::readonly_property([](sol::this_state lua, const AiPackage& p) -> sol::optional<sol::table> {
if (p.getTypeId() == MWMechanics::AiPackageTypeId::Wander)
{
sol::table idles(lua, sol::create);
const std::vector<unsigned char>& idle = static_cast<const MWMechanics::AiWander&>(p).getIdle();
if (!idle.empty())
{
for (size_t i = 0; i < idle.size(); ++i)
{
std::string_view groupName = MWMechanics::AiWander::getIdleGroupName(i);
idles[groupName] = idle[i];
}
return idles;
}
}
return sol::nullopt;
});
aiPackage["isRepeat"] = sol::readonly_property([](const AiPackage& p) { return p.getRepeat(); });
@ -226,7 +228,8 @@ namespace MWLua
: LuaUtil::ScriptsContainer(lua, "L" + obj.id().toString())
, mData(obj)
{
this->addPackage("openmw.self", sol::make_object(lua->sol(), &mData));
lua->protectedCall(
[&](LuaUtil::LuaView& view) { addPackage("openmw.self", sol::make_object(view.sol(), &mData)); });
registerEngineHandlers({ &mOnActiveHandlers, &mOnInactiveHandlers, &mOnConsumeHandlers, &mOnActivatedHandlers,
&mOnTeleportedHandlers, &mOnAnimationTextKeyHandlers, &mOnPlayAnimationHandlers, &mOnSkillUse,
&mOnSkillLevelUp });

@ -29,7 +29,7 @@ namespace MWLua
{
std::map<std::string, sol::object> initCommonPackages(const Context& context)
{
sol::state_view lua = context.mLua->sol();
sol::state_view lua = context.mLua->unsafeState();
MWWorld::DateTimeManager* tm = MWBase::Environment::get().getWorld()->getTimeManager();
return {
{ "openmw.animation", initAnimationPackage(context) },
@ -69,7 +69,7 @@ namespace MWLua
{
return {
{ "openmw.ambient", initAmbientPackage(context) },
{ "openmw.camera", initCameraPackage(context.mLua->sol()) },
{ "openmw.camera", initCameraPackage(context.sol()) },
{ "openmw.debug", initDebugPackage(context) },
{ "openmw.input", initInputPackage(context) },
{ "openmw.postprocessing", initPostprocessingPackage(context) },

@ -83,47 +83,49 @@ namespace MWLua
void LuaManager::init()
{
Context globalContext;
globalContext.mType = Context::Global;
globalContext.mLuaManager = this;
globalContext.mLua = &mLua;
globalContext.mObjectLists = &mObjectLists;
globalContext.mLuaEvents = &mLuaEvents;
globalContext.mSerializer = mGlobalSerializer.get();
Context localContext = globalContext;
localContext.mType = Context::Local;
localContext.mSerializer = mLocalSerializer.get();
Context menuContext = globalContext;
menuContext.mType = Context::Menu;
for (const auto& [name, package] : initCommonPackages(globalContext))
mLua.addCommonPackage(name, package);
for (const auto& [name, package] : initGlobalPackages(globalContext))
mGlobalScripts.addPackage(name, package);
for (const auto& [name, package] : initMenuPackages(menuContext))
mMenuScripts.addPackage(name, package);
mLocalPackages = initLocalPackages(localContext);
mPlayerPackages = initPlayerPackages(localContext);
mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end());
LuaUtil::LuaStorage::initLuaBindings(mLua.sol());
mGlobalScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(mLua, &mGlobalStorage));
mMenuScripts.addPackage(
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(mLua, &mGlobalStorage, &mPlayerStorage));
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(mLua, &mGlobalStorage);
mPlayerPackages["openmw.storage"]
= LuaUtil::LuaStorage::initPlayerPackage(mLua, &mGlobalStorage, &mPlayerStorage);
mPlayerStorage.setActive(true);
mGlobalStorage.setActive(false);
initConfiguration();
mInitialized = true;
mMenuScripts.addAutoStartedScripts();
mLua.protectedCall([&](LuaUtil::LuaView& view) {
Context globalContext;
globalContext.mType = Context::Global;
globalContext.mLuaManager = this;
globalContext.mLua = &mLua;
globalContext.mObjectLists = &mObjectLists;
globalContext.mLuaEvents = &mLuaEvents;
globalContext.mSerializer = mGlobalSerializer.get();
Context localContext = globalContext;
localContext.mType = Context::Local;
localContext.mSerializer = mLocalSerializer.get();
Context menuContext = globalContext;
menuContext.mType = Context::Menu;
for (const auto& [name, package] : initCommonPackages(globalContext))
mLua.addCommonPackage(name, package);
for (const auto& [name, package] : initGlobalPackages(globalContext))
mGlobalScripts.addPackage(name, package);
for (const auto& [name, package] : initMenuPackages(menuContext))
mMenuScripts.addPackage(name, package);
mLocalPackages = initLocalPackages(localContext);
mPlayerPackages = initPlayerPackages(localContext);
mPlayerPackages.insert(mLocalPackages.begin(), mLocalPackages.end());
LuaUtil::LuaStorage::initLuaBindings(view);
mGlobalScripts.addPackage("openmw.storage", LuaUtil::LuaStorage::initGlobalPackage(view, &mGlobalStorage));
mMenuScripts.addPackage(
"openmw.storage", LuaUtil::LuaStorage::initMenuPackage(view, &mGlobalStorage, &mPlayerStorage));
mLocalPackages["openmw.storage"] = LuaUtil::LuaStorage::initLocalPackage(view, &mGlobalStorage);
mPlayerPackages["openmw.storage"]
= LuaUtil::LuaStorage::initPlayerPackage(view, &mGlobalStorage, &mPlayerStorage);
mPlayerStorage.setActive(true);
mGlobalStorage.setActive(false);
initConfiguration();
mInitialized = true;
mMenuScripts.addAutoStartedScripts();
});
}
void LuaManager::loadPermanentStorage(const std::filesystem::path& userConfigPath)
@ -132,23 +134,28 @@ namespace MWLua
mGlobalStorage.setActive(true);
const auto globalPath = userConfigPath / "global_storage.bin";
const auto playerPath = userConfigPath / "player_storage.bin";
if (std::filesystem::exists(globalPath))
mGlobalStorage.load(globalPath);
if (std::filesystem::exists(playerPath))
mPlayerStorage.load(playerPath);
mLua.protectedCall([&](LuaUtil::LuaView& view) {
if (std::filesystem::exists(globalPath))
mGlobalStorage.load(view.sol(), globalPath);
if (std::filesystem::exists(playerPath))
mPlayerStorage.load(view.sol(), playerPath);
});
}
void LuaManager::savePermanentStorage(const std::filesystem::path& userConfigPath)
{
if (mGlobalScriptsStarted)
mGlobalStorage.save(userConfigPath / "global_storage.bin");
mPlayerStorage.save(userConfigPath / "player_storage.bin");
mLua.protectedCall([&](LuaUtil::LuaView& view) {
if (mGlobalScriptsStarted)
mGlobalStorage.save(view.sol(), userConfigPath / "global_storage.bin");
mPlayerStorage.save(view.sol(), userConfigPath / "player_storage.bin");
});
}
void LuaManager::update()
{
if (const int steps = Settings::lua().mGcStepsPerFrame; steps > 0)
lua_gc(mLua.sol(), LUA_GCSTEP, steps);
lua_gc(mLua.unsafeState(), LUA_GCSTEP, steps);
if (mPlayer.isEmpty())
return; // The game is not started yet.
@ -330,7 +337,7 @@ namespace MWLua
mInputActions.clear();
mInputTriggers.clear();
for (int i = 0; i < 5; ++i)
lua_gc(mLua.sol(), LUA_GCCOLLECT, 0);
lua_gc(mLua.unsafeState(), LUA_GCCOLLECT, 0);
}
void LuaManager::setupPlayer(const MWWorld::Ptr& ptr)
@ -424,35 +431,37 @@ namespace MWLua
const MWRender::AnimPriority& priority, int blendMask, bool autodisable, float speedmult,
std::string_view start, std::string_view stop, float startpoint, uint32_t loops, bool loopfallback)
{
sol::table options = mLua.newTable();
options["blendMask"] = blendMask;
options["autoDisable"] = autodisable;
options["speed"] = speedmult;
options["startKey"] = start;
options["stopKey"] = stop;
options["startPoint"] = startpoint;
options["loops"] = loops;
options["forceLoop"] = loopfallback;
bool priorityAsTable = false;
for (uint32_t i = 1; i < MWRender::sNumBlendMasks; i++)
if (priority[static_cast<MWRender::BoneGroup>(i)] != priority[static_cast<MWRender::BoneGroup>(0)])
priorityAsTable = true;
if (priorityAsTable)
{
sol::table priorityTable = mLua.newTable();
for (uint32_t i = 0; i < MWRender::sNumBlendMasks; i++)
priorityTable[static_cast<MWRender::BoneGroup>(i)] = priority[static_cast<MWRender::BoneGroup>(i)];
options["priority"] = priorityTable;
}
else
options["priority"] = priority[MWRender::BoneGroup_LowerBody];
mLua.protectedCall([&](LuaUtil::LuaView& view) {
sol::table options = view.newTable();
options["blendMask"] = blendMask;
options["autoDisable"] = autodisable;
options["speed"] = speedmult;
options["startKey"] = start;
options["stopKey"] = stop;
options["startPoint"] = startpoint;
options["loops"] = loops;
options["forceLoop"] = loopfallback;
bool priorityAsTable = false;
for (uint32_t i = 1; i < MWRender::sNumBlendMasks; i++)
if (priority[static_cast<MWRender::BoneGroup>(i)] != priority[static_cast<MWRender::BoneGroup>(0)])
priorityAsTable = true;
if (priorityAsTable)
{
sol::table priorityTable = view.newTable();
for (uint32_t i = 0; i < MWRender::sNumBlendMasks; i++)
priorityTable[static_cast<MWRender::BoneGroup>(i)] = priority[static_cast<MWRender::BoneGroup>(i)];
options["priority"] = priorityTable;
}
else
options["priority"] = priority[MWRender::BoneGroup_LowerBody];
// mEngineEvents.addToQueue(event);
// Has to be called immediately, otherwise engine details that depend on animations playing immediately
// break.
if (auto* scripts = actor.getRefData().getLuaScripts())
scripts->onPlayAnimation(groupname, options);
// mEngineEvents.addToQueue(event);
// Has to be called immediately, otherwise engine details that depend on animations playing immediately
// break.
if (auto* scripts = actor.getRefData().getLuaScripts())
scripts->onPlayAnimation(groupname, options);
});
}
void LuaManager::skillUse(const MWWorld::Ptr& actor, ESM::RefId skillId, int useType, float scale)
@ -593,7 +602,9 @@ namespace MWLua
ESM::LuaScripts globalScripts;
globalScripts.load(reader);
mLuaEvents.load(mLua.sol(), reader, mContentFileMapping, mGlobalLoader.get());
mLua.protectedCall([&](LuaUtil::LuaView& view) {
mLuaEvents.load(view.sol(), reader, mContentFileMapping, mGlobalLoader.get());
});
mGlobalScripts.setSavedDataDeserializer(mGlobalLoader.get());
mGlobalScripts.load(globalScripts);
@ -696,7 +707,9 @@ namespace MWLua
{
sol::object selected = sol::nil;
if (!selectedPtr.isEmpty())
selected = sol::make_object(mLua.sol(), LObject(getId(selectedPtr)));
mLua.protectedCall([&](LuaUtil::LuaView& view) {
selected = sol::make_object(view.sol(), LObject(getId(selectedPtr)));
});
if (playerScripts->consoleCommand(consoleMode, command, selected))
processed = true;
}

@ -150,8 +150,9 @@ namespace MWLua
template <class Arg>
std::function<void(Arg)> wrapLuaCallback(const LuaUtil::Callback& c)
{
return
[this, c](Arg arg) { this->queueCallback(c, sol::main_object(this->mLua.sol(), sol::in_place, arg)); };
return [this, c](Arg arg) {
this->queueCallback(c, sol::main_object(this->mLua.unsafeState(), sol::in_place, arg));
};
}
LuaUi::ResourceManager* uiResourceManager() { return &mUiResourceManager; }
@ -227,8 +228,8 @@ namespace MWLua
std::vector<std::pair<std::string, Misc::Color>> mInGameConsoleMessages;
std::optional<ObjectId> mDelayedUiModeChangedArg;
LuaUtil::LuaStorage mGlobalStorage{ mLua.sol() };
LuaUtil::LuaStorage mPlayerStorage{ mLua.sol() };
LuaUtil::LuaStorage mGlobalStorage;
LuaUtil::LuaStorage mPlayerStorage;
LuaUtil::InputAction::Registry mInputActions;
LuaUtil::InputTrigger::Registry mInputTriggers;

@ -214,33 +214,36 @@ namespace MWLua
sol::table initCoreMagicBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table magicApi(lua, sol::create);
// Constants
magicApi["RANGE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, ESM::RangeType>({
{ "Self", ESM::RT_Self },
{ "Touch", ESM::RT_Touch },
{ "Target", ESM::RT_Target },
}));
magicApi["SPELL_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, ESM::Spell::SpellType>({
{ "Spell", ESM::Spell::ST_Spell },
{ "Ability", ESM::Spell::ST_Ability },
{ "Blight", ESM::Spell::ST_Blight },
{ "Disease", ESM::Spell::ST_Disease },
{ "Curse", ESM::Spell::ST_Curse },
{ "Power", ESM::Spell::ST_Power },
magicApi["RANGE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, ESM::RangeType>(lua,
{
{ "Self", ESM::RT_Self },
{ "Touch", ESM::RT_Touch },
{ "Target", ESM::RT_Target },
}));
magicApi["SPELL_TYPE"]
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, ESM::Spell::SpellType>(lua,
{
{ "Spell", ESM::Spell::ST_Spell },
{ "Ability", ESM::Spell::ST_Ability },
{ "Blight", ESM::Spell::ST_Blight },
{ "Disease", ESM::Spell::ST_Disease },
{ "Curse", ESM::Spell::ST_Curse },
{ "Power", ESM::Spell::ST_Power },
}));
magicApi["ENCHANTMENT_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, ESM::Enchantment::Type>({
{ "CastOnce", ESM::Enchantment::Type::CastOnce },
{ "CastOnStrike", ESM::Enchantment::Type::WhenStrikes },
{ "CastOnUse", ESM::Enchantment::Type::WhenUsed },
{ "ConstantEffect", ESM::Enchantment::Type::ConstantEffect },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, ESM::Enchantment::Type>(lua,
{
{ "CastOnce", ESM::Enchantment::Type::CastOnce },
{ "CastOnStrike", ESM::Enchantment::Type::WhenStrikes },
{ "CastOnUse", ESM::Enchantment::Type::WhenUsed },
{ "ConstantEffect", ESM::Enchantment::Type::ConstantEffect },
}));
sol::table effect(context.mLua->sol(), sol::create);
sol::table effect(lua, sol::create);
magicApi["EFFECT_TYPE"] = LuaUtil::makeStrictReadOnly(effect);
for (const auto& name : ESM::MagicEffect::sIndexNames)
{
@ -369,7 +372,7 @@ namespace MWLua
= sol::readonly_property([](const ESM::IndexedENAMstruct& params) -> int { return params.mIndex; });
// MagicEffect record
auto magicEffectT = context.mLua->sol().new_usertype<ESM::MagicEffect>("ESM3_MagicEffect");
auto magicEffectT = lua.new_usertype<ESM::MagicEffect>("ESM3_MagicEffect");
magicEffectT[sol::meta_function::to_string] = [](const ESM::MagicEffect& rec) {
return "ESM3_MagicEffect[" + ESM::MagicEffect::indexToGmstString(rec.mIndex) + "]";
@ -437,7 +440,7 @@ namespace MWLua
// magicEffectT["projectileSpeed"]
// = sol::readonly_property([](const ESM::MagicEffect& rec) -> float { return rec.mData.mSpeed; });
auto activeSpellEffectT = context.mLua->sol().new_usertype<ESM::ActiveEffect>("ActiveSpellEffect");
auto activeSpellEffectT = lua.new_usertype<ESM::ActiveEffect>("ActiveSpellEffect");
activeSpellEffectT[sol::meta_function::to_string] = [](const ESM::ActiveEffect& effect) {
return "ActiveSpellEffect[" + ESM::MagicEffect::indexToGmstString(effect.mEffectId) + "]";
};
@ -508,7 +511,7 @@ namespace MWLua
return effect.mDuration;
});
auto activeSpellT = context.mLua->sol().new_usertype<ActiveSpell>("ActiveSpellParams");
auto activeSpellT = lua.new_usertype<ActiveSpell>("ActiveSpellParams");
activeSpellT[sol::meta_function::to_string] = [](const ActiveSpell& activeSpell) {
return "ActiveSpellParams[" + activeSpell.mParams.getSourceSpellId().serializeText() + "]";
};
@ -569,7 +572,7 @@ namespace MWLua
return activeSpell.mParams.getActiveSpellId().serializeText();
});
auto activeEffectT = context.mLua->sol().new_usertype<ActiveEffect>("ActiveEffect");
auto activeEffectT = lua.new_usertype<ActiveEffect>("ActiveEffect");
activeEffectT[sol::meta_function::to_string] = [](const ActiveEffect& effect) {
return "ActiveEffect[" + ESM::MagicEffect::indexToGmstString(effect.key.mId) + "]";
@ -680,23 +683,24 @@ namespace MWLua
void addActorMagicBindings(sol::table& actor, const Context& context)
{
auto lua = context.sol();
const MWWorld::Store<ESM::Spell>* spellStore
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
// types.Actor.spells(o)
actor["spells"] = [](const sol::object& actor) { return ActorSpells{ actor }; };
auto spellsT = context.mLua->sol().new_usertype<ActorSpells>("ActorSpells");
auto spellsT = lua.new_usertype<ActorSpells>("ActorSpells");
spellsT[sol::meta_function::to_string]
= [](const ActorSpells& spells) { return "ActorSpells[" + spells.mActor.object().toString() + "]"; };
actor["activeSpells"] = [](const sol::object& actor) { return ActorActiveSpells{ actor }; };
auto activeSpellsT = context.mLua->sol().new_usertype<ActorActiveSpells>("ActorActiveSpells");
auto activeSpellsT = lua.new_usertype<ActorActiveSpells>("ActorActiveSpells");
activeSpellsT[sol::meta_function::to_string] = [](const ActorActiveSpells& spells) {
return "ActorActiveSpells[" + spells.mActor.object().toString() + "]";
};
actor["activeEffects"] = [](const sol::object& actor) { return ActorActiveEffects{ actor }; };
auto activeEffectsT = context.mLua->sol().new_usertype<ActorActiveEffects>("ActorActiveEffects");
auto activeEffectsT = lua.new_usertype<ActorActiveEffects>("ActorActiveEffects");
activeEffectsT[sol::meta_function::to_string] = [](const ActorActiveEffects& effects) {
return "ActorActiveEffects[" + effects.mActor.object().toString() + "]";
};
@ -798,10 +802,10 @@ namespace MWLua
});
// pairs(types.Actor.spells(o))
spellsT[sol::meta_function::pairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
spellsT[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
// ipairs(types.Actor.spells(o))
spellsT[sol::meta_function::ipairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
spellsT[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
// types.Actor.spells(o):add(id)
spellsT["add"] = [context](const ActorSpells& spells, const sol::object& spellOrId) {

@ -14,17 +14,17 @@ namespace MWLua
{
sol::table initMarkupPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
api["loadYaml"] = [lua = context.mLua, vfs](std::string_view fileName) {
api["loadYaml"] = [lua, vfs](std::string_view fileName) {
Files::IStreamPtr file = vfs->get(VFS::Path::Normalized(fileName));
return LuaUtil::loadYaml(*file, lua->sol());
};
api["decodeYaml"] = [lua = context.mLua](std::string_view inputData) {
return LuaUtil::loadYaml(std::string(inputData), lua->sol());
return LuaUtil::loadYaml(*file, lua);
};
api["decodeYaml"]
= [lua](std::string_view inputData) { return LuaUtil::loadYaml(std::string(inputData), lua); };
return LuaUtil::makeReadOnly(api);
}

@ -30,15 +30,16 @@ namespace MWLua
sol::table initMenuPackage(const Context& context)
{
sol::state_view lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
api["STATE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWBase::StateManager::State>({
{ "NoGame", MWBase::StateManager::State_NoGame },
{ "Running", MWBase::StateManager::State_Running },
{ "Ended", MWBase::StateManager::State_Ended },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWBase::StateManager::State>(lua,
{
{ "NoGame", MWBase::StateManager::State_NoGame },
{ "Running", MWBase::StateManager::State_Running },
{ "Ended", MWBase::StateManager::State_Ended },
}));
api["getState"] = []() -> int { return MWBase::Environment::get().getStateManager()->getState(); };

@ -95,7 +95,8 @@ namespace MWLua
sol::table initMWScriptBindings(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
api["getGlobalScript"]
= [](std::string_view recordId, sol::optional<GObject> player) -> sol::optional<MWScriptRef> {
@ -121,10 +122,8 @@ namespace MWLua
// api["getGlobalScripts"] = [](std::string_view recordId) -> list of scripts
// api["getLocalScripts"] = [](const GObject& obj) -> list of scripts
sol::state_view& lua = context.mLua->sol();
sol::usertype<MWScriptRef> mwscript = context.mLua->sol().new_usertype<MWScriptRef>("MWScript");
sol::usertype<MWScriptVariables> mwscriptVars
= context.mLua->sol().new_usertype<MWScriptVariables>("MWScriptVariables");
sol::usertype<MWScriptRef> mwscript = lua.new_usertype<MWScriptRef>("MWScript");
sol::usertype<MWScriptVariables> mwscriptVars = lua.new_usertype<MWScriptVariables>("MWScriptVariables");
mwscript[sol::meta_function::to_string]
= [](const MWScriptRef& s) { return std::string("MWScript{") + s.mId.toDebugString() + "}"; };
mwscript["isRunning"] = sol::readonly_property([](const MWScriptRef& s) { return s.isRunning(); });

@ -53,11 +53,12 @@ namespace MWLua
{
sol::table initNearbyPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
ObjectLists* objectLists = context.mObjectLists;
sol::usertype<MWPhysics::RayCastingResult> rayResult
= context.mLua->sol().new_usertype<MWPhysics::RayCastingResult>("RayCastingResult");
= lua.new_usertype<MWPhysics::RayCastingResult>("RayCastingResult");
rayResult["hit"] = sol::readonly_property([](const MWPhysics::RayCastingResult& r) { return r.mHit; });
rayResult["hitPos"]
= sol::readonly_property([](const MWPhysics::RayCastingResult& r) -> sol::optional<osg::Vec3f> {
@ -82,18 +83,19 @@ namespace MWLua
});
api["COLLISION_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWPhysics::CollisionType>({
{ "World", MWPhysics::CollisionType_World },
{ "Door", MWPhysics::CollisionType_Door },
{ "Actor", MWPhysics::CollisionType_Actor },
{ "HeightMap", MWPhysics::CollisionType_HeightMap },
{ "Projectile", MWPhysics::CollisionType_Projectile },
{ "Water", MWPhysics::CollisionType_Water },
{ "Default", MWPhysics::CollisionType_Default },
{ "AnyPhysical", MWPhysics::CollisionType_AnyPhysical },
{ "Camera", MWPhysics::CollisionType_CameraOnly },
{ "VisualOnly", MWPhysics::CollisionType_VisualOnly },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWPhysics::CollisionType>(lua,
{
{ "World", MWPhysics::CollisionType_World },
{ "Door", MWPhysics::CollisionType_Door },
{ "Actor", MWPhysics::CollisionType_Actor },
{ "HeightMap", MWPhysics::CollisionType_HeightMap },
{ "Projectile", MWPhysics::CollisionType_Projectile },
{ "Water", MWPhysics::CollisionType_Water },
{ "Default", MWPhysics::CollisionType_Default },
{ "AnyPhysical", MWPhysics::CollisionType_AnyPhysical },
{ "Camera", MWPhysics::CollisionType_CameraOnly },
{ "VisualOnly", MWPhysics::CollisionType_VisualOnly },
}));
api["castRay"] = [](const osg::Vec3f& from, const osg::Vec3f& to, sol::optional<sol::table> options) {
std::vector<MWWorld::ConstPtr> ignore;
@ -163,12 +165,13 @@ namespace MWLua
ignore = parseIgnoreList(*options);
}
context.mLuaManager->addAction([context, ignore = std::move(ignore),
callback = LuaUtil::Callback::fromLua(callback), from, to] {
MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
context.mLuaManager->queueCallback(callback, sol::main_object(context.mLua->sol(), sol::in_place, res));
});
context.mLuaManager->addAction(
[context, ignore = std::move(ignore), callback = LuaUtil::Callback::fromLua(callback), from, to] {
MWPhysics::RayCastingResult res;
MWBase::Environment::get().getWorld()->castRenderingRay(res, from, to, false, false, ignore);
context.mLuaManager->queueCallback(
callback, sol::main_object(context.mLua->unsafeState(), sol::in_place, res));
});
};
api["getObjectByFormId"] = [](std::string_view formIdStr) -> LObject {
@ -186,32 +189,35 @@ namespace MWLua
api["players"] = LObjectList{ objectLists->getPlayers() };
api["NAVIGATOR_FLAGS"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, DetourNavigator::Flag>({
{ "Walk", DetourNavigator::Flag_walk },
{ "Swim", DetourNavigator::Flag_swim },
{ "OpenDoor", DetourNavigator::Flag_openDoor },
{ "UsePathgrid", DetourNavigator::Flag_usePathgrid },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, DetourNavigator::Flag>(lua,
{
{ "Walk", DetourNavigator::Flag_walk },
{ "Swim", DetourNavigator::Flag_swim },
{ "OpenDoor", DetourNavigator::Flag_openDoor },
{ "UsePathgrid", DetourNavigator::Flag_usePathgrid },
}));
api["COLLISION_SHAPE_TYPE"] = LuaUtil::makeStrictReadOnly(
context.mLua->tableFromPairs<std::string_view, DetourNavigator::CollisionShapeType>({
{ "Aabb", DetourNavigator::CollisionShapeType::Aabb },
{ "RotatingBox", DetourNavigator::CollisionShapeType::RotatingBox },
{ "Cylinder", DetourNavigator::CollisionShapeType::Cylinder },
}));
LuaUtil::tableFromPairs<std::string_view, DetourNavigator::CollisionShapeType>(lua,
{
{ "Aabb", DetourNavigator::CollisionShapeType::Aabb },
{ "RotatingBox", DetourNavigator::CollisionShapeType::RotatingBox },
{ "Cylinder", DetourNavigator::CollisionShapeType::Cylinder },
}));
api["FIND_PATH_STATUS"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, DetourNavigator::Status>({
{ "Success", DetourNavigator::Status::Success },
{ "PartialPath", DetourNavigator::Status::PartialPath },
{ "NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound },
{ "StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound },
{ "EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound },
{ "MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed },
{ "FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed },
{ "InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed },
{ "FindStraightPathFailed", DetourNavigator::Status::FindStraightPathFailed },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, DetourNavigator::Status>(lua,
{
{ "Success", DetourNavigator::Status::Success },
{ "PartialPath", DetourNavigator::Status::PartialPath },
{ "NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound },
{ "StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound },
{ "EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound },
{ "MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed },
{ "FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed },
{ "InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed },
{ "FindStraightPathFailed", DetourNavigator::Status::FindStraightPathFailed },
}));
static const DetourNavigator::AgentBounds defaultAgentBounds{
Settings::game().mActorCollisionShapeType,

@ -164,7 +164,7 @@ namespace MWLua
void registerObjectList(const std::string& prefix, const Context& context)
{
using ListT = ObjectList<ObjectT>;
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
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}"; };
@ -205,7 +205,7 @@ namespace MWLua
void addOwnerbindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{
using OwnerT = Owner<ObjectT>;
sol::usertype<OwnerT> ownerT = context.mLua->sol().new_usertype<OwnerT>(prefix + "Owner");
sol::usertype<OwnerT> ownerT = context.sol().new_usertype<OwnerT>(prefix + "Owner");
ownerT[sol::meta_function::to_string] = [](const OwnerT& o) { return "Owner[" + o.mObj.toString() + "]"; };
@ -329,9 +329,10 @@ namespace MWLua
return LuaUtil::Box{ bb.center(), bb._max - bb.center() };
};
objectT["type"] = sol::readonly_property(
[types = getTypeToPackageTable(context.mLua->sol())](
const ObjectT& o) mutable { return types[getLiveCellRefType(o.ptr().mRef)]; });
objectT["type"]
= sol::readonly_property([types = getTypeToPackageTable(context.sol())](const ObjectT& o) mutable {
return types[getLiveCellRefType(o.ptr().mRef)];
});
objectT["count"] = sol::readonly_property([](const ObjectT& o) { return o.ptr().getCellRef().getCount(); });
objectT[sol::meta_function::equal_to] = [](const ObjectT& a, const ObjectT& b) { return a.id() == b.id(); };
@ -553,12 +554,12 @@ namespace MWLua
void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{
using InventoryT = Inventory<ObjectT>;
sol::usertype<InventoryT> inventoryT = context.mLua->sol().new_usertype<InventoryT>(prefix + "Inventory");
sol::usertype<InventoryT> inventoryT = context.sol().new_usertype<InventoryT>(prefix + "Inventory");
inventoryT[sol::meta_function::to_string]
= [](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; };
inventoryT["getAll"] = [ids = getPackageToTypeTable(context.mLua->sol())](
inventoryT["getAll"] = [ids = getPackageToTypeTable(context.mLua->unsafeState())](
const InventoryT& inventory, sol::optional<sol::table> type) {
int mask = -1;
sol::optional<uint32_t> typeId = sol::nullopt;
@ -681,7 +682,7 @@ namespace MWLua
void initObjectBindings(const std::string& prefix, const Context& context)
{
sol::usertype<ObjectT> objectT
= context.mLua->sol().new_usertype<ObjectT>(prefix + "Object", sol::base_classes, sol::bases<Object>());
= context.sol().new_usertype<ObjectT>(prefix + "Object", sol::base_classes, sol::bases<Object>());
addBasicBindings<ObjectT>(objectT, context);
addInventoryBindings<ObjectT>(objectT, prefix, context);
addOwnerbindings<ObjectT>(objectT, prefix, context);

@ -96,9 +96,10 @@ namespace MWLua
sol::table initPostprocessingPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
sol::usertype<Shader> shader = context.mLua->sol().new_usertype<Shader>("Shader");
sol::usertype<Shader> shader = lua.new_usertype<Shader>("Shader");
shader[sol::meta_function::to_string] = [](const Shader& shader) { return shader.toString(); };
shader["enable"] = [context](Shader& shader, sol::optional<int> optPos) {

@ -43,8 +43,8 @@ namespace MWLua
{
sol::table initRaceRecordBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::table races(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table races(lua, sol::create);
addRecordFunctionBinding<ESM::Race>(races, context);
auto raceT = lua.new_usertype<ESM::Race>("ESM3_Race");

@ -39,7 +39,7 @@ namespace MWLua
// Define a custom user type for the store.
// Provide the interface of a read-only array.
using StoreT = MWWorld::Store<T>;
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::usertype<StoreT> storeT = lua.new_usertype<StoreT>(recordName + "WorldStore");
storeT[sol::meta_function::to_string] = [recordName](const StoreT& store) {
return "{" + std::to_string(store.getSize()) + " " + recordName + " records}";

@ -100,7 +100,7 @@ namespace MWLua
{
sol::table initAmbientPackage(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
if (lua["openmw_ambient"] != sol::nil)
return lua["openmw_ambient"];
@ -171,7 +171,7 @@ namespace MWLua
sol::table initCoreSoundBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table api(lua, sol::create);
api["isEnabled"] = []() { return MWBase::Environment::get().getSoundManager()->isEnabled(); };

@ -54,7 +54,7 @@ namespace
if (it != self->mStatsCache.end())
return it->second;
}
return sol::make_object(context.mLua->sol(), getter(obj.ptr()));
return sol::make_object(context.mLua->unsafeState(), getter(obj.ptr()));
}
}
@ -531,11 +531,12 @@ namespace MWLua
{
void addActorStatsBindings(sol::table& actor, const Context& context)
{
sol::table stats(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table stats(lua, sol::create);
actor["stats"] = LuaUtil::makeReadOnly(stats);
auto skillIncreasesForAttributeStatsT
= context.mLua->sol().new_usertype<SkillIncreasesForAttributeStats>("SkillIncreasesForAttributeStats");
= lua.new_usertype<SkillIncreasesForAttributeStats>("SkillIncreasesForAttributeStats");
for (const auto& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
{
skillIncreasesForAttributeStatsT[ESM::RefId(attribute.mId).serializeText()] = sol::property(
@ -546,8 +547,7 @@ namespace MWLua
}
// ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization)
auto skillIncreasesForSpecializationStatsT
= context.mLua->sol().new_usertype<SkillIncreasesForSpecializationStats>(
"skillIncreasesForSpecializationStats");
= lua.new_usertype<SkillIncreasesForSpecializationStats>("skillIncreasesForSpecializationStats");
for (int i = 0; i < 3; i++)
{
std::string_view index = ESM::Class::specializationIndexToLuaId.at(i);
@ -558,7 +558,7 @@ namespace MWLua
});
}
auto levelStatT = context.mLua->sol().new_usertype<LevelStat>("LevelStat");
auto levelStatT = lua.new_usertype<LevelStat>("LevelStat");
levelStatT["current"] = sol::property([context](const LevelStat& stat) { return stat.getCurrent(context); },
[context](const LevelStat& stat, const sol::object& value) { stat.setCurrent(context, value); });
levelStatT["progress"] = sol::property([context](const LevelStat& stat) { return stat.getProgress(context); },
@ -569,32 +569,32 @@ namespace MWLua
[](const LevelStat& stat) { return stat.getSkillIncreasesForSpecializationStats(); });
stats["level"] = addIndexedAccessor<LevelStat>(0);
auto dynamicStatT = context.mLua->sol().new_usertype<DynamicStat>("DynamicStat");
auto dynamicStatT = lua.new_usertype<DynamicStat>("DynamicStat");
addProp(context, dynamicStatT, "base", &MWMechanics::DynamicStat<float>::getBase);
addProp(context, dynamicStatT, "current", &MWMechanics::DynamicStat<float>::getCurrent);
addProp(context, dynamicStatT, "modifier", &MWMechanics::DynamicStat<float>::getModifier);
sol::table dynamic(context.mLua->sol(), sol::create);
sol::table dynamic(lua, sol::create);
stats["dynamic"] = LuaUtil::makeReadOnly(dynamic);
dynamic["health"] = addIndexedAccessor<DynamicStat>(0);
dynamic["magicka"] = addIndexedAccessor<DynamicStat>(1);
dynamic["fatigue"] = addIndexedAccessor<DynamicStat>(2);
auto attributeStatT = context.mLua->sol().new_usertype<AttributeStat>("AttributeStat");
auto attributeStatT = lua.new_usertype<AttributeStat>("AttributeStat");
addProp(context, attributeStatT, "base", &MWMechanics::AttributeValue::getBase);
addProp(context, attributeStatT, "damage", &MWMechanics::AttributeValue::getDamage);
attributeStatT["modified"]
= sol::readonly_property([=](const AttributeStat& stat) { return stat.getModified(context); });
addProp(context, attributeStatT, "modifier", &MWMechanics::AttributeValue::getModifier);
sol::table attributes(context.mLua->sol(), sol::create);
sol::table attributes(lua, sol::create);
stats["attributes"] = LuaUtil::makeReadOnly(attributes);
for (const ESM::Attribute& attribute : MWBase::Environment::get().getESMStore()->get<ESM::Attribute>())
attributes[ESM::RefId(attribute.mId).serializeText()] = addIndexedAccessor<AttributeStat>(attribute.mId);
auto aiStatT = context.mLua->sol().new_usertype<AIStat>("AIStat");
auto aiStatT = lua.new_usertype<AIStat>("AIStat");
addProp(context, aiStatT, "base", &MWMechanics::Stat<int>::getBase);
addProp(context, aiStatT, "modifier", &MWMechanics::Stat<int>::getModifier);
aiStatT["modified"] = sol::readonly_property([=](const AIStat& stat) { return stat.getModified(context); });
sol::table ai(context.mLua->sol(), sol::create);
sol::table ai(lua, sol::create);
stats["ai"] = LuaUtil::makeReadOnly(ai);
ai["alarm"] = addIndexedAccessor<AIStat>(MWMechanics::AiSetting::Alarm);
ai["fight"] = addIndexedAccessor<AIStat>(MWMechanics::AiSetting::Fight);
@ -604,13 +604,14 @@ namespace MWLua
void addNpcStatsBindings(sol::table& npc, const Context& context)
{
sol::table npcStats(context.mLua->sol(), sol::create);
sol::table baseMeta(context.mLua->sol(), sol::create);
sol::state_view lua = context.sol();
sol::table npcStats(lua, sol::create);
sol::table baseMeta(lua, sol::create);
baseMeta[sol::meta_function::index] = LuaUtil::getMutableFromReadOnly(npc["baseType"]["stats"]);
npcStats[sol::metatable_key] = baseMeta;
npc["stats"] = LuaUtil::makeReadOnly(npcStats);
auto skillStatT = context.mLua->sol().new_usertype<SkillStat>("SkillStat");
auto skillStatT = lua.new_usertype<SkillStat>("SkillStat");
addProp(context, skillStatT, "base", &MWMechanics::SkillValue::getBase);
addProp(context, skillStatT, "damage", &MWMechanics::SkillValue::getDamage);
skillStatT["modified"]
@ -618,7 +619,7 @@ namespace MWLua
addProp(context, skillStatT, "modifier", &MWMechanics::SkillValue::getModifier);
skillStatT["progress"] = sol::property([context](const SkillStat& stat) { return stat.getProgress(context); },
[context](const SkillStat& stat, const sol::object& value) { stat.cache(context, "progress", value); });
sol::table skills(context.mLua->sol(), sol::create);
sol::table skills(lua, sol::create);
npcStats["skills"] = LuaUtil::makeReadOnly(skills);
for (const ESM::Skill& skill : MWBase::Environment::get().getESMStore()->get<ESM::Skill>())
skills[ESM::RefId(skill.mId).serializeText()] = addIndexedAccessor<SkillStat>(skill.mId);
@ -626,7 +627,7 @@ namespace MWLua
sol::table initCoreStatsBindings(const Context& context)
{
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table statsApi(lua, sol::create);
auto* vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
@ -635,7 +636,7 @@ namespace MWLua
statsApi["Attribute"] = LuaUtil::makeReadOnly(attributes);
statsApi["Attribute"][sol::metatable_key][sol::meta_function::to_string] = ESM::Attribute::getRecordType;
auto attributeT = context.mLua->sol().new_usertype<ESM::Attribute>("Attribute");
auto attributeT = lua.new_usertype<ESM::Attribute>("Attribute");
attributeT[sol::meta_function::to_string]
= [](const ESM::Attribute& rec) { return "ESM3_Attribute[" + rec.mId.toDebugString() + "]"; };
attributeT["id"] = sol::readonly_property(
@ -653,7 +654,7 @@ namespace MWLua
statsApi["Skill"] = LuaUtil::makeReadOnly(skills);
statsApi["Skill"][sol::metatable_key][sol::meta_function::to_string] = ESM::Skill::getRecordType;
auto skillT = context.mLua->sol().new_usertype<ESM::Skill>("Skill");
auto skillT = lua.new_usertype<ESM::Skill>("Skill");
skillT[sol::meta_function::to_string]
= [](const ESM::Skill& rec) { return "ESM3_Skill[" + rec.mId.toDebugString() + "]"; };
skillT["id"] = sol::readonly_property(
@ -683,7 +684,7 @@ namespace MWLua
return res;
});
auto schoolT = context.mLua->sol().new_usertype<ESM::MagicSchool>("MagicSchool");
auto schoolT = lua.new_usertype<ESM::MagicSchool>("MagicSchool");
schoolT[sol::meta_function::to_string]
= [](const ESM::MagicSchool& rec) { return "ESM3_MagicSchool[" + rec.mName + "]"; };
schoolT["name"]

@ -43,7 +43,7 @@ namespace MWLua
activator["createRecordDraft"] = tableToActivator;
addRecordFunctionBinding<ESM::Activator>(activator, context);
sol::usertype<ESM::Activator> record = context.mLua->sol().new_usertype<ESM::Activator>("ESM3_Activator");
sol::usertype<ESM::Activator> record = context.sol().new_usertype<ESM::Activator>("ESM3_Activator");
record[sol::meta_function::to_string]
= [](const ESM::Activator& rec) { return "ESM3_Activator[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -172,13 +172,15 @@ namespace MWLua
void addActorBindings(sol::table actor, const Context& context)
{
sol::state_view lua = context.sol();
actor["STANCE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWMechanics::DrawState>({
{ "Nothing", MWMechanics::DrawState::Nothing },
{ "Weapon", MWMechanics::DrawState::Weapon },
{ "Spell", MWMechanics::DrawState::Spell },
}));
actor["EQUIPMENT_SLOT"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>(
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWMechanics::DrawState>(lua,
{
{ "Nothing", MWMechanics::DrawState::Nothing },
{ "Weapon", MWMechanics::DrawState::Weapon },
{ "Spell", MWMechanics::DrawState::Spell },
}));
actor["EQUIPMENT_SLOT"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{ { "Helmet", MWWorld::InventoryStore::Slot_Helmet }, { "Cuirass", MWWorld::InventoryStore::Slot_Cuirass },
{ "Greaves", MWWorld::InventoryStore::Slot_Greaves },
{ "LeftPauldron", MWWorld::InventoryStore::Slot_LeftPauldron },

@ -23,7 +23,7 @@ namespace MWLua
void addActorServicesBindings(sol::usertype<T>& record, const Context& context)
{
record["servicesOffered"] = sol::readonly_property([context](const T& rec) -> sol::table {
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table providedServices(lua, sol::create);
constexpr std::array<std::pair<int, std::string_view>, 19> serviceNames = { { { ESM::NPC::Spells,
"Spells" },
@ -51,7 +51,7 @@ namespace MWLua
});
record["travelDestinations"] = sol::readonly_property([context](const T& rec) -> sol::table {
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::table travelDests(lua, sol::create);
if (!rec.getTransport().empty())
{

@ -20,18 +20,20 @@ namespace MWLua
{
void addApparatusBindings(sol::table apparatus, const Context& context)
{
apparatus["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "MortarPestle", ESM::Apparatus::MortarPestle },
{ "Alembic", ESM::Apparatus::Alembic },
{ "Calcinator", ESM::Apparatus::Calcinator },
{ "Retort", ESM::Apparatus::Retort },
}));
sol::state_view lua = context.sol();
apparatus["TYPE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "MortarPestle", ESM::Apparatus::MortarPestle },
{ "Alembic", ESM::Apparatus::Alembic },
{ "Calcinator", ESM::Apparatus::Calcinator },
{ "Retort", ESM::Apparatus::Retort },
}));
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
addRecordFunctionBinding<ESM::Apparatus>(apparatus, context);
sol::usertype<ESM::Apparatus> record = context.mLua->sol().new_usertype<ESM::Apparatus>("ESM3_Apparatus");
sol::usertype<ESM::Apparatus> record = lua.new_usertype<ESM::Apparatus>("ESM3_Apparatus");
record[sol::meta_function::to_string]
= [](const ESM::Apparatus& rec) { return "ESM3_Apparatus[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -69,26 +69,28 @@ namespace MWLua
{
void addArmorBindings(sol::table armor, const Context& context)
{
armor["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "Helmet", ESM::Armor::Helmet },
{ "Cuirass", ESM::Armor::Cuirass },
{ "LPauldron", ESM::Armor::LPauldron },
{ "RPauldron", ESM::Armor::RPauldron },
{ "Greaves", ESM::Armor::Greaves },
{ "Boots", ESM::Armor::Boots },
{ "LGauntlet", ESM::Armor::LGauntlet },
{ "RGauntlet", ESM::Armor::RGauntlet },
{ "Shield", ESM::Armor::Shield },
{ "LBracer", ESM::Armor::LBracer },
{ "RBracer", ESM::Armor::RBracer },
}));
sol::state_view lua = context.sol();
armor["TYPE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "Helmet", ESM::Armor::Helmet },
{ "Cuirass", ESM::Armor::Cuirass },
{ "LPauldron", ESM::Armor::LPauldron },
{ "RPauldron", ESM::Armor::RPauldron },
{ "Greaves", ESM::Armor::Greaves },
{ "Boots", ESM::Armor::Boots },
{ "LGauntlet", ESM::Armor::LGauntlet },
{ "RGauntlet", ESM::Armor::RGauntlet },
{ "Shield", ESM::Armor::Shield },
{ "LBracer", ESM::Armor::LBracer },
{ "RBracer", ESM::Armor::RBracer },
}));
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
addRecordFunctionBinding<ESM::Armor>(armor, context);
armor["createRecordDraft"] = tableToArmor;
sol::usertype<ESM::Armor> record = context.mLua->sol().new_usertype<ESM::Armor>("ESM3_Armor");
sol::usertype<ESM::Armor> record = lua.new_usertype<ESM::Armor>("ESM3_Armor");
record[sol::meta_function::to_string]
= [](const ESM::Armor& rec) -> std::string { return "ESM3_Armor[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -82,9 +82,10 @@ namespace MWLua
{
void addBookBindings(sol::table book, const Context& context)
{
sol::state_view lua = context.sol();
// types.book.SKILL is deprecated (core.SKILL should be used instead)
// TODO: Remove book.SKILL after branching 0.49
sol::table skill(context.mLua->sol(), sol::create);
sol::table skill(lua, sol::create);
book["SKILL"] = LuaUtil::makeStrictReadOnly(skill);
book["createRecordDraft"] = tableToBook;
for (int id = 0; id < ESM::Skill::Length; ++id)
@ -97,7 +98,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Book>(book, context);
sol::usertype<ESM::Book> record = context.mLua->sol().new_usertype<ESM::Book>("ESM3_Book");
sol::usertype<ESM::Book> record = lua.new_usertype<ESM::Book>("ESM3_Book");
record[sol::meta_function::to_string]
= [](const ESM::Book& rec) { return "ESM3_Book[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -66,24 +66,26 @@ namespace MWLua
{
clothing["createRecordDraft"] = tableToClothing;
clothing["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "Amulet", ESM::Clothing::Amulet },
{ "Belt", ESM::Clothing::Belt },
{ "LGlove", ESM::Clothing::LGlove },
{ "Pants", ESM::Clothing::Pants },
{ "RGlove", ESM::Clothing::RGlove },
{ "Ring", ESM::Clothing::Ring },
{ "Robe", ESM::Clothing::Robe },
{ "Shirt", ESM::Clothing::Shirt },
{ "Shoes", ESM::Clothing::Shoes },
{ "Skirt", ESM::Clothing::Skirt },
}));
sol::state_view lua = context.sol();
clothing["TYPE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "Amulet", ESM::Clothing::Amulet },
{ "Belt", ESM::Clothing::Belt },
{ "LGlove", ESM::Clothing::LGlove },
{ "Pants", ESM::Clothing::Pants },
{ "RGlove", ESM::Clothing::RGlove },
{ "Ring", ESM::Clothing::Ring },
{ "Robe", ESM::Clothing::Robe },
{ "Shirt", ESM::Clothing::Shirt },
{ "Shoes", ESM::Clothing::Shoes },
{ "Skirt", ESM::Clothing::Skirt },
}));
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
addRecordFunctionBinding<ESM::Clothing>(clothing, context);
sol::usertype<ESM::Clothing> record = context.mLua->sol().new_usertype<ESM::Clothing>("ESM3_Clothing");
sol::usertype<ESM::Clothing> record = lua.new_usertype<ESM::Clothing>("ESM3_Clothing");
record[sol::meta_function::to_string]
= [](const ESM::Clothing& rec) -> std::string { return "ESM3_Clothing[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -42,7 +42,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Container>(container, context);
sol::usertype<ESM::Container> record = context.mLua->sol().new_usertype<ESM::Container>("ESM3_Container");
sol::usertype<ESM::Container> record = context.sol().new_usertype<ESM::Container>("ESM3_Container");
record[sol::meta_function::to_string] = [](const ESM::Container& rec) -> std::string {
return "ESM3_Container[" + rec.mId.toDebugString() + "]";
};

@ -20,16 +20,18 @@ namespace MWLua
{
void addCreatureBindings(sol::table creature, const Context& context)
{
creature["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "Creatures", ESM::Creature::Creatures },
{ "Daedra", ESM::Creature::Daedra },
{ "Undead", ESM::Creature::Undead },
{ "Humanoid", ESM::Creature::Humanoid },
}));
sol::state_view lua = context.sol();
creature["TYPE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "Creatures", ESM::Creature::Creatures },
{ "Daedra", ESM::Creature::Daedra },
{ "Undead", ESM::Creature::Undead },
{ "Humanoid", ESM::Creature::Humanoid },
}));
addRecordFunctionBinding<ESM::Creature>(creature, context);
sol::usertype<ESM::Creature> record = context.mLua->sol().new_usertype<ESM::Creature>("ESM3_Creature");
sol::usertype<ESM::Creature> record = lua.new_usertype<ESM::Creature>("ESM3_Creature");
record[sol::meta_function::to_string]
= [](const ESM::Creature& rec) { return "ESM3_Creature[" + rec.mId.toDebugString() + "]"; };
record["id"]
@ -50,8 +52,7 @@ namespace MWLua
record["magicSkill"] = sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mMagic; });
record["stealthSkill"]
= sol::readonly_property([](const ESM::Creature& rec) -> int { return rec.mData.mStealth; });
record["attack"] = sol::readonly_property([context](const ESM::Creature& rec) -> sol::table {
sol::state_view& lua = context.mLua->sol();
record["attack"] = sol::readonly_property([](sol::this_state lua, const ESM::Creature& rec) -> sol::table {
sol::table res(lua, sol::create);
int index = 1;
for (auto attack : rec.mData.mAttack)

@ -41,11 +41,13 @@ namespace MWLua
void addDoorBindings(sol::table door, const Context& context)
{
door["STATE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWWorld::DoorState>({
{ "Idle", MWWorld::DoorState::Idle },
{ "Opening", MWWorld::DoorState::Opening },
{ "Closing", MWWorld::DoorState::Closing },
}));
sol::state_view lua = context.sol();
door["STATE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWWorld::DoorState>(lua,
{
{ "Idle", MWWorld::DoorState::Idle },
{ "Opening", MWWorld::DoorState::Opening },
{ "Closing", MWWorld::DoorState::Closing },
}));
door["getDoorState"] = [](const Object& o) -> MWWorld::DoorState {
const MWWorld::Ptr& door = doorPtr(o);
return door.getClass().getDoorState(door);
@ -99,7 +101,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Door>(door, context);
sol::usertype<ESM::Door> record = context.mLua->sol().new_usertype<ESM::Door>("ESM3_Door");
sol::usertype<ESM::Door> record = lua.new_usertype<ESM::Door>("ESM3_Door");
record[sol::meta_function::to_string]
= [](const ESM::Door& rec) -> std::string { return "ESM3_Door[" + rec.mId.toDebugString() + "]"; };
record["id"]
@ -136,7 +138,7 @@ namespace MWLua
addRecordFunctionBinding<ESM4::Door>(door, context, "ESM4Door");
sol::usertype<ESM4::Door> record = context.mLua->sol().new_usertype<ESM4::Door>("ESM4_Door");
sol::usertype<ESM4::Door> record = context.sol().new_usertype<ESM4::Door>("ESM4_Door");
record[sol::meta_function::to_string] = [](const ESM4::Door& rec) -> std::string {
return "ESM4_Door[" + ESM::RefId(rec.mId).toDebugString() + "]";
};

@ -25,7 +25,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Ingredient>(ingredient, context);
sol::usertype<ESM::Ingredient> record = context.mLua->sol().new_usertype<ESM::Ingredient>(("ESM3_Ingredient"));
sol::usertype<ESM::Ingredient> record = context.sol().new_usertype<ESM::Ingredient>(("ESM3_Ingredient"));
record[sol::meta_function::to_string]
= [](const ESM::Ingredient& rec) { return "ESM3_Ingredient[" + rec.mId.toDebugString() + "]"; };
record["id"]
@ -43,8 +43,8 @@ namespace MWLua
record["weight"]
= sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Ingredient& rec) -> int { return rec.mData.mValue; });
record["effects"] = sol::readonly_property([context](const ESM::Ingredient& rec) -> sol::table {
sol::table res(context.mLua->sol(), sol::create);
record["effects"] = sol::readonly_property([](sol::this_state lua, const ESM::Ingredient& rec) -> sol::table {
sol::table res(lua, sol::create);
for (size_t i = 0; i < 4; ++i)
{
if (rec.mData.mEffectID[i] < 0)

@ -23,7 +23,7 @@ namespace MWLua
{
void addLevelledCreatureBindings(sol::table list, const Context& context)
{
auto& state = context.mLua->sol();
auto state = context.sol();
auto item = state.new_usertype<ESM::LevelledListBase::LevelItem>("ESM3_LevelledListItem");
item["id"] = sol::readonly_property(
[](const ESM::LevelledListBase::LevelItem& rec) -> std::string { return rec.mId.serializeText(); });

@ -84,7 +84,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Light>(light, context);
light["createRecordDraft"] = tableToLight;
sol::usertype<ESM::Light> record = context.mLua->sol().new_usertype<ESM::Light>("ESM3_Light");
sol::usertype<ESM::Light> record = context.sol().new_usertype<ESM::Light>("ESM3_Light");
record[sol::meta_function::to_string]
= [](const ESM::Light& rec) -> std::string { return "ESM3_Light[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -24,7 +24,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Lockpick>(lockpick, context);
sol::usertype<ESM::Lockpick> record = context.mLua->sol().new_usertype<ESM::Lockpick>("ESM3_Lockpick");
sol::usertype<ESM::Lockpick> record = context.sol().new_usertype<ESM::Lockpick>("ESM3_Lockpick");
record[sol::meta_function::to_string]
= [](const ESM::Lockpick& rec) { return "ESM3_Lockpick[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -75,8 +75,7 @@ namespace MWLua
};
miscellaneous["soul"] = miscellaneous["getSoul"]; // for compatibility; should be removed later
sol::usertype<ESM::Miscellaneous> record
= context.mLua->sol().new_usertype<ESM::Miscellaneous>("ESM3_Miscellaneous");
sol::usertype<ESM::Miscellaneous> record = context.sol().new_usertype<ESM::Miscellaneous>("ESM3_Miscellaneous");
record[sol::meta_function::to_string]
= [](const ESM::Miscellaneous& rec) { return "ESM3_Miscellaneous[" + rec.mId.toDebugString() + "]"; };
record["id"] = sol::readonly_property(

@ -73,7 +73,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::NPC>(npc, context);
sol::state_view& lua = context.mLua->sol();
sol::state_view lua = context.sol();
sol::usertype<ESM::NPC> record = lua.new_usertype<ESM::NPC>("ESM3_NPC");
record[sol::meta_function::to_string]

@ -71,7 +71,8 @@ namespace MWLua
|| dynamic_cast<const SelfObject*>(&player) != nullptr;
return Quests{ .mMutable = allowChanges };
};
sol::usertype<Quests> quests = context.mLua->sol().new_usertype<Quests>("Quests");
sol::state_view lua = context.sol();
sol::usertype<Quests> quests = lua.new_usertype<Quests>("Quests");
quests[sol::meta_function::to_string] = [](const Quests& quests) { return "Quests"; };
quests[sol::meta_function::index] = [](const Quests& quests, std::string_view questId) -> sol::optional<Quest> {
ESM::RefId quest = ESM::RefId::deserializeText(questId);
@ -94,7 +95,7 @@ namespace MWLua
};
};
sol::usertype<Quest> quest = context.mLua->sol().new_usertype<Quest>("Quest");
sol::usertype<Quest> quest = lua.new_usertype<Quest>("Quest");
quest[sol::meta_function::to_string]
= [](const Quest& quest) { return "Quest[" + quest.mQuestId.serializeText() + "]"; };
@ -146,15 +147,16 @@ namespace MWLua
};
player["CONTROL_SWITCH"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, std::string_view>({
{ "Controls", "playercontrols" },
{ "Fighting", "playerfighting" },
{ "Jumping", "playerjumping" },
{ "Looking", "playerlooking" },
{ "Magic", "playermagic" },
{ "ViewMode", "playerviewswitch" },
{ "VanityMode", "vanitymode" },
}));
= LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, std::string_view>(lua,
{
{ "Controls", "playercontrols" },
{ "Fighting", "playerfighting" },
{ "Jumping", "playerjumping" },
{ "Looking", "playerlooking" },
{ "Magic", "playermagic" },
{ "ViewMode", "playerviewswitch" },
{ "VanityMode", "vanitymode" },
}));
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
player["getControlSwitch"] = [input](const Object& player, std::string_view key) {

@ -69,7 +69,7 @@ namespace MWLua
potion["createRecordDraft"] = tableToPotion;
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
sol::usertype<ESM::Potion> record = context.mLua->sol().new_usertype<ESM::Potion>("ESM3_Potion");
sol::usertype<ESM::Potion> record = context.sol().new_usertype<ESM::Potion>("ESM3_Potion");
record[sol::meta_function::to_string]
= [](const ESM::Potion& rec) { return "ESM3_Potion[" + rec.mId.toDebugString() + "]"; };
record["id"]
@ -84,8 +84,8 @@ namespace MWLua
[](const ESM::Potion& rec) -> sol::optional<std::string> { return LuaUtil::serializeRefId(rec.mScript); });
record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Potion& rec) -> int { return rec.mData.mValue; });
record["effects"] = sol::readonly_property([context](const ESM::Potion& rec) -> sol::table {
sol::table res(context.mLua->sol(), sol::create);
record["effects"] = sol::readonly_property([](sol::this_state lua, const ESM::Potion& rec) -> sol::table {
sol::table res(lua, sol::create);
for (size_t i = 0; i < rec.mEffects.mList.size(); ++i)
res[LuaUtil::toLuaIndex(i)] = rec.mEffects.mList[i]; // ESM::IndexedENAMstruct (effect params)
return res;

@ -24,7 +24,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Probe>(probe, context);
sol::usertype<ESM::Probe> record = context.mLua->sol().new_usertype<ESM::Probe>("ESM3_Probe");
sol::usertype<ESM::Probe> record = context.sol().new_usertype<ESM::Probe>("ESM3_Probe");
record[sol::meta_function::to_string]
= [](const ESM::Probe& rec) { return "ESM3_Probe[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -24,7 +24,7 @@ namespace MWLua
addRecordFunctionBinding<ESM::Repair>(repair, context);
sol::usertype<ESM::Repair> record = context.mLua->sol().new_usertype<ESM::Repair>("ESM3_Repair");
sol::usertype<ESM::Repair> record = context.sol().new_usertype<ESM::Repair>("ESM3_Repair");
record[sol::meta_function::to_string]
= [](const ESM::Repair& rec) { return "ESM3_Repair[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -19,7 +19,7 @@ namespace MWLua
{
addRecordFunctionBinding<ESM::Static>(stat, context);
sol::usertype<ESM::Static> record = context.mLua->sol().new_usertype<ESM::Static>("ESM3_Static");
sol::usertype<ESM::Static> record = context.sol().new_usertype<ESM::Static>("ESM3_Static");
record[sol::meta_function::to_string]
= [](const ESM::Static& rec) -> std::string { return "ESM3_Static[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -21,7 +21,7 @@ namespace MWLua
{
addRecordFunctionBinding<ESM4::Terminal>(term, context, "ESM4Terminal");
sol::usertype<ESM4::Terminal> record = context.mLua->sol().new_usertype<ESM4::Terminal>("ESM4_Terminal");
sol::usertype<ESM4::Terminal> record = context.sol().new_usertype<ESM4::Terminal>("ESM4_Terminal");
record[sol::meta_function::to_string] = [](const ESM4::Terminal& rec) -> std::string {
return "ESM4_Terminal[" + ESM::RefId(rec.mId).toDebugString() + "]";
};

@ -165,22 +165,22 @@ namespace MWLua
sol::table initTypesPackage(const Context& context)
{
auto* lua = context.mLua;
auto lua = context.sol();
if (lua->sol()["openmw_types"] != sol::nil)
return lua->sol()["openmw_types"];
if (lua["openmw_types"] != sol::nil)
return lua["openmw_types"];
sol::table types(lua->sol(), sol::create);
sol::table types(lua, sol::create);
auto addType = [&](std::string_view name, std::vector<ESM::RecNameInts> recTypes,
std::optional<std::string_view> base = std::nullopt) -> sol::table {
sol::table t(lua->sol(), sol::create);
sol::table t(lua, sol::create);
sol::table ro = LuaUtil::makeReadOnly(t);
sol::table meta = ro[sol::metatable_key];
meta[sol::meta_function::to_string] = [name]() { return name; };
if (base)
{
t["baseType"] = types[*base];
sol::table baseMeta(lua->sol(), sol::create);
sol::table baseMeta(lua, sol::create);
baseMeta[sol::meta_function::index] = LuaUtil::getMutableFromReadOnly(types[*base]);
t[sol::metatable_key] = baseMeta;
}
@ -251,8 +251,8 @@ namespace MWLua
addType(ObjectTypeName::ESM4Tree, { ESM::REC_TREE4 });
addType(ObjectTypeName::ESM4Weapon, { ESM::REC_WEAP4 });
sol::table typeToPackage = getTypeToPackageTable(context.mLua->sol());
sol::table packageToType = getPackageToTypeTable(context.mLua->sol());
sol::table typeToPackage = getTypeToPackageTable(lua);
sol::table packageToType = getPackageToTypeTable(lua);
for (const auto& [type, name] : luaObjectTypeInfo)
{
sol::object t = types[name];
@ -262,7 +262,7 @@ namespace MWLua
packageToType[t] = type;
}
lua->sol()["openmw_types"] = LuaUtil::makeReadOnly(types);
return lua->sol()["openmw_types"];
lua["openmw_types"] = LuaUtil::makeReadOnly(types);
return lua["openmw_types"];
}
}

@ -99,29 +99,31 @@ namespace MWLua
{
void addWeaponBindings(sol::table weapon, const Context& context)
{
weapon["TYPE"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, int>({
{ "ShortBladeOneHand", ESM::Weapon::ShortBladeOneHand },
{ "LongBladeOneHand", ESM::Weapon::LongBladeOneHand },
{ "LongBladeTwoHand", ESM::Weapon::LongBladeTwoHand },
{ "BluntOneHand", ESM::Weapon::BluntOneHand },
{ "BluntTwoClose", ESM::Weapon::BluntTwoClose },
{ "BluntTwoWide", ESM::Weapon::BluntTwoWide },
{ "SpearTwoWide", ESM::Weapon::SpearTwoWide },
{ "AxeOneHand", ESM::Weapon::AxeOneHand },
{ "AxeTwoHand", ESM::Weapon::AxeTwoHand },
{ "MarksmanBow", ESM::Weapon::MarksmanBow },
{ "MarksmanCrossbow", ESM::Weapon::MarksmanCrossbow },
{ "MarksmanThrown", ESM::Weapon::MarksmanThrown },
{ "Arrow", ESM::Weapon::Arrow },
{ "Bolt", ESM::Weapon::Bolt },
}));
sol::state_view lua = context.sol();
weapon["TYPE"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, int>(lua,
{
{ "ShortBladeOneHand", ESM::Weapon::ShortBladeOneHand },
{ "LongBladeOneHand", ESM::Weapon::LongBladeOneHand },
{ "LongBladeTwoHand", ESM::Weapon::LongBladeTwoHand },
{ "BluntOneHand", ESM::Weapon::BluntOneHand },
{ "BluntTwoClose", ESM::Weapon::BluntTwoClose },
{ "BluntTwoWide", ESM::Weapon::BluntTwoWide },
{ "SpearTwoWide", ESM::Weapon::SpearTwoWide },
{ "AxeOneHand", ESM::Weapon::AxeOneHand },
{ "AxeTwoHand", ESM::Weapon::AxeTwoHand },
{ "MarksmanBow", ESM::Weapon::MarksmanBow },
{ "MarksmanCrossbow", ESM::Weapon::MarksmanCrossbow },
{ "MarksmanThrown", ESM::Weapon::MarksmanThrown },
{ "Arrow", ESM::Weapon::Arrow },
{ "Bolt", ESM::Weapon::Bolt },
}));
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
addRecordFunctionBinding<ESM::Weapon>(weapon, context);
weapon["createRecordDraft"] = tableToWeapon;
sol::usertype<ESM::Weapon> record = context.mLua->sol().new_usertype<ESM::Weapon>("ESM3_Weapon");
sol::usertype<ESM::Weapon> record = lua.new_usertype<ESM::Weapon>("ESM3_Weapon");
record[sol::meta_function::to_string]
= [](const ESM::Weapon& rec) -> std::string { return "ESM3_Weapon[" + rec.mId.toDebugString() + "]"; };
record["id"]

@ -82,11 +82,12 @@ namespace MWLua
sol::table registerUiApi(const Context& context)
{
sol::state_view lua = context.sol();
bool menu = context.mType == Context::Menu;
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager();
sol::table api = context.mLua->newTable();
sol::table api(lua, sol::create);
api["_setHudVisibility"] = [luaManager = context.mLuaManager](bool state) {
luaManager->addAction([state] { MWBase::Environment::get().getWindowManager()->setHudVisibility(state); });
};
@ -107,12 +108,13 @@ namespace MWLua
}
luaManager->addUIMessage(message, mode);
};
api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string, Misc::Color>({
{ "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) },
{ "Error", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Error.substr(1)) },
{ "Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1)) },
{ "Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1)) },
}));
api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string, Misc::Color>(lua,
{
{ "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) },
{ "Error", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Error.substr(1)) },
{ "Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1)) },
{ "Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1)) },
}));
api["printToConsole"]
= [luaManager = context.mLuaManager](const std::string& message, const Misc::Color& color) {
luaManager->addInGameConsoleMessage(message + "\n", color);
@ -150,7 +152,7 @@ namespace MWLua
};
api["_getMenuTransparency"] = []() -> float { return Settings::gui().mMenuTransparency; };
sol::table layersTable = context.mLua->newTable();
sol::table layersTable(lua, sol::create);
layersTable["indexOf"] = [](std::string_view name) -> sol::optional<size_t> {
size_t index = LuaUi::Layer::indexOf(name);
if (index == LuaUi::Layer::count())
@ -202,14 +204,14 @@ namespace MWLua
}
api["layers"] = layers;
sol::table typeTable = context.mLua->newTable();
sol::table typeTable(lua, sol::create);
for (const auto& it : LuaUi::widgetTypeToName())
typeTable.set(it.second, it.first);
api["TYPE"] = LuaUtil::makeStrictReadOnly(typeTable);
api["ALIGNMENT"] = LuaUtil::makeStrictReadOnly(
context.mLua->tableFromPairs<std::string_view, LuaUi::Alignment>({ { "Start", LuaUi::Alignment::Start },
{ "Center", LuaUi::Alignment::Center }, { "End", LuaUi::Alignment::End } }));
api["ALIGNMENT"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, LuaUi::Alignment>(lua,
{ { "Start", LuaUi::Alignment::Start }, { "Center", LuaUi::Alignment::Center },
{ "End", LuaUi::Alignment::End } }));
api["registerSettingsPage"] = &LuaUi::registerSettingsPage;
api["removeSettingsPage"] = &LuaUi::removeSettingsPage;
@ -297,7 +299,7 @@ namespace MWLua
{
if (context.initializeOnce("openmw_ui_usertypes"))
{
auto element = context.mLua->sol().new_usertype<LuaUi::Element>("UiElement");
auto element = context.sol().new_usertype<LuaUi::Element>("UiElement");
element[sol::meta_function::to_string] = [](const LuaUi::Element& element) {
std::stringstream res;
res << "UiElement";
@ -321,7 +323,7 @@ namespace MWLua
[element] { wrapAction(element, [&] { LuaUi::Element::erase(element.get()); }); }, "Destroy UI");
};
auto uiLayer = context.mLua->sol().new_usertype<LuaUi::Layer>("UiLayer");
auto uiLayer = context.sol().new_usertype<LuaUi::Layer>("UiLayer");
uiLayer["name"]
= sol::readonly_property([](LuaUi::Layer& self) -> std::string_view { return self.name(); });
uiLayer["size"] = sol::readonly_property([](LuaUi::Layer& self) { return self.size(); });

@ -68,7 +68,7 @@ namespace MWLua
Log(Debug::Verbose) << "Read a large data chunk (" << size << " bytes) from '" << file.mFileName << "'.";
}
sol::object readFile(LuaUtil::LuaState* lua, FileHandle& file)
sol::object readFile(sol::this_state lua, FileHandle& file)
{
std::ostringstream os;
if (file.mFilePtr && file.mFilePtr->peek() != EOF)
@ -76,34 +76,34 @@ namespace MWLua
auto result = os.str();
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), std::move(result));
return sol::make_object<std::string>(lua, std::move(result));
}
sol::object readLineFromFile(LuaUtil::LuaState* lua, FileHandle& file)
sol::object readLineFromFile(sol::this_state lua, FileHandle& file)
{
std::string result;
if (file.mFilePtr && std::getline(*file.mFilePtr, result))
{
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result);
return sol::make_object<std::string>(lua, result);
}
return sol::nil;
}
sol::object readNumberFromFile(LuaUtil::LuaState* lua, Files::IStreamPtr& file)
sol::object readNumberFromFile(sol::this_state lua, Files::IStreamPtr& file)
{
double number = 0;
if (file && *file >> number)
return sol::make_object<double>(lua->sol(), number);
return sol::make_object<double>(lua, number);
return sol::nil;
}
sol::object readCharactersFromFile(LuaUtil::LuaState* lua, FileHandle& file, size_t count)
sol::object readCharactersFromFile(sol::this_state lua, FileHandle& file, size_t count)
{
if (count <= 0 && file.mFilePtr->peek() != EOF)
return sol::make_object<std::string>(lua->sol(), std::string());
return sol::make_object<std::string>(lua, std::string());
auto bytesLeft = getBytesLeftInStream(file.mFilePtr);
if (bytesLeft <= 0)
@ -116,7 +116,7 @@ namespace MWLua
if (file.mFilePtr->read(&result[0], count))
{
printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result);
return sol::make_object<std::string>(lua, result);
}
return sol::nil;
@ -131,7 +131,7 @@ namespace MWLua
}
sol::variadic_results seek(
LuaUtil::LuaState* lua, FileHandle& self, std::ios_base::seekdir dir, std::streamoff off)
sol::this_state lua, FileHandle& self, std::ios_base::seekdir dir, std::streamoff off)
{
sol::variadic_results values;
try
@ -141,16 +141,16 @@ namespace MWLua
{
auto msg = "Failed to seek in file '" + self.mFileName + "'";
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(lua, msg));
}
else
values.push_back(sol::make_object<std::streampos>(lua->sol(), self.mFilePtr->tellg()));
values.push_back(sol::make_object<std::streampos>(lua, self.mFilePtr->tellg()));
}
catch (std::exception& e)
{
auto msg = "Failed to seek in file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(lua, msg));
}
return values;
@ -159,18 +159,18 @@ namespace MWLua
sol::table initVFSPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::table api(context.mLua->unsafeState(), sol::create);
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
sol::usertype<FileHandle> handle = context.mLua->sol().new_usertype<FileHandle>("FileHandle");
sol::usertype<FileHandle> handle = context.sol().new_usertype<FileHandle>("FileHandle");
handle["fileName"]
= sol::readonly_property([](const FileHandle& self) -> std::string_view { return self.mFileName; });
handle[sol::meta_function::to_string] = [](const FileHandle& self) {
return "FileHandle{'" + self.mFileName + "'" + (!self.mFilePtr ? ", closed" : "") + "}";
};
handle["seek"] = sol::overload(
[lua = context.mLua](FileHandle& self, std::string_view whence, sol::optional<long> offset) {
[](sol::this_state lua, FileHandle& self, std::string_view whence, sol::optional<long> offset) {
validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0));
@ -178,21 +178,21 @@ namespace MWLua
return seek(lua, self, dir, off);
},
[lua = context.mLua](FileHandle& self, sol::optional<long> offset) {
[](sol::this_state lua, FileHandle& self, sol::optional<long> offset) {
validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0));
return seek(lua, self, std::ios_base::cur, off);
});
handle["lines"] = [lua = context.mLua](FileHandle& self) {
handle["lines"] = [](sol::this_state lua, FileHandle& self) {
return sol::as_function([&lua, &self]() mutable {
validateFile(self);
return readLineFromFile(lua, self);
});
};
api["lines"] = [lua = context.mLua, vfs](std::string_view fileName) {
api["lines"] = [vfs](sol::this_state lua, std::string_view fileName) {
auto normalizedName = VFS::Path::normalizeFilename(fileName);
return sol::as_function(
[lua, file = FileHandle(vfs->getNormalized(normalizedName), normalizedName)]() mutable {
@ -205,7 +205,7 @@ namespace MWLua
});
};
handle["close"] = [lua = context.mLua](FileHandle& self) {
handle["close"] = [](lua_State* L, FileHandle& self) {
sol::variadic_results values;
try
{
@ -214,22 +214,22 @@ namespace MWLua
{
auto msg = "Can not close file '" + self.mFileName + "': file handle is still opened.";
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(L, msg));
}
else
values.push_back(sol::make_object<bool>(lua->sol(), true));
values.push_back(sol::make_object<bool>(L, true));
}
catch (std::exception& e)
{
auto msg = "Can not close file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(L, msg));
}
return values;
};
handle["read"] = [lua = context.mLua](FileHandle& self, const sol::variadic_args args) {
handle["read"] = [](sol::this_state lua, FileHandle& self, const sol::variadic_args args) {
validateFile(self);
if (args.size() > sMaximumReadArguments)
@ -297,25 +297,25 @@ namespace MWLua
{
auto msg = "Error when handling '" + self.mFileName + "': can not read data for argument #"
+ std::to_string(i);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(lua, msg));
}
return values;
};
api["open"] = [lua = context.mLua, vfs](std::string_view fileName) {
api["open"] = [vfs](sol::this_state lua, std::string_view fileName) {
sol::variadic_results values;
try
{
auto normalizedName = VFS::Path::normalizeFilename(fileName);
auto handle = FileHandle(vfs->getNormalized(normalizedName), normalizedName);
values.push_back(sol::make_object<FileHandle>(lua->sol(), std::move(handle)));
values.push_back(sol::make_object<FileHandle>(lua, std::move(handle)));
}
catch (std::exception& e)
{
auto msg = "Can not open file: " + std::string(e.what());
values.push_back(sol::nil);
values.push_back(sol::make_object<std::string>(lua->sol(), msg));
values.push_back(sol::make_object<std::string>(lua, msg));
}
return values;

@ -102,7 +102,8 @@ namespace MWLua
const MWWorld::Store<ESM::Cell>* cells3Store = &MWBase::Environment::get().getESMStore()->get<ESM::Cell>();
const MWWorld::Store<ESM4::Cell>* cells4Store = &MWBase::Environment::get().getESMStore()->get<ESM4::Cell>();
sol::usertype<CellsStore> cells = context.mLua->sol().new_usertype<CellsStore>("Cells");
auto view = context.sol();
sol::usertype<CellsStore> cells = view.new_usertype<CellsStore>("Cells");
cells[sol::meta_function::length]
= [cells3Store, cells4Store](const CellsStore&) { return cells3Store->getSize() + cells4Store->getSize(); };
cells[sol::meta_function::index]
@ -124,14 +125,14 @@ namespace MWLua
cellRecord->mId, /*forceLoad=*/false) };
}
};
cells[sol::meta_function::pairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
cells[sol::meta_function::ipairs] = context.mLua->sol()["ipairsForArray"].template get<sol::function>();
cells[sol::meta_function::pairs] = view["ipairsForArray"].template get<sol::function>();
cells[sol::meta_function::ipairs] = view["ipairsForArray"].template get<sol::function>();
api["cells"] = CellsStore{};
}
sol::table initWorldPackage(const Context& context)
{
sol::table api(context.mLua->sol(), sol::create);
sol::table api(context.mLua->unsafeState(), sol::create);
addCoreTimeBindings(api, context);
addWorldTimeBindings(api, context);

@ -183,121 +183,125 @@ namespace LuaUtil
if (sProfilerEnabled)
lua_sethook(mLuaHolder.get(), &countHook, LUA_MASKCOUNT, countHookStep);
mSol.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::math, sol::lib::bit32, sol::lib::string,
sol::lib::table, sol::lib::os, sol::lib::debug);
protectedCall([&](LuaView& view) {
auto& sol = view.sol();
sol.open_libraries(sol::lib::base, sol::lib::coroutine, sol::lib::math, sol::lib::bit32, sol::lib::string,
sol::lib::table, sol::lib::os, sol::lib::debug);
#ifndef NO_LUAJIT
mSol.open_libraries(sol::lib::jit);
sol.open_libraries(sol::lib::jit);
#endif // NO_LUAJIT
mSol["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
mSol["math"]["randomseed"] = [] {};
sol["math"]["randomseed"](static_cast<unsigned>(std::time(nullptr)));
sol["math"]["randomseed"] = [] {};
mSol["utf8"] = LuaUtf8::initUtf8Package(mSol);
sol["utf8"] = LuaUtf8::initUtf8Package(sol);
mSol["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
sol["writeToLog"] = [](std::string_view s) { Log(Debug::Level::Info) << s; };
mSol["setEnvironment"]
= [](const sol::environment& env, const sol::function& fn) { sol::set_environment(env, fn); };
mSol["loadFromVFS"] = [this](std::string_view packageName) {
return loadScriptAndCache(packageNameToVfsPath(packageName, mVFS));
};
mSol["loadInternalLib"] = [this](std::string_view packageName) { return loadInternalLib(packageName); };
// Some fixes for compatibility between different Lua versions
if (mSol["unpack"] == sol::nil)
mSol["unpack"] = mSol["table"]["unpack"];
else if (mSol["table"]["unpack"] == sol::nil)
mSol["table"]["unpack"] = mSol["unpack"];
if (LUA_VERSION_NUM <= 501)
{
mSol.script(R"(
local _pairs = pairs
local _ipairs = ipairs
pairs = function(v) return (rawget(getmetatable(v) or {}, '__pairs') or _pairs)(v) end
ipairs = function(v) return (rawget(getmetatable(v) or {}, '__ipairs') or _ipairs)(v) end
)");
}
sol["setEnvironment"]
= [](const sol::environment& env, const sol::function& fn) { sol::set_environment(env, fn); };
sol["loadFromVFS"] = [this](std::string_view packageName) {
return loadScriptAndCache(packageNameToVfsPath(packageName, mVFS));
};
sol["loadInternalLib"] = [this](std::string_view packageName) { return loadInternalLib(packageName); };
// Some fixes for compatibility between different Lua versions
if (sol["unpack"] == sol::nil)
sol["unpack"] = sol["table"]["unpack"];
else if (sol["table"]["unpack"] == sol::nil)
sol["table"]["unpack"] = sol["unpack"];
if (LUA_VERSION_NUM <= 501)
{
sol.script(R"(
local _pairs = pairs
local _ipairs = ipairs
pairs = function(v) return (rawget(getmetatable(v) or {}, '__pairs') or _pairs)(v) end
ipairs = function(v) return (rawget(getmetatable(v) or {}, '__ipairs') or _ipairs)(v) end
)");
}
mSol.script(R"(
local printToLog = function(...)
local strs = {}
for i = 1, select('#', ...) do
strs[i] = tostring(select(i, ...))
sol.script(R"(
local printToLog = function(...)
local strs = {}
for i = 1, select('#', ...) do
strs[i] = tostring(select(i, ...))
end
return writeToLog(table.concat(strs, '\t'))
end
return writeToLog(table.concat(strs, '\t'))
end
printGen = function(name) return function(...) return printToLog(name, ...) end end
function requireGen(env, loaded, loadFn)
return function(packageName)
local p = loaded[packageName]
if p == nil then
local loader = loadFn(packageName)
setEnvironment(env, loader)
p = loader(packageName)
loaded[packageName] = p
printGen = function(name) return function(...) return printToLog(name, ...) end end
function requireGen(env, loaded, loadFn)
return function(packageName)
local p = loaded[packageName]
if p == nil then
local loader = loadFn(packageName)
setEnvironment(env, loader)
p = loader(packageName)
loaded[packageName] = p
end
return p
end
return p
end
end
function createStrictIndexFn(tbl)
return function(_, key)
local res = tbl[key]
if res ~= nil then
return res
else
error('Key not found: '..tostring(key), 2)
function createStrictIndexFn(tbl)
return function(_, key)
local res = tbl[key]
if res ~= nil then
return res
else
error('Key not found: '..tostring(key), 2)
end
end
end
end
function pairsForReadOnly(v)
local nextFn, t, firstKey = pairs(getmetatable(v).t)
return function(_, k) return nextFn(t, k) end, v, firstKey
end
function ipairsForReadOnly(v)
local nextFn, t, firstKey = ipairs(getmetatable(v).t)
return function(_, k) return nextFn(t, k) end, v, firstKey
end
function lenForReadOnly(v)
return #getmetatable(v).t
end
local function nextForArray(array, index)
index = (index or 0) + 1
if index <= #array then
return index, array[index]
function pairsForReadOnly(v)
local nextFn, t, firstKey = pairs(getmetatable(v).t)
return function(_, k) return nextFn(t, k) end, v, firstKey
end
end
function ipairsForArray(array)
return nextForArray, array, 0
end
getmetatable('').__metatable = false
getSafeMetatable = function(v)
if type(v) ~= 'table' then error('getmetatable is allowed only for tables', 2) end
return getmetatable(v)
end
)");
mSandboxEnv = sol::table(mSol, sol::create);
mSandboxEnv["_VERSION"] = mSol["_VERSION"];
for (const std::string& s : safeFunctions)
{
if (mSol[s] == sol::nil)
throw std::logic_error("Lua function not found: " + s);
mSandboxEnv[s] = mSol[s];
}
for (const std::string& s : safePackages)
{
if (mSol[s] == sol::nil)
throw std::logic_error("Lua package not found: " + s);
mCommonPackages[s] = mSandboxEnv[s] = makeReadOnly(mSol[s]);
}
mSandboxEnv["getmetatable"] = mSol["getSafeMetatable"];
mCommonPackages["os"] = mSandboxEnv["os"]
= makeReadOnly(tableFromPairs<std::string_view, sol::function>({ { "date", mSol["os"]["date"] },
{ "difftime", mSol["os"]["difftime"] }, { "time", mSol["os"]["time"] } }));
function ipairsForReadOnly(v)
local nextFn, t, firstKey = ipairs(getmetatable(v).t)
return function(_, k) return nextFn(t, k) end, v, firstKey
end
function lenForReadOnly(v)
return #getmetatable(v).t
end
local function nextForArray(array, index)
index = (index or 0) + 1
if index <= #array then
return index, array[index]
end
end
function ipairsForArray(array)
return nextForArray, array, 0
end
getmetatable('').__metatable = false
getSafeMetatable = function(v)
if type(v) ~= 'table' then error('getmetatable is allowed only for tables', 2) end
return getmetatable(v)
end
)");
mSandboxEnv = sol::table(sol, sol::create);
mSandboxEnv["_VERSION"] = sol["_VERSION"];
for (const std::string& s : safeFunctions)
{
if (sol[s] == sol::nil)
throw std::logic_error("Lua function not found: " + s);
mSandboxEnv[s] = sol[s];
}
for (const std::string& s : safePackages)
{
if (sol[s] == sol::nil)
throw std::logic_error("Lua package not found: " + s);
mCommonPackages[s] = mSandboxEnv[s] = makeReadOnly(sol[s]);
}
mSandboxEnv["getmetatable"] = sol["getSafeMetatable"];
mCommonPackages["os"] = mSandboxEnv["os"]
= makeReadOnly(tableFromPairs<std::string_view, sol::function>(sol,
{ { "date", sol["os"]["date"] }, { "difftime", sol["os"]["difftime"] },
{ "time", sol["os"]["time"] } }));
});
}
sol::table makeReadOnly(const sol::table& table, bool strictIndex)
@ -340,6 +344,7 @@ namespace LuaUtil
sol::protected_function_result LuaState::runInNewSandbox(const std::string& path, const std::string& namePrefix,
const std::map<std::string, sol::object>& packages, const sol::object& hiddenData)
{
// TODO
sol::protected_function script = loadScriptAndCache(path);
sol::environment env(mSol, sol::create, mSandboxEnv);
@ -373,6 +378,7 @@ namespace LuaUtil
sol::environment LuaState::newInternalLibEnvironment()
{
// TODO
sol::environment env(mSol, sol::create, mSandboxEnv);
sol::table loaded(mSol, sol::create);
for (const std::string& s : safePackages)

@ -34,6 +34,36 @@ namespace LuaUtil
bool mLogMemoryUsage = false;
};
class LuaState;
class LuaView
{
sol::state_view mSol;
LuaView(const LuaView&) = delete;
LuaView(lua_State* L)
: mSol(L)
{
}
public:
friend class LuaState;
// Returns underlying sol::state.
sol::state_view& sol() { return mSol; }
// A shortcut to create a new Lua table.
sol::table newTable() { return sol::table(mSol, sol::create); }
};
template <typename Key, typename Value>
sol::table tableFromPairs(lua_State* L, std::initializer_list<std::pair<Key, Value>> list)
{
sol::table res(L, sol::create);
for (const auto& [k, v] : list)
res[k] = v;
return res;
}
// Holds Lua state.
// Provides additional features:
// - Load scripts from the virtual filesystem;
@ -54,26 +84,53 @@ namespace LuaUtil
LuaState(const LuaState&) = delete;
LuaState(LuaState&&) = delete;
// Returns underlying sol::state.
sol::state_view& sol() { return mSol; }
// Pushing to the stack from outside a Lua context crashes the engine if no memory can be allocated to grow the
// stack
template <class Lambda>
[[nodiscard]] int invokeProtectedCall(Lambda&& f) const
{
if (!lua_checkstack(mSol.lua_state(), 2))
return LUA_ERRMEM;
lua_pushcfunction(mSol.lua_state(), [](lua_State* L) {
void* f = lua_touserdata(L, 1);
LuaView view(L);
(*static_cast<Lambda*>(f))(view);
return 0;
});
lua_pushlightuserdata(mSol.lua_state(), &f);
return lua_pcall(mSol.lua_state(), 1, 0, 0);
}
template <class Lambda>
void protectedCall(Lambda&& f) const
{
int result = invokeProtectedCall(std::forward<Lambda>(f));
switch (result)
{
case LUA_OK:
break;
case LUA_ERRMEM:
throw std::runtime_error("Lua error: out of memory");
case LUA_ERRRUN:
{
sol::optional<std::string> error = sol::stack::check_get<std::string>(mSol.lua_state());
if (error)
throw std::runtime_error(*error);
}
[[fallthrough]];
default:
throw std::runtime_error("Lua error: " + std::to_string(result));
}
}
// Note that constructing a sol::state_view is only safe from a Lua context. Use protectedCall to get one
lua_State* unsafeState() const { return mSol.lua_state(); }
// Can be used by a C++ function that is called from Lua to get the Lua traceback.
// Makes no sense if called not from Lua code.
// Note: It is a slow function, should be used for debug purposes only.
std::string debugTraceback() { return mSol["debug"]["traceback"]().get<std::string>(); }
// A shortcut to create a new Lua table.
sol::table newTable() { return sol::table(mSol, sol::create); }
template <typename Key, typename Value>
sol::table tableFromPairs(std::initializer_list<std::pair<Key, Value>> list)
{
sol::table res(mSol, sol::create);
for (const auto& [k, v] : list)
res[k] = v;
return res;
}
// Registers a package that will be available from every sandbox via `require(name)`.
// The package can be either a sol::table with an API or a sol::function. If it is a function,
// it will be evaluated (once per sandbox) the first time when requested. If the package

@ -24,8 +24,10 @@ namespace LuaUtil
{
sInstanceCount++;
registerEngineHandlers({ &mUpdateHandlers });
mPublicInterfaces = sol::table(lua->sol(), sol::create);
addPackage("openmw.interfaces", mPublicInterfaces);
lua->protectedCall([&](LuaView& view) {
mPublicInterfaces = sol::table(view.sol(), sol::create);
addPackage("openmw.interfaces", mPublicInterfaces);
});
}
void ScriptsContainer::printError(int scriptId, std::string_view msg, const std::exception& e)
@ -41,26 +43,31 @@ namespace LuaUtil
bool ScriptsContainer::addCustomScript(int scriptId, std::string_view initData)
{
assert(mLua.getConfiguration().isCustomScript(scriptId));
std::optional<sol::function> onInit, onLoad;
bool ok = addScript(scriptId, onInit, onLoad);
if (ok && onInit)
callOnInit(scriptId, *onInit, initData);
bool ok = false;
mLua.protectedCall([&](LuaView& view) {
std::optional<sol::function> onInit, onLoad;
ok = addScript(view, scriptId, onInit, onLoad);
if (ok && onInit)
callOnInit(view, scriptId, *onInit, initData);
});
return ok;
}
void ScriptsContainer::addAutoStartedScripts()
{
for (const auto& [scriptId, data] : mAutoStartScripts)
{
std::optional<sol::function> onInit, onLoad;
bool ok = addScript(scriptId, onInit, onLoad);
if (ok && onInit)
callOnInit(scriptId, *onInit, data);
}
mLua.protectedCall([&](LuaView& view) {
for (const auto& [scriptId, data] : mAutoStartScripts)
{
std::optional<sol::function> onInit, onLoad;
bool ok = addScript(view, scriptId, onInit, onLoad);
if (ok && onInit)
callOnInit(view, scriptId, *onInit, data);
}
});
}
bool ScriptsContainer::addScript(
int scriptId, std::optional<sol::function>& onInit, std::optional<sol::function>& onLoad)
LuaView& view, int scriptId, std::optional<sol::function>& onInit, std::optional<sol::function>& onLoad)
{
assert(scriptId >= 0 && scriptId < static_cast<int>(mLua.getConfiguration().size()));
if (mScripts.count(scriptId) != 0)
@ -73,7 +80,7 @@ namespace LuaUtil
debugName.push_back(']');
Script& script = mScripts[scriptId];
script.mHiddenData = mLua.newTable();
script.mHiddenData = view.newTable();
script.mHiddenData[sScriptIdKey] = ScriptId{ this, scriptId };
script.mHiddenData[sScriptDebugNameKey] = debugName;
script.mPath = path;
@ -298,32 +305,34 @@ namespace LuaUtil
auto it = mEventHandlers.find(eventName);
if (it == mEventHandlers.end())
return;
sol::object data;
try
{
data = LuaUtil::deserialize(mLua.sol(), eventData, mSerializer);
}
catch (std::exception& e)
{
Log(Debug::Error) << mNamePrefix << " can not parse eventData for '" << eventName << "': " << e.what();
return;
}
EventHandlerList& list = it->second;
for (int i = list.size() - 1; i >= 0; --i)
{
const Handler& h = list[i];
mLua.protectedCall([&](LuaView& view) {
sol::object data;
try
{
sol::object res = LuaUtil::call({ this, h.mScriptId }, h.mFn, data);
if (res.is<bool>() && !res.as<bool>())
break; // Skip other handlers if 'false' was returned.
data = LuaUtil::deserialize(view.sol(), eventData, mSerializer);
}
catch (std::exception& e)
{
Log(Debug::Error) << mNamePrefix << "[" << scriptPath(h.mScriptId) << "] eventHandler[" << eventName
<< "] failed. " << e.what();
Log(Debug::Error) << mNamePrefix << " can not parse eventData for '" << eventName << "': " << e.what();
return;
}
}
EventHandlerList& list = it->second;
for (int i = list.size() - 1; i >= 0; --i)
{
const Handler& h = list[i];
try
{
sol::object res = LuaUtil::call({ this, h.mScriptId }, h.mFn, data);
if (res.is<bool>() && !res.as<bool>())
break; // Skip other handlers if 'false' was returned.
}
catch (std::exception& e)
{
Log(Debug::Error) << mNamePrefix << "[" << scriptPath(h.mScriptId) << "] eventHandler[" << eventName
<< "] failed. " << e.what();
}
}
});
}
void ScriptsContainer::registerEngineHandlers(std::initializer_list<EngineHandlerList*> handlers)
@ -332,11 +341,11 @@ namespace LuaUtil
mEngineHandlers[h->mName] = h;
}
void ScriptsContainer::callOnInit(int scriptId, const sol::function& onInit, std::string_view data)
void ScriptsContainer::callOnInit(LuaView& view, int scriptId, const sol::function& onInit, std::string_view data)
{
try
{
LuaUtil::call({ this, scriptId }, onInit, deserialize(mLua.sol(), data, mSerializer));
LuaUtil::call({ this, scriptId }, onInit, deserialize(view.sol(), data, mSerializer));
}
catch (std::exception& e)
{
@ -418,57 +427,61 @@ namespace LuaUtil
<< "]; this script is not allowed here";
}
for (const auto& [scriptId, scriptInfo] : scripts)
{
std::optional<sol::function> onInit, onLoad;
if (!addScript(scriptId, onInit, onLoad))
continue;
if (scriptInfo.mSavedData == nullptr)
mLua.protectedCall([&](LuaView& view) {
for (const auto& [scriptId, scriptInfo] : scripts)
{
if (onInit)
callOnInit(scriptId, *onInit, scriptInfo.mInitData);
continue;
}
if (onLoad)
{
try
std::optional<sol::function> onInit, onLoad;
if (!addScript(view, scriptId, onInit, onLoad))
continue;
if (scriptInfo.mSavedData == nullptr)
{
sol::object state = deserialize(mLua.sol(), scriptInfo.mSavedData->mData, mSavedDataDeserializer);
sol::object initializationData = deserialize(mLua.sol(), scriptInfo.mInitData, mSerializer);
LuaUtil::call({ this, scriptId }, *onLoad, state, initializationData);
if (onInit)
callOnInit(view, scriptId, *onInit, scriptInfo.mInitData);
continue;
}
catch (std::exception& e)
{
printError(scriptId, "onLoad failed", e);
}
}
for (const ESM::LuaTimer& savedTimer : scriptInfo.mSavedData->mTimers)
{
Timer timer;
timer.mCallback = savedTimer.mCallbackName;
timer.mSerializable = true;
timer.mScriptId = scriptId;
timer.mTime = savedTimer.mTime;
try
if (onLoad)
{
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
// updates refnums, so timer.mSerializedArg may be not equal to savedTimer.mCallbackArgument.
timer.mSerializedArg = serialize(timer.mArg, mSerializer);
if (savedTimer.mType == TimerType::GAME_TIME)
mGameTimersQueue.push_back(std::move(timer));
else
mSimulationTimersQueue.push_back(std::move(timer));
try
{
sol::object state
= deserialize(view.sol(), scriptInfo.mSavedData->mData, mSavedDataDeserializer);
sol::object initializationData = deserialize(view.sol(), scriptInfo.mInitData, mSerializer);
LuaUtil::call({ this, scriptId }, *onLoad, state, initializationData);
}
catch (std::exception& e)
{
printError(scriptId, "onLoad failed", e);
}
}
catch (std::exception& e)
for (const ESM::LuaTimer& savedTimer : scriptInfo.mSavedData->mTimers)
{
printError(scriptId, "can not load timer", e);
Timer timer;
timer.mCallback = savedTimer.mCallbackName;
timer.mSerializable = true;
timer.mScriptId = scriptId;
timer.mTime = savedTimer.mTime;
try
{
timer.mArg = sol::main_object(
deserialize(view.sol(), savedTimer.mCallbackArgument, mSavedDataDeserializer));
// 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.
timer.mSerializedArg = serialize(timer.mArg, mSerializer);
if (savedTimer.mType == TimerType::GAME_TIME)
mGameTimersQueue.push_back(std::move(timer));
else
mSimulationTimersQueue.push_back(std::move(timer));
}
catch (std::exception& e)
{
printError(scriptId, "can not load timer", e);
}
}
}
}
});
std::make_heap(mSimulationTimersQueue.begin(), mSimulationTimersQueue.end());
std::make_heap(mGameTimersQueue.begin(), mGameTimersQueue.end());

@ -232,14 +232,15 @@ namespace LuaUtil
void addMemoryUsage(int scriptId, int64_t memoryDelta);
// Add to container without calling onInit/onLoad.
bool addScript(int scriptId, std::optional<sol::function>& onInit, std::optional<sol::function>& onLoad);
bool addScript(
LuaView& view, int scriptId, std::optional<sol::function>& onInit, std::optional<sol::function>& onLoad);
// Returns script by id (throws an exception if doesn't exist)
Script& getScript(int scriptId);
void printError(int scriptId, std::string_view msg, const std::exception& e);
const std::string& scriptPath(int scriptId) const { return mLua.getConfiguration()[scriptId].mScriptPath; }
void callOnInit(int scriptId, const sol::function& onInit, std::string_view data);
void callOnInit(LuaView& view, int scriptId, const sol::function& onInit, std::string_view data);
void callTimer(const Timer& t);
void updateTimerQueue(std::vector<Timer>& timerQueue, double time);
static void insertTimer(std::vector<Timer>& timerQueue, Timer&& t);

@ -5,6 +5,8 @@
#include <components/debug/debuglog.hpp>
#include "luastate.hpp"
namespace sol
{
template <>
@ -17,13 +19,14 @@ namespace LuaUtil
{
LuaStorage::Value LuaStorage::Section::sEmpty;
void LuaStorage::registerLifeTime(LuaUtil::LuaState& luaState, sol::table& res)
void LuaStorage::registerLifeTime(LuaUtil::LuaView& view, sol::table& res)
{
res["LIFE_TIME"] = LuaUtil::makeStrictReadOnly(luaState.tableFromPairs<std::string_view, Section::LifeTime>({
{ "Persistent", Section::LifeTime::Persistent },
{ "GameSession", Section::LifeTime::GameSession },
{ "Temporary", Section::LifeTime::Temporary },
}));
res["LIFE_TIME"] = LuaUtil::makeStrictReadOnly(tableFromPairs<std::string_view, Section::LifeTime>(view.sol(),
{
{ "Persistent", Section::LifeTime::Persistent },
{ "GameSession", Section::LifeTime::GameSession },
{ "Temporary", Section::LifeTime::Temporary },
}));
}
sol::object LuaStorage::Value::getCopy(lua_State* L) const
@ -112,26 +115,26 @@ namespace LuaUtil
runCallbacks(sol::nullopt);
}
sol::table LuaStorage::Section::asTable()
sol::table LuaStorage::Section::asTable(lua_State* L)
{
checkIfActive();
sol::table res(mStorage->mLua, sol::create);
sol::table res(L, sol::create);
for (const auto& [k, v] : mValues)
res[k] = v.getCopy(mStorage->mLua);
res[k] = v.getCopy(L);
return res;
}
void LuaStorage::initLuaBindings(lua_State* L)
void LuaStorage::initLuaBindings(LuaUtil::LuaView& view)
{
sol::state_view lua(L);
sol::usertype<SectionView> sview = lua.new_usertype<SectionView>("Section");
sol::usertype<SectionView> sview = view.sol().new_usertype<SectionView>("Section");
sview["get"] = [](sol::this_state s, const SectionView& section, std::string_view key) {
return section.mSection->get(key).getReadOnly(s);
};
sview["getCopy"] = [](sol::this_state s, const SectionView& section, std::string_view key) {
return section.mSection->get(key).getCopy(s);
};
sview["asTable"] = [](const SectionView& section) { return section.mSection->asTable(); };
sview["asTable"]
= [](sol::this_state lua, const SectionView& section) { return section.mSection->asTable(lua); };
sview["subscribe"] = [](const SectionView& section, const sol::table& callback) {
std::vector<Callback>& callbacks
= section.mForMenuScripts ? section.mSection->mMenuScriptsCallbacks : section.mSection->mCallbacks;
@ -165,53 +168,57 @@ namespace LuaUtil
};
}
sol::table LuaStorage::initGlobalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage)
sol::table LuaStorage::initGlobalPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage)
{
sol::table res(luaState.sol(), sol::create);
registerLifeTime(luaState, res);
sol::table res(view.sol(), sol::create);
registerLifeTime(view, res);
res["globalSection"]
= [globalStorage](std::string_view section) { return globalStorage->getMutableSection(section); };
res["allGlobalSections"] = [globalStorage]() { return globalStorage->getAllSections(); };
res["globalSection"] = [globalStorage](sol::this_state lua, std::string_view section) {
return globalStorage->getMutableSection(lua, section);
};
res["allGlobalSections"] = [globalStorage](sol::this_state lua) { return globalStorage->getAllSections(lua); };
return LuaUtil::makeReadOnly(res);
}
sol::table LuaStorage::initLocalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage)
sol::table LuaStorage::initLocalPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage)
{
sol::table res(luaState.sol(), sol::create);
registerLifeTime(luaState, res);
sol::table res(view.sol(), sol::create);
registerLifeTime(view, res);
res["globalSection"]
= [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
res["globalSection"] = [globalStorage](sol::this_state lua, std::string_view section) {
return globalStorage->getReadOnlySection(lua, section);
};
return LuaUtil::makeReadOnly(res);
}
sol::table LuaStorage::initPlayerPackage(
LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage)
LuaUtil::LuaView& view, LuaStorage* globalStorage, LuaStorage* playerStorage)
{
sol::table res(luaState.sol(), sol::create);
registerLifeTime(luaState, res);
sol::table res(view.sol(), sol::create);
registerLifeTime(view, res);
res["globalSection"]
= [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
res["playerSection"]
= [playerStorage](std::string_view section) { return playerStorage->getMutableSection(section); };
res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); };
res["globalSection"] = [globalStorage](sol::this_state lua, std::string_view section) {
return globalStorage->getReadOnlySection(lua, section);
};
res["playerSection"] = [playerStorage](sol::this_state lua, std::string_view section) {
return playerStorage->getMutableSection(lua, section);
};
res["allPlayerSections"] = [playerStorage](sol::this_state lua) { return playerStorage->getAllSections(lua); };
return LuaUtil::makeReadOnly(res);
}
sol::table LuaStorage::initMenuPackage(
LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage)
sol::table LuaStorage::initMenuPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage, LuaStorage* playerStorage)
{
sol::table res(luaState.sol(), sol::create);
registerLifeTime(luaState, res);
sol::table res(view.sol(), sol::create);
registerLifeTime(view, res);
res["playerSection"] = [playerStorage](std::string_view section) {
return playerStorage->getMutableSection(section, /*forMenuScripts=*/true);
res["playerSection"] = [playerStorage](sol::this_state lua, std::string_view section) {
return playerStorage->getMutableSection(lua, section, /*forMenuScripts=*/true);
};
res["globalSection"] = [globalStorage](sol::this_state lua, std::string_view section) {
return globalStorage->getReadOnlySection(lua, section);
};
res["globalSection"]
= [globalStorage](std::string_view section) { return globalStorage->getReadOnlySection(section); };
res["allPlayerSections"] = [playerStorage]() { return playerStorage->getAllSections(); };
res["allPlayerSections"] = [playerStorage](sol::this_state lua) { return playerStorage->getAllSections(lua); };
return LuaUtil::makeReadOnly(res);
}
@ -234,7 +241,7 @@ namespace LuaUtil
}
}
void LuaStorage::load(const std::filesystem::path& path)
void LuaStorage::load(lua_State* L, const std::filesystem::path& path)
{
assert(mData.empty()); // Shouldn't be used before loading
try
@ -246,7 +253,7 @@ namespace LuaUtil
std::ifstream fin(path, std::fstream::binary);
std::string serializedData((std::istreambuf_iterator<char>(fin)), std::istreambuf_iterator<char>());
sol::table data = deserialize(mLua, serializedData);
sol::table data = deserialize(L, serializedData);
for (const auto& [sectionName, sectionTable] : data)
{
const std::shared_ptr<Section>& section = getSection(cast<std::string_view>(sectionName));
@ -260,13 +267,13 @@ namespace LuaUtil
}
}
void LuaStorage::save(const std::filesystem::path& path) const
void LuaStorage::save(lua_State* L, const std::filesystem::path& path) const
{
sol::table data(mLua, sol::create);
sol::table data(L, sol::create);
for (const auto& [sectionName, section] : mData)
{
if (section->mLifeTime == Section::Persistent && !section->mValues.empty())
data[sectionName] = section->asTable();
data[sectionName] = section->asTable(L);
}
std::string serializedData = serialize(data);
Log(Debug::Info) << "Saving Lua storage \"" << path << "\" (" << serializedData.size() << " bytes)";
@ -287,19 +294,19 @@ namespace LuaUtil
return newIt->second;
}
sol::object LuaStorage::getSection(std::string_view sectionName, bool readOnly, bool forMenuScripts)
sol::object LuaStorage::getSection(lua_State* L, std::string_view sectionName, bool readOnly, bool forMenuScripts)
{
checkIfActive();
const std::shared_ptr<Section>& section = getSection(sectionName);
return sol::make_object<SectionView>(mLua, SectionView{ section, readOnly, forMenuScripts });
return sol::make_object<SectionView>(L, SectionView{ section, readOnly, forMenuScripts });
}
sol::table LuaStorage::getAllSections(bool readOnly)
sol::table LuaStorage::getAllSections(lua_State* L, bool readOnly)
{
checkIfActive();
sol::table res(mLua, sol::create);
sol::table res(L, sol::create);
for (const auto& [sectionName, _] : mData)
res[sectionName] = getSection(sectionName, readOnly);
res[sectionName] = getSection(L, sectionName, readOnly);
return res;
}

@ -10,35 +10,34 @@
namespace LuaUtil
{
class LuaView;
class LuaStorage
{
public:
static void initLuaBindings(lua_State* L);
static sol::table initGlobalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage);
static sol::table initLocalPackage(LuaUtil::LuaState& luaState, LuaStorage* globalStorage);
static void initLuaBindings(LuaUtil::LuaView& view);
static sol::table initGlobalPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage);
static sol::table initLocalPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage);
static sol::table initPlayerPackage(
LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage);
static sol::table initMenuPackage(
LuaUtil::LuaState& luaState, LuaStorage* globalStorage, LuaStorage* playerStorage);
LuaUtil::LuaView& view, LuaStorage* globalStorage, LuaStorage* playerStorage);
static sol::table initMenuPackage(LuaUtil::LuaView& view, LuaStorage* globalStorage, LuaStorage* playerStorage);
explicit LuaStorage(lua_State* lua)
: mLua(lua)
, mActive(false)
{
}
explicit LuaStorage() {}
void clearTemporaryAndRemoveCallbacks();
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path) const;
void load(lua_State* L, const std::filesystem::path& path);
void save(lua_State* L, const std::filesystem::path& path) const;
sol::object getSection(std::string_view sectionName, bool readOnly, bool forMenuScripts = false);
sol::object getMutableSection(std::string_view sectionName, bool forMenuScripts = false)
sol::object getSection(lua_State* L, std::string_view sectionName, bool readOnly, bool forMenuScripts = false);
sol::object getMutableSection(lua_State* L, std::string_view sectionName, bool forMenuScripts = false)
{
return getSection(L, sectionName, false, forMenuScripts);
}
sol::object getReadOnlySection(lua_State* L, std::string_view sectionName)
{
return getSection(sectionName, false, forMenuScripts);
return getSection(L, sectionName, true);
}
sol::object getReadOnlySection(std::string_view sectionName) { return getSection(sectionName, true); }
sol::table getAllSections(bool readOnly = false);
sol::table getAllSections(lua_State* L, bool readOnly = false);
void setSingleValue(std::string_view section, std::string_view key, const sol::object& value)
{
@ -95,7 +94,7 @@ namespace LuaUtil
const Value& get(std::string_view key) const;
void set(std::string_view key, const sol::object& value);
void setAll(const sol::optional<sol::table>& values);
sol::table asTable();
sol::table asTable(lua_State* L);
void runCallbacks(sol::optional<std::string_view> changedKey);
void throwIfCallbackRecursionIsTooDeep();
@ -119,17 +118,16 @@ namespace LuaUtil
const std::shared_ptr<Section>& getSection(std::string_view sectionName);
lua_State* mLua;
std::map<std::string_view, std::shared_ptr<Section>> mData;
const Listener* mListener = nullptr;
std::set<const Section*> mRunningCallbacks;
bool mActive;
bool mActive = false;
void checkIfActive() const
{
if (!mActive)
throw std::logic_error("Trying to access inactive storage");
}
static void registerLifeTime(LuaUtil::LuaState& luaState, sol::table& res);
static void registerLifeTime(LuaUtil::LuaView& view, sol::table& res);
};
}

Loading…
Cancel
Save