Compare commits

..

No commits in common. "master" and "openmw-49-rc5" have entirely different histories.

147 changed files with 1101 additions and 1866 deletions

View file

@ -1,15 +1,18 @@
Checks: > Checks: >
-*, -*,
boost-*,
portability-*, portability-*,
clang-analyzer-*, clang-analyzer-*,
-clang-analyzer-optin.*, -clang-analyzer-optin*,
-clang-analyzer-cplusplus.NewDeleteLeaks, -clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-analyzer-cplusplus.NewDelete,
-clang-analyzer-core.CallAndMessage, -clang-analyzer-core.CallAndMessage,
modernize-avoid-bind, -modernize-avoid-bind
readability-identifier-naming WarningsAsErrors: >
WarningsAsErrors: '*' -*,
HeaderFilterRegex: '(apps|components)/' boost-*,
CheckOptions: portability-*,
- key: readability-identifier-naming.ConceptCase clang-analyzer-*,
value: CamelCase -clang-analyzer-optin*,
-clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-analyzer-core.CallAndMessage
HeaderFilterRegex: '^(apps|components)'

View file

@ -632,16 +632,12 @@ macOS14_Xcode15_arm64:
- | - |
if (Get-ChildItem -Recurse *.pdb) { if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
} }
Push-Location .. Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress ..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt 7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item Get-ChildItem -Recurse *.pdb | Remove-Item
} }
@ -649,20 +645,13 @@ macOS14_Xcode15_arm64:
- | - |
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
} }
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script: after_script:
- Get-Volume - Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache: cache:
key: ninja-2022-v12 key: ninja-2022-v11
paths: paths:
- ccache - ccache
- deps - deps
@ -790,16 +779,12 @@ macOS14_Xcode15_arm64:
- | - |
if (Get-ChildItem -Recurse *.pdb) { if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt 7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
} }
Push-Location .. Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress ..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt 7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item Get-ChildItem -Recurse *.pdb | Remove-Item
} }
@ -807,20 +792,13 @@ macOS14_Xcode15_arm64:
- | - |
if (Test-Path env:AWS_ACCESS_KEY_ID) { if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory} aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
} }
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script: after_script:
- Get-Volume - Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log - Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache: cache:
key: msbuild-2022-v12 key: msbuild-2022-v11
paths: paths:
- deps - deps
- MSVC2022_64/deps/Qt - MSVC2022_64/deps/Qt

View file

@ -35,6 +35,7 @@
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
Bug #6025: Subrecords cannot overlap records Bug #6025: Subrecords cannot overlap records
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item
Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly
Bug #6190: Unintuitive sun specularity time of day dependence Bug #6190: Unintuitive sun specularity time of day dependence
@ -227,11 +228,6 @@
Bug #8252: Plugin dependencies are not required to be loaded Bug #8252: Plugin dependencies are not required to be loaded
Bug #8295: Post-processing chain is case-sensitive Bug #8295: Post-processing chain is case-sensitive
Bug #8299: Crash while smoothing landscape Bug #8299: Crash while smoothing landscape
Bug #8364: Crash when clicking scrollbar without handle (divide by zero)
Bug #8378: Korean bitmap fonts are unusable
Bug #8439: Creatures without models can crash the game
Bug #8441: Freeze when using video main menu replacers
Bug #8462: Crashes when resizing the window on macOS
Feature #1415: Infinite fall failsafe Feature #1415: Infinite fall failsafe
Feature #2566: Handle NAM9 records for manual cell references Feature #2566: Handle NAM9 records for manual cell references
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
@ -396,7 +392,6 @@
Bug #6066: Addtopic "return" does not work from within script. No errors thrown Bug #6066: Addtopic "return" does not work from within script. No errors thrown
Bug #6067: ESP loader fails for certain subrecord orders Bug #6067: ESP loader fails for certain subrecord orders
Bug #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends Bug #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime
Bug #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed Bug #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed
Bug #6109: Crash when playing a custom made menu_background file Bug #6109: Crash when playing a custom made menu_background file

View file

