Merge branch 'vfs_normalized_path_12' into 'master'

Store Lua script path as VFS normalized (#8138)

See merge request OpenMW/openmw!4373
pull/3236/head
psi29a 3 months ago
commit b9cb028809

@ -9,8 +9,9 @@
namespace
{
constexpr VFS::Path::NormalizedView techniquePropertiesPath("shaders/technique_properties.omwfx");
TestingOpenMW::VFSTestFile technique_properties(R"(
TestingOpenMW::VFSTestFile techniqueProperties(R"(
fragment main {}
vertex main {}
technique {
@ -26,7 +27,9 @@ namespace
}
)");
TestingOpenMW::VFSTestFile rendertarget_properties{ R"(
constexpr VFS::Path::NormalizedView rendertargetPropertiesPath("shaders/rendertarget_properties.omwfx");
TestingOpenMW::VFSTestFile rendertargetProperties{ R"(
render_target rendertarget {
width_ratio = 0.5;
height_ratio = 0.5;
@ -52,7 +55,9 @@ namespace
technique { passes = downsample2x, main; }
)" };
TestingOpenMW::VFSTestFile uniform_properties{ R"(
constexpr VFS::Path::NormalizedView uniformPropertiesPath("shaders/uniform_properties.omwfx");
TestingOpenMW::VFSTestFile uniformProperties{ R"(
uniform_vec4 uVec4 {
default = vec4(0,0,0,0);
min = vec4(0,1,0,0);
@ -66,13 +71,17 @@ namespace
technique { passes = main; }
)" };
TestingOpenMW::VFSTestFile missing_sampler_source{ R"(
constexpr VFS::Path::NormalizedView missingSamplerSourcePath("shaders/missing_sampler_source.omwfx");
TestingOpenMW::VFSTestFile missingSamplerSource{ R"(
sampler_1d mysampler1d { }
fragment main { }
technique { passes = main; }
)" };
TestingOpenMW::VFSTestFile repeated_shared_block{ R"(
constexpr VFS::Path::NormalizedView repeatedSharedBlockPath("shaders/repeated_shared_block.omwfx");
TestingOpenMW::VFSTestFile repeatedSharedBlock{ R"(
shared {
float myfloat = 1.0;
}
@ -92,11 +101,11 @@ namespace
TechniqueTest()
: mVFS(TestingOpenMW::createTestVFS({
{ "shaders/technique_properties.omwfx", &technique_properties },
{ "shaders/rendertarget_properties.omwfx", &rendertarget_properties },
{ "shaders/uniform_properties.omwfx", &uniform_properties },
{ "shaders/missing_sampler_source.omwfx", &missing_sampler_source },
{ "shaders/repeated_shared_block.omwfx", &repeated_shared_block },
{ techniquePropertiesPath, &techniqueProperties },
{ rendertargetPropertiesPath, &rendertargetProperties },
{ uniformPropertiesPath, &uniformProperties },
{ missingSamplerSourcePath, &missingSamplerSource },
{ repeatedSharedBlockPath, &repeatedSharedBlock },
}))
, mImageManager(mVFS.get(), 0)
{

@ -44,7 +44,7 @@ namespace
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[1]), "PLAYER : my_mod/player.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[2]), "CUSTOM : my_mod/some_other_script.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[3]), "PLAYER NPC CREATURE : my_mod/some_other_script.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[4]), ": my_mod/player.LUA");
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[4]), ": my_mod/player.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(cfg.mScripts[5]), "CUSTOM CREATURE : my_mod/creature.lua");
LuaUtil::ScriptsConfiguration conf;
@ -54,7 +54,7 @@ namespace
// cfg.mScripts[1] is overridden by cfg.mScripts[4]
// cfg.mScripts[2] is overridden by cfg.mScripts[3]
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[1]), "PLAYER NPC CREATURE : my_mod/some_other_script.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[2]), ": my_mod/player.LUA");
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[2]), ": my_mod/player.lua");
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[3]), "CUSTOM CREATURE : my_mod/creature.lua");
EXPECT_THAT(asVector(conf.getGlobalConf()), ElementsAre(Pair(0, "")));
@ -89,7 +89,7 @@ namespace
{
ESM::LuaScriptsCfg cfg;
ESM::LuaScriptCfg& script1 = cfg.mScripts.emplace_back();
script1.mScriptPath = "Script1.lua";
script1.mScriptPath = VFS::Path::Normalized("Script1.lua");
script1.mInitializationData = "data1";
script1.mFlags = ESM::LuaScriptCfg::sPlayer;
script1.mTypes.push_back(ESM::REC_CREA);
@ -98,12 +98,12 @@ namespace
script1.mRefs.push_back({ true, 2, 4, "" });
ESM::LuaScriptCfg& script2 = cfg.mScripts.emplace_back();
script2.mScriptPath = "Script2.lua";
script2.mScriptPath = VFS::Path::Normalized("Script2.lua");
script2.mFlags = ESM::LuaScriptCfg::sCustom;
script2.mTypes.push_back(ESM::REC_CONT);
ESM::LuaScriptCfg& script1Extra = cfg.mScripts.emplace_back();
script1Extra.mScriptPath = "script1.LUA";
script1Extra.mScriptPath = VFS::Path::Normalized("script1.LUA");
script1Extra.mFlags = ESM::LuaScriptCfg::sCustom | ESM::LuaScriptCfg::sMerge;
script1Extra.mTypes.push_back(ESM::REC_NPC_);
script1Extra.mRecords.push_back({ false, ESM::RefId::stringRefId("rat"), "" });
@ -115,8 +115,8 @@ namespace
conf.init(cfg);
ASSERT_EQ(conf.size(), 2);
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[0]),
"CUSTOM PLAYER CREATURE NPC : Script1.lua ; data 5 bytes ; 3 records ; 4 objects");
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[1]), "CUSTOM CONTAINER : Script2.lua");
"CUSTOM PLAYER CREATURE NPC : script1.lua ; data 5 bytes ; 3 records ; 4 objects");
EXPECT_EQ(LuaUtil::scriptCfgToString(conf[1]), "CUSTOM CONTAINER : script2.lua");
EXPECT_THAT(asVector(conf.getPlayerConf()), ElementsAre(Pair(0, "data1")));
EXPECT_THAT(asVector(conf.getLocalConf(ESM::REC_CONT, ESM::RefId::stringRefId("something"), ESM::RefNum())),
@ -139,7 +139,7 @@ namespace
ElementsAre(Pair(0, "data1"), Pair(1, "")));
ESM::LuaScriptCfg& script3 = cfg.mScripts.emplace_back();
script3.mScriptPath = "script1.lua";
script3.mScriptPath = VFS::Path::Normalized("script1.lua");
script3.mFlags = ESM::LuaScriptCfg::sGlobal;
EXPECT_ERROR(conf.init(cfg), "Flags mismatch for script1.lua");
}
@ -168,13 +168,13 @@ namespace
}
{
ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
script.mScriptPath = "test_global.lua";
script.mScriptPath = VFS::Path::Normalized("test_global.lua");
script.mFlags = ESM::LuaScriptCfg::sGlobal;
script.mInitializationData = luaData;
}
{
ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
script.mScriptPath = "test_local.lua";
script.mScriptPath = VFS::Path::Normalized("test_local.lua");
script.mFlags = ESM::LuaScriptCfg::sMerge;
script.mTypes.push_back(ESM::REC_DOOR);
script.mTypes.push_back(ESM::REC_MISC);

@ -18,6 +18,13 @@ namespace
return lua.safe_script("return " + luaCode).get<T>();
}
constexpr VFS::Path::NormalizedView test1EnPath("l10n/test1/en.yaml");
constexpr VFS::Path::NormalizedView test1EnUsPath("l10n/test1/en_us.yaml");
constexpr VFS::Path::NormalizedView test1DePath("l10n/test1/de.yaml");
constexpr VFS::Path::NormalizedView test2EnPath("l10n/test2/en.yaml");
constexpr VFS::Path::NormalizedView test3EnPath("l10n/test3/en.yaml");
constexpr VFS::Path::NormalizedView test3DePath("l10n/test3/de.yaml");
VFSTestFile invalidScript("not a script");
VFSTestFile incorrectScript(
"return { incorrectSection = {}, engineHandlers = { incorrectHandler = function() end } }");
@ -67,12 +74,12 @@ you_have_arrows: "Arrows count: {count}"
struct LuaL10nTest : Test
{
std::unique_ptr<VFS::Manager> mVFS = createTestVFS({
{ "l10n/Test1/en.yaml", &test1En },
{ "l10n/Test1/en_US.yaml", &test1EnUS },
{ "l10n/Test1/de.yaml", &test1De },
{ "l10n/Test2/en.yaml", &test2En },
{ "l10n/Test3/en.yaml", &test1En },
{ "l10n/Test3/de.yaml", &test1De },
{ test1EnPath, &test1En },
{ test1EnUsPath, &test1EnUS },
{ test1DePath, &test1De },
{ test2EnPath, &test2En },
{ test3EnPath, &test1En },
{ test3DePath, &test1De },
});
LuaUtil::ScriptsConfiguration mCfg;

@ -9,6 +9,8 @@ namespace
{
using namespace testing;
constexpr VFS::Path::NormalizedView counterPath("aaa/counter.lua");
TestingOpenMW::VFSTestFile counterFile(R"X(
x = 42
return {
@ -17,8 +19,12 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView invalidPath("invalid.lua");
TestingOpenMW::VFSTestFile invalidScriptFile("Invalid script");
constexpr VFS::Path::NormalizedView testsPath("bbb/tests.lua");
TestingOpenMW::VFSTestFile testsFile(R"X(
return {
-- should work
@ -51,6 +57,8 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView metaIndexErrorPath("metaindexerror.lua");
TestingOpenMW::VFSTestFile metaIndexErrorFile(
"return setmetatable({}, { __index = function(t, key) error('meta index error') end })");
@ -66,14 +74,24 @@ return {
return buf.str();
}
constexpr VFS::Path::NormalizedView bigPath("big.lua");
TestingOpenMW::VFSTestFile bigScriptFile(genBigScript());
constexpr VFS::Path::NormalizedView requireBigPath("requirebig.lua");
TestingOpenMW::VFSTestFile requireBigScriptFile("local x = require('big') ; return {x}");
struct LuaStateTest : Test
{
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "aaa/counter.lua", &counterFile },
{ "bbb/tests.lua", &testsFile }, { "invalid.lua", &invalidScriptFile }, { "big.lua", &bigScriptFile },
{ "requireBig.lua", &requireBigScriptFile }, { "metaIndexError.lua", &metaIndexErrorFile } });
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({
{ counterPath, &counterFile },
{ testsPath, &testsFile },
{ invalidPath, &invalidScriptFile },
{ bigPath, &bigScriptFile },
{ requireBigPath, &requireBigScriptFile },
{ metaIndexErrorPath, &metaIndexErrorFile },
});
LuaUtil::ScriptsConfiguration mCfg;
LuaUtil::LuaState mLua{ mVFS.get(), &mCfg };
@ -81,7 +99,7 @@ return {
TEST_F(LuaStateTest, Sandbox)
{
const VFS::Path::Normalized path("aaa/counter.lua");
const VFS::Path::Normalized path(counterPath);
sol::table script1 = mLua.runInNewSandbox(path);
EXPECT_EQ(LuaUtil::call(script1["get"]).get<int>(), 42);

@ -14,11 +14,22 @@ namespace
using namespace testing;
using namespace TestingOpenMW;
constexpr VFS::Path::NormalizedView invalidPath("invalid.lua");
VFSTestFile invalidScript("not a script");
constexpr VFS::Path::NormalizedView incorrectPath("incorrect.lua");
VFSTestFile incorrectScript(
"return { incorrectSection = {}, engineHandlers = { incorrectHandler = function() end } }");
constexpr VFS::Path::NormalizedView emptyPath("empty.lua");
VFSTestFile emptyScript("");
constexpr VFS::Path::NormalizedView test1Path("test1.lua");
constexpr VFS::Path::NormalizedView test2Path("test2.lua");
VFSTestFile testScript(R"X(
return {
engineHandlers = {
@ -33,6 +44,8 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView stopEventPath("stopevent.lua");
VFSTestFile stopEventScript(R"X(
return {
eventHandlers = {
@ -44,6 +57,9 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView loadSave1Path("loadsave1.lua");
constexpr VFS::Path::NormalizedView loadSave2Path("loadsave2.lua");
VFSTestFile loadSaveScript(R"X(
x = 0
y = 0
@ -70,6 +86,8 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView testInterfacePath("testinterface.lua");
VFSTestFile interfaceScript(R"X(
return {
interfaceName = "TestInterface",
@ -80,6 +98,8 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView overrideInterfacePath("overrideinterface.lua");
VFSTestFile overrideInterfaceScript(R"X(
local old = nil
local interface = {
@ -104,6 +124,8 @@ return {
}
)X");
constexpr VFS::Path::NormalizedView useInterfacePath("useinterface.lua");
VFSTestFile useInterfaceScript(R"X(
local interfaces = require('openmw.interfaces')
return {
@ -118,17 +140,17 @@ return {
struct LuaScriptsContainerTest : Test
{
std::unique_ptr<VFS::Manager> mVFS = createTestVFS({
{ "invalid.lua", &invalidScript },
{ "incorrect.lua", &incorrectScript },
{ "empty.lua", &emptyScript },
{ "test1.lua", &testScript },
{ "test2.lua", &testScript },
{ "stopEvent.lua", &stopEventScript },
{ "loadSave1.lua", &loadSaveScript },
{ "loadSave2.lua", &loadSaveScript },
{ "testInterface.lua", &interfaceScript },
{ "overrideInterface.lua", &overrideInterfaceScript },
{ "useInterface.lua", &useInterfaceScript },
{ invalidPath, &invalidScript },
{ incorrectPath, &incorrectScript },
{ emptyPath, &emptyScript },
{ test1Path, &testScript },
{ test2Path, &testScript },
{ stopEventPath, &stopEventScript },
{ loadSave1Path, &loadSaveScript },
{ loadSave2Path, &loadSaveScript },
{ testInterfacePath, &interfaceScript },
{ overrideInterfacePath, &overrideInterfaceScript },
{ useInterfacePath, &useInterfaceScript },
});
LuaUtil::ScriptsConfiguration mCfg;
@ -152,39 +174,49 @@ CUSTOM, PLAYER: useInterface.lua
)X");
mCfg.init(std::move(cfg));
}
int getId(VFS::Path::NormalizedView path) const
{
const std::optional<int> id = mCfg.findId(path);
if (!id.has_value())
throw std::invalid_argument("Script id is not found: " + std::string(path.value()));
return *id;
}
};
TEST_F(LuaScriptsContainerTest, VerifyStructure)
TEST_F(LuaScriptsContainerTest, addCustomScriptShouldNotStartInvalidScript)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
{
testing::internal::CaptureStdout();
EXPECT_FALSE(scripts.addCustomScript(*mCfg.findId("invalid.lua")));
std::string output = testing::internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Can't start Test[invalid.lua]"));
}
{
testing::internal::CaptureStdout();
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("incorrect.lua")));
std::string output = testing::internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Not supported handler 'incorrectHandler' in Test[incorrect.lua]"));
EXPECT_THAT(output, HasSubstr("Not supported section 'incorrectSection' in Test[incorrect.lua]"));
}
{
testing::internal::CaptureStdout();
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("empty.lua")));
EXPECT_FALSE(scripts.addCustomScript(*mCfg.findId("empty.lua"))); // already present
EXPECT_EQ(internal::GetCapturedStdout(), "");
}
testing::internal::CaptureStdout();
EXPECT_FALSE(scripts.addCustomScript(getId(invalidPath)));
std::string output = testing::internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Can't start Test[invalid.lua]"));
}
TEST_F(LuaScriptsContainerTest, addCustomScriptShouldNotSuportScriptsWithInvalidHandlerAndSection)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
testing::internal::CaptureStdout();
EXPECT_TRUE(scripts.addCustomScript(getId(incorrectPath)));
std::string output = testing::internal::GetCapturedStdout();
EXPECT_THAT(output, HasSubstr("Not supported handler 'incorrectHandler' in Test[incorrect.lua]"));
EXPECT_THAT(output, HasSubstr("Not supported section 'incorrectSection' in Test[incorrect.lua]"));
}
TEST_F(LuaScriptsContainerTest, addCustomScriptShouldReturnFalseForDuplicates)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
EXPECT_TRUE(scripts.addCustomScript(getId(emptyPath)));
EXPECT_FALSE(scripts.addCustomScript(getId(emptyPath)));
}
TEST_F(LuaScriptsContainerTest, CallHandler)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
testing::internal::CaptureStdout();
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test1.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
EXPECT_TRUE(scripts.addCustomScript(getId(test1Path)));
EXPECT_TRUE(scripts.addCustomScript(getId(stopEventPath)));
EXPECT_TRUE(scripts.addCustomScript(getId(test2Path)));
scripts.update(1.5f);
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test1.lua]:\t update 1.5\n"
@ -194,9 +226,10 @@ CUSTOM, PLAYER: useInterface.lua
TEST_F(LuaScriptsContainerTest, CallEvent)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test1.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
EXPECT_TRUE(scripts.addCustomScript(getId(test1Path)));
EXPECT_TRUE(scripts.addCustomScript(getId(stopEventPath)));
EXPECT_TRUE(scripts.addCustomScript(getId(test2Path)));
sol::state_view sol = mLua.unsafeState();
std::string X0 = LuaUtil::serialize(sol.create_table_with("x", 0.5));
@ -212,7 +245,7 @@ CUSTOM, PLAYER: useInterface.lua
scripts.receiveEvent("Event1", X1);
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event1 1.5\n"
"Test[stopEvent.lua]:\t event1 1.5\n"
"Test[stopevent.lua]:\t event1 1.5\n"
"Test[test1.lua]:\t event1 1.5\n");
}
{
@ -227,7 +260,7 @@ CUSTOM, PLAYER: useInterface.lua
scripts.receiveEvent("Event1", X0);
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test2.lua]:\t event1 0.5\n"
"Test[stopEvent.lua]:\t event1 0.5\n");
"Test[stopevent.lua]:\t event1 0.5\n");
}
{
testing::internal::CaptureStdout();
@ -241,9 +274,11 @@ CUSTOM, PLAYER: useInterface.lua
TEST_F(LuaScriptsContainerTest, RemoveScript)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test1.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("stopEvent.lua")));
EXPECT_TRUE(scripts.addCustomScript(*mCfg.findId("test2.lua")));
EXPECT_TRUE(scripts.addCustomScript(getId(test1Path)));
EXPECT_TRUE(scripts.addCustomScript(getId(stopEventPath)));
EXPECT_TRUE(scripts.addCustomScript(getId(test2Path)));
sol::state_view sol = mLua.unsafeState();
std::string X = LuaUtil::serialize(sol.create_table_with("x", 0.5));
@ -255,11 +290,11 @@ CUSTOM, PLAYER: useInterface.lua
"Test[test1.lua]:\t update 1.5\n"
"Test[test2.lua]:\t update 1.5\n"
"Test[test2.lua]:\t event1 0.5\n"
"Test[stopEvent.lua]:\t event1 0.5\n");
"Test[stopevent.lua]:\t event1 0.5\n");
}
{
testing::internal::CaptureStdout();
int stopEventScriptId = *mCfg.findId("stopEvent.lua");
const int stopEventScriptId = getId(stopEventPath);
EXPECT_TRUE(scripts.hasScript(stopEventScriptId));
scripts.removeScript(stopEventScriptId);
EXPECT_FALSE(scripts.hasScript(stopEventScriptId));
@ -273,7 +308,7 @@ CUSTOM, PLAYER: useInterface.lua
}
{
testing::internal::CaptureStdout();
scripts.removeScript(*mCfg.findId("test1.lua"));
scripts.removeScript(getId(test1Path));
scripts.update(1.5f);
scripts.receiveEvent("Event1", X);
EXPECT_EQ(internal::GetCapturedStdout(),
@ -290,19 +325,19 @@ CUSTOM, PLAYER: useInterface.lua
scripts.addAutoStartedScripts();
scripts.update(1.5f);
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[overrideInterface.lua]:\toverride\n"
"Test[overrideInterface.lua]:\tinit\n"
"Test[overrideInterface.lua]:\tNEW FN\t4.5\n"
"Test[testInterface.lua]:\tFN\t4.5\n");
"Test[overrideinterface.lua]:\toverride\n"
"Test[overrideinterface.lua]:\tinit\n"
"Test[overrideinterface.lua]:\tNEW FN\t4.5\n"
"Test[testinterface.lua]:\tFN\t4.5\n");
}
TEST_F(LuaScriptsContainerTest, Interface)
{
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
scripts.setAutoStartConf(mCfg.getLocalConf(ESM::REC_CREA, ESM::RefId(), ESM::RefNum()));
int addIfaceId = *mCfg.findId("testInterface.lua");
int overrideIfaceId = *mCfg.findId("overrideInterface.lua");
int useIfaceId = *mCfg.findId("useInterface.lua");
const int addIfaceId = getId(testInterfacePath);
const int overrideIfaceId = getId(overrideInterfacePath);
const int useIfaceId = getId(useInterfacePath);
testing::internal::CaptureStdout();
scripts.addAutoStartedScripts();
@ -317,11 +352,11 @@ CUSTOM, PLAYER: useInterface.lua
scripts.removeScript(overrideIfaceId);
scripts.update(1.5f);
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[overrideInterface.lua]:\toverride\n"
"Test[overrideInterface.lua]:\tinit\n"
"Test[overrideInterface.lua]:\tNEW FN\t4.5\n"
"Test[testInterface.lua]:\tFN\t4.5\n"
"Test[testInterface.lua]:\tFN\t3.5\n");
"Test[overrideinterface.lua]:\toverride\n"
"Test[overrideinterface.lua]:\tinit\n"
"Test[overrideinterface.lua]:\tNEW FN\t4.5\n"
"Test[testinterface.lua]:\tFN\t4.5\n"
"Test[testinterface.lua]:\tFN\t3.5\n");
}
TEST_F(LuaScriptsContainerTest, LoadSave)
@ -334,7 +369,7 @@ CUSTOM, PLAYER: useInterface.lua
scripts3.setAutoStartConf(mCfg.getPlayerConf());
scripts1.addAutoStartedScripts();
EXPECT_TRUE(scripts1.addCustomScript(*mCfg.findId("test1.lua")));
EXPECT_TRUE(scripts1.addCustomScript(getId(test1Path)));
sol::state_view sol = mLua.unsafeState();
scripts1.receiveEvent("Set", LuaUtil::serialize(sol.create_table_with("n", 1, "x", 0.5, "y", 3.5)));
@ -349,23 +384,23 @@ CUSTOM, PLAYER: useInterface.lua
scripts2.receiveEvent("Print", "");
EXPECT_EQ(internal::GetCapturedStdout(),
"Test[test1.lua]:\tload\n"
"Test[loadSave2.lua]:\t0.5\t3.5\n"
"Test[loadSave1.lua]:\t2.5\t1.5\n"
"Test[loadsave2.lua]:\t0.5\t3.5\n"
"Test[loadsave1.lua]:\t2.5\t1.5\n"
"Test[test1.lua]:\tprint\n");
EXPECT_FALSE(scripts2.hasScript(*mCfg.findId("testInterface.lua")));
EXPECT_FALSE(scripts2.hasScript(getId(testInterfacePath)));
}
{
testing::internal::CaptureStdout();
scripts3.load(data);
scripts3.receiveEvent("Print", "");
EXPECT_EQ(internal::GetCapturedStdout(),
"Ignoring Test[loadSave1.lua]; this script is not allowed here\n"
"Ignoring Test[loadsave1.lua]; this script is not allowed here\n"
"Test[test1.lua]:\tload\n"
"Test[overrideInterface.lua]:\toverride\n"
"Test[overrideInterface.lua]:\tinit\n"
"Test[loadSave2.lua]:\t0.5\t3.5\n"
"Test[overrideinterface.lua]:\toverride\n"
"Test[overrideinterface.lua]:\tinit\n"
"Test[loadsave2.lua]:\t0.5\t3.5\n"
"Test[test1.lua]:\tprint\n");
EXPECT_TRUE(scripts3.hasScript(*mCfg.findId("testInterface.lua")));
EXPECT_TRUE(scripts3.hasScript(getId(testInterfacePath)));
}
}
@ -373,8 +408,8 @@ CUSTOM, PLAYER: useInterface.lua
{
using TimerType = LuaUtil::ScriptsContainer::TimerType;
LuaUtil::ScriptsContainer scripts(&mLua, "Test");
int test1Id = *mCfg.findId("test1.lua");
int test2Id = *mCfg.findId("test2.lua");
const int test1Id = getId(test1Path);
const int test2Id = getId(test2Path);
testing::internal::CaptureStdout();
EXPECT_TRUE(scripts.addCustomScript(test1Id));

@ -8,16 +8,17 @@ namespace
using namespace Misc::ResourceHelpers;
TEST(CorrectSoundPath, wav_files_not_overridden_with_mp3_in_vfs_are_not_corrected)
{
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/bar.wav", nullptr } });
constexpr VFS::Path::NormalizedView path("sound/bar.wav");
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { path, nullptr } });
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/bar.wav");
}
TEST(CorrectSoundPath, wav_files_overridden_with_mp3_in_vfs_are_corrected)
{
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { "sound/foo.mp3", nullptr } });
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
EXPECT_EQ(correctSoundPath(path, *mVFS), "sound/foo.mp3");
constexpr VFS::Path::NormalizedView mp3("sound/foo.mp3");
std::unique_ptr<VFS::Manager> mVFS = TestingOpenMW::createTestVFS({ { mp3, nullptr } });
constexpr VFS::Path::NormalizedView wav("sound/foo.wav");
EXPECT_EQ(correctSoundPath(wav, *mVFS), "sound/foo.mp3");
}
TEST(CorrectSoundPath, corrected_path_does_not_check_existence_in_vfs)

