1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-11 01:13:23 +00:00

Merge branch 'lua_ref_id' into 'master'

Use ESM::RefId for Lua records

See merge request OpenMW/openmw!2857
This commit is contained in:
psi29a 2023-03-26 11:09:31 +00:00
commit 6756e1ba97
39 changed files with 571 additions and 248 deletions

View File

@ -50,6 +50,7 @@ variables:
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw_test_suite --gtest_output="xml:openmw_tests.xml"; fi
- if [[ "${BUILD_TESTS_ONLY}" ]]; then ./openmw-cs-tests --gtest_output="xml:openmw_cs_tests.xml"; fi
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_detournavigator_navmeshtilescache_benchmark; fi
- if [[ "${BUILD_TESTS_ONLY}" && ! "${BUILD_WITH_CODE_COVERAGE}" ]]; then ./openmw_esm_refid_benchmark; fi
- ccache -s
- df -h
- if [[ "${BUILD_WITH_CODE_COVERAGE}" ]]; then gcovr --xml-pretty --exclude-unreachable-branches --print-summary --root "${CI_PROJECT_DIR}" -j $(nproc) -o ../coverage.xml; fi

View File

@ -2,19 +2,5 @@ if(OPENMW_USE_SYSTEM_BENCHMARK)
find_package(benchmark REQUIRED)
endif()
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark detournavigator/navmeshtilescache.cpp)
target_compile_features(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE cxx_std_17)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
endif()
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE --coverage)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark gcov)
endif()
add_subdirectory(detournavigator)
add_subdirectory(esm)

View File

@ -0,0 +1,15 @@
openmw_add_executable(openmw_detournavigator_navmeshtilescache_benchmark navmeshtilescache.cpp)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark benchmark::benchmark components)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
endif()
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE --coverage)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark gcov)
endif()

View File

@ -191,7 +191,7 @@ namespace
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
std::size_t n = 0;
while (state.KeepRunning())
for (auto _ : state)
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);

View File

@ -0,0 +1,15 @@
openmw_add_executable(openmw_esm_refid_benchmark benchrefid.cpp)
target_link_libraries(openmw_esm_refid_benchmark benchmark::benchmark components)
if (UNIX AND NOT APPLE)
target_link_libraries(openmw_esm_refid_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.16 AND MSVC)
target_precompile_headers(openmw_esm_refid_benchmark PRIVATE <algorithm>)
endif()
if (BUILD_WITH_CODE_COVERAGE)
target_compile_options(openmw_esm_refid_benchmark PRIVATE --coverage)
target_link_libraries(openmw_esm_refid_benchmark gcov)
endif()

View File

@ -0,0 +1,203 @@
#include <benchmark/benchmark.h>
#include "components/esm/refid.hpp"
#include <algorithm>
#include <cstddef>
#include <random>
#include <string>
#include <vector>
namespace
{
constexpr std::size_t refIdsCount = 64 * 1024;
template <class Random>
std::string generateText(std::size_t size, Random& random)
{
std::uniform_int_distribution<int> distribution('A', 'z');
std::string result;
result.reserve(size);
std::generate_n(std::back_inserter(result), size, [&] { return distribution(random); });
return result;
}
template <class Random>
std::vector<ESM::RefId> generateStringRefIds(std::size_t size, Random& random)
{
std::vector<ESM::RefId> result;
result.reserve(refIdsCount);
std::generate_n(
std::back_inserter(result), refIdsCount, [&] { return ESM::StringRefId(generateText(size, random)); });
return result;
}
template <class Serialize>
std::vector<std::string> generateSerializedRefIds(const std::vector<ESM::RefId>& generated, Serialize&& serialize)
{
std::vector<std::string> result;
result.reserve(generated.size());
for (ESM::RefId refId : generated)
result.push_back(serialize(refId));
return result;
}
template <class Random, class Serialize>
std::vector<std::string> generateSerializedStringRefIds(std::size_t size, Random& random, Serialize&& serialize)
{
return generateSerializedRefIds(generateStringRefIds(size, random), serialize);
}
template <class Random>
std::vector<ESM::RefId> generateIndexRefIds(Random& random)
{
std::vector<ESM::RefId> result;
result.reserve(refIdsCount);
std::uniform_int_distribution<std::uint32_t> distribution(0, std::numeric_limits<std::uint32_t>::max());
std::generate_n(std::back_inserter(result), refIdsCount,
[&] { return ESM::IndexRefId(ESM::REC_ARMO, distribution(random)); });
return result;
}
template <class Random, class Serialize>
std::vector<std::string> generateSerializedIndexRefIds(Random& random, Serialize&& serialize)
{
return generateSerializedRefIds(generateIndexRefIds(random), serialize);
}
template <class Random>
std::vector<ESM::RefId> generateGeneratedRefIds(Random& random)
{
std::vector<ESM::RefId> result;
result.reserve(refIdsCount);
std::uniform_int_distribution<std::uint64_t> distribution(0, std::numeric_limits<std::uint64_t>::max());
std::generate_n(
std::back_inserter(result), refIdsCount, [&] { return ESM::GeneratedRefId(distribution(random)); });
return result;
}
template <class Random, class Serialize>
std::vector<std::string> generateSerializedGeneratedRefIds(Random& random, Serialize&& serialize)
{
return generateSerializedRefIds(generateGeneratedRefIds(random), serialize);
}
void serializeRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serialize());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
void serializeTextStringRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextStringRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
void serializeTextGeneratedRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextGeneratedRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
void serializeTextIndexRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateIndexRefIds(random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextIndexRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
}
BENCHMARK(serializeRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(deserializeRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(serializeTextStringRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(deserializeTextStringRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(serializeTextGeneratedRefId);
BENCHMARK(deserializeTextGeneratedRefId);
BENCHMARK(serializeTextIndexRefId);
BENCHMARK(deserializeTextIndexRefId);
BENCHMARK_MAIN();

View File

@ -40,8 +40,8 @@ namespace MWLua
};
cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getNameId(); });
cellT["region"]
= sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getRegion().getRefIdString(); });
cellT["region"] = sol::readonly_property(
[](const CellT& c) -> std::string { return c.mStore->getCell()->getRegion().serializeText(); });
cellT["gridX"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridX(); });
cellT["gridY"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridY(); });
cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); });