@ -38,7 +38,7 @@ fi
if [[ $CI_CLANG_TIDY ]]; then if [[ $CI_CLANG_TIDY ]]; then
CMAKE_CONF_OPTS+=( CMAKE_CONF_OPTS+=(
-DCMAKE_CXX_CLANG_TIDY=clang-tidy -DCMAKE_CXX_CLANG_TIDY="clang-tidy;--warnings-as-errors=*"
-DBUILD_COMPONENTS_TESTS=ON -DBUILD_COMPONENTS_TESTS=ON
-DBUILD_OPENMW_TESTS=ON -DBUILD_OPENMW_TESTS=ON
-DBUILD_OPENCS_TESTS=ON -DBUILD_OPENCS_TESTS=ON

View file

@ -126,24 +126,10 @@ export APT_CACHE_DIR="${PWD}/apt-cache"
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
set -x set -x
mkdir -pv "$APT_CACHE_DIR" mkdir -pv "$APT_CACHE_DIR"
apt-get update -yqq
while true; do
apt-get update -yqq && break
done
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null
add-apt-repository -y ppa:openmw/openmw
while true; do add-apt-repository -y ppa:openmw/openmw-daily
add-apt-repository -y ppa:openmw/openmw && break add-apt-repository -y ppa:openmw/staging
done
while true; do
add-apt-repository -y ppa:openmw/openmw-daily && break
done
while true; do
add-apt-repository -y ppa:openmw/staging && break
done
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
apt list --installed apt list --installed

View file

@ -9,7 +9,7 @@ git checkout FETCH_HEAD
cd .. cd ..
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \ xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
scripts/integration_tests.py --verbose --omw build/install/bin/openmw --workdir integration_tests_output example-suite/ scripts/integration_tests.py --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
ls integration_tests_output/*.osg_stats.log | while read v; do ls integration_tests_output/*.osg_stats.log | while read v; do
scripts/osg_stats.py --stats '.*' --regexp_match < "${v}" scripts/osg_stats.py --stats '.*' --regexp_match < "${v}"

View file

@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0) set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 49) set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0) set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 72) set(OPENMW_LUA_API_REVISION 70)
set(OPENMW_POSTPROCESSING_API_REVISION 2) set(OPENMW_POSTPROCESSING_API_REVISION 2)
set(OPENMW_VERSION_COMMITHASH "") set(OPENMW_VERSION_COMMITHASH "")
@ -860,6 +860,7 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
set(BU_CHMOD_BUNDLE_ITEMS ON) set(BU_CHMOD_BUNDLE_ITEMS ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
include(BundleUtilities) include(BundleUtilities)
cmake_minimum_required(VERSION 3.1)
" COMPONENT Runtime) " COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "") set(ABSOLUTE_PLUGINS "")

View file

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

View file

@ -104,7 +104,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random); std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serialize()); benchmark::DoNotOptimize(refIds[i].serialize());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -118,7 +118,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); }); = generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -131,7 +131,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random); std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -145,7 +145,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -158,7 +158,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random); std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -172,7 +172,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -185,7 +185,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateIndexRefIds(random); std::vector<ESM::RefId> refIds = generateIndexRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -199,7 +199,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())
@ -212,7 +212,7 @@ namespace
std::minstd_rand random; std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateESM3ExteriorCellRefIds(random); std::vector<ESM::RefId> refIds = generateESM3ExteriorCellRefIds(random);
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(refIds[i].serializeText()); benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size()) if (++i >= refIds.size())
@ -226,7 +226,7 @@ namespace
std::vector<std::string> serializedRefIds std::vector<std::string> serializedRefIds
= generateSerializedESM3ExteriorCellRefIds(random, [](ESM::RefId v) { return v.serializeText(); }); = generateSerializedESM3ExteriorCellRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0; std::size_t i = 0;
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i])); benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size()) if (++i >= serializedRefIds.size())

View file

@ -9,7 +9,7 @@ namespace
{ {
void settingsManager(benchmark::State& state) void settingsManager(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("sky blending start", "Fog")); benchmark::DoNotOptimize(Settings::Manager::getFloat("sky blending start", "Fog"));
} }
@ -17,7 +17,7 @@ namespace
void settingsManager2(benchmark::State& state) void settingsManager2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera")); benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing")); benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -26,7 +26,7 @@ namespace
void settingsManager3(benchmark::State& state) void settingsManager3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera")); benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing")); benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -36,7 +36,7 @@ namespace
void localStatic(benchmark::State& state) void localStatic(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v = Settings::Manager::getFloat("sky blending start", "Fog"); static float v = Settings::Manager::getFloat("sky blending start", "Fog");
benchmark::DoNotOptimize(v); benchmark::DoNotOptimize(v);
@ -45,7 +45,7 @@ namespace
void localStatic2(benchmark::State& state) void localStatic2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v1 = Settings::Manager::getFloat("near clip", "Camera"); static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing"); static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -56,7 +56,7 @@ namespace
void localStatic3(benchmark::State& state) void localStatic3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
static float v1 = Settings::Manager::getFloat("near clip", "Camera"); static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing"); static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -69,7 +69,7 @@ namespace
void settingsStorage(benchmark::State& state) void settingsStorage(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
float v = Settings::fog().mSkyBlendingStart.get(); float v = Settings::fog().mSkyBlendingStart.get();
benchmark::DoNotOptimize(v); benchmark::DoNotOptimize(v);
@ -78,7 +78,7 @@ namespace
void settingsStorage2(benchmark::State& state) void settingsStorage2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
bool v1 = Settings::postProcessing().mTransparentPostpass.get(); bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get(); float v2 = Settings::camera().mNearClip.get();
@ -89,7 +89,7 @@ namespace
void settingsStorage3(benchmark::State& state) void settingsStorage3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
bool v1 = Settings::postProcessing().mTransparentPostpass.get(); bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get(); float v2 = Settings::camera().mNearClip.get();
@ -102,7 +102,7 @@ namespace
void settingsStorageGet(benchmark::State& state) void settingsStorageGet(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<float>("Fog", "sky blending start")); benchmark::DoNotOptimize(Settings::get<float>("Fog", "sky blending start"));
} }
@ -110,7 +110,7 @@ namespace
void settingsStorageGet2(benchmark::State& state) void settingsStorageGet2(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass")); benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip")); benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));
@ -119,7 +119,7 @@ namespace
void settingsStorageGet3(benchmark::State& state) void settingsStorageGet3(benchmark::State& state)
{ {
for ([[maybe_unused]] auto _ : state) for (auto _ : state)
{ {
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass")); benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip")); benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));

View file

@ -53,6 +53,8 @@ namespace
bpo::options_description makeOptionsDescription() bpo::options_description makeOptionsDescription()
{ {
using Fallback::FallbackMap;
bpo::options_description result; bpo::options_description result;
auto addOption = result.add_options(); auto addOption = result.add_options();
addOption("help", "print help message"); addOption("help", "print help message");
@ -85,8 +87,7 @@ namespace
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n" "\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default"); "\n\twin1252 - Western European (Latin) alphabet, used by default");
addOption("fallback", addOption("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
bpo::value<Fallback::FallbackMap>()->default_value(Fallback::FallbackMap(), "")->multitoken()->composing(),
"fallback values"); "fallback values");
Files::ConfigurationManager::addCommonOptions(result); Files::ConfigurationManager::addCommonOptions(result);

View file

@ -5,9 +5,7 @@
#include <components/detournavigator/makenavmesh.hpp> #include <components/detournavigator/makenavmesh.hpp>
#include <components/detournavigator/navmeshdbutils.hpp> #include <components/detournavigator/navmeshdbutils.hpp>
#include <components/detournavigator/serialization.hpp> #include <components/detournavigator/serialization.hpp>
#include <components/files/conversion.hpp>
#include <components/loadinglistener/loadinglistener.hpp> #include <components/loadinglistener/loadinglistener.hpp>
#include <components/testing/util.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h> #include <BulletCollision/CollisionShapes/btBoxShape.h>
@ -374,106 +372,6 @@ namespace
} }
} }
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
mSettings.mEnableRecastMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.1.2.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_recast_mesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
mSettings.mEnableNavMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.1.1.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_navmesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
struct DetourNavigatorSpatialJobQueueTest : Test struct DetourNavigatorSpatialJobQueueTest : Test
{ {
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) }; const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) };

View file

@ -88,31 +88,4 @@ namespace
EXPECT_EQ(reader->getFileOffset(), sInitialOffset); EXPECT_EQ(reader->getFileOffset(), sInitialOffset);
} }
} }
TEST_F(ESM3ReadersCacheWithContentFile, CachedSizeAndName)
{
ESM::ReadersCache readers(2);
{
readers.get(0)->openRaw(std::make_unique<std::istringstream>("123"), "closed0.omwaddon");
readers.get(1)->openRaw(std::make_unique<std::istringstream>("12345"), "closed1.omwaddon");
readers.get(2)->openRaw(std::make_unique<std::istringstream>("1234567"), "free.omwaddon");
}
auto busy = readers.get(3);
busy->openRaw(std::make_unique<std::istringstream>("123456789"), "busy.omwaddon");
EXPECT_EQ(readers.getFileSize(0), 3);
EXPECT_EQ(readers.getName(0), "closed0.omwaddon");
EXPECT_EQ(readers.getFileSize(1), 5);
EXPECT_EQ(readers.getName(1), "closed1.omwaddon");
EXPECT_EQ(readers.getFileSize(2), 7);
EXPECT_EQ(readers.getName(2), "free.omwaddon");
EXPECT_EQ(readers.getFileSize(3), 9);
EXPECT_EQ(readers.getName(3), "busy.omwaddon");
// not-yet-seen indices give zero for their size
EXPECT_EQ(readers.getFileSize(4), 0);
}
} }

View file

@ -30,7 +30,7 @@ namespace
TEST(FilesGetHash, shouldClearErrors) TEST(FilesGetHash, shouldClearErrors)
{ {
const auto fileName = outputFilePath("fileName"); const auto fileName = temporaryFilePath("fileName");
std::string content; std::string content;
std::fill_n(std::back_inserter(content), 1, 'a'); std::fill_n(std::back_inserter(content), 1, 'a');
std::istringstream stream(content); std::istringstream stream(content);
@ -41,7 +41,7 @@ namespace
TEST_P(FilesGetHash, shouldReturnHashForStringStream) TEST_P(FilesGetHash, shouldReturnHashForStringStream)
{ {
const auto fileName = outputFilePath("fileName"); const auto fileName = temporaryFilePath("fileName");
std::string content; std::string content;
std::fill_n(std::back_inserter(content), GetParam().mSize, 'a'); std::fill_n(std::back_inserter(content), GetParam().mSize, 'a');
std::istringstream stream(content); std::istringstream stream(content);

View file

@ -38,10 +38,10 @@ namespace
sol::state lua; sol::state lua;
LuaUtil::InputAction::Registry registry; LuaUtil::InputAction::Registry registry;
LuaUtil::InputAction::Info a({ "a", LuaUtil::InputAction::Type::Boolean, "test", "a_name", "a_description", LuaUtil::InputAction::Info a({ "a", LuaUtil::InputAction::Type::Boolean, "test", "a_name", "a_description",
sol::make_object(lua, false), false }); sol::make_object(lua, false) });
registry.insert(a); registry.insert(a);
LuaUtil::InputAction::Info b({ "b", LuaUtil::InputAction::Type::Boolean, "test", "b_name", "b_description", LuaUtil::InputAction::Info b({ "b", LuaUtil::InputAction::Type::Boolean, "test", "b_name", "b_description",
sol::make_object(lua, false), false }); sol::make_object(lua, false) });
registry.insert(b); registry.insert(b);
LuaUtil::Callback bindA({ lua.load("return function() return true end")(), sol::table(lua, sol::create) }); LuaUtil::Callback bindA({ lua.load("return function() return true end")(), sol::table(lua, sol::create) });
LuaUtil::Callback bindBToA( LuaUtil::Callback bindBToA(

View file

@ -2,7 +2,6 @@
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
#include <components/settings/parser.hpp> #include <components/settings/parser.hpp>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -25,9 +24,5 @@ int main(int argc, char** argv)
Settings::StaticValues::init(); Settings::StaticValues::init();
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
} }

View file

@ -51,7 +51,7 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesXZQuat[] = { const std::pair<osg::Quat, osg::Vec3f> eulerAnglesXZQuat[] = {
{ {
osg::Quat(1, 0, 0, 0), osg::Quat(1, 0, 0, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 1, 0, 0), osg::Quat(0, 1, 0, 0),
@ -59,7 +59,7 @@ namespace Misc
}, },
{ {
osg::Quat(0, 0, 1, 0), osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 0, 1), osg::Quat(0, 0, 0, 1),
@ -128,15 +128,15 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesZYXQuat[] = { const std::pair<osg::Quat, osg::Vec3f> eulerAnglesZYXQuat[] = {
{ {
osg::Quat(1, 0, 0, 0), osg::Quat(1, 0, 0, 0),
osg::Vec3f(osg::PIf, 0, 0), osg::Vec3f(osg::PI, 0, 0),
}, },
{ {
osg::Quat(0, 1, 0, 0), osg::Quat(0, 1, 0, 0),
osg::Vec3f(osg::PIf, 0, osg::PIf), osg::Vec3f(osg::PI, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 1, 0), osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf), osg::Vec3f(0, 0, osg::PI),
}, },
{ {
osg::Quat(0, 0, 0, 1), osg::Quat(0, 0, 0, 1),

View file

@ -16,7 +16,7 @@ namespace
ShaderManager mManager; ShaderManager mManager;
ShaderManager::DefineMap mDefines; ShaderManager::DefineMap mDefines;
ShaderManagerTest() { mManager.setShaderPath(TestingOpenMW::outputDir()); } ShaderManagerTest() { mManager.setShaderPath("tests_output"); }
template <class F> template <class F>
void withShaderFile(const std::string& content, F&& f) void withShaderFile(const std::string& content, F&& f)

View file

@ -8,7 +8,6 @@
#include <QList> #include <QList>
#include <QMessageBox> #include <QMessageBox>
#include <QPair> #include <QPair>
#include <QProgressDialog>
#include <QPushButton> #include <QPushButton>
#include <algorithm> #include <algorithm>
@ -352,17 +351,9 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!resourcesVfs.isEmpty()) if (!resourcesVfs.isEmpty())
directories.insert(0, { resourcesVfs }); directories.insert(0, { resourcesVfs });
QIcon containsDataIcon(":/images/openmw-plugin.png");
QProgressDialog progressBar("Adding data directories", {}, 0, directories.count(), this);
progressBar.setWindowModality(Qt::WindowModal);
progressBar.setValue(0);
std::unordered_set<QString> visitedDirectories; std::unordered_set<QString> visitedDirectories;
for (const Config::SettingValue& currentDir : directories) for (const Config::SettingValue& currentDir : directories)
{ {
progressBar.setValue(progressBar.value() + 1);
if (!visitedDirectories.insert(currentDir.value).second) if (!visitedDirectories.insert(currentDir.value).second)
continue; continue;
@ -411,7 +402,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
// Add a "data file" icon if the directory contains a content file // Add a "data file" icon if the directory contains a content file
if (mSelector->containsDataFiles(currentDir.value)) if (mSelector->containsDataFiles(currentDir.value))
{ {
item->setIcon(containsDataIcon); item->setIcon(QIcon(":/images/openmw-plugin.png"));
tooltip << tr("Contains content file(s)"); tooltip << tr("Contains content file(s)");
} }
@ -774,7 +765,7 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
return; return;
QString rootPath = QFileDialog::getExistingDirectory( QString rootPath = QFileDialog::getExistingDirectory(
this, tr("Select Directory"), {}, QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly); this, tr("Select Directory"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (rootPath.isEmpty()) if (rootPath.isEmpty())
return; return;

View file

@ -1,16 +1,20 @@
set(NAVMESHTOOL_LIB set(NAVMESHTOOL
worldspacedata.cpp worldspacedata.cpp
navmesh.cpp navmesh.cpp
main.cpp
)
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL})
add_library(openmw-navmeshtool-lib STATIC
${NAVMESHTOOL}
) )
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL_LIB} main.cpp)
add_library(openmw-navmeshtool-lib STATIC ${NAVMESHTOOL_LIB})
if (ANDROID) if (ANDROID)
add_library(openmw-navmeshtool SHARED main.cpp) add_library(openmw-navmeshtool SHARED
main.cpp
)
else() else()
openmw_add_executable(openmw-navmeshtool main.cpp) openmw_add_executable(openmw-navmeshtool ${NAVMESHTOOL})
endif() endif()
target_link_libraries(openmw-navmeshtool openmw-navmeshtool-lib) target_link_libraries(openmw-navmeshtool openmw-navmeshtool-lib)

View file

@ -62,6 +62,8 @@ namespace NavMeshTool
bpo::options_description makeOptionsDescription() bpo::options_description makeOptionsDescription()
{ {
using Fallback::FallbackMap;
bpo::options_description result; bpo::options_description result;
auto addOption = result.add_options(); auto addOption = result.add_options();
addOption("help", "print help message"); addOption("help", "print help message");
@ -223,8 +225,7 @@ namespace NavMeshTool
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay); Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay);
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay); Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
DetourNavigator::RecastGlobalAllocator::init(); DetourNavigator::RecastGlobalAllocator::init();
DetourNavigator::Settings navigatorSettings DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
= DetourNavigator::makeSettingsFromSettingsManager(Debug::getRecastMaxLogLevel());
navigatorSettings.mRecast.mSwimHeightScale navigatorSettings.mRecast.mSwimHeightScale
= EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat(); = EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat();

View file

@ -38,6 +38,8 @@
#include "view/doc/viewmanager.hpp" #include "view/doc/viewmanager.hpp"
using namespace Fallback;
CS::Editor::Editor(int argc, char** argv) CS::Editor::Editor(int argc, char** argv)
: mConfigVariables(readConfiguration()) : mConfigVariables(readConfiguration())
, mSettingsState(mCfgMgr) , mSettingsState(mCfgMgr)
@ -122,10 +124,7 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
->default_value(std::vector<std::string>(), "fallback-archive") ->default_value(std::vector<std::string>(), "fallback-archive")
->multitoken()); ->multitoken());
addOption("fallback", addOption("fallback",
boost::program_options::value<Fallback::FallbackMap>() boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
->default_value(Fallback::FallbackMap(), "")
->multitoken()
->composing(),
"fallback values"); "fallback values");
Files::ConfigurationManager::addCommonOptions(desc); Files::ConfigurationManager::addCommonOptions(desc);
@ -142,7 +141,7 @@ std::pair<Files::PathContainer, std::vector<std::string>> CS::Editor::readConfig
{ {
boost::program_options::variables_map& variables = mConfigVariables; boost::program_options::variables_map& variables = mConfigVariables;
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap); Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
mEncodingName = variables["encoding"].as<std::string>(); mEncodingName = variables["encoding"].as<std::string>();
mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));

View file

@ -1,5 +1,4 @@
#include <components/debug/debugging.hpp> #include <components/debug/debugging.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -8,9 +7,5 @@ int main(int argc, char* argv[])
Log::sMinDebugLevel = Debug::getDebugLevel(); Log::sMinDebugLevel = Debug::getDebugLevel();
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
} }

View file

@ -373,15 +373,11 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mScriptConsoleMode(false) , mScriptConsoleMode(false)
, mActivationDistanceOverride(-1) , mActivationDistanceOverride(-1)
, mGrab(true) , mGrab(true)
, mExportFonts(false)
, mRandomSeed(0) , mRandomSeed(0)
, mNewGame(false) , mNewGame(false)
, mCfgMgr(configurationManager) , mCfgMgr(configurationManager)
, mGlMaxTextureImageUnits(0) , mGlMaxTextureImageUnits(0)
{ {
#if SDL_VERSION_ATLEAST(2, 24, 0)
SDL_SetHint(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, "1");
#endif
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads
Uint32 flags Uint32 flags
@ -811,7 +807,7 @@ void OMW::Engine::prepareEngine()
rootNode->addChild(guiRoot); rootNode->addChild(guiRoot);
mWindowManager = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(), mWindowManager = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(),
mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts, mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding,
Version::getOpenmwVersionDescription(), shadersSupported, mCfgMgr); Version::getOpenmwVersionDescription(), shadersSupported, mCfgMgr);
mEnvironment.setWindowManager(*mWindowManager); mEnvironment.setWindowManager(*mWindowManager);
@ -851,7 +847,7 @@ void OMW::Engine::prepareEngine()
} }
listener->loadingOff(); listener->loadingOff();
mWorld->init(mMaxRecastLogLevel, mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue); mWorld->init(mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
mEnvironment.setWorldScene(mWorld->getWorldScene()); mEnvironment.setWorldScene(mWorld->getWorldScene());
mWorld->setupPlayer(); mWorld->setupPlayer();
mWorld->setRandomSeed(mRandomSeed); mWorld->setRandomSeed(mRandomSeed);
@ -1113,11 +1109,6 @@ void OMW::Engine::setWarningsMode(int mode)
mWarningsMode = mode; mWarningsMode = mode;
} }
void OMW::Engine::enableFontExport(bool exportFonts)
{
mExportFonts = exportFonts;
}
void OMW::Engine::setSaveGameFile(const std::filesystem::path& savegame) void OMW::Engine::setSaveGameFile(const std::filesystem::path& savegame)
{ {
mSaveGameFile = savegame; mSaveGameFile = savegame;

View file

@ -4,7 +4,6 @@
#include <filesystem> #include <filesystem>
#include <components/compiler/extensions.hpp> #include <components/compiler/extensions.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/files/collections.hpp> #include <components/files/collections.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -172,9 +171,7 @@ namespace OMW
// Grab mouse? // Grab mouse?
bool mGrab; bool mGrab;
bool mExportFonts;
unsigned int mRandomSeed; unsigned int mRandomSeed;
Debug::Level mMaxRecastLogLevel = Debug::Error;
Compiler::Extensions mExtensions; Compiler::Extensions mExtensions;
std::unique_ptr<Compiler::Context> mScriptContext; std::unique_ptr<Compiler::Context> mScriptContext;
@ -183,9 +180,6 @@ namespace OMW
Translation::Storage mTranslationDataStorage; Translation::Storage mTranslationDataStorage;
bool mNewGame; bool mNewGame;
Files::ConfigurationManager& mCfgMgr;
int mGlMaxTextureImageUnits;
// not implemented // not implemented
Engine(const Engine&); Engine(const Engine&);
Engine& operator=(const Engine&); Engine& operator=(const Engine&);
@ -257,14 +251,14 @@ namespace OMW
void setWarningsMode(int mode); void setWarningsMode(int mode);
void enableFontExport(bool exportFonts);
/// Set the save game file to load after initialising the engine. /// Set the save game file to load after initialising the engine.
void setSaveGameFile(const std::filesystem::path& savegame); void setSaveGameFile(const std::filesystem::path& savegame);
void setRandomSeed(unsigned int seed); void setRandomSeed(unsigned int seed);
void setRecastMaxLogLevel(Debug::Level value) { mMaxRecastLogLevel = value; } private:
Files::ConfigurationManager& mCfgMgr;
int mGlMaxTextureImageUnits;
}; };
} }

View file

@ -28,6 +28,8 @@ extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x
#include <unistd.h> #include <unistd.h>
#endif #endif
using namespace Fallback;
/** /**
* \brief Parses application command line and calls \ref Cfg::ConfigurationManager * \brief Parses application command line and calls \ref Cfg::ConfigurationManager
* to parse configuration files. * to parse configuration files.
@ -150,10 +152,9 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati
engine.setSaveGameFile(variables["load-savegame"].as<Files::MaybeQuotedPath>().u8string()); engine.setSaveGameFile(variables["load-savegame"].as<Files::MaybeQuotedPath>().u8string());
// other settings // other settings
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap); Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
engine.setSoundUsage(!variables["no-sound"].as<bool>()); engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setActivationDistanceOverride(variables["activate-dist"].as<int>()); engine.setActivationDistanceOverride(variables["activate-dist"].as<int>());
engine.enableFontExport(variables["export-fonts"].as<bool>());
engine.setRandomSeed(variables["random-seed"].as<unsigned int>()); engine.setRandomSeed(variables["random-seed"].as<unsigned int>());
return true; return true;
@ -219,8 +220,6 @@ int runApplication(int argc, char* argv[])
Files::ConfigurationManager cfgMgr; Files::ConfigurationManager cfgMgr;
std::unique_ptr<OMW::Engine> engine = std::make_unique<OMW::Engine>(cfgMgr); std::unique_ptr<OMW::Engine> engine = std::make_unique<OMW::Engine>(cfgMgr);
engine->setRecastMaxLogLevel(Debug::getRecastMaxLogLevel());
if (parseOptions(argc, argv, *engine, cfgMgr)) if (parseOptions(argc, argv, *engine, cfgMgr))
{ {
if (!Misc::checkRequiredOSGPluginsArePresent()) if (!Misc::checkRequiredOSGPluginsArePresent())

View file

@ -200,7 +200,7 @@ namespace MWBase
///< Skip the animation for the given MW-reference for one frame. Calls to this function for ///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored. /// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) = 0; virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
virtual bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0; virtual bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0;

View file

@ -363,7 +363,7 @@ namespace MWBase
void windowVisibilityChange(bool visible) override = 0; void windowVisibilityChange(bool visible) override = 0;
void windowResized(int x, int y) override = 0; void windowResized(int x, int y) override = 0;
void windowClosed() override = 0; void windowClosed() override = 0;
virtual bool isWindowVisible() const = 0; virtual bool isWindowVisible() = 0;
virtual void watchActor(const MWWorld::Ptr& ptr) = 0; virtual void watchActor(const MWWorld::Ptr& ptr) = 0;
virtual MWWorld::Ptr getWatchedActor() const = 0; virtual MWWorld::Ptr getWatchedActor() const = 0;

View file

@ -237,12 +237,7 @@ namespace MWClass
bool Container::hasToolTip(const MWWorld::ConstPtr& ptr) const bool Container::hasToolTip(const MWWorld::ConstPtr& ptr) const
{ {
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData()) if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
{ return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();
if (!canBeHarvested(ptr))
return true;
const MWWorld::ContainerStore& store = data->asContainerCustomData().mStore;
return !store.isResolved() || store.hasVisibleItems();
}
return true; return true;
} }

View file

@ -1343,13 +1343,12 @@ namespace MWGui
return codePoint == '\r'; return codePoint == '\r';
} }
// Normal no-break space (0x00A0) is ignored here
// because Morrowind compatibility requires us to render its glyph
static bool ucsSpace(int codePoint) static bool ucsSpace(int codePoint)
{ {
switch (codePoint) switch (codePoint)
{ {
case 0x0020: // SPACE case 0x0020: // SPACE
case 0x00A0: // NO-BREAK SPACE
case 0x1680: // OGHAM SPACE MARK case 0x1680: // OGHAM SPACE MARK
case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x180E: // MONGOLIAN VOWEL SEPARATOR
case 0x2000: // EN QUAD case 0x2000: // EN QUAD
@ -1374,14 +1373,12 @@ namespace MWGui
} }
} }
// No-break spaces (0x00A0, 0x202F, 0xFEFF - normal, narrow, zero width)
// are ignored here for obvious reasons
// Figure space (0x2007) is not a breaking space either
static bool ucsBreakingSpace(int codePoint) static bool ucsBreakingSpace(int codePoint)
{ {
switch (codePoint) switch (codePoint)
{ {
case 0x0020: // SPACE case 0x0020: // SPACE
// case 0x00A0: // NO-BREAK SPACE
case 0x1680: // OGHAM SPACE MARK case 0x1680: // OGHAM SPACE MARK
case 0x180E: // MONGOLIAN VOWEL SEPARATOR case 0x180E: // MONGOLIAN VOWEL SEPARATOR
case 0x2000: // EN QUAD case 0x2000: // EN QUAD
@ -1391,12 +1388,15 @@ namespace MWGui
case 0x2004: // THREE-PER-EM SPACE case 0x2004: // THREE-PER-EM SPACE
case 0x2005: // FOUR-PER-EM SPACE case 0x2005: // FOUR-PER-EM SPACE
case 0x2006: // SIX-PER-EM SPACE case 0x2006: // SIX-PER-EM SPACE
case 0x2007: // FIGURE SPACE
case 0x2008: // PUNCTUATION SPACE case 0x2008: // PUNCTUATION SPACE
case 0x2009: // THIN SPACE case 0x2009: // THIN SPACE
case 0x200A: // HAIR SPACE case 0x200A: // HAIR SPACE
case 0x200B: // ZERO WIDTH SPACE case 0x200B: // ZERO WIDTH SPACE
case 0x202F: // NARROW NO-BREAK SPACE
case 0x205F: // MEDIUM MATHEMATICAL SPACE case 0x205F: // MEDIUM MATHEMATICAL SPACE
case 0x3000: // IDEOGRAPHIC SPACE case 0x3000: // IDEOGRAPHIC SPACE
// case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
return true; return true;
default: default:
return false; return false;

View file

@ -666,8 +666,7 @@ namespace MWGui
else if (scrollbar) else if (scrollbar)
{ {
mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second)); mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second));
// Scroll range should be >= 2 to enable scrolling and prevent a crash size_t range = book->getSize().second - viewHeight;
size_t range = std::max(book->getSize().second - viewHeight, size_t(2));
mScrollBar->setScrollRange(range); mScrollBar->setScrollRange(range);
mScrollBar->setScrollPosition(range - 1); mScrollBar->setScrollPosition(range - 1);
mScrollBar->setTrackSize( mScrollBar->setTrackSize(

View file

@ -29,26 +29,11 @@ namespace MWGui
{ {
Misc::FrameRateLimiter frameRateLimiter Misc::FrameRateLimiter frameRateLimiter
= Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit()); = Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
const MWBase::WindowManager& windowManager = *MWBase::Environment::get().getWindowManager();
bool paused = false;
while (mRunning) while (mRunning)
{ {
if (windowManager.isWindowVisible()) // If finished playing, start again
{ if (!mVideo->update())
if (paused) mVideo->playVideo("video\\menu_background.bik");
{
mVideo->resume();
paused = false;
}
// If finished playing, start again
if (!mVideo->update())
mVideo->playVideo("video\\menu_background.bik");
}
else if (!paused)
{
paused = true;
mVideo->pause();
}
frameRateLimiter.limit(); frameRateLimiter.limit();
} }
} }

View file

@ -146,7 +146,7 @@ namespace MWGui
WindowManager::WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, WindowManager::WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath,
bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding,
bool exportFonts, const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr) const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr)
: mOldUpdateMask(0) : mOldUpdateMask(0)
, mOldCullMask(0) , mOldCullMask(0)
, mStore(nullptr) , mStore(nullptr)
@ -215,8 +215,7 @@ namespace MWGui
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag); MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
// Load fonts // Load fonts
mFontLoader mFontLoader = std::make_unique<Gui::FontLoader>(encoding, resourceSystem->getVFS(), mScalingFactor);
= std::make_unique<Gui::FontLoader>(encoding, resourceSystem->getVFS(), mScalingFactor, exportFonts);
// Register own widgets with MyGUI // Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
@ -730,9 +729,6 @@ namespace MWGui
return; return;
} }
if (mGuiModes.empty())
return;
GuiModeState& state = mGuiModeStates[mGuiModes.back()]; GuiModeState& state = mGuiModeStates[mGuiModes.back()];
for (const auto& window : state.mWindows) for (const auto& window : state.mWindows)
{ {
@ -1218,7 +1214,7 @@ namespace MWGui
// TODO: check if any windows are now off-screen and move them back if so // TODO: check if any windows are now off-screen and move them back if so
} }
bool WindowManager::isWindowVisible() const bool WindowManager::isWindowVisible()
{ {
return mWindowVisible; return mWindowVisible;
} }

View file

@ -128,7 +128,7 @@ namespace MWGui
WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot, WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, bool useShaders, ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders,
Files::ConfigurationManager& cfgMgr); Files::ConfigurationManager& cfgMgr);
virtual ~WindowManager(); virtual ~WindowManager();
@ -290,7 +290,7 @@ namespace MWGui
void windowVisibilityChange(bool visible) override; void windowVisibilityChange(bool visible) override;
void windowResized(int x, int y) override; void windowResized(int x, int y) override;
void windowClosed() override; void windowClosed() override;
bool isWindowVisible() const override; bool isWindowVisible() override;
void watchActor(const MWWorld::Ptr& ptr) override; void watchActor(const MWWorld::Ptr& ptr) override;
MWWorld::Ptr getWatchedActor() const override; MWWorld::Ptr getWatchedActor() const override;

View file

@ -148,7 +148,7 @@ namespace MWLua
}; };
} }
sol::table readOnlyApi = LuaUtil::makeReadOnly(api); sol::table readOnly = LuaUtil::makeReadOnly(api);
return context.setTypePackage(readOnlyApi, "openmw_core"); return context.setTypePackage(readOnly, "openmw_core");
} }
} }

View file

@ -38,116 +38,94 @@ namespace MWLua
sol::table initInputPackage(const Context& context) sol::table initInputPackage(const Context& context)
{ {
sol::object cached = context.getTypePackage("openmw_input");
if (cached != sol::nil)
return cached;
sol::state_view lua = context.sol(); sol::state_view lua = context.sol();
{
if (lua["openmw_input"] != sol::nil)
return lua["openmw_input"];
}
context.cachePackage("openmw_input_keyevent", [&lua]() { sol::usertype<SDL_Keysym> keyEvent = lua.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)); else
else return std::string();
return std::string();
});
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.scancode; });
keyEvent["withShift"]
= sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_SHIFT; });
keyEvent["withCtrl"]
= sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_CTRL; });
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; });
return sol::table(lua, sol::create);
}); });
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.scancode; });
keyEvent["withShift"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_SHIFT; });
keyEvent["withCtrl"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_CTRL; });
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; });
context.cachePackage("openmw_input_touchpadevent", [&lua]() { auto touchpadEvent = lua.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"] touchpadEvent["finger"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mDevice; }); touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
touchpadEvent["finger"] return { e.mX, e.mY };
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
return { e.mX, e.mY };
});
touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; });
return sol::table(lua, sol::create);
}); });
touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; });
context.cachePackage("openmw_input_inputactions", [&lua]() { auto inputActions = lua.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]; }; {
{ auto pairs = [](LuaUtil::InputAction::Registry& registry) {
auto pairs = [](LuaUtil::InputAction::Registry& registry) { auto next
auto next = [](LuaUtil::InputAction::Registry& registry, std::string_view key) = [](LuaUtil::InputAction::Registry& registry,
-> sol::optional<std::tuple<std::string, LuaUtil::InputAction::Info>> { std::string_view key) -> sol::optional<std::tuple<std::string, LuaUtil::InputAction::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key)); std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value()) if (!nextKey.has_value())
return sol::nullopt; return sol::nullopt;
else else
return std::make_tuple(*nextKey, registry[*nextKey].value()); return std::make_tuple(*nextKey, registry[*nextKey].value());
};
return std::make_tuple(next, registry, registry.firstKey());
}; };
inputActions[sol::meta_function::pairs] = pairs; return std::make_tuple(next, registry, registry.firstKey());
} };
return sol::table(lua, sol::create); inputActions[sol::meta_function::pairs] = pairs;
}); }
context.cachePackage("openmw_input_actioninfo", [&lua]() { auto actionInfo = lua.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( [](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; });
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; }); actionInfo["description"] = sol::readonly_property(
actionInfo["description"] = sol::readonly_property( [](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mDescription; });
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mDescription; }); actionInfo["l10n"] = sol::readonly_property(
actionInfo["l10n"] = sol::readonly_property( [](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mL10n; });
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mL10n; }); actionInfo["type"] = sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mType; });
actionInfo["type"] actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mType; }); = sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
return sol::table(lua, sol::create);
});
context.cachePackage("openmw_input_inputtriggers", [&lua]() { auto inputTriggers = lua.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]; }; {
{ auto pairs = [](LuaUtil::InputTrigger::Registry& registry) {
auto pairs = [](LuaUtil::InputTrigger::Registry& registry) { auto next
auto next = [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) = [](LuaUtil::InputTrigger::Registry& registry,
-> sol::optional<std::tuple<std::string, LuaUtil::InputTrigger::Info>> { std::string_view key) -> sol::optional<std::tuple<std::string, LuaUtil::InputTrigger::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key)); std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value()) if (!nextKey.has_value())
return sol::nullopt; return sol::nullopt;
else else
return std::make_tuple(*nextKey, registry[*nextKey].value()); return std::make_tuple(*nextKey, registry[*nextKey].value());
};
return std::make_tuple(next, registry, registry.firstKey());
}; };
inputTriggers[sol::meta_function::pairs] = pairs; return std::make_tuple(next, registry, registry.firstKey());
} };
return sol::table(lua, sol::create); inputTriggers[sol::meta_function::pairs] = pairs;
}); }
context.cachePackage("openmw_input_triggerinfo", [&lua]() { auto triggerInfo = lua.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( [](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mName; });
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mName; }); triggerInfo["description"] = sol::readonly_property(
triggerInfo["description"] = sol::readonly_property( [](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mDescription; });
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mDescription; }); triggerInfo["l10n"] = sol::readonly_property(
triggerInfo["l10n"] = sol::readonly_property( [](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
return sol::table(lua, sol::create);
});
MWBase::InputManager* input = MWBase::Environment::get().getInputManager(); MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
sol::table api(lua, sol::create); sol::table api(lua, sol::create);
@ -161,18 +139,16 @@ namespace MWLua
})); }));
api["actions"] = std::ref(context.mLuaManager->inputActions()); api["actions"] = std::ref(context.mLuaManager->inputActions());
api["registerAction"] api["registerAction"] = [manager = context.mLuaManager](sol::table options) {
= [manager = context.mLuaManager, persistent = context.mType == Context::Menu](sol::table options) { LuaUtil::InputAction::Info parsedOptions;
LuaUtil::InputAction::Info parsedOptions; parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mKey = options["key"].get<std::string_view>(); parsedOptions.mType = options["type"].get<LuaUtil::InputAction::Type>();
parsedOptions.mType = options["type"].get<LuaUtil::InputAction::Type>(); parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>(); parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>(); parsedOptions.mDescription = options["description"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>(); parsedOptions.mDefaultValue = options["defaultValue"].get<sol::main_object>();
parsedOptions.mDefaultValue = options["defaultValue"].get<sol::main_object>(); manager->inputActions().insert(std::move(parsedOptions));
parsedOptions.mPersistent = persistent; };
manager->inputActions().insert(std::move(parsedOptions));
};
api["bindAction"] = [manager = context.mLuaManager]( api["bindAction"] = [manager = context.mLuaManager](
std::string_view key, const sol::table& callback, sol::table dependencies) { std::string_view key, const sol::table& callback, sol::table dependencies) {
std::vector<std::string_view> parsedDependencies; std::vector<std::string_view> parsedDependencies;
@ -202,16 +178,14 @@ namespace MWLua
}; };
api["triggers"] = std::ref(context.mLuaManager->inputTriggers()); api["triggers"] = std::ref(context.mLuaManager->inputTriggers());
api["registerTrigger"] api["registerTrigger"] = [manager = context.mLuaManager](sol::table options) {
= [manager = context.mLuaManager, persistent = context.mType == Context::Menu](sol::table options) { LuaUtil::InputTrigger::Info parsedOptions;
LuaUtil::InputTrigger::Info parsedOptions; parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mKey = options["key"].get<std::string_view>(); parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>(); parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>(); parsedOptions.mDescription = options["description"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>(); manager->inputTriggers().insert(std::move(parsedOptions));
parsedOptions.mPersistent = persistent; };
manager->inputTriggers().insert(std::move(parsedOptions));
};
api["registerTriggerHandler"] api["registerTriggerHandler"]
= [manager = context.mLuaManager](std::string_view key, const sol::table& callback) { = [manager = context.mLuaManager](std::string_view key, const sol::table& callback) {
manager->inputTriggers().registerHandler(key, LuaUtil::Callback::fromLua(callback)); manager->inputTriggers().registerHandler(key, LuaUtil::Callback::fromLua(callback));
@ -471,8 +445,8 @@ namespace MWLua
{ "Tab", SDL_SCANCODE_TAB }, { "Tab", SDL_SCANCODE_TAB },
})); }));
sol::table readOnlyApi = LuaUtil::makeReadOnly(api); lua["openmw_input"] = LuaUtil::makeReadOnly(api);
return context.setTypePackage(readOnlyApi, "openmw_input"); return lua["openmw_input"];
} }
} }

View file

@ -71,8 +71,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value;
= sol::main_object(value);
} }
else else
throw std::runtime_error("Only global or self scripts can set the value"); throw std::runtime_error("Only global or self scripts can set the value");

View file

@ -54,7 +54,7 @@ namespace MWLua
{ {
} }
MWBase::LuaManager::ActorControls mControls; MWBase::LuaManager::ActorControls mControls;
std::map<CachedStat, sol::main_object> mStatsCache; std::map<CachedStat, sol::object> mStatsCache;
bool mIsActive; bool mIsActive;
}; };

View file

@ -654,8 +654,8 @@ namespace MWLua
MWBase::Environment::get().getL10nManager()->dropCache(); MWBase::Environment::get().getL10nManager()->dropCache();
mUiResourceManager.clear(); mUiResourceManager.clear();
mLua.dropScriptCache(); mLua.dropScriptCache();
mInputActions.clear(true); mInputActions.clear();
mInputTriggers.clear(true); mInputTriggers.clear();
initConfiguration(); initConfiguration();
ESM::LuaScripts globalData; ESM::LuaScripts globalData;

View file

@ -1061,7 +1061,7 @@ namespace MWLua
}; };
// types.Actor.activeEffects(o):removeEffect(id, ?arg) // types.Actor.activeEffects(o):removeEffect(id, ?arg)
activeEffectsT["remove"] = [getEffectKey, context](const ActorActiveEffects& effects, std::string_view idStr, activeEffectsT["remove"] = [getEffectKey](const ActorActiveEffects& effects, std::string_view idStr,
sol::optional<std::string_view> argStr) { sol::optional<std::string_view> argStr) {
if (!effects.isActor()) if (!effects.isActor())
return; return;
@ -1071,14 +1071,12 @@ namespace MWLua
MWMechanics::EffectKey key = getEffectKey(idStr, argStr); MWMechanics::EffectKey key = getEffectKey(idStr, argStr);
context.mLuaManager->addAction([key, effects]() { // Note that, although this is member method of ActorActiveEffects and we are removing an effect (not a
// Note that, although this is member method of ActorActiveEffects and we are removing an effect (not a // spell), we still need to use the active spells store to purge this effect from active spells.
// spell), we still need to use the active spells store to purge this effect from active spells. const auto& ptr = effects.mActor.ptr();
const auto& ptr = effects.mActor.ptr();
auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells(); auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells();
activeSpells.purgeEffect(ptr, key.mId, key.mArg); activeSpells.purgeEffect(ptr, key.mId, key.mArg);
});
}; };
// types.Actor.activeEffects(o):set(value, id, ?arg) // types.Actor.activeEffects(o):set(value, id, ?arg)

View file

@ -213,7 +213,6 @@ namespace MWLua
{ "NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound }, { "NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound },
{ "StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound }, { "StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound },
{ "EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound }, { "EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound },
{ "TargetPolygonNotFound", DetourNavigator::Status::TargetPolygonNotFound },
{ "MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed }, { "MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed },
{ "FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed }, { "FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed },
{ "InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed }, { "InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed },

View file

@ -123,8 +123,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] = value;
= sol::main_object(value);
} }
}; };
@ -160,7 +159,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }] obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }]
= sol::main_object(value); = value;
} }
}; };
@ -184,8 +183,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }] obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }] = value;
= sol::main_object(value);
} }
sol::object getProgress(const Context& context) const sol::object getProgress(const Context& context) const
@ -206,8 +204,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] = value;
= sol::main_object(value);
} }
SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const
@ -261,7 +258,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = sol::main_object(value); obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value;
} }
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -321,7 +318,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] = sol::main_object(value); obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] = value;
} }
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -405,7 +402,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = sol::main_object(value); obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = value;
} }
static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -468,8 +465,7 @@ namespace MWLua
{ {
SelfObject* obj = mObject.asSelfObject(); SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj); addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }] obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }] = value;
= sol::main_object(value);
} }
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value) static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)

View file

@ -168,8 +168,7 @@ namespace MWLua
if (index == LuaUi::Layer::count()) if (index == LuaUi::Layer::count())
throw std::logic_error(std::string("Layer not found")); throw std::logic_error(std::string("Layer not found"));
index++; index++;
context.mLuaManager->addAction( context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
}; };
layersTable["insertBefore"] = [context]( layersTable["insertBefore"] = [context](
std::string_view beforename, std::string_view name, const sol::object& opt) { std::string_view beforename, std::string_view name, const sol::object& opt) {
@ -178,8 +177,7 @@ namespace MWLua
size_t index = LuaUi::Layer::indexOf(beforename); size_t index = LuaUi::Layer::indexOf(beforename);
if (index == LuaUi::Layer::count()) if (index == LuaUi::Layer::count())
throw std::logic_error(std::string("Layer not found")); throw std::logic_error(std::string("Layer not found"));
context.mLuaManager->addAction( context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
}; };
sol::table layers = LuaUtil::makeReadOnly(layersTable); sol::table layers = LuaUtil::makeReadOnly(layersTable);
sol::table layersMeta = layers[sol::metatable_key]; sol::table layersMeta = layers[sol::metatable_key];
@ -287,9 +285,8 @@ namespace MWLua
return res; return res;
}; };
api["_setWindowDisabled"] api["_setWindowDisabled"]
= [windowManager, luaManager = context.mLuaManager](std::string window, bool disabled) { = [windowManager, luaManager = context.mLuaManager](std::string_view window, bool disabled) {
luaManager->addAction( luaManager->addAction([=]() { windowManager->setDisabledByLua(window, disabled); });
[=, window = std::move(window)]() { windowManager->setDisabledByLua(window, disabled); });
}; };
// TODO // TODO
@ -311,7 +308,7 @@ namespace MWLua
return res.str(); return res.str();
}; };
element["layout"] = sol::property([](const LuaUi::Element& element) { return element.mLayout; }, element["layout"] = sol::property([](const LuaUi::Element& element) { return element.mLayout; },
[](LuaUi::Element& element, const sol::main_table& layout) { element.mLayout = layout; }); [](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; });
element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) { element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mState != LuaUi::Element::Created) if (element->mState != LuaUi::Element::Created)
return; return;

View file

@ -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(lua_State* 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)
@ -79,7 +79,7 @@ namespace MWLua
return sol::make_object<std::string>(lua, std::move(result)); return sol::make_object<std::string>(lua, std::move(result));
} }
sol::object readLineFromFile(lua_State* 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))
@ -91,7 +91,7 @@ namespace MWLua
return sol::nil; return sol::nil;
} }
sol::object readNumberFromFile(lua_State* 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)
@ -100,7 +100,7 @@ namespace MWLua
return sol::nil; return sol::nil;
} }
sol::object readCharactersFromFile(lua_State* 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, std::string()); return sol::make_object<std::string>(lua, std::string());
@ -189,7 +189,7 @@ namespace MWLua
return seek(lua, self, std::ios_base::cur, off); return seek(lua, self, std::ios_base::cur, off);
}); });
handle["lines"] = [](sol::this_main_state lua, sol::main_object self) { handle["lines"] = [](sol::this_state lua, sol::object self) {
if (!self.is<FileHandle*>()) if (!self.is<FileHandle*>())
throw std::runtime_error("self should be a file handle"); throw std::runtime_error("self should be a file handle");
return sol::as_function([lua, self]() -> sol::object { return sol::as_function([lua, self]() -> sol::object {
@ -199,7 +199,7 @@ namespace MWLua
}); });
}; };
api["lines"] = [vfs](sol::this_main_state lua, 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 {

View file

@ -289,9 +289,8 @@ namespace MWMechanics
const ESM::RefId& enchantmentId = slot->getClass().getEnchantment(*slot); const ESM::RefId& enchantmentId = slot->getClass().getEnchantment(*slot);
if (enchantmentId.empty()) if (enchantmentId.empty())
continue; continue;
const ESM::Enchantment* enchantment const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(enchantmentId);
= world->getStore().get<ESM::Enchantment>().search(enchantmentId); if (enchantment->mData.mType != ESM::Enchantment::ConstantEffect)
if (enchantment == nullptr || enchantment->mData.mType != ESM::Enchantment::ConstantEffect)
continue; continue;
if (std::find_if(mSpells.begin(), mSpells.end(), if (std::find_if(mSpells.begin(), mSpells.end(),
[&](const ActiveSpellParams& params) { [&](const ActiveSpellParams& params) {

View file

@ -2028,7 +2028,7 @@ namespace MWMechanics
iter->second->getCharacterController().skipAnim(); iter->second->getCharacterController().skipAnim();
} }
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) const bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const
{ {
const auto iter = mIndex.find(ptr.mRef); const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end()) if (iter != mIndex.end())

View file

@ -119,7 +119,7 @@ namespace MWMechanics
std::string_view startKey, std::string_view stopKey, bool forceLoop); std::string_view startKey, std::string_view stopKey, bool forceLoop);
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable); void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable);
void skipAnimation(const MWWorld::Ptr& ptr) const; void skipAnimation(const MWWorld::Ptr& ptr) const;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) const; bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const; bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const;
void persistAnimationStates() const; void persistAnimationStates() const;
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted); void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted);

View file

@ -40,15 +40,15 @@ namespace MWMechanics
static const std::size_t MAX_IDLE_SIZE = 8; static const std::size_t MAX_IDLE_SIZE = 8;
const std::string_view AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = { const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = {
"idle2", std::string("idle2"),
"idle3", std::string("idle3"),
"idle4", std::string("idle4"),
"idle5", std::string("idle5"),
"idle6", std::string("idle6"),
"idle7", std::string("idle7"),
"idle8", std::string("idle8"),
"idle9", std::string("idle9"),
}; };
namespace namespace
@ -680,7 +680,7 @@ namespace MWMechanics
{ {
if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle)) if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))
{ {
const std::string_view groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle]; const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
return MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, groupName, 0, 1); return MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, groupName, 0, 1);
} }
else else
@ -695,7 +695,7 @@ namespace MWMechanics
{ {
if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle)) if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))
{ {
const std::string_view groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle]; const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, groupName); return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, groupName);
} }
else else

View file

@ -3,7 +3,6 @@
#include "typedaipackage.hpp" #include "typedaipackage.hpp"
#include <string_view>
#include <vector> #include <vector>
#include "aitemporarybase.hpp" #include "aitemporarybase.hpp"
@ -182,7 +181,9 @@ namespace MWMechanics
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage); const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage);
/// lookup table for converting idleSelect value to groupName /// lookup table for converting idleSelect value to groupName
static const std::string_view sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1]; static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
static int OffsetToPreventOvercrowding();
}; };
} }

View file

@ -1104,10 +1104,10 @@ namespace MWMechanics
attackType = ESM::Weapon::AT_Thrust; attackType = ESM::Weapon::AT_Thrust;
// We want to avoid hit keys that come out of nowhere (e.g. in the follow animation) // We want to avoid hit keys that come out of nowhere (e.g. in the follow animation)
// and processing multiple hit keys for a single attack // and processing multiple hit keys for a single attack
if (mReadyToHit) if (mAttackStrength != -1.f)
{ {
charClass.hit(mPtr, mAttackStrength, attackType, mAttackVictim, mAttackHitPos, mAttackSuccess); charClass.hit(mPtr, mAttackStrength, attackType, mAttackVictim, mAttackHitPos, mAttackSuccess);
mReadyToHit = false; mAttackStrength = -1.f;
} }
} }
else if (isRandomAttackAnimation(groupname) && action == "start") else if (isRandomAttackAnimation(groupname) && action == "start")
@ -1153,10 +1153,10 @@ namespace MWMechanics
else if (action == "shoot release") else if (action == "shoot release")
{ {
// See notes for melee release above // See notes for melee release above
if (mReadyToHit) if (mAttackStrength != -1.f)
{ {
mAnimation->releaseArrow(mAttackStrength); mAnimation->releaseArrow(mAttackStrength);
mReadyToHit = false; mAttackStrength = -1.f;
} }
} }
else if (action == "shoot follow attach") else if (action == "shoot follow attach")
@ -1246,7 +1246,7 @@ namespace MWMechanics
void CharacterController::prepareHit() void CharacterController::prepareHit()
{ {
if (mReadyToHit) if (mAttackStrength != -1.f)
return; return;
auto& prng = MWBase::Environment::get().getWorld()->getPrng(); auto& prng = MWBase::Environment::get().getWorld()->getPrng();
@ -1261,8 +1261,6 @@ namespace MWMechanics
mAttackStrength = 0.f; mAttackStrength = 0.f;
playSwishSound(); playSwishSound();
} }
mReadyToHit = true;
} }
bool CharacterController::updateWeaponState() bool CharacterController::updateWeaponState()
@ -1522,7 +1520,6 @@ namespace MWMechanics
&& (mHitState == CharState_None || mHitState == CharState_Block)) && (mHitState == CharState_None || mHitState == CharState_Block))
{ {
mAttackStrength = -1.f; mAttackStrength = -1.f;
mReadyToHit = false;
// Randomize attacks for non-bipedal creatures // Randomize attacks for non-bipedal creatures
if (!cls.isBipedal(mPtr) if (!cls.isBipedal(mPtr)
@ -1809,7 +1806,8 @@ namespace MWMechanics
stop = strength + ' ' + stop; stop = strength + ' ' + stop;
} }
mReadyToHit = false; // Reset attack strength to make extra sure hits that come out of nowhere aren't processed
mAttackStrength = -1.f;
if (animPlaying) if (animPlaying)
mAnimation->disable(mCurrentWeapon); mAnimation->disable(mCurrentWeapon);

View file

@ -172,7 +172,6 @@ namespace MWMechanics
std::string mCurrentWeapon; std::string mCurrentWeapon;
float mAttackStrength{ -1.f }; float mAttackStrength{ -1.f };
bool mReadyToHit{ false };
MWWorld::Ptr mAttackVictim; MWWorld::Ptr mAttackVictim;
osg::Vec3f mAttackHitPos; osg::Vec3f mAttackHitPos;
bool mAttackSuccess{ false }; bool mAttackSuccess{ false };

View file

@ -778,8 +778,7 @@ namespace MWMechanics
else else
mObjects.skipAnimation(ptr); mObjects.skipAnimation(ptr);
} }
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName)
{ {
if (ptr.getClass().isActor()) if (ptr.getClass().isActor())
return mActors.checkAnimationPlaying(ptr, groupName); return mActors.checkAnimationPlaying(ptr, groupName);

View file

@ -146,7 +146,7 @@ namespace MWMechanics
std::string_view startKey, std::string_view stopKey, bool forceLoop) override; std::string_view startKey, std::string_view stopKey, bool forceLoop) override;
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable) override; void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable) override;
void skipAnimation(const MWWorld::Ptr& ptr) override; void skipAnimation(const MWWorld::Ptr& ptr) override;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) override; bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const override; bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const override;
void persistAnimationStates() override; void persistAnimationStates() override;
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) override; void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) override;

View file

@ -161,7 +161,7 @@ namespace MWRender
bool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const bool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const
{ {
if (Settings::game().mShieldSheathing && mObjectRoot) if (Settings::game().mShieldSheathing)
{ {
const MWWorld::Class& cls = mPtr.getClass(); const MWWorld::Class& cls = mPtr.getClass();
MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr); MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr);

View file

@ -1695,7 +1695,7 @@ namespace MWRender
mGlowUpdater->setColor(color); mGlowUpdater->setColor(color);
mGlowUpdater->setDuration(glowDuration); mGlowUpdater->setDuration(glowDuration);
} }
else if (mObjectRoot) else
mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, color, glowDuration); mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, color, glowDuration);
} }
} }
@ -1869,7 +1869,7 @@ namespace MWRender
void Animation::setAlpha(float alpha) void Animation::setAlpha(float alpha)
{ {
if (alpha == mAlpha || !mObjectRoot) if (alpha == mAlpha)
return; return;
mAlpha = alpha; mAlpha = alpha;

View file

@ -259,8 +259,7 @@ namespace MWRender
void CreatureWeaponAnimation::addControllers() void CreatureWeaponAnimation::addControllers()
{ {
Animation::addControllers(); Animation::addControllers();
if (mObjectRoot) WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
} }
osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration) osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration)

View file

@ -49,6 +49,11 @@ namespace MWRender
&& exts.glslLanguageVersion >= minimumGLVersionRequiredForCompute; && exts.glslLanguageVersion >= minimumGLVersionRequiredForCompute;
#endif #endif
if (mUseCompute)
Log(Debug::Info) << "Initialized compute shader pipeline for water ripples";
else
Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples";
for (size_t i = 0; i < mState.size(); ++i) for (size_t i = 0; i < mState.size(); ++i)
{ {
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet; osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
@ -86,18 +91,6 @@ namespace MWRender
else else
setupFragmentPipeline(); setupFragmentPipeline();
if (mProgramBlobber != nullptr)
{
static bool pipelineLogged = [&] {
if (mUseCompute)
Log(Debug::Info) << "Initialized compute shader pipeline for water ripples";
else
Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples";
return true;
}();
(void)pipelineLogged;
}
setCullCallback(new osg::NodeCallback); setCullCallback(new osg::NodeCallback);
setUpdateCallback(new osg::NodeCallback); setUpdateCallback(new osg::NodeCallback);
} }
@ -109,36 +102,21 @@ namespace MWRender
Shader::ShaderManager::DefineMap defineMap = { { "rippleMapSize", std::to_string(sRTTSize) + ".0" } }; Shader::ShaderManager::DefineMap defineMap = { { "rippleMapSize", std::to_string(sRTTSize) + ".0" } };
osg::ref_ptr<osg::Shader> vertex = shaderManager.getShader("fullscreen_tri.vert", {}, osg::Shader::VERTEX); osg::ref_ptr<osg::Shader> vertex = shaderManager.getShader("fullscreen_tri.vert", {}, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> blobber
= shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT);
osg::ref_ptr<osg::Shader> simulate
= shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT);
if (vertex == nullptr || blobber == nullptr || simulate == nullptr)
{
Log(Debug::Error) << "Failed to load shaders required for fragment shader ripple pipeline";
return;
}
mProgramBlobber = shaderManager.getProgram(vertex, std::move(blobber)); mProgramBlobber = shaderManager.getProgram(
mProgramSimulation = shaderManager.getProgram(std::move(vertex), std::move(simulate)); vertex, shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT));
mProgramSimulation = shaderManager.getProgram(
std::move(vertex), shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
} }
void RipplesSurface::setupComputePipeline() void RipplesSurface::setupComputePipeline()
{ {
auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager(); auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager();
osg::ref_ptr<osg::Shader> blobber mProgramBlobber = shaderManager.getProgram(
= shaderManager.getShader("core/ripples_blobber.comp", {}, osg::Shader::COMPUTE); nullptr, shaderManager.getShader("core/ripples_blobber.comp", {}, osg::Shader::COMPUTE));
osg::ref_ptr<osg::Shader> simulate mProgramSimulation = shaderManager.getProgram(
= shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE); nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE));
if (blobber == nullptr || simulate == nullptr)
{
Log(Debug::Error) << "Failed to load shaders required for compute shader ripple pipeline";
return;
}
mProgramBlobber = shaderManager.getProgram(nullptr, std::move(blobber));
mProgramSimulation = shaderManager.getProgram(nullptr, std::move(simulate));
} }
void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state) void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state)
@ -206,9 +184,6 @@ namespace MWRender
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
{ {
if (mProgramBlobber == nullptr || mProgramSimulation == nullptr)
return;
osg::State& state = *renderInfo.getState(); osg::State& state = *renderInfo.getState();
const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2; const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
const State& frameState = mState[currentFrame]; const State& frameState = mState[currentFrame];

View file

@ -162,17 +162,11 @@ namespace MWScript
public: public:
void execute(Interpreter::Runtime& runtime) override void execute(Interpreter::Runtime& runtime) override
{ {
MWWorld::Ptr ptr = R()(runtime, false); MWWorld::Ptr ptr = R()(runtime);
int index = runtime[0].mInteger; int index = runtime[0].mInteger;
runtime.pop(); runtime.pop();
if (ptr.isEmpty())
{
runtime.push(0);
return;
}
bool ret = MWBase::Environment::get().getSoundManager()->getSoundPlaying( bool ret = MWBase::Environment::get().getSoundManager()->getSoundPlaying(
ptr, ESM::RefId::stringRefId(runtime.getStringLiteral(index))); ptr, ESM::RefId::stringRefId(runtime.getStringLiteral(index)));

View file

@ -38,7 +38,7 @@ MWState::Character* MWState::CharacterManager::getCurrentCharacter()
return mCurrent; return mCurrent;
} }
void MWState::CharacterManager::deleteSlot(const MWState::Slot* slot, const MWState::Character*& character) void MWState::CharacterManager::deleteSlot(const MWState::Character* character, const MWState::Slot* slot)
{ {
std::list<Character>::iterator it = findCharacter(character); std::list<Character>::iterator it = findCharacter(character);
@ -51,7 +51,6 @@ void MWState::CharacterManager::deleteSlot(const MWState::Slot* slot, const MWSt
if (character == mCurrent) if (character == mCurrent)
mCurrent = nullptr; mCurrent = nullptr;
mCharacters.erase(it); mCharacters.erase(it);
character = nullptr;
} }
} }

View file

@ -33,7 +33,7 @@ namespace MWState
Character* getCurrentCharacter(); Character* getCurrentCharacter();
///< @note May return null ///< @note May return null
void deleteSlot(const MWState::Slot* slot, const Character*& character); void deleteSlot(const MWState::Character* character, const MWState::Slot* slot);
Character* createCharacter(const std::string& name); Character* createCharacter(const std::string& name);
///< Create new character within saved game management ///< Create new character within saved game management

View file

@ -706,10 +706,10 @@ void MWState::StateManager::quickLoad()
void MWState::StateManager::deleteGame(const MWState::Character* character, const MWState::Slot* slot) void MWState::StateManager::deleteGame(const MWState::Character* character, const MWState::Slot* slot)
{ {
const std::filesystem::path savePath = slot->mPath; const std::filesystem::path savePath = slot->mPath;
mCharacterManager.deleteSlot(slot, character); mCharacterManager.deleteSlot(character, slot);
if (mLastSavegame == savePath) if (mLastSavegame == savePath)
{ {
if (character != nullptr) if (character->begin() != character->end())
mLastSavegame = character->begin()->mPath; mLastSavegame = character->begin()->mPath;
else else
mLastSavegame.clear(); mLastSavegame.clear();
@ -757,14 +757,12 @@ void MWState::StateManager::update(float duration)
if (mNewGameRequest) if (mNewGameRequest)
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_MainMenu);
newGame(); newGame();
mNewGameRequest = false; mNewGameRequest = false;
} }
if (mLoadRequest) if (mLoadRequest)
{ {
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_MainMenu);
loadGame(*mLoadRequest); loadGame(*mLoadRequest);
mLoadRequest = std::nullopt; mLoadRequest = std::nullopt;
} }

View file

@ -337,13 +337,13 @@ namespace
// helper function for forEachInternal // helper function for forEachInternal
template <class Visitor, class List> template <class Visitor, class List>
bool forEachImp(Visitor& visitor, List& list, MWWorld::CellStore& cellStore, bool includeDeleted) bool forEachImp(Visitor& visitor, List& list, MWWorld::CellStore* cellStore)
{ {
for (auto& v : list.mList) for (typename List::List::iterator iter(list.mList.begin()); iter != list.mList.end(); ++iter)
{ {
if (!includeDeleted && !MWWorld::CellStore::isAccessible(v.mData, v.mRef)) if (!MWWorld::CellStore::isAccessible(iter->mData, iter->mRef))
continue; continue;
if (!visitor(MWWorld::Ptr(&v, &cellStore))) if (!visitor(MWWorld::Ptr(&*iter, cellStore)))
return false; return false;
} }
return true; return true;
@ -399,12 +399,12 @@ namespace MWWorld
// listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved // listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved
// objects are accounted for. // objects are accounted for.
template <class Visitor> template <class Visitor>
static bool forEachInternal(Visitor& visitor, MWWorld::CellStore& cellStore, bool includeDeleted) static bool forEachInternal(Visitor& visitor, MWWorld::CellStore& cellStore)
{ {
bool returnValue = true; bool returnValue = true;
Misc::tupleForEach(cellStore.mCellStoreImp->mRefLists, [&](auto& store) { Misc::tupleForEach(cellStore.mCellStoreImp->mRefLists, [&visitor, &returnValue, &cellStore](auto& store) {
returnValue = returnValue && forEachImp(visitor, store, cellStore, includeDeleted); returnValue = returnValue && forEachImp(visitor, store, &cellStore);
}); });
return returnValue; return returnValue;
@ -583,11 +583,11 @@ namespace MWWorld
mMergedRefsNeedsUpdate = true; mMergedRefsNeedsUpdate = true;
} }
void CellStore::updateMergedRefs(bool includeDeleted) const void CellStore::updateMergedRefs() const
{ {
mMergedRefs.clear(); mMergedRefs.clear();
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell); MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
CellStoreImp::forEachInternal(visitor, const_cast<CellStore&>(*this), includeDeleted); CellStoreImp::forEachInternal(visitor, const_cast<CellStore&>(*this));
visitor.merge(); visitor.merge();
mMergedRefsNeedsUpdate = false; mMergedRefsNeedsUpdate = false;
} }

View file

@ -219,7 +219,7 @@ namespace MWWorld
return false; return false;
if (mMergedRefsNeedsUpdate) if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted); updateMergedRefs();
if (mMergedRefs.empty()) if (mMergedRefs.empty())
return true; return true;
@ -248,7 +248,7 @@ namespace MWWorld
return false; return false;
if (mMergedRefsNeedsUpdate) if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted); updateMergedRefs();
for (const LiveCellRefBase* mergedRef : mMergedRefs) for (const LiveCellRefBase* mergedRef : mMergedRefs)
{ {
@ -273,7 +273,7 @@ namespace MWWorld
return false; return false;
if (mMergedRefsNeedsUpdate) if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted); updateMergedRefs();
if (mMergedRefs.empty()) if (mMergedRefs.empty())
return true; return true;
@ -403,7 +403,7 @@ namespace MWWorld
/// Repopulate mMergedRefs. /// Repopulate mMergedRefs.
void requestMergedRefsUpdate(); void requestMergedRefsUpdate();
void updateMergedRefs(bool includeDeleted = false) const; void updateMergedRefs() const;
// (item, max charge) // (item, max charge)
typedef std::vector<std::pair<LiveCellRefBase*, float>> TRechargingItems; typedef std::vector<std::pair<LiveCellRefBase*, float>> TRechargingItems;

View file

@ -406,7 +406,8 @@ namespace MWWorld
template <class T> template <class T>
ContainerStoreIteratorBase(const ContainerStoreIteratorBase<T>& other) ContainerStoreIteratorBase(const ContainerStoreIteratorBase<T>& other)
{ {
static_assert(IsConvertible<T, PtrType, void>::value); char CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR[IsConvertible<T, PtrType, void>::value ? 1 : -1];
((void)CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR);
copy(other); copy(other);
} }

View file

@ -290,14 +290,14 @@ namespace MWWorld
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat(); mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
} }
void World::init(Debug::Level maxRecastLogLevel, osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, void World::init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,
SceneUtil::WorkQueue* workQueue, SceneUtil::UnrefQueue& unrefQueue) SceneUtil::UnrefQueue& unrefQueue)
{ {
mPhysics = std::make_unique<MWPhysics::PhysicsSystem>(mResourceSystem, rootNode); mPhysics = std::make_unique<MWPhysics::PhysicsSystem>(mResourceSystem, rootNode);
if (Settings::navigator().mEnable) if (Settings::navigator().mEnable)
{ {
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager(maxRecastLogLevel); auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale; navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale;
mNavigator = DetourNavigator::makeNavigator(navigatorSettings, mUserDataPath); mNavigator = DetourNavigator::makeNavigator(navigatorSettings, mUserDataPath);
} }

View file

@ -4,7 +4,6 @@
#include <osg/Timer> #include <osg/Timer>
#include <osg/ref_ptr> #include <osg/ref_ptr>
#include <components/debug/debuglog.hpp>
#include <components/esm3/readerscache.hpp> #include <components/esm3/readerscache.hpp>
#include <components/misc/rng.hpp> #include <components/misc/rng.hpp>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -202,8 +201,8 @@ namespace MWWorld
Loading::Listener* listener); Loading::Listener* listener);
// Must be called after `loadData`. // Must be called after `loadData`.
void init(Debug::Level maxRecastLogLevel, osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, void init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,
SceneUtil::WorkQueue* workQueue, SceneUtil::UnrefQueue& unrefQueue); SceneUtil::UnrefQueue& unrefQueue);
virtual ~World(); virtual ~World();

View file

@ -2,7 +2,6 @@
#include <components/misc/strings/conversion.hpp> #include <components/misc/strings/conversion.hpp>
#include <components/settings/parser.hpp> #include <components/settings/parser.hpp>
#include <components/settings/values.hpp> #include <components/settings/values.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -25,9 +24,5 @@ int main(int argc, char* argv[])
Settings::StaticValues::init(); Settings::StaticValues::init();
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
} }

View file

@ -59,7 +59,7 @@ list(APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${OSG_PLUGIN_CHECKER_CPP_FILE}
add_component_dir (lua add_component_dir (lua
luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8 luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8
shapes/box inputactions yamlloader scripttracker luastateptr shapes/box inputactions yamlloader scripttracker
) )
add_component_dir (l10n add_component_dir (l10n

View file

@ -1,7 +1,6 @@
#include "gamesettings.hpp" #include "gamesettings.hpp"
#include <QDir> #include <QDir>
#include <QProgressDialog>
#include <QRegularExpression> #include <QRegularExpression>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
@ -38,13 +37,8 @@ void Config::GameSettings::validatePaths()
mDataDirs.clear(); mDataDirs.clear();
QProgressDialog progressBar("Validating paths", {}, 0, paths.count() + 1);
progressBar.setWindowModality(Qt::WindowModal);
progressBar.setValue(0);
for (const auto& dataDir : paths) for (const auto& dataDir : paths)
{ {
progressBar.setValue(progressBar.value() + 1);
if (QDir(dataDir.value).exists()) if (QDir(dataDir.value).exists())
{ {
SettingValue copy = dataDir; SettingValue copy = dataDir;
@ -56,8 +50,6 @@ void Config::GameSettings::validatePaths()
// Do the same for data-local // Do the same for data-local
const QString& local = mSettings.value(QString("data-local")).value; const QString& local = mSettings.value(QString("data-local")).value;
progressBar.setValue(progressBar.value() + 1);
if (!local.isEmpty() && QDir(local).exists()) if (!local.isEmpty() && QDir(local).exists())
{ {
mDataLocal = QDir(local).canonicalPath(); mDataLocal = QDir(local).canonicalPath();

View file

@ -10,7 +10,6 @@
#include <QDataStream> #include <QDataStream>
#include <QDebug> #include <QDebug>
#include <QDir> #include <QDir>
#include <QDirIterator>
#include <QFont> #include <QFont>
#include <QIODevice> #include <QIODevice>
@ -79,10 +78,14 @@ ContentSelectorModel::EsmFile* ContentSelectorModel::ContentModel::item(int row)
} }
const ContentSelectorModel::EsmFile* ContentSelectorModel::ContentModel::item(const QString& name) const const ContentSelectorModel::EsmFile* ContentSelectorModel::ContentModel::item(const QString& name) const
{ {
bool path = name.contains('/'); EsmFile::FileProperty fp = EsmFile::FileProperty_FileName;
if (name.contains('/'))
fp = EsmFile::FileProperty_FilePath;
for (const EsmFile* file : mFiles) for (const EsmFile* file : mFiles)
{ {
if (name.compare(path ? file->filePath() : file->fileName(), Qt::CaseInsensitive) == 0) if (name.compare(file->fileProperty(fp).toString(), Qt::CaseInsensitive) == 0)
return file; return file;
} }
return nullptr; return nullptr;
@ -307,6 +310,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
{ {
setCheckState(file->filePath(), success); setCheckState(file->filePath(), success);
emit dataChanged(index, index); emit dataChanged(index, index);
checkForLoadOrderErrors();
} }
else else
return success; return success;
@ -421,6 +425,7 @@ bool ContentSelectorModel::ContentModel::dropMimeData(
dataChanged(index(minRow, 0), index(maxRow, 0)); dataChanged(index(minRow, 0), index(maxRow, 0));
// at this point we know that drag and drop has finished. // at this point we know that drag and drop has finished.
checkForLoadOrderErrors();
return true; return true;
} }
@ -547,13 +552,15 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf
bool ContentSelectorModel::ContentModel::containsDataFiles(const QString& path) bool ContentSelectorModel::ContentModel::containsDataFiles(const QString& path)
{ {
QDir dir(path);
QStringList filters; QStringList filters;
filters << "*.esp" filters << "*.esp"
<< "*.esm" << "*.esm"
<< "*.omwgame" << "*.omwgame"
<< "*.omwaddon"; << "*.omwaddon";
QDirIterator it(path, filters, QDir::Files | QDir::NoDotAndDotDot); dir.setNameFilters(filters);
return it.hasNext();
return dir.entryList().count() != 0;
} }
void ContentSelectorModel::ContentModel::clearFiles() void ContentSelectorModel::ContentModel::clearFiles()
@ -700,13 +707,12 @@ void ContentSelectorModel::ContentModel::setNonUserContent(const QStringList& fi
bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile* file) const bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile* file) const
{ {
int index = indexFromItem(file).row(); return mPluginsWithLoadOrderError.contains(file->filePath());
auto errors = checkForLoadOrderErrors(file, index);
return !errors.empty();
} }
void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileList) void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileList)
{ {
mPluginsWithLoadOrderError.clear();
int previousPosition = -1; int previousPosition = -1;
for (const QString& filepath : fileList) for (const QString& filepath : fileList)
{ {
@ -719,6 +725,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
if (filePosition < previousPosition) if (filePosition < previousPosition)
{ {
mFiles.move(filePosition, previousPosition); mFiles.move(filePosition, previousPosition);
emit dataChanged(index(filePosition, 0, QModelIndex()), index(previousPosition, 0, QModelIndex()));
} }
else else
{ {
@ -726,7 +733,24 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
} }
} }
} }
emit dataChanged(index(0, 0), index(rowCount(), columnCount())); checkForLoadOrderErrors();
}
void ContentSelectorModel::ContentModel::checkForLoadOrderErrors()
{
for (int row = 0; row < mFiles.count(); ++row)
{
EsmFile* file = mFiles.at(row);
bool isRowInError = checkForLoadOrderErrors(file, row).count() != 0;
if (isRowInError)
{
mPluginsWithLoadOrderError.insert(file->filePath());
}
else
{
mPluginsWithLoadOrderError.remove(file->filePath());
}
}
} }
QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors( QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(
@ -767,12 +791,11 @@ QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::
QString ContentSelectorModel::ContentModel::toolTip(const EsmFile* file) const QString ContentSelectorModel::ContentModel::toolTip(const EsmFile* file) const
{ {
int index = indexFromItem(file).row(); if (isLoadOrderError(file))
auto errors = checkForLoadOrderErrors(file, index);
if (!errors.empty())
{ {
QString text("<b>"); QString text("<b>");
for (const LoadOrderError& error : errors) int index = indexFromItem(item(file->filePath())).row();
for (const LoadOrderError& error : checkForLoadOrderErrors(file, index))
{ {
assert(error.errorCode() != LoadOrderError::ErrorCode::ErrorCode_None); assert(error.errorCode() != LoadOrderError::ErrorCode::ErrorCode_None);
@ -877,6 +900,7 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke
void ContentSelectorModel::ContentModel::uncheckAll() void ContentSelectorModel::ContentModel::uncheckAll()
{ {
emit layoutAboutToBeChanged();
mCheckedFiles.clear(); mCheckedFiles.clear();
emit dataChanged(index(0, 0), index(rowCount(), columnCount()), { Qt::CheckStateRole, Qt::UserRole + 1 }); emit layoutChanged();
} }

View file

@ -69,6 +69,9 @@ namespace ContentSelectorModel
void refreshModel(); void refreshModel();
/// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues
void checkForLoadOrderErrors();
private: private:
void addFile(EsmFile* file); void addFile(EsmFile* file);
@ -86,6 +89,7 @@ namespace ContentSelectorModel
QStringList mNonUserContent; QStringList mNonUserContent;
std::set<const EsmFile*> mCheckedFiles; std::set<const EsmFile*> mCheckedFiles;
QHash<QString, bool> mNewFiles; QHash<QString, bool> mNewFiles;
QSet<QString> mPluginsWithLoadOrderError;
QString mEncoding; QString mEncoding;
QIcon mWarningIcon; QIcon mWarningIcon;
QIcon mErrorIcon; QIcon mErrorIcon;

View file

@ -48,18 +48,18 @@ namespace ContentSelectorModel
void addGameFile(const QString& name) { mGameFiles.append(name); } void addGameFile(const QString& name) { mGameFiles.append(name); }
QVariant fileProperty(const FileProperty prop) const; QVariant fileProperty(const FileProperty prop) const;
const QString& fileName() const { return mFileName; } QString fileName() const { return mFileName; }
const QString& author() const { return mAuthor; } QString author() const { return mAuthor; }
QDateTime modified() const { return mModified; } QDateTime modified() const { return mModified; }
const QString& formatVersion() const { return mVersion; } QString formatVersion() const { return mVersion; }
const QString& filePath() const { return mPath; } QString filePath() const { return mPath; }
bool builtIn() const { return mBuiltIn; } bool builtIn() const { return mBuiltIn; }
bool fromAnotherConfigFile() const { return mFromAnotherConfigFile; } bool fromAnotherConfigFile() const { return mFromAnotherConfigFile; }
bool isMissing() const { return mPath.isEmpty(); } bool isMissing() const { return mPath.isEmpty(); }
/// @note Contains file names, not paths. /// @note Contains file names, not paths.
const QStringList& gameFiles() const { return mGameFiles; } const QStringList& gameFiles() const { return mGameFiles; }
const QString& description() const { return mDescription; } QString description() const { return mDescription; }
QString toolTip() const QString toolTip() const
{ {
if (isMissing()) if (isMissing())

View file

@ -211,6 +211,7 @@ void ContentSelectorView::ContentSelector::addFiles(const QString& path, bool ne
ui->gameFileView->setCurrentIndex(0); ui->gameFileView->setCurrentIndex(0);
mContentModel->uncheckAll(); mContentModel->uncheckAll();
mContentModel->checkForLoadOrderErrors();
} }
void ContentSelectorView::ContentSelector::sortFiles() void ContentSelectorView::ContentSelector::sortFiles()
@ -253,6 +254,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i
oldIndex = index; oldIndex = index;
setGameFileSelected(index, true); setGameFileSelected(index, true);
mContentModel->checkForLoadOrderErrors();
} }
emit signalCurrentGamefileIndexChanged(index); emit signalCurrentGamefileIndexChanged(index);

View file

@ -237,7 +237,7 @@ namespace Crash
// must remain until monitor has finished // must remain until monitor has finished
waitMonitor(); waitMonitor();
std::string message = "OpenMW has encountered a fatal error.\nCrash dump saved to '" std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '"
+ Misc::StringUtils::u8StringToString(getCrashDumpPath(*mShm).u8string()) + Misc::StringUtils::u8StringToString(getCrashDumpPath(*mShm).u8string())
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !"; + "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);

View file

@ -21,7 +21,6 @@ namespace Crash
// the main openmw process in task manager. // the main openmw process in task manager.
static constexpr const int CrashCatcherTimeout = 2500; static constexpr const int CrashCatcherTimeout = 2500;
static constexpr const int CrashCatcherThawTimeout = 250;
struct CrashSHM; struct CrashSHM;

View file

@ -87,10 +87,9 @@ namespace Crash
SetEvent(mSignalAppEvent); SetEvent(mSignalAppEvent);
} }
bool CrashMonitor::waitApp(bool thawMode) const bool CrashMonitor::waitApp() const
{ {
return WaitForSingleObject(mSignalMonitorEvent, thawMode ? CrashCatcherThawTimeout : CrashCatcherTimeout) return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;
== WAIT_OBJECT_0;
} }
bool CrashMonitor::isAppAlive() const bool CrashMonitor::isAppAlive() const
@ -186,7 +185,7 @@ namespace Crash
frozen = false; frozen = false;
} }
if (!mFreezeAbort && waitApp(frozen)) if (!mFreezeAbort && waitApp())
{ {
shmLock(); shmLock();
@ -216,7 +215,7 @@ namespace Crash
{ {
handleCrash(true); handleCrash(true);
TerminateProcess(mAppProcessHandle, 0xDEAD); TerminateProcess(mAppProcessHandle, 0xDEAD);
std::string message = "OpenMW has frozen.\nCrash dump saved to '" std::string message = "OpenMW appears to have frozen.\nCrash log saved to '"
+ Misc::StringUtils::u8StringToString(getFreezeDumpPath(*mShm).u8string()) + Misc::StringUtils::u8StringToString(getFreezeDumpPath(*mShm).u8string())
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !"; + "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr); SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
@ -290,10 +289,10 @@ namespace Crash
{ {
std::thread messageBoxThread([&]() { std::thread messageBoxThread([&]() {
SDL_MessageBoxButtonData button = { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Abort" }; SDL_MessageBoxButtonData button = { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Abort" };
SDL_MessageBoxData messageBoxData = { SDL_MESSAGEBOX_ERROR, nullptr, "OpenMW has frozen", SDL_MessageBoxData messageBoxData = { SDL_MESSAGEBOX_ERROR, nullptr, "OpenMW appears to have frozen",
"OpenMW has frozen. This should never happen. Press Abort to terminate it and generate a crash dump to " "OpenMW appears to have frozen. Press Abort to terminate it and generate a crash dump.\nIf OpenMW "
"help diagnose the problem.\nOpenMW may unfreeze if you wait, and this message box will disappear " "hasn't actually frozen, this message box will disappear a within a few seconds of it becoming "
"after it becomes responsive.", "responsive.",
1, &button, nullptr }; 1, &button, nullptr };
int buttonId; int buttonId;

View file

@ -41,7 +41,7 @@ namespace Crash
void signalApp() const; void signalApp() const;
bool waitApp(bool thawMode) const; bool waitApp() const;
bool isAppAlive() const; bool isAppAlive() const;

View file

@ -106,96 +106,94 @@ namespace Debug
logListener = std::move(listener); logListener = std::move(listener);
} }
namespace class DebugOutputBase : public boost::iostreams::sink
{ {
class DebugOutputBase : public boost::iostreams::sink public:
virtual std::streamsize write(const char* str, std::streamsize size)
{ {
public: if (size <= 0)
virtual std::streamsize write(const char* str, std::streamsize size) return size;
std::string_view msg{ str, static_cast<size_t>(size) };
// Skip debug level marker
Level level = All;
if (Log::sWriteLevel)
{ {
if (size <= 0) level = getLevelMarker(msg[0]);
return size; msg = msg.substr(1);
std::string_view msg{ str, static_cast<size_t>(size) }; }
// Skip debug level marker char prefix[32];
Level level = All; std::size_t prefixSize;
if (Log::sWriteLevel) {
{ prefix[0] = '[';
level = getLevelMarker(msg[0]); const auto now = std::chrono::system_clock::now();
msg = msg.substr(1); const auto time = std::chrono::system_clock::to_time_t(now);
} tm time_info{};
char prefix[32];
std::size_t prefixSize;
{
prefix[0] = '[';
const auto now = std::chrono::system_clock::now();
const auto time = std::chrono::system_clock::to_time_t(now);
tm time_info{};
#ifdef _WIN32 #ifdef _WIN32
(void)localtime_s(&time_info, &time); (void)localtime_s(&time_info, &time);
#else #else
(void)localtime_r(&time, &time_info); (void)localtime_r(&time, &time_info);
#endif #endif
prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", &time_info) + 1; prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", &time_info) + 1;
char levelLetter = " EWIVD*"[int(level)]; char levelLetter = " EWIVD*"[int(level)];
const auto ms const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
= std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, ".%03u %c] ",
prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, ".%03u %c] ", static_cast<unsigned>(ms % 1000), levelLetter);
static_cast<unsigned>(ms % 1000), levelLetter);
}
while (!msg.empty())
{
if (msg[0] == 0)
break;
size_t lineSize = 1;
while (lineSize < msg.size() && msg[lineSize - 1] != '\n')
lineSize++;
writeImpl(prefix, prefixSize, level);
writeImpl(msg.data(), lineSize, level);
if (logListener)
logListener(
level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize));
msg = msg.substr(lineSize);
}
return size;
} }
virtual ~DebugOutputBase() = default; while (!msg.empty())
protected:
static Level getLevelMarker(char marker)
{ {
if (0 <= marker && static_cast<unsigned>(marker) < static_cast<unsigned>(All)) if (msg[0] == 0)
return static_cast<Level>(marker); break;
return All; size_t lineSize = 1;
while (lineSize < msg.size() && msg[lineSize - 1] != '\n')
lineSize++;
writeImpl(prefix, prefixSize, level);
writeImpl(msg.data(), lineSize, level);
if (logListener)
logListener(level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize));
msg = msg.substr(lineSize);
} }
virtual std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel) return size;
{ }
return size;
} virtual ~DebugOutputBase() = default;
};
protected:
static Level getLevelMarker(char marker)
{
if (0 <= marker && static_cast<unsigned>(marker) < static_cast<unsigned>(All))
return static_cast<Level>(marker);
return All;
}
virtual std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{
return size;
}
};
#if defined _WIN32 && defined _DEBUG #if defined _WIN32 && defined _DEBUG
class DebugOutput : public DebugOutputBase class DebugOutput : public DebugOutputBase
{
public:
std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{ {
public: // Make a copy for null termination
std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel) std::string tmp(str, static_cast<unsigned int>(size));
{ // Write string to Visual Studio Debug output
// Make a copy for null termination OutputDebugString(tmp.c_str());
std::string tmp(str, static_cast<unsigned int>(size)); return size;
// Write string to Visual Studio Debug output }
OutputDebugString(tmp.c_str());
return size;
}
virtual ~DebugOutput() = default; virtual ~DebugOutput() = default;
}; };
#else #else
namespace
{
struct Record struct Record
{ {
std::string mValue; std::string mValue;
@ -326,38 +324,22 @@ namespace Debug
First mFirst; First mFirst;
Second mSecond; Second mSecond;
}; };
}
#endif #endif
Level toLevel(std::string_view value) static std::unique_ptr<std::ostream> rawStdout = nullptr;
{ static std::unique_ptr<std::ostream> rawStderr = nullptr;
if (value == "ERROR") static std::unique_ptr<std::mutex> rawStderrMutex = nullptr;
return Error; static std::ofstream logfile;
if (value == "WARNING")
return Warning;
if (value == "INFO")
return Info;
if (value == "VERBOSE")
return Verbose;
if (value == "DEBUG")
return Debug;
return Verbose;
}
static std::unique_ptr<std::ostream> rawStdout = nullptr;
static std::unique_ptr<std::ostream> rawStderr = nullptr;
static std::unique_ptr<std::mutex> rawStderrMutex = nullptr;
static std::ofstream logfile;
#if defined(_WIN32) && defined(_DEBUG) #if defined(_WIN32) && defined(_DEBUG)
static boost::iostreams::stream_buffer<DebugOutput> sb; static boost::iostreams::stream_buffer<DebugOutput> sb;
#else #else
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardOut; static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardOut;
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardErr; static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardErr;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedOut; static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedOut;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedErr; static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedErr;
#endif #endif
}
std::ostream& getRawStdout() std::ostream& getRawStdout()
{ {
@ -377,19 +359,23 @@ namespace Debug
Level getDebugLevel() Level getDebugLevel()
{ {
if (const char* env = getenv("OPENMW_DEBUG_LEVEL")) if (const char* env = getenv("OPENMW_DEBUG_LEVEL"))
return toLevel(env); {
const std::string_view value(env);
if (value == "ERROR")
return Error;
if (value == "WARNING")
return Warning;
if (value == "INFO")
return Info;
if (value == "VERBOSE")
return Verbose;
if (value == "DEBUG")
return Debug;
}
return Verbose; return Verbose;
} }
Level getRecastMaxLogLevel()
{
if (const char* env = getenv("OPENMW_RECAST_MAX_LOG_LEVEL"))
return toLevel(env);
return Error;
}
void setupLogging(const std::filesystem::path& logDir, std::string_view appName) void setupLogging(const std::filesystem::path& logDir, std::string_view appName)
{ {
Log::sMinDebugLevel = getDebugLevel(); Log::sMinDebugLevel = getDebugLevel();

View file

@ -36,8 +36,6 @@ namespace Debug
Level getDebugLevel(); Level getDebugLevel();
Level getRecastMaxLogLevel();
// Redirect cout and cerr to the log file // Redirect cout and cerr to the log file
void setupLogging(const std::filesystem::path& logDir, std::string_view appName); void setupLogging(const std::filesystem::path& logDir, std::string_view appName);

View file

@ -453,9 +453,9 @@ namespace DetourNavigator
Misc::setCurrentThreadIdlePriority(); Misc::setCurrentThreadIdlePriority();
while (!mShouldStop) while (!mShouldStop)
{ {
if (JobIt job = getNextJob(); job != mJobs.end()) try
{ {
try if (JobIt job = getNextJob(); job != mJobs.end())
{ {
const JobStatus status = processJob(*job); const JobStatus status = processJob(*job);
Log(Debug::Debug) << "Processed job " << job->mId << " with status=" << status Log(Debug::Debug) << "Processed job " << job->mId << " with status=" << status
@ -480,20 +480,12 @@ namespace DetourNavigator
} }
} }
} }
catch (const std::exception& e) else
{ cleanupLastUpdates();
Log(Debug::Warning) << "Failed to process navmesh job " << job->mId
<< " for worldspace=" << job->mWorldspace << " agent=" << job->mAgentBounds
<< " changedTile=(" << job->mChangedTile << ")"
<< " changeType=" << job->mChangeType
<< " by thread=" << std::this_thread::get_id() << ": " << e.what();
unlockTile(job->mId, job->mAgentBounds, job->mChangedTile);
removeJob(job);
}
} }
else catch (const std::exception& e)
{ {
cleanupLastUpdates(); Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what();
} }
} }
Log(Debug::Debug) << "Stop navigator jobs processing by thread=" << std::this_thread::get_id(); Log(Debug::Debug) << "Stop navigator jobs processing by thread=" << std::this_thread::get_id();
@ -501,8 +493,7 @@ namespace DetourNavigator
JobStatus AsyncNavMeshUpdater::processJob(Job& job) JobStatus AsyncNavMeshUpdater::processJob(Job& job)
{ {
Log(Debug::Debug) << "Processing job " << job.mId << " for worldspace=" << job.mWorldspace Log(Debug::Debug) << "Processing job " << job.mId << " for agent=(" << job.mAgentBounds << ")"
<< " agent=" << job.mAgentBounds << ""
<< " changedTile=(" << job.mChangedTile << ")" << " changedTile=(" << job.mChangedTile << ")"
<< " changeType=" << job.mChangeType << " by thread=" << std::this_thread::get_id(); << " changeType=" << job.mChangeType << " by thread=" << std::this_thread::get_id();
@ -552,14 +543,7 @@ namespace DetourNavigator
return JobStatus::Done; return JobStatus::Done;
} }
try writeDebugRecastMesh(mSettings, job.mChangedTile, *recastMesh);
{
writeDebugRecastMesh(mSettings, job.mChangedTile, *recastMesh);
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Failed to write debug recast mesh: " << e.what();
}
NavMeshTilesCache::Value cachedNavMeshData NavMeshTilesCache::Value cachedNavMeshData
= mNavMeshTilesCache.get(job.mAgentBounds, job.mChangedTile, *recastMesh); = mNavMeshTilesCache.get(job.mAgentBounds, job.mChangedTile, *recastMesh);
@ -682,19 +666,12 @@ namespace DetourNavigator
mPresentTiles.insert(std::make_tuple(job.mAgentBounds, job.mChangedTile)); mPresentTiles.insert(std::make_tuple(job.mAgentBounds, job.mChangedTile));
} }
try writeDebugNavMesh(mSettings, navMeshCacheItem, navMeshVersion);
{
writeDebugNavMesh(mSettings, navMeshCacheItem, navMeshVersion);
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Failed to write debug navmesh: " << e.what();
}
return isSuccess(status) ? JobStatus::Done : JobStatus::Fail; return isSuccess(status) ? JobStatus::Done : JobStatus::Fail;
} }
JobIt AsyncNavMeshUpdater::getNextJob() noexcept JobIt AsyncNavMeshUpdater::getNextJob()
{ {
std::unique_lock<std::mutex> lock(mMutex); std::unique_lock<std::mutex> lock(mMutex);
@ -769,7 +746,7 @@ namespace DetourNavigator
return mJobs.size(); return mJobs.size();
} }
void AsyncNavMeshUpdater::cleanupLastUpdates() noexcept void AsyncNavMeshUpdater::cleanupLastUpdates()
{ {
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();

View file

@ -244,7 +244,7 @@ namespace DetourNavigator
inline JobStatus handleUpdateNavMeshStatus(UpdateNavMeshStatus status, const Job& job, inline JobStatus handleUpdateNavMeshStatus(UpdateNavMeshStatus status, const Job& job,
const GuardedNavMeshCacheItem& navMeshCacheItem, const RecastMesh& recastMesh); const GuardedNavMeshCacheItem& navMeshCacheItem, const RecastMesh& recastMesh);
inline JobIt getNextJob() noexcept; JobIt getNextJob();
void postThreadJob(JobIt job, std::deque<JobIt>& queue); void postThreadJob(JobIt job, std::deque<JobIt>& queue);
@ -254,7 +254,7 @@ namespace DetourNavigator
inline std::size_t getTotalJobs() const; inline std::size_t getTotalJobs() const;
inline void cleanupLastUpdates() noexcept; void cleanupLastUpdates();
inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener); inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener);

View file

@ -523,7 +523,7 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, ESM::RefId worldspace, std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, ESM::RefId worldspace,
const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings) const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings)
{ {
RecastContext context(worldspace, tilePosition, agentBounds, recastMesh.getVersion(), settings.mMaxLogLevel); RecastContext context(worldspace, tilePosition, agentBounds);
const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentBounds.mHalfExtents.z(), settings); const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentBounds.mHalfExtents.z(), settings);

View file

@ -1,7 +1,7 @@
#include "recastcontext.hpp" #include "recastcontext.hpp"
#include "debug.hpp" #include "debug.hpp"
#include <components/debug/debuglog.hpp> #include "components/debug/debuglog.hpp"
#include <sstream> #include <sstream>
@ -23,30 +23,25 @@ namespace DetourNavigator
return Debug::Debug; return Debug::Debug;
} }
std::string formatPrefix(ESM::RefId worldspace, const TilePosition& tilePosition, std::string formatPrefix(
const AgentBounds& agentBounds, const Version& version) ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << "Worldspace: " << worldspace << "; tile position: " << tilePosition.x() << ", " stream << "Worldspace: " << worldspace << "; tile position: " << tilePosition.x() << ", "
<< tilePosition.y() << "; agent bounds: " << agentBounds << "; version: " << version << "; "; << tilePosition.y() << "; agent bounds: " << agentBounds << "; ";
return stream.str(); return stream.str();
} }
} }
RecastContext::RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, RecastContext::RecastContext(
const AgentBounds& agentBounds, const Version& version, Debug::Level maxLogLevel) ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
: mMaxLogLevel(maxLogLevel) : mPrefix(formatPrefix(worldspace, tilePosition, agentBounds))
, mPrefix(formatPrefix(worldspace, tilePosition, agentBounds, version))
{ {
} }
void RecastContext::doLog(const rcLogCategory category, const char* msg, const int len) void RecastContext::doLog(const rcLogCategory category, const char* msg, const int len)
{ {
if (msg == nullptr || len <= 0) if (len > 0)
return; Log(getLogLevel(category)) << mPrefix << std::string_view(msg, static_cast<std::size_t>(len));
const Debug::Level level = getLogLevel(category);
if (level > mMaxLogLevel)
return;
Log(level) << mPrefix << std::string_view(msg, static_cast<std::size_t>(len));
} }
} }

View file

@ -3,7 +3,6 @@
#include "tileposition.hpp" #include "tileposition.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <string> #include <string>
@ -13,18 +12,15 @@
namespace DetourNavigator namespace DetourNavigator
{ {
struct AgentBounds; struct AgentBounds;
struct Version;
class RecastContext final : public rcContext class RecastContext final : public rcContext
{ {
public: public:
explicit RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds, explicit RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds);
const Version& version, Debug::Level maxLogLevel);
const std::string& getPrefix() const { return mPrefix; } const std::string& getPrefix() const { return mPrefix; }
private: private:
Debug::Level mMaxLogLevel;
std::string mPrefix; std::string mPrefix;
void doLog(rcLogCategory category, const char* msg, int len) override; void doLog(rcLogCategory category, const char* msg, int len) override;

View file

@ -44,7 +44,7 @@ namespace DetourNavigator
}; };
} }
RecastSettings makeRecastSettingsFromSettingsManager(Debug::Level maxLogLevel) RecastSettings makeRecastSettingsFromSettingsManager()
{ {
RecastSettings result; RecastSettings result;
@ -63,7 +63,6 @@ namespace DetourNavigator
result.mRegionMergeArea = ::Settings::navigator().mRegionMergeArea; result.mRegionMergeArea = ::Settings::navigator().mRegionMergeArea;
result.mRegionMinArea = ::Settings::navigator().mRegionMinArea; result.mRegionMinArea = ::Settings::navigator().mRegionMinArea;
result.mTileSize = ::Settings::navigator().mTileSize; result.mTileSize = ::Settings::navigator().mTileSize;
result.mMaxLogLevel = maxLogLevel;
return result; return result;
} }
@ -81,11 +80,11 @@ namespace DetourNavigator
} }
} }
Settings makeSettingsFromSettingsManager(Debug::Level maxLogLevel) Settings makeSettingsFromSettingsManager()
{ {
Settings result; Settings result;
result.mRecast = makeRecastSettingsFromSettingsManager(maxLogLevel); result.mRecast = makeRecastSettingsFromSettingsManager();
result.mDetour = makeDetourSettingsFromSettingsManager(); result.mDetour = makeDetourSettingsFromSettingsManager();
const NavMeshLimits limits = getNavMeshTileLimits(result.mDetour); const NavMeshLimits limits = getNavMeshTileLimits(result.mDetour);

View file

@ -1,8 +1,6 @@
#ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H #ifndef OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H
#define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H #define OPENMW_COMPONENTS_DETOURNAVIGATOR_SETTINGS_H
#include <components/debug/debuglog.hpp>
#include <chrono> #include <chrono>
#include <string> #include <string>
@ -25,7 +23,6 @@ namespace DetourNavigator
int mRegionMergeArea = 0; int mRegionMergeArea = 0;
int mRegionMinArea = 0; int mRegionMinArea = 0;
int mTileSize = 0; int mTileSize = 0;
Debug::Level mMaxLogLevel = Debug::Error;
}; };
struct DetourSettings struct DetourSettings
@ -58,7 +55,7 @@ namespace DetourNavigator
inline constexpr std::int64_t navMeshFormatVersion = 2; inline constexpr std::int64_t navMeshFormatVersion = 2;
Settings makeSettingsFromSettingsManager(Debug::Level maxLogLevel); Settings makeSettingsFromSettingsManager();
} }
#endif #endif

View file

@ -1,9 +1,8 @@
#include "luascripts.hpp" #include "luascripts.hpp"
#include <components/esm3/esmreader.hpp> #include "components/esm3/esmreader.hpp"
#include <components/esm3/esmwriter.hpp> #include "components/esm3/esmwriter.hpp"
#include <components/lua/luastateptr.hpp>
#include <components/lua/serialization.hpp> #include <components/lua/serialization.hpp>
// List of all records, that are related to Lua. // List of all records, that are related to Lua.
@ -103,16 +102,13 @@ void ESM::LuaScriptsCfg::adjustRefNums(const ESMReader& esm)
throw std::runtime_error("Incorrect contentFile index"); throw std::runtime_error("Incorrect contentFile index");
}; };
LuaUtil::LuaStatePtr state(luaL_newstate()); lua_State* L = luaL_newstate();
if (state == nullptr)
throw std::runtime_error("Failed to create Lua runtime");
LuaUtil::BasicSerializer serializer(adjustRefNumFn); LuaUtil::BasicSerializer serializer(adjustRefNumFn);
auto adjustLuaData = [&](std::string& data) { auto adjustLuaData = [&](std::string& data) {
if (data.empty()) if (data.empty())
return; return;
sol::object luaData = LuaUtil::deserialize(state.get(), data, &serializer); sol::object luaData = LuaUtil::deserialize(L, data, &serializer);
data = LuaUtil::serialize(luaData, &serializer); data = LuaUtil::serialize(luaData, &serializer);
}; };
@ -127,6 +123,7 @@ void ESM::LuaScriptsCfg::adjustRefNums(const ESMReader& esm)
refCfg.mRefnumContentFile = adjustRefNumFn(refCfg.mRefnumContentFile); refCfg.mRefnumContentFile = adjustRefNumFn(refCfg.mRefnumContentFile);
} }
} }
lua_close(L);
} }
void ESM::LuaScriptsCfg::save(ESMWriter& esm) const void ESM::LuaScriptsCfg::save(ESMWriter& esm) const

View file

@ -32,7 +32,7 @@ namespace ESM
void EffectList::updateIndexes() void EffectList::updateIndexes()
{ {
for (size_t i = 0; i < mList.size(); i++) for (size_t i = 0; i < mList.size(); i++)
mList[i].mIndex = static_cast<uint32_t>(i); mList[i].mIndex = i;
} }
void EffectList::add(ESMReader& esm) void EffectList::add(ESMReader& esm)

View file

@ -73,10 +73,10 @@ namespace ESM
int index = getIndex(); int index = getIndex();
for (int i = 0; i < getIndex(); i++) for (int i = 0; i < getIndex(); i++)
{ {
if (readers.getFileSize(static_cast<std::size_t>(i)) == 0) const ESM::ReadersCache::BusyItem reader = readers.get(static_cast<std::size_t>(i));
if (reader->getFileSize() == 0)
continue; // Content file in non-ESM format continue; // Content file in non-ESM format
const auto fnamecandidate const auto fnamecandidate = Files::pathToUnicodeString(reader->getName().filename());
= Files::pathToUnicodeString(readers.getName(static_cast<std::size_t>(i)).filename());
if (Misc::StringUtils::ciEqual(fname, fnamecandidate)) if (Misc::StringUtils::ciEqual(fname, fnamecandidate))
{ {
index = i; index = i;

View file

@ -47,7 +47,6 @@ namespace ESM
{ {
it->mReader.open(*it->mName); it->mReader.open(*it->mName);
it->mName.reset(); it->mName.reset();
it->mFileSize.reset();
} }
mBusyItems.splice(mBusyItems.end(), mClosedItems, it); mBusyItems.splice(mBusyItems.end(), mClosedItems, it);
break; break;
@ -58,46 +57,6 @@ namespace ESM
return BusyItem(*this, it); return BusyItem(*this, it);
} }
const std::filesystem::path& ReadersCache::getName(std::size_t index) const
{
const auto indexIt = mIndex.find(index);
if (indexIt == mIndex.end())
throw std::logic_error("ESMReader at index " + std::to_string(index) + " has not been created yet");
switch (indexIt->second->mState)
{
case State::Busy:
case State::Free:
return indexIt->second->mReader.getName();
case State::Closed:
if (indexIt->second->mName)
return *indexIt->second->mName;
throw std::logic_error("ESMReader at index " + std::to_string(index) + " has forgotten its filename");
default:
throw std::logic_error("ESMReader at index " + std::to_string(index) + " in unknown state");
}
}
std::size_t ReadersCache::getFileSize(std::size_t index)
{
const auto indexIt = mIndex.find(index);
if (indexIt == mIndex.end())
return 0;
switch (indexIt->second->mState)
{
case State::Busy:
case State::Free:
if (!indexIt->second->mReader.getName().empty())
return indexIt->second->mReader.getFileSize();
throw std::logic_error("ESMReader at index " + std::to_string(index) + " has not been opened yet");
case State::Closed:
if (indexIt->second->mFileSize)
return *indexIt->second->mFileSize;
throw std::logic_error("ESMReader at index " + std::to_string(index) + " has forgotten its file size");
default:
throw std::logic_error("ESMReader at index " + std::to_string(index) + " in unknown state");
}
}
void ReadersCache::closeExtraReaders() void ReadersCache::closeExtraReaders()
{ {
while (!mFreeItems.empty() && mBusyItems.size() + mFreeItems.size() + 1 > mCapacity) while (!mFreeItems.empty() && mBusyItems.size() + mFreeItems.size() + 1 > mCapacity)
@ -106,7 +65,6 @@ namespace ESM
if (it->mReader.isOpen()) if (it->mReader.isOpen())
{ {
it->mName = it->mReader.getName(); it->mName = it->mReader.getName();
it->mFileSize = it->mReader.getFileSize();
it->mReader.close(); it->mReader.close();
} }
mClosedItems.splice(mClosedItems.end(), mFreeItems, it); mClosedItems.splice(mClosedItems.end(), mFreeItems, it);

View file

@ -26,7 +26,6 @@ namespace ESM
State mState = State::Busy; State mState = State::Busy;
ESMReader mReader; ESMReader mReader;
std::optional<std::filesystem::path> mName; std::optional<std::filesystem::path> mName;
std::optional<std::size_t> mFileSize;
Item() = default; Item() = default;
}; };
@ -56,10 +55,6 @@ namespace ESM
BusyItem get(std::size_t index); BusyItem get(std::size_t index);
const std::filesystem::path& getName(std::size_t index) const;
std::size_t getFileSize(std::size_t index);
void clear(); void clear();
private: private:

View file

@ -114,9 +114,6 @@ void ESM4::Quest::load(ESM4::Reader& reader)
case ESM::fourCC("NNAM"): // FO3 case ESM::fourCC("NNAM"): // FO3
case ESM::fourCC("QOBJ"): // FO3 case ESM::fourCC("QOBJ"): // FO3
case ESM::fourCC("NAM0"): // FO3 case ESM::fourCC("NAM0"): // FO3
case ESM::fourCC("SLSD"): // FO3
case ESM::fourCC("SCVR"): // FO3
case ESM::fourCC("SCRV"): // FO3
case ESM::fourCC("ANAM"): // TES5 case ESM::fourCC("ANAM"): // TES5
case ESM::fourCC("DNAM"): // TES5 case ESM::fourCC("DNAM"): // TES5
case ESM::fourCC("ENAM"): // TES5 case ESM::fourCC("ENAM"): // TES5

View file

@ -24,6 +24,8 @@ namespace Fallback
}; };
// Parses and validates a fallback map from boost program_options. // Parses and validates a fallback map from boost program_options.
// Note: for boost to pick up the validate function, you need to pull in the namespace e.g.
// by using namespace Fallback;
void validate(boost::any& v, std::vector<std::string> const& tokens, FallbackMap*, int); void validate(boost::any& v, std::vector<std::string> const& tokens, FallbackMap*, int);
} }

View file

@ -36,14 +36,12 @@ namespace Files
{ {
std::filesystem::path userPath = std::filesystem::current_path(); std::filesystem::path userPath = std::filesystem::current_path();
PWSTR cString; WCHAR path[MAX_PATH + 1] = {};
HRESULT result = SHGetKnownFolderPath(FOLDERID_Documents, 0, nullptr, &cString);
if (SUCCEEDED(result))
userPath = std::filesystem::path(cString);
else
Log(Debug::Error) << "Error " << result << " when getting Documents path";
CoTaskMemFree(cString); if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, nullptr, 0, path)))
{
userPath = std::filesystem::path(path);
}
return userPath / "My Games" / mName; return userPath / "My Games" / mName;
} }
@ -56,19 +54,14 @@ namespace Files
std::filesystem::path WindowsPath::getGlobalConfigPath() const std::filesystem::path WindowsPath::getGlobalConfigPath() const
{ {
// The concept of a global config path is absurd on Windows.
// Always use local config instead.
// The virtual base class requires that we provide this, though.
std::filesystem::path globalPath = std::filesystem::current_path(); std::filesystem::path globalPath = std::filesystem::current_path();
PWSTR cString; WCHAR path[MAX_PATH + 1] = {};
HRESULT result = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, nullptr, &cString);
if (SUCCEEDED(result))
globalPath = std::filesystem::path(cString);
else
Log(Debug::Error) << "Error " << result << " when getting Program Files path";
CoTaskMemFree(cString); if (SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_PROGRAM_FILES | CSIDL_FLAG_CREATE, nullptr, 0, path)))
{
globalPath = std::filesystem::path(path);
}
return globalPath / mName; return globalPath / mName;
} }

View file

@ -227,10 +227,9 @@ namespace
namespace Gui namespace Gui
{ {
FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor, bool exportFonts) FontLoader::FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor)
: mVFS(vfs) : mVFS(vfs)
, mScalingFactor(scalingFactor) , mScalingFactor(scalingFactor)
, mExportFonts(exportFonts)
{ {
if (encoding == ToUTF8::WINDOWS_1252) if (encoding == ToUTF8::WINDOWS_1252)
mEncoding = ToUTF8::CP437; mEncoding = ToUTF8::CP437;
@ -364,8 +363,8 @@ namespace Gui
Point bottom_right; Point bottom_right;
float width; float width;
float height; float height;
float kerningLeft; float u2; // appears unused, always 0
float kerningRight; float kerning;
float ascent; float ascent;
} GlyphInfo; } GlyphInfo;
@ -408,8 +407,7 @@ namespace Gui
file.reset(); file.reset();
// Create the font texture // Create the font texture
const std::string name(name_); std::string bitmapFilename = "fonts/" + std::string(name_) + ".tex";
const std::string bitmapFilename = "fonts/" + name + ".tex";
Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename); Files::IStreamPtr bitmapFile = mVFS->get(bitmapFilename);
@ -427,23 +425,9 @@ namespace Gui
textureData.resize(width * height * 4); textureData.resize(width * height * 4);
bitmapFile->read(textureData.data(), width * height * 4); bitmapFile->read(textureData.data(), width * height * 4);
if (!bitmapFile->good()) if (!bitmapFile->good())
Log(Debug::Warning) << "Font bitmap " << bitmapFilename << " ended prematurely, using partial data (" fail(*bitmapFile, bitmapFilename, "File too small to be a valid bitmap");
<< bitmapFile->gcount() << "/" << (width * height * 4) << " bytes)";
bitmapFile.reset(); bitmapFile.reset();
if (mExportFonts)
{
osg::ref_ptr<osg::Image> image = new osg::Image;
image->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
assert(image->isDataContiguous());
memcpy(image->data(), textureData.data(), textureData.size());
// Convert to OpenGL origin for sensible output
image->flipVertical();
Log(Debug::Info) << "Writing " << name + ".png";
osgDB::writeImageFile(*image, name + ".png");
}
MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(bitmapFilename); MyGUI::ITexture* tex = MyGUI::RenderManager::getInstance().createTexture(bitmapFilename);
tex->createManual(width, height, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8); tex->createManual(width, height, MyGUI::TextureUsage::Write, MyGUI::PixelFormat::R8G8B8A8);
unsigned char* texData = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write)); unsigned char* texData = reinterpret_cast<unsigned char*>(tex->lock(MyGUI::TextureUsage::Write));
@ -481,7 +465,7 @@ namespace Gui
// € (Euro Sign, 0x80/U+20AC) is replaced with underscore // € (Euro Sign, 0x80/U+20AC) is replaced with underscore
// 0x81 (unused) is replaced with underscore // 0x81 (unused) is replaced with underscore
additional.emplace(44, 0x201A); // (Single Low-9 Quotation Mark, 0x82) => , (comma) additional.emplace(44, 0x201A); // (Single Low-9 Quotation Mark, 0x82) => , (comma)
// ƒ (Latin Small Letter F with Hook, 0x83) is unavailable, not replaced additional.emplace(102, 0x0192); // ƒ (Latin Small Letter F with Hook, 0x83) => f (latin small F) (custom)
additional.emplace(44, 0x201E); // „ (Double Low-9 Quotation Mark, 0x84) => , (comma) additional.emplace(44, 0x201E); // „ (Double Low-9 Quotation Mark, 0x84) => , (comma)
additional.emplace(46, 0x2026); // … (Horizontal Ellipsis, 0x85) => . (period) additional.emplace(46, 0x2026); // … (Horizontal Ellipsis, 0x85) => . (period)
additional.emplace(43, 0x2020); // † (Dagger, 0x86) => + (plus sign) additional.emplace(43, 0x2020); // † (Dagger, 0x86) => + (plus sign)
@ -516,7 +500,7 @@ namespace Gui
// £ (Pound Sign, 0xA3) is available but its glyph looks like œ (small oe ligature) // £ (Pound Sign, 0xA3) is available but its glyph looks like œ (small oe ligature)
omitted.push_back(0x00A4); // ¤ (Currency Sign) omitted.push_back(0x00A4); // ¤ (Currency Sign)
// ¥ (Yen Sign, 0xA5) is unavailable, not replaced // ¥ (Yen Sign, 0xA5) is unavailable, not replaced
additional.emplace(221, 0x00A6); // ¦ (Broken Bar, 0xA6) => ▌ // ¦ (Broken Bar, 0xA6) is unavailable, not replaced
omitted.push_back(0x00A7); // § (Section Sign) omitted.push_back(0x00A7); // § (Section Sign)
additional.emplace(34, 0x00A8); // ¨ (Diaeresis) => " (double quote mark) additional.emplace(34, 0x00A8); // ¨ (Diaeresis) => " (double quote mark)
additional.emplace(99, 0x00A9); // © (Copyright Sign) => c (latin small C) additional.emplace(99, 0x00A9); // © (Copyright Sign) => c (latin small C)
@ -528,7 +512,7 @@ namespace Gui
additional.emplace(95, 0x00AF); // ¯ (Macron) => _ (underscore) additional.emplace(95, 0x00AF); // ¯ (Macron) => _ (underscore)
// ° (Degree Sign, 0xB0) is unavailable, not replaced // ° (Degree Sign, 0xB0) is unavailable, not replaced
// ± (Plus-Minus Sign, 0xB1) is unavailable, not replaced // ± (Plus-Minus Sign, 0xB1) is unavailable, not replaced
// ² (Superscript Two, 0xB2) is unavailable, not replaced additional.emplace(50, 0x00B2); // ² (Superscript Two) => 2 (two digit) (custom)
additional.emplace(51, 0x00B3); // ³ (Superscript Three) => 3 (three digit) additional.emplace(51, 0x00B3); // ³ (Superscript Three) => 3 (three digit)
additional.emplace(39, 0x00B4); // ´ (Acute Accent) => ' (apostrophe) additional.emplace(39, 0x00B4); // ´ (Acute Accent) => ' (apostrophe)
// µ (Micro Sign, 0xB5) is unavailable, not replaced // µ (Micro Sign, 0xB5) is unavailable, not replaced
@ -548,7 +532,7 @@ namespace Gui
additional.emplace(65, 0x00C3); // Ã (Latin Capital Letter A with Tilde) => A (latin capital A) additional.emplace(65, 0x00C3); // Ã (Latin Capital Letter A with Tilde) => A (latin capital A)
// Ä (Latin Capital Letter A with Diaeresis, 0xC4) is available // Ä (Latin Capital Letter A with Diaeresis, 0xC4) is available
// Å (Latin Capital Letter A with Ring Above, 0xC5) is available // Å (Latin Capital Letter A with Ring Above, 0xC5) is available
// Æ (Latin Capital Letter Ae, 0xC6) is unavailable, not replaced additional.emplace(65, 0x00C6); // Æ (Latin Capital Letter Ae) => A (latin capital A) (custom)
// Ç (Latin Capital Letter C with Cedilla, 0xC7) is available // Ç (Latin Capital Letter C with Cedilla, 0xC7) is available
additional.emplace(69, 0x00C8); // È (Latin Capital Letter E with Grave) => E (latin capital E) additional.emplace(69, 0x00C8); // È (Latin Capital Letter E with Grave) => E (latin capital E)
// É (Latin Capital Letter E with Acute, 0xC9) is available // É (Latin Capital Letter E with Acute, 0xC9) is available
@ -559,7 +543,7 @@ namespace Gui
additional.emplace(73, 0x00CE); // Î (Latin Capital Letter I with Circumflex) => I (latin capital I) additional.emplace(73, 0x00CE); // Î (Latin Capital Letter I with Circumflex) => I (latin capital I)
additional.emplace(73, 0x00CF); // Ï (Latin Capital Letter I with Diaeresis) => I (latin capital I) additional.emplace(73, 0x00CF); // Ï (Latin Capital Letter I with Diaeresis) => I (latin capital I)
additional.emplace(68, 0x00D0); // Ð (Latin Capital Letter Eth) => D (latin capital D) additional.emplace(68, 0x00D0); // Ð (Latin Capital Letter Eth) => D (latin capital D)
// Ñ (Latin Capital Letter N with Tilde, 0xD1) is unavailable, not replaced additional.emplace(78, 0x00D1); // Ñ (Latin Capital Letter N with Tilde) => N (latin capital N) (custom)
additional.emplace(79, 0x00D2); // Ò (Latin Capital Letter O with Grave) => O (latin capital O) additional.emplace(79, 0x00D2); // Ò (Latin Capital Letter O with Grave) => O (latin capital O)
additional.emplace(79, 0x00D3); // Ó (Latin Capital Letter O with Acute) => O (latin capital O) additional.emplace(79, 0x00D3); // Ó (Latin Capital Letter O with Acute) => O (latin capital O)
additional.emplace(79, 0x00D4); // Ô (Latin Capital Letter O with Circumflex) => O (latin capital O) additional.emplace(79, 0x00D4); // Ô (Latin Capital Letter O with Circumflex) => O (latin capital O)
@ -572,12 +556,7 @@ namespace Gui
additional.emplace(85, 0x00DB); // Û (Latin Capital Letter U with Circumflex) => U (latin capital U) additional.emplace(85, 0x00DB); // Û (Latin Capital Letter U with Circumflex) => U (latin capital U)
// Ü (Latin Capital Letter U with Diaeresis, 0xDC) is available // Ü (Latin Capital Letter U with Diaeresis, 0xDC) is available
additional.emplace(89, 0x00DD); // Ý (Latin Capital Letter Y with Acute) => Y (latin capital Y) additional.emplace(89, 0x00DD); // Ý (Latin Capital Letter Y with Acute) => Y (latin capital Y)
// 0xDE to 0xFF are generally not replaced with certain exceptions // 0xDE to 0xFF are not replaced
additional.emplace(97, 0x00E3); // ã (Latin Small Letter A with Tilde) => a (latin small A)
additional.emplace(100, 0x00F0); // ð (Latin Small Letter Eth) => d (latin small D)
additional.emplace(111, 0x00F5); // õ (Latin Small Letter O with Tilde) => o (latin small O)
additional.emplace(111, 0x00F8); // ø (Latin Small Letter O with Stroke) => o (latin small O)
additional.emplace(121, 0x00FD); // ý (Latin Small Letter Y with Acute) => y (latin small Y)
// Russian Morrowind which uses Win-1251 encoding only does equivalent (often garbage) Win-1252 replacements // Russian Morrowind which uses Win-1251 encoding only does equivalent (often garbage) Win-1252 replacements
// However, we'll provide custom replacements for Cyrillic io letters // However, we'll provide custom replacements for Cyrillic io letters
@ -585,12 +564,6 @@ namespace Gui
additional.emplace(69, 0x0401); // Ё (Cyrillic Capital Letter Io) => E (latin capital E) additional.emplace(69, 0x0401); // Ё (Cyrillic Capital Letter Io) => E (latin capital E)
additional.emplace(137, 0x0451); // ё (Cyrillic Small Letter Io) => ë (latin small E-diaeresis) additional.emplace(137, 0x0451); // ё (Cyrillic Small Letter Io) => ë (latin small E-diaeresis)
// ASCII vertical bar, use this as text input cursor
additional.emplace(124, MyGUI::FontCodeType::Cursor);
// Underscore, use for NotDefined marker (used for glyphs not existing in the font)
additional.emplace(95, MyGUI::FontCodeType::NotDefined);
for (int i = 0; i < 256; i++) for (int i = 0; i < 256; i++)
{ {
float x1 = data[i].top_left.x * width; float x1 = data[i].top_left.x * width;
@ -600,31 +573,64 @@ namespace Gui
ToUTF8::Utf8Encoder encoder(mEncoding); ToUTF8::Utf8Encoder encoder(mEncoding);
unsigned long unicodeVal = getUnicode(i, encoder, mEncoding); unsigned long unicodeVal = getUnicode(i, encoder, mEncoding);
const std::string coord = MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h);
float advance = data[i].width + data[i].kerningRight;
// Yes MyGUI, we really do want an advance of 0 sometimes, thank you.
if (advance == 0.f && data[i].width != 0.f)
advance = std::numeric_limits<float>::min();
const std::string bearing = MyGUI::utility::toString(data[i].kerningLeft) + ' '
+ MyGUI::utility::toString((fontSize - data[i].ascent));
const MyGUI::IntSize size(static_cast<int>(data[i].width), static_cast<int>(data[i].height));
MyGUI::xml::ElementPtr code = codes->createChild("Code"); MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", unicodeVal); code->addAttribute("index", unicodeVal);
code->addAttribute("coord", coord); code->addAttribute("coord",
code->addAttribute("advance", advance); MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(w)
code->addAttribute("bearing", bearing); + " " + MyGUI::utility::toString(h));
code->addAttribute("size", size); code->addAttribute("advance", data[i].width);
code->addAttribute("bearing",
MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize - data[i].ascent)));
code->addAttribute(
"size", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));
for (auto [it, end] = additional.equal_range(i); it != end; ++it) for (auto [it, end] = additional.equal_range(i); it != end; ++it)
{ {
code = codes->createChild("Code"); code = codes->createChild("Code");
code->addAttribute("index", it->second); code->addAttribute("index", it->second);
code->addAttribute("coord", coord); code->addAttribute("coord",
code->addAttribute("advance", advance); MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " "
code->addAttribute("bearing", bearing); + MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h));
code->addAttribute("size", size); code->addAttribute("advance", data[i].width);
code->addAttribute("bearing",
MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize - data[i].ascent)));
code->addAttribute(
"size", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));
}
// ASCII vertical bar, use this as text input cursor
if (i == 124)
{
MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code");
cursorCode->addAttribute("index", MyGUI::FontCodeType::Cursor);
cursorCode->addAttribute("coord",
MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h));
cursorCode->addAttribute("advance", data[i].width);
cursorCode->addAttribute("bearing",
MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize - data[i].ascent)));
cursorCode->addAttribute(
"size", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));
}
// Underscore, use for NotDefined marker (used for glyphs not existing in the font)
if (i == 95)
{
MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code");
cursorCode->addAttribute("index", MyGUI::FontCodeType::NotDefined);
cursorCode->addAttribute("coord",
MyGUI::utility::toString(x1) + " " + MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(h));
cursorCode->addAttribute("advance", data[i].width);
cursorCode->addAttribute("bearing",
MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize - data[i].ascent)));
cursorCode->addAttribute(
"size", MyGUI::IntSize(static_cast<int>(data[i].width), static_cast<int>(data[i].height)));
} }
} }
@ -633,19 +639,12 @@ namespace Gui
omitted.push_back(MyGUI::FontCodeType::SelectedBack); omitted.push_back(MyGUI::FontCodeType::SelectedBack);
for (const UnicodeIndex index : omitted) for (const UnicodeIndex index : omitted)
{ {
MyGUI::xml::ElementPtr code = codes->createChild("Code"); MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code");
code->addAttribute("index", index); cursorCode->addAttribute("index", index);
code->addAttribute("coord", "0 0 0 0"); cursorCode->addAttribute("coord", "0 0 0 0");
code->addAttribute("advance", "0"); cursorCode->addAttribute("advance", "0");
code->addAttribute("bearing", "0 0"); cursorCode->addAttribute("bearing", "0 0");
code->addAttribute("size", "0 0"); cursorCode->addAttribute("size", "0 0");
}
if (mExportFonts)
{
Log(Debug::Info) << "Writing " << name + ".xml";
xmlDocument.createDeclaration();
xmlDocument.save(name + ".xml");
} }
// Register the font with MyGUI // Register the font with MyGUI

View file

@ -25,8 +25,7 @@ namespace Gui
class FontLoader class FontLoader
{ {
public: public:
/// @param exportFonts export the converted fonts (Images and XML with glyph metrics) to files? FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor);
FontLoader(ToUTF8::FromType encoding, const VFS::Manager* vfs, float scalingFactor, bool exportFonts);
void overrideLineHeight(MyGUI::xml::ElementPtr _node, std::string_view _file, MyGUI::Version _version); void overrideLineHeight(MyGUI::xml::ElementPtr _node, std::string_view _file, MyGUI::Version _version);
@ -36,7 +35,6 @@ namespace Gui
ToUTF8::FromType mEncoding; ToUTF8::FromType mEncoding;
const VFS::Manager* mVFS; const VFS::Manager* mVFS;
float mScalingFactor; float mScalingFactor;
bool mExportFonts;
void loadFonts(); void loadFonts();
void loadFont(const std::string& fontName, const std::string& fontId); void loadFont(const std::string& fontName, const std::string& fontId);

View file

@ -239,29 +239,6 @@ namespace LuaUtil
} }
}); });
} }
void Registry::clear(bool force)
{
std::vector<Info> infoToKeep;
if (!force)
{
for (const Info& info : mInfo)
if (info.mPersistent)
infoToKeep.push_back(info);
}
mKeys.clear();
mIds.clear();
mInfo.clear();
mHandlers.clear();
mBindings.clear();
mValues.clear();
mBindingTree.clear();
if (!force)
{
for (const Info& i : infoToKeep)
insert(i);
}
}
} }
namespace InputTrigger namespace InputTrigger
@ -315,24 +292,5 @@ namespace LuaUtil
}), }),
handlers.end()); handlers.end());
} }
void Registry::clear(bool force)
{
std::vector<Info> infoToKeep;
if (!force)
{
for (const Info& info : mInfo)
if (info.mPersistent)
infoToKeep.push_back(info);
}
mInfo.clear();
mHandlers.clear();
mIds.clear();
if (!force)
{
for (const Info& i : infoToKeep)
insert(i);
}
}
} }
} }

View file

@ -29,7 +29,6 @@ namespace LuaUtil::InputAction
std::string mName; std::string mName;
std::string mDescription; std::string mDescription;
sol::main_object mDefaultValue; sol::main_object mDefaultValue;
bool mPersistent;
}; };
class MultiTree class MultiTree
@ -74,7 +73,16 @@ namespace LuaUtil::InputAction
{ {
mHandlers[safeIdByKey(key)].push_back(handler); mHandlers[safeIdByKey(key)].push_back(handler);
} }
void clear(bool force = false); void clear()
{
mKeys.clear();
mIds.clear();
mInfo.clear();
mHandlers.clear();
mBindings.clear();
mValues.clear();
mBindingTree.clear();
}
private: private:
using Id = MultiTree::Node; using Id = MultiTree::Node;
@ -102,7 +110,6 @@ namespace LuaUtil::InputTrigger
std::string mL10n; std::string mL10n;
std::string mName; std::string mName;
std::string mDescription; std::string mDescription;
bool mPersistent;
}; };
class Registry class Registry
@ -123,7 +130,12 @@ namespace LuaUtil::InputTrigger
void insert(const Info& info); void insert(const Info& info);
void registerHandler(std::string_view key, const LuaUtil::Callback& callback); void registerHandler(std::string_view key, const LuaUtil::Callback& callback);
void activate(std::string_view key); void activate(std::string_view key);
void clear(bool force = false); void clear()
{
mInfo.clear();
mHandlers.clear();
mIds.clear();
}
private: private:
using Id = size_t; using Id = size_t;

View file

@ -32,8 +32,7 @@ namespace
// Argument names // Argument names
const auto str = LuaUtil::cast<std::string>(key); const auto str = LuaUtil::cast<std::string>(key);
argNames.push_back( argNames.push_back(icu::UnicodeString::fromUTF8(icu::StringPiece(str.data(), str.size())));
icu::UnicodeString::fromUTF8(icu::StringPiece(str.data(), static_cast<int32_t>(str.size()))));
} }
} }
} }

View file

@ -11,7 +11,6 @@
#include <components/files/conversion.hpp> #include <components/files/conversion.hpp>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
#include "luastateptr.hpp"
#include "scriptscontainer.hpp" #include "scriptscontainer.hpp"
#include "utf8.hpp" #include "utf8.hpp"
@ -152,37 +151,37 @@ namespace LuaUtil
return newPtr; return newPtr;
} }
LuaStatePtr LuaState::createLuaRuntime(LuaState* luaState) lua_State* LuaState::createLuaRuntime(LuaState* luaState)
{ {
if (sProfilerEnabled) if (sProfilerEnabled)
{ {
Log(Debug::Info) << "Initializing LuaUtil::LuaState with profiler"; Log(Debug::Info) << "Initializing LuaUtil::LuaState with profiler";
LuaStatePtr state(lua_newstate(&trackingAllocator, luaState)); lua_State* L = lua_newstate(&trackingAllocator, luaState);
if (state != nullptr) if (L)
return state; return L;
sProfilerEnabled = false; else
Log(Debug::Error) << "Failed to initialize LuaUtil::LuaState with custom allocator; disabling Lua profiler"; {
sProfilerEnabled = false;
Log(Debug::Error)
<< "Failed to initialize LuaUtil::LuaState with custom allocator; disabling Lua profiler";
}
} }
Log(Debug::Info) << "Initializing LuaUtil::LuaState without profiler"; Log(Debug::Info) << "Initializing LuaUtil::LuaState without profiler";
LuaStatePtr state(luaL_newstate()); lua_State* L = luaL_newstate();
if (state == nullptr) if (!L)
throw std::runtime_error("Failed to create Lua runtime"); throw std::runtime_error("Can't create Lua runtime");
return state; return L;
} }
LuaState::LuaState(const VFS::Manager* vfs, const ScriptsConfiguration* conf, const LuaStateSettings& settings) LuaState::LuaState(const VFS::Manager* vfs, const ScriptsConfiguration* conf, const LuaStateSettings& settings)
: mSettings(settings) : mSettings(settings)
, mLuaState([&] { , mLuaHolder(createLuaRuntime(this))
LuaStatePtr state = createLuaRuntime(this); , mSol(mLuaHolder.get())
sol::set_default_state(state.get());
return state;
}())
, mSol(mLuaState.get())
, mConf(conf) , mConf(conf)
, mVFS(vfs) , mVFS(vfs)
{ {
if (sProfilerEnabled) if (sProfilerEnabled)
lua_sethook(mLuaState.get(), &countHook, LUA_MASKCOUNT, countHookStep); lua_sethook(mLuaHolder.get(), &countHook, LUA_MASKCOUNT, countHookStep);
protectedCall([&](LuaView& view) { protectedCall([&](LuaView& view) {
auto& sol = view.sol(); auto& sol = view.sol();
@ -344,8 +343,7 @@ namespace LuaUtil
} }
sol::protected_function_result LuaState::runInNewSandbox(const VFS::Path::Normalized& path, sol::protected_function_result LuaState::runInNewSandbox(const VFS::Path::Normalized& path,
const std::string& envName, const std::map<std::string, sol::main_object>& packages, const std::string& envName, const std::map<std::string, sol::object>& packages, const sol::object& hiddenData)
const sol::main_object& hiddenData)
{ {
// TODO // TODO
sol::protected_function script = loadScriptAndCache(path); sol::protected_function script = loadScriptAndCache(path);

Some files were not shown because too many files have changed in this diff Show more