@ -850,8 +850,8 @@ namespace MWLua
bool isMenu = mConfiguration[i].mFlags & ESM::LuaScriptCfg::sMenu;
out << std::left;
out << " " << std::setw(nameW) << mConfiguration[i].mScriptPath;
if (mConfiguration[i].mScriptPath.size() > nameW)
out << " " << std::setw(nameW) << mConfiguration[i].mScriptPath.value();
if (mConfiguration[i].mScriptPath.value().size() > nameW)
out << "\n " << std::setw(nameW) << ""; // if path is too long, break line
out << std::right;
out << std::setw(valueW) << static_cast<int64_t>(activeStats[i].mAvgInstructionCount);

@ -390,7 +390,7 @@ namespace MWLua
};
objectT["addScript"] = [context](const GObject& object, std::string_view path, sol::object initData) {
const LuaUtil::ScriptsConfiguration& cfg = context.mLua->getConfiguration();
std::optional<int> scriptId = cfg.findId(path);
std::optional<int> scriptId = cfg.findId(VFS::Path::Normalized(path));
if (!scriptId)
throw std::runtime_error("Unknown script: " + std::string(path));
if (!(cfg[*scriptId].mFlags & ESM::LuaScriptCfg::sCustom))
@ -407,7 +407,7 @@ namespace MWLua
};
objectT["hasScript"] = [lua = context.mLua](const GObject& object, std::string_view path) {
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
std::optional<int> scriptId = cfg.findId(path);
std::optional<int> scriptId = cfg.findId(VFS::Path::Normalized(path));
if (!scriptId)
return false;
MWWorld::Ptr ptr = object.ptr();
@ -419,7 +419,7 @@ namespace MWLua
};
objectT["removeScript"] = [lua = context.mLua](const GObject& object, std::string_view path) {
const LuaUtil::ScriptsConfiguration& cfg = lua->getConfiguration();
std::optional<int> scriptId = cfg.findId(path);
std::optional<int> scriptId = cfg.findId(VFS::Path::Normalized(path));
if (!scriptId)
throw std::runtime_error("Unknown script: " + std::string(path));
MWWorld::Ptr ptr = object.ptr();

@ -2,6 +2,8 @@
#define OPENMW_ESM_LUASCRIPTS_H
#include <components/esm/refid.hpp>
#include <components/vfs/pathutil.hpp>
#include <cstdint>
#include <string>
#include <vector>
@ -25,7 +27,7 @@ namespace ESM
static constexpr Flags sMenu = 1ull << 4; // start as a menu script
std::string mScriptPath; // VFS path to the script.
VFS::Path::Normalized mScriptPath;
std::string mInitializationData; // Serialized Lua table. It is a binary data. Can contain '\0'.
Flags mFlags; // bitwise OR of Flags.
@ -85,7 +87,7 @@ namespace ESM
struct LuaScript
{
std::string mScriptPath;
VFS::Path::Normalized mScriptPath;
std::string mData; // Serialized Lua table. It is a binary data. Can contain '\0'.
std::vector<LuaTimer> mTimers;
};

@ -60,16 +60,17 @@ namespace LuaUtil
const ESM::LuaScriptCfg& script = cfg.mScripts[i];
bool global = script.mFlags & ESM::LuaScriptCfg::sGlobal;
if (global && (script.mFlags & ~ESM::LuaScriptCfg::sMerge) != ESM::LuaScriptCfg::sGlobal)
throw std::runtime_error(std::string("Global script can not have local flags: ") + script.mScriptPath);
throw std::runtime_error(
std::string("Global script can not have local flags: ") + script.mScriptPath.value());
if (global && (!script.mTypes.empty() || !script.mRecords.empty() || !script.mRefs.empty()))
throw std::runtime_error(std::string("Global script can not have per-type and per-object configuration")
+ script.mScriptPath);
auto [it, inserted] = mPathToIndex.emplace(Misc::StringUtils::lowerCase(script.mScriptPath), i);
+ script.mScriptPath.value());
auto [it, inserted] = mPathToIndex.emplace(script.mScriptPath, i);
if (inserted)
continue;
ESM::LuaScriptCfg& oldScript = cfg.mScripts[it->second];
if (global != bool(oldScript.mFlags & ESM::LuaScriptCfg::sGlobal))
throw std::runtime_error(std::string("Flags mismatch for ") + script.mScriptPath);
throw std::runtime_error(std::string("Flags mismatch for ") + script.mScriptPath.value());
if (script.mFlags & ESM::LuaScriptCfg::sMerge)
{
oldScript.mFlags |= (script.mFlags & ~ESM::LuaScriptCfg::sMerge);
@ -113,7 +114,7 @@ namespace LuaUtil
}
}
std::optional<int> ScriptsConfiguration::findId(std::string_view path) const
std::optional<int> ScriptsConfiguration::findId(VFS::Path::NormalizedView path) const
{
auto it = mPathToIndex.find(path);
if (it != mPathToIndex.end())
@ -199,7 +200,7 @@ namespace LuaUtil
scriptPath = scriptPath.substr(1);
ESM::LuaScriptCfg& script = cfg.mScripts.emplace_back();
script.mScriptPath = std::string(scriptPath);
script.mScriptPath = VFS::Path::Normalized(scriptPath);
script.mFlags = 0;
// Parse tags

@ -4,8 +4,9 @@
#include <map>
#include <optional>
#include "components/esm/luascripts.hpp"
#include "components/esm3/cellref.hpp"
#include <components/esm/luascripts.hpp>
#include <components/esm3/refnum.hpp>
#include <components/vfs/pathutil.hpp>
namespace LuaUtil
{
@ -19,7 +20,8 @@ namespace LuaUtil
size_t size() const { return mScripts.size(); }
const ESM::LuaScriptCfg& operator[](int id) const { return mScripts[id]; }
std::optional<int> findId(std::string_view path) const;
std::optional<int> findId(VFS::Path::NormalizedView path) const;
bool isCustomScript(int id) const { return mScripts[id].mFlags & ESM::LuaScriptCfg::sCustom; }
ScriptIdsWithInitializationData getMenuConf() const { return getConfByFlag(ESM::LuaScriptCfg::sMenu); }
@ -32,7 +34,7 @@ namespace LuaUtil
ScriptIdsWithInitializationData getConfByFlag(ESM::LuaScriptCfg::Flags flag) const;
std::vector<ESM::LuaScriptCfg> mScripts;
std::map<std::string, int, std::less<>> mPathToIndex;
std::map<VFS::Path::Normalized, int, std::less<>> mPathToIndex;
struct DetailedConf
{

@ -73,7 +73,7 @@ namespace LuaUtil
if (mScripts.count(scriptId) != 0)
return false; // already present
const std::string& path = scriptPath(scriptId);
const VFS::Path::Normalized& path = scriptPath(scriptId);
std::string debugName = mNamePrefix;
debugName.push_back('[');
debugName.append(path);

@ -211,7 +211,7 @@ namespace LuaUtil
sol::table mHiddenData;
std::map<std::string, sol::main_protected_function> mRegisteredCallbacks;
std::map<int64_t, sol::main_protected_function> mTemporaryCallbacks;
std::string mPath;
VFS::Path::Normalized mPath;
ScriptStats mStats;
};
struct Timer
@ -239,7 +239,12 @@ namespace LuaUtil
Script& getScript(int scriptId);
void printError(int scriptId, std::string_view msg, const std::exception& e);
const std::string& scriptPath(int scriptId) const { return mLua.getConfiguration()[scriptId].mScriptPath; }
const VFS::Path::Normalized& scriptPath(int scriptId) const
{
return mLua.getConfiguration()[scriptId].mScriptPath;
}
void callOnInit(LuaView& view, int scriptId, const sol::function& onInit, std::string_view data);
void callTimer(const Timer& t);
void updateTimerQueue(std::vector<Timer>& timerQueue, double time);

@ -75,7 +75,7 @@ namespace TestingOpenMW
}
inline std::unique_ptr<VFS::Manager> createTestVFS(
std::initializer_list<std::pair<std::string_view, VFS::File*>> files)
std::initializer_list<std::pair<VFS::Path::NormalizedView, VFS::File*>> files)
{
return createTestVFS(VFS::FileMap(files.begin(), files.end()));
}

Loading…
Cancel
Save