View File

@ -83,7 +83,7 @@ namespace MWLua
sol::usertype<LocalMWScript> mwscript = context.mLua->sol().new_usertype<LocalMWScript>("LocalMWScript");
mwscript[sol::meta_function::to_string] = [](const LocalMWScript& s) {
return s.mSelf.ptr().getRefData().getLocals().getScriptId().getRefIdString();
return s.mSelf.ptr().getRefData().getLocals().getScriptId().toDebugString();
};
mwscript[sol::meta_function::index] = [](const LocalMWScript& s, std::string_view var) {
MWScript::Locals& locals = s.mSelf.ptr().getRefData().getLocals();
@ -92,8 +92,8 @@ namespace MWLua
mwscript[sol::meta_function::new_index] = [](const LocalMWScript& s, std::string_view var, double val) {
MWScript::Locals& locals = s.mSelf.ptr().getRefData().getLocals();
if (!locals.setVar(locals.getScriptId(), Misc::StringUtils::lowerCase(var), val))
throw std::runtime_error("No variable \"" + std::string(var) + "\" in mwscript \""
+ locals.getScriptId().getRefIdString() + "\"");
throw std::runtime_error(
"No variable \"" + std::string(var) + "\" in mwscript " + locals.getScriptId().toDebugString());
};
using AiPackage = MWMechanics::AiPackage;

View File

@ -167,7 +167,7 @@ namespace MWLua
{
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
objectT["recordId"] = sol::readonly_property(
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().toDebugString(); });
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
const MWWorld::Ptr& ptr = o.ptr();
if (ptr.isInCell())

View File

@ -23,23 +23,18 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Activator>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Activator>();
activator["record"] = sol::overload(
[](const Object& obj) -> const ESM::Activator* { return obj.ptr().get<ESM::Activator>()->mBase; },
[store](const std::string& recordId) -> const ESM::Activator* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Activator>(activator);
sol::usertype<ESM::Activator> record = context.mLua->sol().new_usertype<ESM::Activator>("ESM3_Activator");
record[sol::meta_function::to_string]
= [](const ESM::Activator& rec) { return "ESM3_Activator[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Activator& rec) { return "ESM3_Activator[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Activator& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Activator& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Activator& rec) -> std::string { return rec.mScript.serializeText(); });
}
}

View File

