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

@ -81,84 +81,87 @@ you_have_arrows: "Arrows count: {count}"
TEST_F(LuaL10nTest, L10n) TEST_F(LuaL10nTest, L10n)
{ {
LuaUtil::LuaState lua{ mVFS.get(), &mCfg }; LuaUtil::LuaState lua{ mVFS.get(), &mCfg };
sol::state_view& l = lua.sol(); lua.protectedCall([&](LuaUtil::LuaView& view) {
internal::CaptureStdout(); sol::state_view& l = view.sol();
l10n::Manager l10nManager(mVFS.get()); internal::CaptureStdout();
l10nManager.setPreferredLocales({ "de", "en" }); l10n::Manager l10nManager(mVFS.get());
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n"); l10nManager.setPreferredLocales({ "de", "en" });
EXPECT_THAT(internal::GetCapturedStdout(), "Preferred locales: gmst de en\n");
l["l10n"] = LuaUtil::initL10nLoader(l, &l10nManager);
l["l10n"] = LuaUtil::initL10nLoader(l, &l10nManager);
internal::CaptureStdout();
l.safe_script("t1 = l10n('Test1')"); internal::CaptureStdout();
EXPECT_THAT(internal::GetCapturedStdout(), l.safe_script("t1 = l10n('Test1')");
"Language file \"l10n/Test1/de.yaml\" is enabled\n" EXPECT_THAT(internal::GetCapturedStdout(),
"Language file \"l10n/Test1/en.yaml\" is enabled\n"); "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')"); 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")); 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('good_morning')"), "Guten Morgen.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "Du hast 5 Pfeile."); EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "Du hast ein Pfeil.");
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!"); EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "Du hast 5 Pfeile.");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!"); EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3"); 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" }); internal::CaptureStdout();
EXPECT_THAT(internal::GetCapturedStdout(), l10nManager.setPreferredLocales({ "en", "de" });
"Preferred locales: gmst en de\n" EXPECT_THAT(internal::GetCapturedStdout(),
"Language file \"l10n/Test1/en.yaml\" is enabled\n" "Preferred locales: gmst en de\n"
"Language file \"l10n/Test1/de.yaml\" is enabled\n" "Language file \"l10n/Test1/en.yaml\" is enabled\n"
"Language file \"l10n/Test2/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('good_morning')"), "Good morning.");
EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=5})"), "You have 5 arrows."); EXPECT_EQ(get<std::string>(l, "t1('you_have_arrows', {count=1})"), "You have one arrow.");
EXPECT_EQ(get<std::string>(l, "t1('pc_must_come', {PCGender=\"male\"})"), "He is coming with us."); 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=\"female\"})"), "She is coming with us."); 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=\"blah\"})"), "They are 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=\"other\"})"), "They are 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('quest_completion', {done=0.1})"), "The quest is 10% complete."); 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=1})"), "The quest is 100% complete."); EXPECT_EQ(get<std::string>(l, "t1('quest_completion', {done=0.1})"), "The quest is 10% complete.");
EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=1})"), "You came in 1st place."); 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=100})"), "You came in 100th place."); EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=1})"), "You came in 1st place.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=1})"), "There is one thing."); EXPECT_EQ(get<std::string>(l, "t1('ordinal', {num=100})"), "You came in 100th place.");
EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=100})"), "There are one hundred things."); EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=1})"), "There is one thing.");
EXPECT_EQ(get<std::string>(l, "t1('duration', {num=100})"), "It took 1:40"); EXPECT_EQ(get<std::string>(l, "t1('spellout', {num=100})"), "There are one hundred things.");
EXPECT_EQ(get<std::string>(l, "t1('numbers', {int=123, double=123.456})"), EXPECT_EQ(get<std::string>(l, "t1('duration', {num=100})"), "It took 1:40");
"123 and 123 are integers, but 123.456 is a double"); EXPECT_EQ(get<std::string>(l, "t1('numbers', {int=123, double=123.456})"),
EXPECT_EQ(get<std::string>(l, "t1('rounding', {value=123.456789})"), "123.46"); "123 and 123 are integers, but 123.456 is a double");
// Check that failed messages display the key instead of an empty string EXPECT_EQ(get<std::string>(l, "t1('rounding', {value=123.456789})"), "123.46");
EXPECT_EQ(get<std::string>(l, "t1('{mismatched_braces')"), "{mismatched_braces"); // Check that failed messages display the key instead of an empty string
EXPECT_EQ(get<std::string>(l, "t1('{unknown_arg}')"), "{unknown_arg}"); EXPECT_EQ(get<std::string>(l, "t1('{mismatched_braces')"), "{mismatched_braces");
EXPECT_EQ(get<std::string>(l, "t1('{num, integer}', {num=1})"), "{num, integer}"); EXPECT_EQ(get<std::string>(l, "t1('{unknown_arg}')"), "{unknown_arg}");
// Doesn't give a valid currency symbol with `en`. Not that openmw is designed for real world currency. EXPECT_EQ(get<std::string>(l, "t1('{num, integer}', {num=1})"), "{num, integer}");
l10nManager.setPreferredLocales({ "en-US", "de" }); // Doesn't give a valid currency symbol with `en`. Not that openmw is designed for real world currency.
EXPECT_EQ(get<std::string>(l, "t1('currency', {money=10000.10})"), "You have $10,000.10"); l10nManager.setPreferredLocales({ "en-US", "de" });
// 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('currency', {money=10000.10})"), "You have $10,000.10");
EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!"); // Note: Not defined in English localisation file, so we fall back to the German before falling back to the
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!"); // key
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3"); EXPECT_EQ(get<std::string>(l, "t1('Hello {name}!', {name='World'})"), "Hallo World!");
EXPECT_EQ(get<std::string>(l, "t2('good_morning')"), "Morning!");
// Test that locales with variants and country codes fall back to more generic locales EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
internal::CaptureStdout();
l10nManager.setPreferredLocales({ "en-GB-oed", "de" }); // Test that locales with variants and country codes fall back to more generic locales
EXPECT_THAT(internal::GetCapturedStdout(), internal::CaptureStdout();
"Preferred locales: gmst en_GB_OED de\n" l10nManager.setPreferredLocales({ "en-GB-oed", "de" });
"Language file \"l10n/Test1/en.yaml\" is enabled\n" EXPECT_THAT(internal::GetCapturedStdout(),
"Language file \"l10n/Test1/de.yaml\" is enabled\n" "Preferred locales: gmst en_GB_OED de\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n"); "Language file \"l10n/Test1/en.yaml\" is enabled\n"
EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3"); "Language file \"l10n/Test1/de.yaml\" is enabled\n"
"Language file \"l10n/Test2/en.yaml\" is enabled\n");
// Test setting fallback language EXPECT_EQ(get<std::string>(l, "t2('you_have_arrows', {count=3})"), "Arrows count: 3");
l.safe_script("t3 = l10n('Test3', 'de')");
l10nManager.setPreferredLocales({ "en" }); // Test setting fallback language
EXPECT_EQ(get<std::string>(l, "t3('Hello {name}!', {name='World'})"), "Hallo World!"); 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) 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.unsafeState(), 3.14)), "3.14");
EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.sol(), true)), "true"); EXPECT_EQ(LuaUtil::toString(sol::make_object(mLua.unsafeState(), true)), "true");
EXPECT_EQ(LuaUtil::toString(sol::nil), "nil"); 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) TEST_F(LuaStateTest, Cast)
{ {
EXPECT_EQ(LuaUtil::cast<int>(sol::make_object(mLua.sol(), 3.14)), 3); EXPECT_EQ(LuaUtil::cast<int>(sol::make_object(mLua.unsafeState(), 3.14)), 3);
EXPECT_ERROR( EXPECT_ERROR(LuaUtil::cast<int>(sol::make_object(mLua.unsafeState(), "3.14")),
LuaUtil::cast<int>(sol::make_object(mLua.sol(), "3.14")), "Value \"\"3.14\"\" can not be casted to int"); "Value \"\"3.14\"\" can not be casted to int");
EXPECT_ERROR(LuaUtil::cast<std::string_view>(sol::make_object(mLua.sol(), sol::nil)), EXPECT_ERROR(LuaUtil::cast<std::string_view>(sol::make_object(mLua.unsafeState(), sol::nil)),
"Value \"nil\" can not be casted to string"); "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"); "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"); "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"); "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"); "Value \"\"3.14\"\" can not be casted to sol::function");
} }
@ -186,21 +186,22 @@ return {
TEST_F(LuaStateTest, ProvideAPI) TEST_F(LuaStateTest, ProvideAPI)
{ {
LuaUtil::LuaState lua(mVFS.get(), &mCfg); 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 script1 = lua.runInNewSandbox("bbb/tests.lua", "", { { "test.api", api1 } });
sol::table api2 = LuaUtil::makeReadOnly(lua.sol().create_table_with("name", "api2"));
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_ERROR(LuaUtil::call(script1["sqr"], 3), "module not found: sqrlib");
EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9); EXPECT_EQ(LuaUtil::call(script2["sqr"], 3).get<int>(), 9);
EXPECT_EQ(LuaUtil::call(script1["apiName"]).get<std::string>(), "api1"); EXPECT_EQ(LuaUtil::call(script1["apiName"]).get<std::string>(), "api1");
EXPECT_EQ(LuaUtil::call(script2["apiName"]).get<std::string>(), "api2"); EXPECT_EQ(LuaUtil::call(script2["apiName"]).get<std::string>(), "api2");
});
} }
TEST_F(LuaStateTest, GetLuaVersion) TEST_F(LuaStateTest, GetLuaVersion)
@ -212,7 +213,7 @@ return {
{ {
auto getMem = [&] { auto getMem = [&] {
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
lua_gc(mLua.sol(), LUA_GCCOLLECT, 0); lua_gc(mLua.unsafeState(), LUA_GCCOLLECT, 0);
return mLua.getTotalMemoryUsage(); return mLua.getTotalMemoryUsage();
}; };
int64_t memWithScript; 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("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua"))); EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
std::string X0 = LuaUtil::serialize(mLua.sol().create_table_with("x", 0.5)); sol::state_view sol = mLua.unsafeState();
std::string X1 = LuaUtil::serialize(mLua.sol().create_table_with("x", 1.5)); 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(); 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("test1.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua"))); EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.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(); testing::internal::CaptureStdout();
@ -334,8 +336,9 @@ CUSTOM, PLAYER: useInterface.lua
scripts1.addAutoStartedScripts(); scripts1.addAutoStartedScripts();
EXPECT_TRUE(scripts1.addCustomScript(*mCfg.findId("test1.lua"))); 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))); sol::state_view sol = mLua.unsafeState();
scripts1.receiveEvent("Set", LuaUtil::serialize(mLua.sol().create_table_with("n", 2, "x", 2.5, "y", 1.5))); 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; ESM::LuaScripts data;
scripts1.save(data); scripts1.save(data);
@ -379,10 +382,10 @@ CUSTOM, PLAYER: useInterface.lua
EXPECT_EQ(internal::GetCapturedStdout(), ""); EXPECT_EQ(internal::GetCapturedStdout(), "");
int counter1 = 0, counter2 = 0, counter3 = 0, counter4 = 0; int counter1 = 0, counter2 = 0, counter3 = 0, counter4 = 0;
sol::function fn1 = sol::make_object(mLua.sol(), [&]() { counter1++; }); sol::function fn1 = sol::make_object(mLua.unsafeState(), [&]() { counter1++; });
sol::function fn2 = sol::make_object(mLua.sol(), [&]() { counter2++; }); sol::function fn2 = sol::make_object(mLua.unsafeState(), [&]() { counter2++; });
sol::function fn3 = sol::make_object(mLua.sol(), [&](int d) { counter3 += d; }); sol::function fn3 = sol::make_object(mLua.unsafeState(), [&](int d) { counter3 += d; });
sol::function fn4 = sol::make_object(mLua.sol(), [&](int d) { counter4 += d; }); sol::function fn4 = sol::make_object(mLua.unsafeState(), [&](int d) { counter4 += d; });
scripts.registerTimerCallback(test1Id, "A", fn3); scripts.registerTimerCallback(test1Id, "A", fn3);
scripts.registerTimerCallback(test1Id, "B", fn4); scripts.registerTimerCallback(test1Id, "B", fn4);
@ -391,12 +394,16 @@ CUSTOM, PLAYER: useInterface.lua
scripts.processTimers(1, 2); scripts.processTimers(1, 2);
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 10, test1Id, "B", sol::make_object(mLua.sol(), 3)); scripts.setupSerializableTimer(
scripts.setupSerializableTimer(TimerType::GAME_TIME, 10, test2Id, "B", sol::make_object(mLua.sol(), 4)); TimerType::SIMULATION_TIME, 10, test1Id, "B", sol::make_object(mLua.unsafeState(), 3));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 5, test1Id, "A", sol::make_object(mLua.sol(), 1)); scripts.setupSerializableTimer(TimerType::GAME_TIME, 10, test2Id, "B", sol::make_object(mLua.unsafeState(), 4));
scripts.setupSerializableTimer(TimerType::GAME_TIME, 5, test2Id, "A", sol::make_object(mLua.sol(), 2)); scripts.setupSerializableTimer(
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "A", sol::make_object(mLua.sol(), 10)); TimerType::SIMULATION_TIME, 5, test1Id, "A", sol::make_object(mLua.unsafeState(), 1));
scripts.setupSerializableTimer(TimerType::SIMULATION_TIME, 15, test1Id, "B", sol::make_object(mLua.sol(), 20)); 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::SIMULATION_TIME, 10, test2Id, fn2);
scripts.setupUnsavableTimer(TimerType::GAME_TIME, 10, test1Id, fn2); scripts.setupUnsavableTimer(TimerType::GAME_TIME, 10, test1Id, fn2);
@ -446,7 +453,8 @@ CUSTOM, PLAYER: useInterface.lua
TEST_F(LuaScriptsContainerTest, CallbackWrapper) 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::sScriptDebugNameKey] = "some_script.lua";
callback.mHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{ nullptr, 0 }; 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 // Note: LuaUtil::Callback can be used only if Lua is initialized via LuaUtil::LuaState
LuaUtil::LuaState luaState{ nullptr, nullptr }; LuaUtil::LuaState luaState{ nullptr, nullptr };
sol::state_view& mLua = luaState.sol(); luaState.protectedCall([](LuaUtil::LuaView& view) {
LuaUtil::LuaStorage::initLuaBindings(mLua); sol::state_view& lua = view.sol();
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage::initLuaBindings(view);
storage.setActive(true); LuaUtil::LuaStorage storage;
storage.setActive(true);
sol::table callbackHiddenData(mLua, sol::create);
callbackHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{}; sol::table callbackHiddenData(lua, sol::create);
LuaUtil::getAsyncPackageInitializer( callbackHiddenData[LuaUtil::ScriptsContainer::sScriptIdKey] = LuaUtil::ScriptId{};
mLua.lua_state(), []() { return 0.0; }, []() { return 0.0; })(callbackHiddenData); LuaUtil::getAsyncPackageInitializer(
mLua["async"] = LuaUtil::AsyncPackageId{ nullptr, 0, callbackHiddenData }; lua.lua_state(), []() { return 0.0; }, []() { return 0.0; })(callbackHiddenData);
lua["async"] = LuaUtil::AsyncPackageId{ nullptr, 0, callbackHiddenData };
mLua["mutable"] = storage.getMutableSection("test");
mLua["ro"] = storage.getReadOnlySection("test"); lua["mutable"] = storage.getMutableSection(lua, "test");
lua["ro"] = storage.getReadOnlySection(lua, "test");
mLua.safe_script(R"(
callbackCalls = {} lua.safe_script(R"(
ro:subscribe(async:callback(function(section, key) callbackCalls = {}
table.insert(callbackCalls, section .. '_' .. (key or '*')) ro:subscribe(async:callback(function(section, key)
end)) table.insert(callbackCalls, section .. '_' .. (key or '*'))
)"); end))
)");
mLua.safe_script("mutable:set('x', 5)");
EXPECT_EQ(get<int>(mLua, "mutable:get('x')"), 5); lua.safe_script("mutable:set('x', 5)");
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 5); EXPECT_EQ(get<int>(lua, "mutable:get('x')"), 5);
EXPECT_EQ(get<int>(lua, "ro:get('x')"), 5);
EXPECT_THROW(mLua.safe_script("ro:set('y', 3)"), std::exception);
EXPECT_THROW(lua.safe_script("ro:set('y', 3)"), std::exception);
mLua.safe_script("t1 = mutable:asTable()");
mLua.safe_script("t2 = ro:asTable()"); lua.safe_script("t1 = mutable:asTable()");
EXPECT_EQ(get<int>(mLua, "t1.x"), 5); lua.safe_script("t2 = ro:asTable()");
EXPECT_EQ(get<int>(mLua, "t2.x"), 5); EXPECT_EQ(get<int>(lua, "t1.x"), 5);
EXPECT_EQ(get<int>(lua, "t2.x"), 5);
mLua.safe_script("mutable:reset()");
EXPECT_TRUE(get<bool>(mLua, "ro:get('x') == nil")); lua.safe_script("mutable:reset()");
EXPECT_TRUE(get<bool>(lua, "ro:get('x') == nil"));
mLua.safe_script("mutable:reset({x=4, y=7})");
EXPECT_EQ(get<int>(mLua, "ro:get('x')"), 4); lua.safe_script("mutable:reset({x=4, y=7})");
EXPECT_EQ(get<int>(mLua, "ro:get('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>(mLua, "table.concat(callbackCalls, ', ')"), "test_x, test_*, test_*");
EXPECT_THAT(get<std::string>(lua, "table.concat(callbackCalls, ', ')"), "test_x, test_*, test_*");
});
} }
TEST(LuaUtilStorageTest, Table) TEST(LuaUtilStorageTest, Table)
{ {
sol::state mLua; LuaUtil::LuaState luaState{ nullptr, nullptr };
LuaUtil::LuaStorage::initLuaBindings(mLua); luaState.protectedCall([](LuaUtil::LuaView& view) {
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage::initLuaBindings(view);
storage.setActive(true); LuaUtil::LuaStorage storage;
mLua["mutable"] = storage.getMutableSection("test"); auto& lua = view.sol();
mLua["ro"] = storage.getReadOnlySection("test"); storage.setActive(true);
lua["mutable"] = storage.getMutableSection(lua, "test");
mLua.safe_script("mutable:set('x', { y = 'abc', z = 7 })"); lua["ro"] = storage.getReadOnlySection(lua, "test");
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7);
EXPECT_THROW(mLua.safe_script("mutable:get('x').z = 3"), std::exception); lua.safe_script("mutable:set('x', { y = 'abc', z = 7 })");
EXPECT_NO_THROW(mLua.safe_script("mutable:getCopy('x').z = 3")); EXPECT_EQ(get<int>(lua, "mutable:get('x').z"), 7);
EXPECT_EQ(get<int>(mLua, "mutable:get('x').z"), 7); EXPECT_THROW(lua.safe_script("mutable:get('x').z = 3"), std::exception);
EXPECT_EQ(get<int>(mLua, "ro:get('x').z"), 7); EXPECT_NO_THROW(lua.safe_script("mutable:getCopy('x').z = 3"));
EXPECT_EQ(get<std::string>(mLua, "ro:get('x').y"), "abc"); 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) TEST(LuaUtilStorageTest, Saving)
{ {
sol::state mLua; LuaUtil::LuaState luaState{ nullptr, nullptr };
LuaUtil::LuaStorage::initLuaBindings(mLua); luaState.protectedCall([](LuaUtil::LuaView& view) {
LuaUtil::LuaStorage storage(mLua); LuaUtil::LuaStorage::initLuaBindings(view);
storage.setActive(true); LuaUtil::LuaStorage storage;
auto& lua = view.sol();
mLua["permanent"] = storage.getMutableSection("permanent"); storage.setActive(true);
mLua["temporary"] = storage.getMutableSection("temporary");
mLua.safe_script("temporary:removeOnExit()"); lua["permanent"] = storage.getMutableSection(lua, "permanent");
mLua.safe_script("permanent:set('x', 1)"); lua["temporary"] = storage.getMutableSection(lua, "temporary");
mLua.safe_script("temporary:set('y', 2)"); lua.safe_script("temporary:removeOnExit()");
lua.safe_script("permanent:set('x', 1)");
const auto tmpFile = std::filesystem::temp_directory_path() / "test_storage.bin"; lua.safe_script("temporary:set('y', 2)");
storage.save(tmpFile);
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1); const auto tmpFile = std::filesystem::temp_directory_path() / "test_storage.bin";
EXPECT_EQ(get<int>(mLua, "temporary:get('y')"), 2); storage.save(lua, tmpFile);
EXPECT_EQ(get<int>(lua, "permanent:get('x')"), 1);
storage.clearTemporaryAndRemoveCallbacks(); EXPECT_EQ(get<int>(lua, "temporary:get('y')"), 2);
mLua["permanent"] = storage.getMutableSection("permanent");
mLua["temporary"] = storage.getMutableSection("temporary"); storage.clearTemporaryAndRemoveCallbacks();
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1); lua["permanent"] = storage.getMutableSection(lua, "permanent");
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil")); lua["temporary"] = storage.getMutableSection(lua, "temporary");
EXPECT_EQ(get<int>(lua, "permanent:get('x')"), 1);
mLua.safe_script("permanent:set('x', 3)"); EXPECT_TRUE(get<bool>(lua, "temporary:get('y') == nil"));
mLua.safe_script("permanent:set('z', 4)");
lua.safe_script("permanent:set('x', 3)");
LuaUtil::LuaStorage storage2(mLua); lua.safe_script("permanent:set('z', 4)");
storage2.setActive(true);
storage2.load(tmpFile); LuaUtil::LuaStorage storage2;
mLua["permanent"] = storage2.getMutableSection("permanent"); storage2.setActive(true);
mLua["temporary"] = storage2.getMutableSection("temporary"); storage2.load(lua, tmpFile);
lua["permanent"] = storage2.getMutableSection(lua, "permanent");
EXPECT_EQ(get<int>(mLua, "permanent:get('x')"), 1); lua["temporary"] = storage2.getMutableSection(lua, "temporary");
EXPECT_TRUE(get<bool>(mLua, "permanent:get('z') == nil"));
EXPECT_TRUE(get<bool>(mLua, "temporary:get('y') == nil")); 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>()); 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) sol::table makeTable(std::string name)
{ {
@ -39,13 +39,14 @@ namespace
TEST_F(LuaUiContentTest, ProtectedMetatable) TEST_F(LuaUiContentTest, ProtectedMetatable)
{ {
mLuaState.sol()["makeContent"] = mNew; sol::state_view sol = mLuaState.unsafeState();
mLuaState.sol()["M"] = makeContent(makeTable()).getMetatable(); sol["makeContent"] = mNew;
sol["M"] = makeContent(makeTable()).getMetatable();
std::string testScript = R"( std::string testScript = R"(
assert(not pcall(function() setmetatable(makeContent{}, {}) end), 'Metatable is not protected') assert(not pcall(function() setmetatable(makeContent{}, {}) end), 'Metatable is not protected')
assert(getmetatable(makeContent{}) == false, '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) TEST_F(LuaUiContentTest, Create)

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

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

@ -14,7 +14,7 @@ namespace MWLua
using CameraMode = MWRender::Camera::Mode; 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::Camera* camera = MWBase::Environment::get().getWorld()->getCamera();
MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager(); MWRender::RenderingManager* renderingManager = MWBase::Environment::get().getWorld()->getRenderingManager();

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

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

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

@ -48,7 +48,7 @@ namespace MWLua
template <class... Str> template <class... Str>
sol::object getCachedPackage(std::string_view first, const Str&... str) const 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) if constexpr (sizeof...(str) == 0)
return package; return package;
else else
@ -58,7 +58,7 @@ namespace MWLua
template <class... Str> template <class... Str>
const sol::object& setCachedPackage(const sol::object& value, std::string_view first, const Str&... str) const 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) if constexpr (sizeof...(str) == 0)
lua[first] = value; lua[first] = value;
else else
@ -90,10 +90,17 @@ namespace MWLua
bool initializeOnce(std::string_view key) const bool initializeOnce(std::string_view key) const
{ {
sol::object flag = mLua->sol()[key]; auto view = sol();
mLua->sol()[key] = sol::make_object(mLua->sol(), true); sol::object flag = view[key];
view[key] = sol::make_object(view, true);
return flag == sol::nil; 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) sol::table initCorePackage(const Context& context)
{ {
auto* lua = context.mLua; auto lua = context.sol();
sol::object cached = context.getTypePackage("openmw_core"); sol::object cached = context.getTypePackage("openmw_core");
if (cached != sol::nil) if (cached != sol::nil)
return cached; 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["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(); Log(Debug::Warning) << "Quit requested by a Lua script.\n" << lua->debugTraceback();
MWBase::Environment::get().getStateManager()->requestQuit(); 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 { api["getFormId"] = [](std::string_view contentFile, unsigned int index) -> std::string {
const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles(); const std::vector<std::string>& contentList = MWBase::Environment::get().getWorld()->getContentFiles();
for (size_t i = 0; i < contentList.size(); ++i) for (size_t i = 0; i < contentList.size(); ++i)
@ -103,10 +102,10 @@ namespace MWLua
api["dialogue"] api["dialogue"]
= context.cachePackage("openmw_core_dialogue", [context]() { return initCoreDialogueBindings(context); }); = context.cachePackage("openmw_core_dialogue", [context]() { return initCoreDialogueBindings(context); });
api["l10n"] = context.cachePackage("openmw_core_l10n", 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 const MWWorld::Store<ESM::GameSetting>* gmstStore
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>(); = &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); const ESM::GameSetting* gmst = gmstStore->search(setting);
if (gmst == nullptr) if (gmst == nullptr)
return sol::nil; return sol::nil;
@ -114,13 +113,13 @@ namespace MWLua
switch (value.getType()) switch (value.getType())
{ {
case ESM::VT_Float: 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_Short:
case ESM::VT_Long: case ESM::VT_Long:
case ESM::VT_Int: 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: 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_Unknown:
case ESM::VT_None: case ESM::VT_None:
break; break;

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

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

@ -46,7 +46,7 @@ namespace MWLua
{ {
sol::table initCoreFactionBindings(const Context& context) 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); sol::table factions(lua, sol::create);
addRecordFunctionBinding<ESM::Faction>(factions, context); addRecordFunctionBinding<ESM::Faction>(factions, context);
// Faction record // Faction record

@ -38,13 +38,13 @@ namespace MWLua
sol::table initInputPackage(const Context& context) 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) if (lua["openmw_input"] != sol::nil)
return lua["openmw_input"]; 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) { keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e) {
if (e.sym > 0 && e.sym <= 255) if (e.sym > 0 && e.sym <= 255)
return std::string(1, static_cast<char>(e.sym)); 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["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; }); 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["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["finger"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f { touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
@ -66,7 +66,7 @@ namespace MWLua
touchpadEvent["pressure"] touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; }); = 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] inputActions[sol::meta_function::index]
= [](LuaUtil::InputAction::Registry& registry, std::string_view key) { return registry[key]; }; = [](LuaUtil::InputAction::Registry& registry, std::string_view key) { return registry[key]; };
{ {
@ -85,7 +85,7 @@ namespace MWLua
inputActions[sol::meta_function::pairs] = pairs; 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( actionInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; }); [](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
actionInfo["name"] = sol::readonly_property( actionInfo["name"] = sol::readonly_property(
@ -98,7 +98,7 @@ namespace MWLua
actionInfo["defaultValue"] actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; }); = 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] inputTriggers[sol::meta_function::index]
= [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) { return registry[key]; }; = [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) { return registry[key]; };
{ {
@ -117,7 +117,7 @@ namespace MWLua
inputTriggers[sol::meta_function::pairs] = pairs; 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( triggerInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; }); [](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
triggerInfo["name"] = sol::readonly_property( triggerInfo["name"] = sol::readonly_property(
@ -128,14 +128,15 @@ namespace MWLua
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; }); [](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
sol::table api(context.mLua->sol(), sol::create); sol::table api(lua, sol::create);
api["ACTION_TYPE"] api["ACTION_TYPE"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, LuaUtil::InputAction::Type>({ = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, LuaUtil::InputAction::Type>(lua,
{ "Boolean", LuaUtil::InputAction::Type::Boolean }, {
{ "Number", LuaUtil::InputAction::Type::Number }, { "Boolean", LuaUtil::InputAction::Type::Boolean },
{ "Range", LuaUtil::InputAction::Type::Range }, { "Number", LuaUtil::InputAction::Type::Number },
})); { "Range", LuaUtil::InputAction::Type::Range },
}));
api["actions"] = std::ref(context.mLuaManager->inputActions()); api["actions"] = std::ref(context.mLuaManager->inputActions());
api["registerAction"] = [manager = context.mLuaManager](sol::table options) { api["registerAction"] = [manager = context.mLuaManager](sol::table options) {
@ -231,215 +232,219 @@ namespace MWLua
api["getKeyName"] = [](SDL_Scancode code) { return SDL_GetScancodeName(code); }; api["getKeyName"] = [](SDL_Scancode code) { return SDL_GetScancodeName(code); };
api["ACTION"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, MWInput::Actions>({ api["ACTION"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, MWInput::Actions>(lua,
{ "GameMenu", MWInput::A_GameMenu }, {
{ "Screenshot", MWInput::A_Screenshot }, { "GameMenu", MWInput::A_GameMenu },
{ "Inventory", MWInput::A_Inventory }, { "Screenshot", MWInput::A_Screenshot },
{ "Console", MWInput::A_Console }, { "Inventory", MWInput::A_Inventory },
{ "Console", MWInput::A_Console },
{ "MoveLeft", MWInput::A_MoveLeft },
{ "MoveRight", MWInput::A_MoveRight }, { "MoveLeft", MWInput::A_MoveLeft },
{ "MoveForward", MWInput::A_MoveForward }, { "MoveRight", MWInput::A_MoveRight },
{ "MoveBackward", MWInput::A_MoveBackward }, { "MoveForward", MWInput::A_MoveForward },
{ "MoveBackward", MWInput::A_MoveBackward },
{ "Activate", MWInput::A_Activate },
{ "Use", MWInput::A_Use }, { "Activate", MWInput::A_Activate },
{ "Jump", MWInput::A_Jump }, { "Use", MWInput::A_Use },
{ "AutoMove", MWInput::A_AutoMove }, { "Jump", MWInput::A_Jump },
{ "Rest", MWInput::A_Rest }, { "AutoMove", MWInput::A_AutoMove },
{ "Journal", MWInput::A_Journal }, { "Rest", MWInput::A_Rest },
{ "Run", MWInput::A_Run }, { "Journal", MWInput::A_Journal },
{ "CycleSpellLeft", MWInput::A_CycleSpellLeft }, { "Run", MWInput::A_Run },
{ "CycleSpellRight", MWInput::A_CycleSpellRight }, { "CycleSpellLeft", MWInput::A_CycleSpellLeft },
{ "CycleWeaponLeft", MWInput::A_CycleWeaponLeft }, { "CycleSpellRight", MWInput::A_CycleSpellRight },
{ "CycleWeaponRight", MWInput::A_CycleWeaponRight }, { "CycleWeaponLeft", MWInput::A_CycleWeaponLeft },
{ "AlwaysRun", MWInput::A_AlwaysRun }, { "CycleWeaponRight", MWInput::A_CycleWeaponRight },
{ "Sneak", MWInput::A_Sneak }, { "AlwaysRun", MWInput::A_AlwaysRun },
{ "Sneak", MWInput::A_Sneak },
{ "QuickSave", MWInput::A_QuickSave },
{ "QuickLoad", MWInput::A_QuickLoad }, { "QuickSave", MWInput::A_QuickSave },
{ "QuickMenu", MWInput::A_QuickMenu }, { "QuickLoad", MWInput::A_QuickLoad },
{ "ToggleWeapon", MWInput::A_ToggleWeapon }, { "QuickMenu", MWInput::A_QuickMenu },
{ "ToggleSpell", MWInput::A_ToggleSpell }, { "ToggleWeapon", MWInput::A_ToggleWeapon },
{ "TogglePOV", MWInput::A_TogglePOV }, { "ToggleSpell", MWInput::A_ToggleSpell },
{ "TogglePOV", MWInput::A_TogglePOV },
{ "QuickKey1", MWInput::A_QuickKey1 },
{ "QuickKey2", MWInput::A_QuickKey2 }, { "QuickKey1", MWInput::A_QuickKey1 },
{ "QuickKey3", MWInput::A_QuickKey3 }, { "QuickKey2", MWInput::A_QuickKey2 },
{ "QuickKey4", MWInput::A_QuickKey4 }, { "QuickKey3", MWInput::A_QuickKey3 },
{ "QuickKey5", MWInput::A_QuickKey5 }, { "QuickKey4", MWInput::A_QuickKey4 },
{ "QuickKey6", MWInput::A_QuickKey6 }, { "QuickKey5", MWInput::A_QuickKey5 },
{ "QuickKey7", MWInput::A_QuickKey7 }, { "QuickKey6", MWInput::A_QuickKey6 },
{ "QuickKey8", MWInput::A_QuickKey8 }, { "QuickKey7", MWInput::A_QuickKey7 },
{ "QuickKey9", MWInput::A_QuickKey9 }, { "QuickKey8", MWInput::A_QuickKey8 },
{ "QuickKey10", MWInput::A_QuickKey10 }, { "QuickKey9", MWInput::A_QuickKey9 },
{ "QuickKeysMenu", MWInput::A_QuickKeysMenu }, { "QuickKey10", MWInput::A_QuickKey10 },
{ "QuickKeysMenu", MWInput::A_QuickKeysMenu },
{ "ToggleHUD", MWInput::A_ToggleHUD },
{ "ToggleDebug", MWInput::A_ToggleDebug }, { "ToggleHUD", MWInput::A_ToggleHUD },
{ "TogglePostProcessorHUD", MWInput::A_TogglePostProcessorHUD }, { "ToggleDebug", MWInput::A_ToggleDebug },
{ "TogglePostProcessorHUD", MWInput::A_TogglePostProcessorHUD },
{ "ZoomIn", MWInput::A_ZoomIn },
{ "ZoomOut", MWInput::A_ZoomOut }, { "ZoomIn", MWInput::A_ZoomIn },
})); { "ZoomOut", MWInput::A_ZoomOut },
}));
// input.CONTROL_SWITCH is deprecated, remove after releasing 0.49 // input.CONTROL_SWITCH is deprecated, remove after releasing 0.49
api["CONTROL_SWITCH"] api["CONTROL_SWITCH"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, std::string_view>({ = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, std::string_view>(lua,
{ "Controls", "playercontrols" }, {
{ "Fighting", "playerfighting" }, { "Controls", "playercontrols" },
{ "Jumping", "playerjumping" }, { "Fighting", "playerfighting" },
{ "Looking", "playerlooking" }, { "Jumping", "playerjumping" },
{ "Magic", "playermagic" }, { "Looking", "playerlooking" },
{ "ViewMode", "playerviewswitch" }, { "Magic", "playermagic" },
{ "VanityMode", "vanitymode" }, { "ViewMode", "playerviewswitch" },
})); { "VanityMode", "vanitymode" },
}));
api["CONTROLLER_BUTTON"] api["CONTROLLER_BUTTON"]
= LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string_view, SDL_GameControllerButton>({ = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, SDL_GameControllerButton>(lua,
{ "A", SDL_CONTROLLER_BUTTON_A }, {
{ "B", SDL_CONTROLLER_BUTTON_B }, { "A", SDL_CONTROLLER_BUTTON_A },
{ "X", SDL_CONTROLLER_BUTTON_X }, { "B", SDL_CONTROLLER_BUTTON_B },
{ "Y", SDL_CONTROLLER_BUTTON_Y }, { "X", SDL_CONTROLLER_BUTTON_X },
{ "Back", SDL_CONTROLLER_BUTTON_BACK }, { "Y", SDL_CONTROLLER_BUTTON_Y },
{ "Guide", SDL_CONTROLLER_BUTTON_GUIDE }, { "Back", SDL_CONTROLLER_BUTTON_BACK },
{ "Start", SDL_CONTROLLER_BUTTON_START }, { "Guide", SDL_CONTROLLER_BUTTON_GUIDE },
{ "LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK }, { "Start", SDL_CONTROLLER_BUTTON_START },
{ "RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK }, { "LeftStick", SDL_CONTROLLER_BUTTON_LEFTSTICK },
{ "LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER }, { "RightStick", SDL_CONTROLLER_BUTTON_RIGHTSTICK },
{ "RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER }, { "LeftShoulder", SDL_CONTROLLER_BUTTON_LEFTSHOULDER },
{ "DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP }, { "RightShoulder", SDL_CONTROLLER_BUTTON_RIGHTSHOULDER },
{ "DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN }, { "DPadUp", SDL_CONTROLLER_BUTTON_DPAD_UP },
{ "DPadLeft", SDL_CONTROLLER_BUTTON_DPAD_LEFT }, { "DPadDown", SDL_CONTROLLER_BUTTON_DPAD_DOWN },
{ "DPadRight", SDL_CONTROLLER_BUTTON_DPAD_RIGHT }, { "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); lua["openmw_input"] = LuaUtil::makeReadOnly(api);
return lua["openmw_input"]; return lua["openmw_input"];
} }

@ -60,7 +60,7 @@ namespace MWLua
if (it != self->mStatsCache.end()) if (it != self->mStatsCache.end())
return it->second; 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 void set(const Context& context, std::string_view prop, const sol::object& value) const
@ -82,9 +82,9 @@ namespace MWLua
if (prop == "condition") if (prop == "condition")
{ {
if (ptr.mRef->getType() == ESM::REC_LIGH) 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)) 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()); ptr.getClass().getItemHealth(ptr) + ptr.getCellRef().getChargeIntRemainder());
} }
else if (prop == "enchantmentCharge") else if (prop == "enchantmentCharge")
@ -99,9 +99,10 @@ namespace MWLua
const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName); const auto* enchantment = store->get<ESM::Enchantment>().find(enchantmentName);
if (charge == -1) // return the full charge 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") else if (prop == "soul")
{ {
@ -109,7 +110,7 @@ namespace MWLua
if (soul.empty()) if (soul.empty())
return sol::lua_nil; 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; return sol::lua_nil;
@ -185,7 +186,7 @@ namespace MWLua
return {}; 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) { 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>() + "'"); throw std::runtime_error("Unknown ItemData property '" + args.get<std::string>() + "'");
}; };

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

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

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

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

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

@ -14,17 +14,17 @@ namespace MWLua
{ {
sol::table initMarkupPackage(const Context& context) 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(); 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)); Files::IStreamPtr file = vfs->get(VFS::Path::Normalized(fileName));
return LuaUtil::loadYaml(*file, lua->sol()); return LuaUtil::loadYaml(*file, lua);
};
api["decodeYaml"] = [lua = context.mLua](std::string_view inputData) {
return LuaUtil::loadYaml(std::string(inputData), lua->sol());
}; };
api["decodeYaml"]
= [lua](std::string_view inputData) { return LuaUtil::loadYaml(std::string(inputData), lua); };
return LuaUtil::makeReadOnly(api); return LuaUtil::makeReadOnly(api);
} }

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

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

@ -164,7 +164,7 @@ namespace MWLua
void registerObjectList(const std::string& prefix, const Context& context) void registerObjectList(const std::string& prefix, const Context& context)
{ {
using ListT = ObjectList<ObjectT>; 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"); sol::usertype<ListT> listT = lua.new_usertype<ListT>(prefix + "ObjectList");
listT[sol::meta_function::to_string] listT[sol::meta_function::to_string]
= [](const ListT& list) { return "{" + std::to_string(list.mIds->size()) + " objects}"; }; = [](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) void addOwnerbindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{ {
using OwnerT = Owner<ObjectT>; 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() + "]"; }; 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() }; return LuaUtil::Box{ bb.center(), bb._max - bb.center() };
}; };
objectT["type"] = sol::readonly_property( objectT["type"]
[types = getTypeToPackageTable(context.mLua->sol())]( = sol::readonly_property([types = getTypeToPackageTable(context.sol())](const ObjectT& o) mutable {
const ObjectT& o) mutable { return types[getLiveCellRefType(o.ptr().mRef)]; }); return types[getLiveCellRefType(o.ptr().mRef)];
});
objectT["count"] = sol::readonly_property([](const ObjectT& o) { return o.ptr().getCellRef().getCount(); }); 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(); }; 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) void addInventoryBindings(sol::usertype<ObjectT>& objectT, const std::string& prefix, const Context& context)
{ {
using InventoryT = Inventory<ObjectT>; 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] inventoryT[sol::meta_function::to_string]
= [](const InventoryT& inv) { return "Inventory[" + inv.mObj.toString() + "]"; }; = [](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) { const InventoryT& inventory, sol::optional<sol::table> type) {
int mask = -1; int mask = -1;
sol::optional<uint32_t> typeId = sol::nullopt; sol::optional<uint32_t> typeId = sol::nullopt;
@ -681,7 +682,7 @@ namespace MWLua
void initObjectBindings(const std::string& prefix, const Context& context) void initObjectBindings(const std::string& prefix, const Context& context)
{ {
sol::usertype<ObjectT> objectT 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); addBasicBindings<ObjectT>(objectT, context);
addInventoryBindings<ObjectT>(objectT, prefix, context); addInventoryBindings<ObjectT>(objectT, prefix, context);
addOwnerbindings<ObjectT>(objectT, prefix, context); addOwnerbindings<ObjectT>(objectT, prefix, context);

@ -96,9 +96,10 @@ namespace MWLua
sol::table initPostprocessingPackage(const Context& context) 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[sol::meta_function::to_string] = [](const Shader& shader) { return shader.toString(); };
shader["enable"] = [context](Shader& shader, sol::optional<int> optPos) { shader["enable"] = [context](Shader& shader, sol::optional<int> optPos) {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -82,11 +82,12 @@ namespace MWLua
sol::table registerUiApi(const Context& context) sol::table registerUiApi(const Context& context)
{ {
sol::state_view lua = context.sol();
bool menu = context.mType == Context::Menu; bool menu = context.mType == Context::Menu;
MWBase::WindowManager* windowManager = MWBase::Environment::get().getWindowManager(); 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) { api["_setHudVisibility"] = [luaManager = context.mLuaManager](bool state) {
luaManager->addAction([state] { MWBase::Environment::get().getWindowManager()->setHudVisibility(state); }); luaManager->addAction([state] { MWBase::Environment::get().getWindowManager()->setHudVisibility(state); });
}; };
@ -107,12 +108,13 @@ namespace MWLua
} }
luaManager->addUIMessage(message, mode); luaManager->addUIMessage(message, mode);
}; };
api["CONSOLE_COLOR"] = LuaUtil::makeStrictReadOnly(context.mLua->tableFromPairs<std::string, Misc::Color>({ 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)) }, { "Default", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Default.substr(1)) },
{ "Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1)) }, { "Error", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Error.substr(1)) },
{ "Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1)) }, { "Success", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Success.substr(1)) },
})); { "Info", Misc::Color::fromHex(MWBase::WindowManager::sConsoleColor_Info.substr(1)) },
}));
api["printToConsole"] api["printToConsole"]
= [luaManager = context.mLuaManager](const std::string& message, const Misc::Color& color) { = [luaManager = context.mLuaManager](const std::string& message, const Misc::Color& color) {
luaManager->addInGameConsoleMessage(message + "\n", color); luaManager->addInGameConsoleMessage(message + "\n", color);
@ -150,7 +152,7 @@ namespace MWLua
}; };
api["_getMenuTransparency"] = []() -> float { return Settings::gui().mMenuTransparency; }; 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> { layersTable["indexOf"] = [](std::string_view name) -> sol::optional<size_t> {
size_t index = LuaUi::Layer::indexOf(name); size_t index = LuaUi::Layer::indexOf(name);
if (index == LuaUi::Layer::count()) if (index == LuaUi::Layer::count())
@ -202,14 +204,14 @@ namespace MWLua
} }
api["layers"] = layers; api["layers"] = layers;
sol::table typeTable = context.mLua->newTable(); sol::table typeTable(lua, sol::create);
for (const auto& it : LuaUi::widgetTypeToName()) for (const auto& it : LuaUi::widgetTypeToName())
typeTable.set(it.second, it.first); typeTable.set(it.second, it.first);
api["TYPE"] = LuaUtil::makeStrictReadOnly(typeTable); api["TYPE"] = LuaUtil::makeStrictReadOnly(typeTable);
api["ALIGNMENT"] = LuaUtil::makeStrictReadOnly( api["ALIGNMENT"] = LuaUtil::makeStrictReadOnly(LuaUtil::tableFromPairs<std::string_view, LuaUi::Alignment>(lua,
context.mLua->tableFromPairs<std::string_view, LuaUi::Alignment>({ { "Start", LuaUi::Alignment::Start }, { { "Start", LuaUi::Alignment::Start }, { "Center", LuaUi::Alignment::Center },
{ "Center", LuaUi::Alignment::Center }, { "End", LuaUi::Alignment::End } })); { "End", LuaUi::Alignment::End } }));
api["registerSettingsPage"] = &LuaUi::registerSettingsPage; api["registerSettingsPage"] = &LuaUi::registerSettingsPage;
api["removeSettingsPage"] = &LuaUi::removeSettingsPage; api["removeSettingsPage"] = &LuaUi::removeSettingsPage;
@ -297,7 +299,7 @@ namespace MWLua
{ {
if (context.initializeOnce("openmw_ui_usertypes")) 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) { element[sol::meta_function::to_string] = [](const LuaUi::Element& element) {
std::stringstream res; std::stringstream res;
res << "UiElement"; res << "UiElement";
@ -321,7 +323,7 @@ namespace MWLua
[element] { wrapAction(element, [&] { LuaUi::Element::erase(element.get()); }); }, "Destroy UI"); [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"] uiLayer["name"]
= sol::readonly_property([](LuaUi::Layer& self) -> std::string_view { return self.name(); }); = sol::readonly_property([](LuaUi::Layer& self) -> std::string_view { return self.name(); });
uiLayer["size"] = sol::readonly_property([](LuaUi::Layer& self) { return self.size(); }); 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 << "'."; 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; std::ostringstream os;
if (file.mFilePtr && file.mFilePtr->peek() != EOF) if (file.mFilePtr && file.mFilePtr->peek() != EOF)
@ -76,34 +76,34 @@ namespace MWLua
auto result = os.str(); auto result = os.str();
printLargeDataMessage(file, result.size()); 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; std::string result;
if (file.mFilePtr && std::getline(*file.mFilePtr, result)) if (file.mFilePtr && std::getline(*file.mFilePtr, result))
{ {
printLargeDataMessage(file, result.size()); printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result); return sol::make_object<std::string>(lua, result);
} }
return sol::nil; 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; double number = 0;
if (file && *file >> number) if (file && *file >> number)
return sol::make_object<double>(lua->sol(), number); return sol::make_object<double>(lua, number);
return sol::nil; 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) 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); auto bytesLeft = getBytesLeftInStream(file.mFilePtr);
if (bytesLeft <= 0) if (bytesLeft <= 0)
@ -116,7 +116,7 @@ namespace MWLua
if (file.mFilePtr->read(&result[0], count)) if (file.mFilePtr->read(&result[0], count))
{ {
printLargeDataMessage(file, result.size()); printLargeDataMessage(file, result.size());
return sol::make_object<std::string>(lua->sol(), result); return sol::make_object<std::string>(lua, result);
} }
return sol::nil; return sol::nil;
@ -131,7 +131,7 @@ namespace MWLua
} }
sol::variadic_results seek( 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; sol::variadic_results values;
try try
@ -141,16 +141,16 @@ namespace MWLua
{ {
auto msg = "Failed to seek in file '" + self.mFileName + "'"; auto msg = "Failed to seek in file '" + self.mFileName + "'";
values.push_back(sol::nil); 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 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) catch (std::exception& e)
{ {
auto msg = "Failed to seek in file '" + self.mFileName + "': " + std::string(e.what()); auto msg = "Failed to seek in file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil); 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; return values;
@ -159,18 +159,18 @@ namespace MWLua
sol::table initVFSPackage(const Context& context) 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(); 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"] handle["fileName"]
= sol::readonly_property([](const FileHandle& self) -> std::string_view { return self.mFileName; }); = sol::readonly_property([](const FileHandle& self) -> std::string_view { return self.mFileName; });
handle[sol::meta_function::to_string] = [](const FileHandle& self) { handle[sol::meta_function::to_string] = [](const FileHandle& self) {
return "FileHandle{'" + self.mFileName + "'" + (!self.mFilePtr ? ", closed" : "") + "}"; return "FileHandle{'" + self.mFileName + "'" + (!self.mFilePtr ? ", closed" : "") + "}";
}; };
handle["seek"] = sol::overload( 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); validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0)); auto off = static_cast<std::streamoff>(offset.value_or(0));
@ -178,21 +178,21 @@ namespace MWLua
return seek(lua, self, dir, off); 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); validateFile(self);
auto off = static_cast<std::streamoff>(offset.value_or(0)); auto off = static_cast<std::streamoff>(offset.value_or(0));
return seek(lua, self, std::ios_base::cur, off); 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 { return sol::as_function([&lua, &self]() mutable {
validateFile(self); validateFile(self);
return readLineFromFile(lua, 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); auto normalizedName = VFS::Path::normalizeFilename(fileName);
return sol::as_function( return sol::as_function(
[lua, file = FileHandle(vfs->getNormalized(normalizedName), normalizedName)]() mutable { [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; sol::variadic_results values;
try try
{ {
@ -214,22 +214,22 @@ namespace MWLua
{ {
auto msg = "Can not close file '" + self.mFileName + "': file handle is still opened."; auto msg = "Can not close file '" + self.mFileName + "': file handle is still opened.";
values.push_back(sol::nil); 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 else
values.push_back(sol::make_object<bool>(lua->sol(), true)); values.push_back(sol::make_object<bool>(L, true));
} }
catch (std::exception& e) catch (std::exception& e)
{ {
auto msg = "Can not close file '" + self.mFileName + "': " + std::string(e.what()); auto msg = "Can not close file '" + self.mFileName + "': " + std::string(e.what());
values.push_back(sol::nil); 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; 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); validateFile(self);
if (args.size() > sMaximumReadArguments) if (args.size() > sMaximumReadArguments)
@ -297,25 +297,25 @@ namespace MWLua
{ {
auto msg = "Error when handling '" + self.mFileName + "': can not read data for argument #" auto msg = "Error when handling '" + self.mFileName + "': can not read data for argument #"
+ std::to_string(i); + 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; 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; sol::variadic_results values;
try try
{ {
auto normalizedName = VFS::Path::normalizeFilename(fileName); auto normalizedName = VFS::Path::normalizeFilename(fileName);
auto handle = FileHandle(vfs->getNormalized(normalizedName), normalizedName); 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) catch (std::exception& e)
{ {
auto msg = "Can not open file: " + std::string(e.what()); auto msg = "Can not open file: " + std::string(e.what());
values.push_back(sol::nil); 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; return values;

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

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

@ -34,6 +34,36 @@ namespace LuaUtil
bool mLogMemoryUsage = false; 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. // Holds Lua state.
// Provides additional features: // Provides additional features:
// - Load scripts from the virtual filesystem; // - Load scripts from the virtual filesystem;
@ -54,26 +84,53 @@ namespace LuaUtil
LuaState(const LuaState&) = delete; LuaState(const LuaState&) = delete;
LuaState(LuaState&&) = delete; LuaState(LuaState&&) = delete;
// Returns underlying sol::state. // Pushing to the stack from outside a Lua context crashes the engine if no memory can be allocated to grow the
sol::state_view& sol() { return mSol; } // 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. // 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. // Makes no sense if called not from Lua code.
// Note: It is a slow function, should be used for debug purposes only. // Note: It is a slow function, should be used for debug purposes only.
std::string debugTraceback() { return mSol["debug"]["traceback"]().get<std::string>(); } 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)`. // 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, // 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 // it will be evaluated (once per sandbox) the first time when requested. If the package

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

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

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

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