@ -30,24 +30,19 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Apparatus>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Apparatus>();
apparatus["record"] = sol::overload(
[](const Object& obj) -> const ESM::Apparatus* { return obj.ptr().get<ESM::Apparatus>()->mBase; },
[store](const std::string& recordId) -> const ESM::Apparatus* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Apparatus>(apparatus);
sol::usertype<ESM::Apparatus> record = context.mLua->sol().new_usertype<ESM::Apparatus>("ESM3_Apparatus");
record[sol::meta_function::to_string]
= [](const ESM::Apparatus& rec) { return "ESM3_Apparatus[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Apparatus& rec) { return "ESM3_Apparatus[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Apparatus& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Apparatus& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -37,17 +37,13 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Armor>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Armor>();
armor["record"]
= sol::overload([](const Object& obj) -> const ESM::Armor* { return obj.ptr().get<ESM::Armor>()->mBase; },
[store](const std::string& recordId) -> const ESM::Armor* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Armor>(armor);
sol::usertype<ESM::Armor> record = context.mLua->sol().new_usertype<ESM::Armor>("ESM3_Armor");
record[sol::meta_function::to_string]
= [](const ESM::Armor& rec) -> std::string { return "ESM3_Armor[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Armor& rec) -> std::string { return "ESM3_Armor[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -55,10 +51,10 @@ namespace MWLua
record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Armor& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
record["enchant"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Armor& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mValue; });
record["type"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mType; });

View File

@ -32,29 +32,25 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Book>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Book>();
book["record"]
= sol::overload([](const Object& obj) -> const ESM::Book* { return obj.ptr().get<ESM::Book>()->mBase; },
[store](const std::string& recordId) -> const ESM::Book* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Book>(book);
sol::usertype<ESM::Book> record = context.mLua->sol().new_usertype<ESM::Book>("ESM3_Book");
record[sol::meta_function::to_string]
= [](const ESM::Book& rec) { return "ESM3_Book[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Book& rec) { return "ESM3_Book[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; });
record["enchant"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["isScroll"] = sol::readonly_property([](const ESM::Book& rec) -> bool { return rec.mData.mIsScroll; });
record["value"] = sol::readonly_property([](const ESM::Book& rec) -> int { return rec.mData.mValue; });
record["weight"] = sol::readonly_property([](const ESM::Book& rec) -> float { return rec.mData.mWeight; });

View File

@ -36,18 +36,13 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Clothing>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Clothing>();
clothing["record"] = sol::overload(
[](const Object& obj) -> const ESM::Clothing* { return obj.ptr().get<ESM::Clothing>()->mBase; },
[store](const std::string& recordId) -> const ESM::Clothing* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Clothing>(clothing);
sol::usertype<ESM::Clothing> record = context.mLua->sol().new_usertype<ESM::Clothing>("ESM3_Clothing");
record[sol::meta_function::to_string]
= [](const ESM::Clothing& rec) -> std::string { return "ESM3_Clothing[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Clothing& rec) -> std::string { return "ESM3_Clothing[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -56,9 +51,9 @@ namespace MWLua
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Clothing& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
[](const ESM::Clothing& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"] = sol::readonly_property(
[](const ESM::Clothing& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Clothing& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Clothing& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mValue; });
record["type"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mType; });

View File

@ -48,25 +48,20 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Container>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Container>();
container["record"] = sol::overload(
[](const Object& obj) -> const ESM::Container* { return obj.ptr().get<ESM::Container>()->mBase; },
[store](const std::string& recordId) -> const ESM::Container* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Container>(container);
sol::usertype<ESM::Container> record = context.mLua->sol().new_usertype<ESM::Container>("ESM3_Container");
record[sol::meta_function::to_string] = [](const ESM::Container& rec) -> std::string {
return "ESM3_Container[" + rec.mId.getRefIdString() + "]";
return "ESM3_Container[" + rec.mId.toDebugString() + "]";
};
record["id"]
= sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Container& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Container& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Container& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Container& rec) -> float { return rec.mWeight; });
}
}

View File

@ -23,23 +23,18 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Creature>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Creature>();
creature["record"] = sol::overload(
[](const Object& obj) -> const ESM::Creature* { return obj.ptr().get<ESM::Creature>()->mBase; },
[store](const std::string& recordId) -> const ESM::Creature* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Creature>(creature);
sol::usertype<ESM::Creature> record = context.mLua->sol().new_usertype<ESM::Creature>("ESM3_Creature");
record[sol::meta_function::to_string]
= [](const ESM::Creature& rec) { return "ESM3_Creature[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Creature& rec) { return "ESM3_Creature[" + rec.mId.toDebugString() + "]"; };
record["name"] = sol::readonly_property([](const ESM::Creature& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Creature& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Creature& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Creature& rec) -> std::string { return rec.mScript.serializeText(); });
record["baseCreature"] = sol::readonly_property(
[](const ESM::Creature& rec) -> std::string { return rec.mOriginal.getRefIdString(); });
[](const ESM::Creature& rec) -> std::string { return rec.mOriginal.serializeText(); });
}
}

View File

@ -44,27 +44,23 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Door>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Door>();
door["record"]
= sol::overload([](const Object& obj) -> const ESM::Door* { return obj.ptr().get<ESM::Door>()->mBase; },
[store](const std::string& recordId) -> const ESM::Door* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Door>(door);
sol::usertype<ESM::Door> record = context.mLua->sol().new_usertype<ESM::Door>("ESM3_Door");
record[sol::meta_function::to_string]
= [](const ESM::Door& rec) -> std::string { return "ESM3_Door[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Door& rec) -> std::string { return "ESM3_Door[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Door& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mScript.serializeText(); });
record["openSound"] = sol::readonly_property(
[](const ESM::Door& rec) -> std::string { return rec.mOpenSound.getRefIdString(); });
[](const ESM::Door& rec) -> std::string { return rec.mOpenSound.serializeText(); });
record["closeSound"] = sol::readonly_property(
[](const ESM::Door& rec) -> std::string { return rec.mCloseSound.getRefIdString(); });
[](const ESM::Door& rec) -> std::string { return rec.mCloseSound.serializeText(); });
}
}

View File

@ -24,24 +24,19 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Ingredient>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>();
ingredient["record"] = sol::overload(
[](const Object& obj) -> const ESM::Ingredient* { return obj.ptr().get<ESM::Ingredient>()->mBase; },
[store](const std::string& recordID) -> const ESM::Ingredient* {
return store->find(ESM::RefId::stringRefId(recordID));
});
addRecordFunctionBinding<ESM::Ingredient>(ingredient);
sol::usertype<ESM::Ingredient> record = context.mLua->sol().new_usertype<ESM::Ingredient>(("ESM3_Ingredient"));
record[sol::meta_function::to_string]
= [](const ESM::Ingredient& rec) { return "ESM3_Ingredient[" + rec.mId.getRefIdString() + "]"; };
record["id"] = sol::readonly_property(
[](const ESM::Ingredient& rec) -> std::string { return rec.mId.getRefIdString(); });
= [](const ESM::Ingredient& rec) { return "ESM3_Ingredient[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Ingredient& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Ingredient& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Ingredient& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Ingredient& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -23,17 +23,13 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Light>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Light>();
light["record"]
= sol::overload([](const Object& obj) -> const ESM::Light* { return obj.ptr().get<ESM::Light>()->mBase; },
[store](const std::string& recordId) -> const ESM::Light* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Light>(light);
sol::usertype<ESM::Light> record = context.mLua->sol().new_usertype<ESM::Light>("ESM3_Light");
record[sol::meta_function::to_string]
= [](const ESM::Light& rec) -> std::string { return "ESM3_Light[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Light& rec) -> std::string { return "ESM3_Light[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -41,11 +37,10 @@ namespace MWLua
record["icon"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["sound"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
return Misc::ResourceHelpers::correctSoundPath(rec.mSound.getRefIdString(), vfs);
});
record["sound"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Light& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mValue; });
record["duration"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mTime; });

View File

@ -23,24 +23,19 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Lockpick>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Lockpick>();
lockpick["record"] = sol::overload(
[](const Object& obj) -> const ESM::Lockpick* { return obj.ptr().get<ESM::Lockpick>()->mBase; },
[store](const std::string& recordId) -> const ESM::Lockpick* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Lockpick>(lockpick);
sol::usertype<ESM::Lockpick> record = context.mLua->sol().new_usertype<ESM::Lockpick>("ESM3_Lockpick");
record[sol::meta_function::to_string]
= [](const ESM::Lockpick& rec) { return "ESM3_Lockpick[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Lockpick& rec) { return "ESM3_Lockpick[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Lockpick& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Lockpick& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -23,25 +23,20 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Miscellaneous>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Miscellaneous>();
miscellaneous["record"] = sol::overload(
[](const Object& obj) -> const ESM::Miscellaneous* { return obj.ptr().get<ESM::Miscellaneous>()->mBase; },
[store](const std::string& recordId) -> const ESM::Miscellaneous* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Miscellaneous>(miscellaneous);
sol::usertype<ESM::Miscellaneous> record
= context.mLua->sol().new_usertype<ESM::Miscellaneous>("ESM3_Miscellaneous");
record[sol::meta_function::to_string]
= [](const ESM::Miscellaneous& rec) { return "ESM3_Miscellaneous[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Miscellaneous& rec) { return "ESM3_Miscellaneous[" + rec.mId.toDebugString() + "]"; };
record["id"] = sol::readonly_property(
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mId.getRefIdString(); });
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Miscellaneous& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -25,26 +25,22 @@ namespace MWLua
{
addNpcStatsBindings(npc, context);
const MWWorld::Store<ESM::NPC>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::NPC>();
npc["record"]
= sol::overload([](const Object& obj) -> const ESM::NPC* { return obj.ptr().get<ESM::NPC>()->mBase; },
[store](const std::string& recordId) -> const ESM::NPC* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::NPC>(npc);
sol::usertype<ESM::NPC> record = context.mLua->sol().new_usertype<ESM::NPC>("ESM3_NPC");
record[sol::meta_function::to_string]
= [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; };
record["name"] = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mName; });
record["race"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mRace.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mRace.serializeText(); });
record["class"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mClass.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mClass.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mScript.serializeText(); });
record["hair"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.serializeText(); });
record["head"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.serializeText(); });
// This function is game-specific, in future we should replace it with something more universal.
npc["isWerewolf"] = [](const Object& o) {

View File

@ -21,20 +21,14 @@ namespace MWLua
{
void addPotionBindings(sol::table potion, const Context& context)
{
const MWWorld::Store<ESM::Potion>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>();
potion["record"]
= sol::overload([](const Object& obj) -> const ESM::Potion* { return obj.ptr().get<ESM::Potion>()->mBase; },
[store](const std::string& recordId) -> const ESM::Potion* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Potion>(potion);
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
sol::usertype<ESM::Potion> record = context.mLua->sol().new_usertype<ESM::Potion>("ESM3_Potion");
record[sol::meta_function::to_string]
= [](const ESM::Potion& rec) { return "ESM3_Potion[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Potion& rec) { return "ESM3_Potion[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -42,8 +36,8 @@ namespace MWLua
record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Potion& rec) -> std::string { return rec.mScript.getRefIdString(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mScript.serializeText(); });
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; });
}

View File

@ -23,23 +23,19 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Probe>* store = &MWBase::Environment::get().getWorld()->getStore().get<ESM::Probe>();
probe["record"]
= sol::overload([](const Object& obj) -> const ESM::Probe* { return obj.ptr().get<ESM::Probe>()->mBase; },
[store](const std::string& recordId) -> const ESM::Probe* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Probe>(probe);
sol::usertype<ESM::Probe> record = context.mLua->sol().new_usertype<ESM::Probe>("ESM3_Probe");
record[sol::meta_function::to_string]
= [](const ESM::Probe& rec) { return "ESM3_Probe[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Probe& rec) { return "ESM3_Probe[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -23,24 +23,19 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Repair>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Repair>();
repair["record"]
= sol::overload([](const Object& obj) -> const ESM::Repair* { return obj.ptr().get<ESM::Repair>()->mBase; },
[store](const std::string& recordId) -> const ESM::Repair* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Repair>(repair);
sol::usertype<ESM::Repair> record = context.mLua->sol().new_usertype<ESM::Repair>("ESM3_Repair");
record[sol::meta_function::to_string]
= [](const ESM::Repair& rec) { return "ESM3_Repair[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Repair& rec) { return "ESM3_Repair[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Repair& rec) -> std::string { return rec.mScript.getRefIdString(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

View File

@ -23,18 +23,13 @@ namespace MWLua
{
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Static>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>();
stat["record"]
= sol::overload([](const Object& obj) -> const ESM::Static* { return obj.ptr().get<ESM::Static>()->mBase; },
[store](const std::string& recordId) -> const ESM::Static* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Static>(stat);
sol::usertype<ESM::Static> record = context.mLua->sol().new_usertype<ESM::Static>("ESM3_Static");
record[sol::meta_function::to_string]
= [](const ESM::Static& rec) -> std::string { return "ESM3_Static[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Static& rec) -> std::string { return "ESM3_Static[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Static& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Static& rec) -> std::string { return rec.mId.serializeText(); });
record["model"] = sol::readonly_property([vfs](const ESM::Static& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});

View File

@ -58,7 +58,6 @@ namespace MWLua
{ ESM::REC_PROB, ObjectTypeName::Probe },
{ ESM::REC_REPA, ObjectTypeName::Repair },
};
}
unsigned int getLiveCellRefType(const MWWorld::LiveCellRefBase* ref)

View File

@ -5,6 +5,11 @@
#include <components/esm/defs.hpp>
#include "apps/openmw/mwbase/environment.hpp"
#include "apps/openmw/mwbase/world.hpp"
#include "apps/openmw/mwworld/esmstore.hpp"
#include "apps/openmw/mwworld/store.hpp"
#include "../context.hpp"
namespace MWLua
@ -45,6 +50,15 @@ namespace MWLua
void addClothingBindings(sol::table clothing, const Context& context);
void addStaticBindings(sol::table stat, const Context& context);
void addLightBindings(sol::table light, const Context& context);
template <class T>
void addRecordFunctionBinding(sol::table table)
{
const MWWorld::Store<T>& store = MWBase::Environment::get().getWorld()->getStore().get<T>();
table["record"] = sol::overload([](const Object& obj) -> const T* { return obj.ptr().get<T>()->mBase; },
[&store](std::string_view id) -> const T* { return store.find(ESM::RefId::deserializeText(id)); });
}
}
#endif // MWLUA_TYPES_H

View File

@ -40,18 +40,13 @@ namespace MWLua
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
const MWWorld::Store<ESM::Weapon>* store
= &MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>();
weapon["record"]
= sol::overload([](const Object& obj) -> const ESM::Weapon* { return obj.ptr().get<ESM::Weapon>()->mBase; },
[store](const std::string& recordId) -> const ESM::Weapon* {
return store->find(ESM::RefId::stringRefId(recordId));
});
addRecordFunctionBinding<ESM::Weapon>(weapon);
sol::usertype<ESM::Weapon> record = context.mLua->sol().new_usertype<ESM::Weapon>("ESM3_Weapon");
record[sol::meta_function::to_string]
= [](const ESM::Weapon& rec) -> std::string { return "ESM3_Weapon[" + rec.mId.getRefIdString() + "]"; };
= [](const ESM::Weapon& rec) -> std::string { return "ESM3_Weapon[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -60,9 +55,9 @@ namespace MWLua
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
record["mwscript"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Weapon& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mScript.serializeText(); });
record["isMagical"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> bool { return rec.mData.mFlags & ESM::Weapon::Magical; });
record["isSilver"] = sol::readonly_property(

View File

@ -211,9 +211,9 @@ namespace ESM
{ RefId(), std::string() },
{ RefId::stringRefId("foo"), "foo" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), { 'a', 0, -1, '\n', '\t' } },
{ RefId::formIdRefId(42), "42" },
{ RefId::generated(42), "42" },
{ RefId::index(REC_ARMO, 42), "ARMO, 42" },
{ RefId::formIdRefId(42), "0x2a" },
{ RefId::generated(42), "0x2a" },
{ RefId::index(REC_ARMO, 42), "ARMO:0x2a" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdToString, ESMRefIdToStringTest, ValuesIn(toStringParams));
@ -240,15 +240,51 @@ namespace ESM
const std::vector<std::pair<RefId, std::string>> toDebugStringParams = {
{ RefId(), "Empty{}" },
{ RefId::stringRefId("foo"), "String{foo}" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), "String{a\\x0\\xFF\\xA\\x9}" },
{ RefId::formIdRefId(42), "FormId{42}" },
{ RefId::generated(42), "Generated{42}" },
{ RefId::index(REC_ARMO, 42), "Index{ARMO, 42}" },
{ RefId::stringRefId("foo"), "\"foo\"" },
{ RefId::stringRefId("BAR"), "\"BAR\"" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), "\"a\\x0\\xFF\\xA\\x9\"" },
{ RefId::formIdRefId(42), "FormId:0x2a" },
{ RefId::generated(42), "Generated:0x2a" },
{ RefId::index(REC_ARMO, 42), "Index:ARMO:0x2a" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdToDebugString, ESMRefIdToDebugStringTest, ValuesIn(toDebugStringParams));
struct ESMRefIdTextTest : TestWithParam<std::pair<RefId, std::string>>
{
};
TEST_P(ESMRefIdTextTest, serializeTextShouldReturnString)
{
EXPECT_EQ(GetParam().first.serializeText(), GetParam().second);
}
TEST_P(ESMRefIdTextTest, deserializeTextShouldReturnRefId)
{
EXPECT_EQ(RefId::deserializeText(GetParam().second), GetParam().first);
}
const std::vector<std::pair<RefId, std::string>> serializedRefIds = {
{ RefId(), "" },
{ RefId::stringRefId("foo"), "foo" },
{ RefId::stringRefId("BAR"), "bar" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), { 'a', 0, -1, '\n', '\t' } },
{ RefId::formIdRefId(0), "FormId:0x0" },
{ RefId::formIdRefId(1), "FormId:0x1" },
{ RefId::formIdRefId(0x1f), "FormId:0x1f" },
{ RefId::formIdRefId(std::numeric_limits<ESM4::FormId>::max()), "FormId:0xffffffff" },
{ RefId::generated(0), "Generated:0x0" },
{ RefId::generated(1), "Generated:0x1" },
{ RefId::generated(0x1f), "Generated:0x1f" },
{ RefId::generated(std::numeric_limits<std::uint64_t>::max()), "Generated:0xffffffffffffffff" },
{ RefId::index(REC_INGR, 0), "Index:INGR:0x0" },
{ RefId::index(REC_INGR, 1), "Index:INGR:0x1" },
{ RefId::index(REC_INGR, 0x1f), "Index:INGR:0x1f" },
{ RefId::index(REC_INGR, std::numeric_limits<std::uint32_t>::max()), "Index:INGR:0xffffffff" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdText, ESMRefIdTextTest, ValuesIn(serializedRefIds));
template <class T>
RefId generateRefId();
@ -295,7 +331,14 @@ namespace ESM
EXPECT_EQ(RefId::deserialize(refId.serialize()), refId);
}
REGISTER_TYPED_TEST_SUITE_P(ESMRefIdSerializeDeserializeTest, serializeThenDeserializeShouldProduceSameValue);
TYPED_TEST_P(ESMRefIdSerializeDeserializeTest, serializeTextThenDeserializeTextShouldProduceSameValue)
{
const RefId refId = generateRefId<TypeParam>();
EXPECT_EQ(RefId::deserializeText(refId.serializeText()), refId);
}
REGISTER_TYPED_TEST_SUITE_P(ESMRefIdSerializeDeserializeTest, serializeThenDeserializeShouldProduceSameValue,
serializeTextThenDeserializeTextShouldProduceSameValue);
using RefIdTypeParams = Types<EmptyRefId, StringRefId, FormIdRefId, GeneratedRefId, IndexRefId>;

View File

@ -90,6 +90,7 @@ add_component_dir(esm attr common defs esmcommon records util luascripts format
stringrefid
generatedrefid
indexrefid
serializerefid
)
add_component_dir(fx pass technique lexer widgets stateupdater)

View File

@ -1,24 +1,28 @@
#include "formidrefid.hpp"
#include "serializerefid.hpp"
#include <ostream>
#include <sstream>
namespace ESM
{
std::string FormIdRefId::toString() const
{
return std::to_string(mValue);
std::string result;
result.resize(getIntegralSize(mValue) + 2, '\0');
serializeIntegral(mValue, 0, result);
return result;
}
std::string FormIdRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdValue(mValue, formIdRefIdPrefix, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, FormIdRefId value)
{
return stream << "FormId{" << value.mValue << '}';
return stream << value.toDebugString();
}
}

View File

@ -1,24 +1,28 @@
#include "generatedrefid.hpp"
#include "serializerefid.hpp"
#include <ostream>
#include <sstream>
namespace ESM
{
std::string GeneratedRefId::toString() const
{
return std::to_string(mValue);
std::string result;
result.resize(getIntegralSize(mValue) + 2, '\0');
serializeIntegral(mValue, 0, result);
return result;
}
std::string GeneratedRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdValue(mValue, generatedRefIdPrefix, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, GeneratedRefId value)
{
return stream << "Generated{" << value.mValue << '}';
return stream << value.toDebugString();
}
}

View File

@ -1,26 +1,33 @@
#include "indexrefid.hpp"
#include <ostream>
#include <sstream>
#include "serializerefid.hpp"
#include "esmcommon.hpp"
#include <ostream>
namespace ESM
{
std::string IndexRefId::toString() const
{
return ESM::NAME(mRecordType).toString() + ", " + std::to_string(mValue);
std::string result;
result.resize(sizeof(mRecordType) + getIntegralSize(mValue) + 3, '\0');
std::memcpy(result.data(), &mRecordType, sizeof(mRecordType));
result[sizeof(mRecordType)] = ':';
serializeIntegral(mValue, sizeof(mRecordType) + 1, result);
return result;
}
std::string IndexRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdPrefix(sizeof(mRecordType) + getIntegralSize(mValue) + 1, indexRefIdPrefix, result);
std::memcpy(result.data() + indexRefIdPrefix.size(), &mRecordType, sizeof(mRecordType));
result[indexRefIdPrefix.size() + sizeof(mRecordType)] = ':';
serializeIntegral(mValue, indexRefIdPrefix.size() + sizeof(mRecordType) + 1, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, IndexRefId value)
{
return stream << "Index{" << ESM::NAME(value.mRecordType).toStringView() << ", " << value.mValue << '}';
return stream << value.toDebugString();
}
}

View File

@ -1,8 +1,15 @@
#include "refid.hpp"
#include "serializerefid.hpp"
#include "components/misc/strings/lower.hpp"
#include <charconv>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <system_error>
#include <variant>
namespace ESM
{
@ -102,6 +109,19 @@ namespace ESM
return false;
}
};
struct SerializeText
{
std::string operator()(ESM::EmptyRefId /*v*/) const { return std::string(); }
std::string operator()(ESM::StringRefId v) const { return Misc::StringUtils::lowerCase(v.getValue()); }
template <class T>
std::string operator()(const T& v) const
{
return v.toDebugString();
}
};
}
const RefId RefId::sEmpty = {};
@ -193,4 +213,31 @@ namespace ESM
std::memcpy(&result.mValue, value.data(), sizeof(result.mValue));
return result;
}
std::string RefId::serializeText() const
{
return std::visit(SerializeText{}, mValue);
}
ESM::RefId RefId::deserializeText(std::string_view value)
{
if (value.empty())
return ESM::RefId();
if (value.starts_with(formIdRefIdPrefix))
return ESM::RefId::formIdRefId(deserializeIntegral<ESM4::FormId>(formIdRefIdPrefix.size(), value));
if (value.starts_with(generatedRefIdPrefix))
return ESM::RefId::generated(deserializeIntegral<std::uint64_t>(generatedRefIdPrefix.size(), value));
if (value.starts_with(indexRefIdPrefix))
{
ESM::RecNameInts recordType{};
std::memcpy(&recordType, value.data() + indexRefIdPrefix.size(), sizeof(recordType));
return ESM::RefId::index(recordType,
deserializeIntegral<std::uint32_t>(indexRefIdPrefix.size() + sizeof(recordType) + 1, value));
}
return ESM::RefId::stringRefId(value);
}
}

View File

@ -48,9 +48,12 @@ namespace ESM
public:
const static RefId sEmpty;
// Constructs RefId from a string containing byte by byte copy of RefId::mValue.
// Constructs RefId from a serialized string containing byte by byte copy of RefId::mValue.
static ESM::RefId deserialize(std::string_view value);
// Constructs RefId from a serialized text.
static ESM::RefId deserializeText(std::string_view value);
// Constructs RefId from a string using a pointer to a static set of strings.
static RefId stringRefId(std::string_view value);
@ -118,6 +121,9 @@ namespace ESM
// Copy mValue byte by byte into a string. Use result only during within the same process.
std::string serialize() const;
// Serialize into stable text format.
std::string serializeText() const;
friend constexpr bool operator==(const RefId& l, const RefId& r) { return l.mValue == r.mValue; }
bool operator==(std::string_view rhs) const;

View File

@ -0,0 +1,64 @@
#ifndef OPENMW_COMPONENTS_ESM_SERIALIZEREFID_HPP
#define OPENMW_COMPONENTS_ESM_SERIALIZEREFID_HPP
#include <charconv>
#include <cstring>
#include <string>
#include <string_view>
#include <system_error>
namespace ESM
{
constexpr std::string_view formIdRefIdPrefix = "FormId:";
constexpr std::string_view generatedRefIdPrefix = "Generated:";
constexpr std::string_view indexRefIdPrefix = "Index:";
template <class T>
std::size_t getIntegralSize(T value)
{
std::size_t result = sizeof(T) * 2;
while (true)
{
if (result == 1 || (value & static_cast<T>(0xf) << ((result - 1) * 4)) != 0)
break;
--result;
}
return result;
}
inline void serializeRefIdPrefix(std::size_t valueSize, std::string_view prefix, std::string& out)
{
out.resize(prefix.size() + valueSize + 2, '\0');
std::memcpy(out.data(), prefix.data(), prefix.size());
}
template <class T>
void serializeIntegral(T value, std::size_t shift, std::string& out)
{
out[shift] = '0';
out[shift + 1] = 'x';
const auto r = std::to_chars(out.data() + shift + 2, out.data() + out.size(), value, 16);
if (r.ec != std::errc())
throw std::system_error(std::make_error_code(r.ec), "Failed to serialize ESM::RefId integral value");
}
template <class T>
void serializeRefIdValue(T value, std::string_view prefix, std::string& out)
{
serializeRefIdPrefix(getIntegralSize(value), prefix, out);
serializeIntegral(value, prefix.size(), out);
}
template <class T>
T deserializeIntegral(std::size_t shift, std::string_view value)
{
T result{};
const auto r = std::from_chars(value.data() + shift + 2, value.data() + value.size(), result, 16);
if (r.ec != std::errc())
throw std::system_error(std::make_error_code(r.ec),
"Failed to deserialize ESM::RefId integral value: \"" + std::string(value) + '"');
return result;
}
}
#endif

View File

@ -60,13 +60,13 @@ namespace ESM
std::ostream& operator<<(std::ostream& stream, StringRefId value)
{
stream << "String{";
stream << '"';
for (char c : *value.mValue)
if (std::isprint(c) && c != '\t' && c != '\n' && c != '\r')
stream << c;
else
stream << "\\x" << std::hex << std::uppercase << static_cast<unsigned>(static_cast<unsigned char>(c));
return stream << '}';
return stream << '"';
}
std::string StringRefId::toDebugString() const

View File

@ -221,8 +221,8 @@ if (BUILD_BENCHMARKS AND NOT OPENMW_USE_SYSTEM_BENCHMARK)
include(FetchContent)
FetchContent_Declare(benchmark
URL https://github.com/google/benchmark/archive/refs/tags/v1.5.2.zip
URL_HASH SHA512=1146deca3b424703800933012ef75805d4657309d58b3484498bb51a99025bd405a69855a75af59c23fc3684bfa027224513999b8d7beaab3320c96fb6d6c540
URL https://github.com/google/benchmark/archive/refs/tags/v1.7.1.zip
URL_HASH SHA512=bec4016263587a57648e02b094c69e838c0a21e16c3dcfc6f03100397ab8f95d5fab1f5fd0d7e0e8adbb8212fff1eb574581158fdda1fa7fd6ff12762154b0cc
SOURCE_DIR fetched/benchmark
)
FetchContent_MakeAvailableExcludeFromAll(benchmark)