Skip to content

Commit 07f13bc

Browse files
committed
upd limit, return anim patch
1 parent e2ea37d commit 07f13bc

7 files changed

Lines changed: 85 additions & 47 deletions

File tree

.Build/F4SE/Plugins/Addictol.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ bUtilityShader = true
150150
# Automatically sets PipBoy Cursor Constraints based on PipBoy resolution for controllers.
151151
bPipBoyCursorConstraints = true
152152

153+
# Fixes a CTD when loading animations with high-bit-set 16-bit event IDs.
154+
bAnimSignedCrash = true
155+
153156

154157
[Warnings]
155158

Addictol/Source/AdRegisterModules.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
#include <Modules/AdModuleActorCauseSaveBloat.h>
5353
#include <Modules/AdModuleUtilityShader.h>
5454
#include <Modules/AdModulePipBoyCursorConstraints.h>
55-
//#include <Modules/AdModuleAnimSignedCrash.h>
55+
#include <Modules/AdModuleAnimSignedCrash.h>
5656
//#include <Modules/AdModuleBethesdaNetCrash.h>
5757
#include <Modules/AdModuleReferenceHandleLimitWarning.h>
5858

@@ -109,7 +109,7 @@ static auto sModuleWorkbenchSound = std::make_shared<Addictol::ModuleWorkben
109109
static auto sModuleActorCauseSaveBloat = std::make_shared<Addictol::ModuleActorCauseSaveBloat>();
110110
static auto sModuleUtilityShader = std::make_shared<Addictol::ModuleUtilityShader>();
111111
static auto sModulePipBoyCursorConstraints = std::make_shared<Addictol::ModulePipBoyCursorConstraints>();
112-
//static auto sModuleAnimSignedCrash = std::make_shared<Addictol::ModuleAnimSignedCrash>();
112+
static auto sModuleAnimSignedCrash = std::make_shared<Addictol::ModuleAnimSignedCrash>();
113113
//static auto sModuleBethesdaNetCrash = std::make_shared<Addictol::ModuleBethesdaNetCrash>();
114114
static auto sModuleReferenceHandleLimitWarning = std::make_shared<Addictol::ModuleReferenceHandleLimitWarning>();
115115

@@ -178,7 +178,8 @@ void AdRegisterModules()
178178
modules.Register(sModuleCombatMusic);
179179
modules.Register(sModuleWorkbenchSound);
180180
modules.Register(sModuleActorCauseSaveBloat);
181-
//modules.Register(sModuleAnimSignedCrash);
181+
modules.Register(sModuleReferenceHandleLimitWarning);
182+
modules.Register(sModuleAnimSignedCrash);
182183
//modules.Register(sModuleBethesdaNetCrash);
183184

184185
// Registers other patches
@@ -191,7 +192,6 @@ void AdRegisterModules()
191192
modules.Register(sModuleLeveledListCrash, kGameDataReady);
192193
modules.Register(sModuleCombatMusic, kGameDataReady);
193194
modules.Register(sModulePipBoyCursorConstraints, kGameDataReady);
194-
modules.Register(sModuleReferenceHandleLimitWarning, kGameDataReady);
195195
modules.Register(sModuleEncounterZoneReset, kGameLoaded);
196196
modules.Register(sModuleInputSwitch, kGameLoaded);
197197
modules.Register(sModuleLoadScreen, kGameLoaded);

Addictol/Source/Modules/AdModuleAnimSignedCrash.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ namespace Addictol
1818

1919
bool ModuleAnimSignedCrash::DoInstall([[maybe_unused]] F4SE::MessagingInterface::Message* a_msg) noexcept
2020
{
21+
// hkbBehaviorGraph::processEventlessGlobalTransitions, movsx->movzx on the 16 - bit event - id read.
22+
2123
if (!RELEX::IsRuntimeOG())
22-
{
23-
// NG/AE: hkbBehaviorGraph::processEventlessGlobalTransitions, movsx -> movzx on the 16-bit event-id read.
24+
// NG/AE
2425
RELEX::WriteSafe(REL::ID(2260478).address() + 0x8E, { 0xB7 });
25-
}
2626
else
27-
{
2827
// OG
2928
RELEX::WriteSafe(REL::ID(919820).address() + 0x8B, { 0xB7 });
30-
}
3129

3230
return true;
3331
}

Addictol/Source/Modules/AdModuleReferenceHandleLimitWarning.cpp

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,9 @@ namespace Addictol
1212
// this can be set to anything, i just set it to 90% for right now
1313
constexpr auto WARNING_MIN_RATIO = 0.90f;
1414

15-
// 21 bits for handle, 5 for age, 1 for active = 26 bit test
16-
static constexpr uint32_t MAX_HANDLE_LIMIT = 1 << 21;
17-
// cached values
18-
static uint32_t lastCount = 0;
19-
static double lastRatio = 0.0f;
20-
2115
static REX::TOML::Bool<> bWarningsReferenceHandleLimit{ "Warnings"sv, "bReferenceHandleLimit"sv, true };
2216

23-
struct HandleManager :
17+
class HandleManager :
2418
public RE::BSPointerHandleManagerInterface<RE::TESObjectREFR>
2519
{
2620
struct Entries
@@ -34,52 +28,79 @@ namespace Addictol
3428
RE::BSReadWriteLock handleManagerLock;
3529
Entries* handleEntries;
3630
const RE::BSPointerHandle<RE::TESObjectREFR> nullHandle{};
37-
};
31+
public:
32+
// 21 bits for handle, 5 for age, 1 for active = 26 bit test
33+
inline static constexpr uint32_t MAX_HANDLE_LIMIT = 1 << 21;
3834

39-
ModuleReferenceHandleLimitWarning::ModuleReferenceHandleLimitWarning() :
40-
Module("Reference Handle Limit Warning", &bWarningsReferenceHandleLimit)
41-
{}
35+
static uint32_t GetCount() noexcept;
36+
static RE::BSPointerHandle<RE::TESObjectREFR> HkCreateHandle(RE::TESObjectREFR* a_ptr) noexcept;
37+
static void DebugInfo(const std::string_view& a_eventName) noexcept;
38+
39+
inline static std::atomic_bool exceededShow{ false };
40+
inline static HandleManager* singleton{ nullptr };
41+
inline static decltype(HkCreateHandle)* CreateHandle_orig{ nullptr };
42+
};
4243

43-
static uint32_t GetReferenceHandleCount() noexcept
44+
uint32_t HandleManager::GetCount() noexcept
4445
{
45-
auto manager = reinterpret_cast<HandleManager*>(REL::VariantID(665313, 2688741, 4796005).address());
46-
if (!manager || (manager->freeListHead == manager->freeListTail == MAX_HANDLE_LIMIT - 1)) return 0;
46+
auto manager = HandleManager::singleton;
47+
if (!manager || (manager->freeListHead == manager->freeListTail == (MAX_HANDLE_LIMIT - 1))) return 0;
4748
return manager ? manager->freeListHead : 0;
4849
}
4950

50-
static void CheckReferenceHandleLimit(std::string_view eventName, bool cacheResults = true) noexcept
51+
RE::BSPointerHandle<RE::TESObjectREFR> HandleManager::HkCreateHandle(RE::TESObjectREFR* a_ptr) noexcept
5152
{
52-
uint32_t count = GetReferenceHandleCount();
53+
uint32_t count = HandleManager::GetCount();
5354
double ratio = static_cast<double>(count) / static_cast<float>(MAX_HANDLE_LIMIT);
54-
55-
if (cacheResults)
56-
{
57-
// we can cache it so we dont have to iterate again unless we need to measure it again
58-
lastCount = count;
59-
lastRatio = ratio;
60-
}
61-
62-
REX::INFO("Reference Handle Count ({}): {}. Ratio: {:.1f}%"sv, eventName, count, ratio * 100.f);
63-
64-
// warn if needed
6555
if (ratio >= WARNING_MIN_RATIO)
6656
{
6757
if (ratio >= 0.999f)
6858
{
69-
REX::CRITICAL("OUT OF HANDLE ARRAY ENTRIES!TERMINATE GAME FORCIBLY!"sv);
59+
REX::CRITICAL("OUT OF HANDLE ARRAY ENTRIES! TERMINATE GAME FORCIBLY!"sv);
7060
REX::W32::TerminateProcess(REX::W32::GetCurrentProcess(), ERANGE);
7161
}
72-
else
62+
else if (!HandleManager::exceededShow)
7363
{
74-
REX::WARN("HANDLE ARRAY ENTRIES ALMOST EXCEEDED"sv);
64+
HandleManager::exceededShow = true;
65+
ratio *= 100.f;
66+
67+
REX::WARN("HANDLE ARRAY ENTRIES ALMOST EXCEEDED {:.1f}%"sv, ratio);
68+
69+
char szBuf[REX::W32::MAX_PATH]{};
70+
sprintf_s(szBuf, "Addictol::ReferenceHandleLimitWarning: HANDLE ARRAY ENTRIES ALMOST EXCEEDED (%.1f%%)", ratio);
7571

7672
auto* consoleLog = RE::ConsoleLog::GetSingleton();
7773
if (consoleLog)
78-
consoleLog->AddString("Addictol::ReferenceHandleLimitWarning: HANDLE ARRAY ENTRIES ALMOST EXCEEDED");
74+
consoleLog->AddString(szBuf);
75+
76+
std::thread th([]{
77+
// 5 minutes in milliseconds
78+
std::chrono::milliseconds delay(5 * 60 * 1000);
79+
std::this_thread::sleep_for(delay);
80+
// reset
81+
HandleManager::exceededShow = false;
82+
});
83+
th.detach();
7984
}
8085
}
86+
87+
assert(CreateHandle_orig);
88+
return CreateHandle_orig(a_ptr);
89+
}
90+
91+
void HandleManager::DebugInfo(const std::string_view& a_eventName) noexcept
92+
{
93+
uint32_t count = HandleManager::GetCount();
94+
double ratio = static_cast<double>(count) / static_cast<float>(MAX_HANDLE_LIMIT);
95+
96+
REX::INFO("Reference Handle Count ({}): {}. Ratio: {:.1f}%"sv, a_eventName, count, ratio * 100.f);
8197
}
8298

99+
ModuleReferenceHandleLimitWarning::ModuleReferenceHandleLimitWarning() :
100+
Module("Reference Handle Limit Warning", &bWarningsReferenceHandleLimit,
101+
{ F4SE::MessagingInterface::kPostLoadGame })
102+
{}
103+
83104
bool ModuleReferenceHandleLimitWarning::DoQuery() const noexcept
84105
{
85106
return true;
@@ -88,19 +109,35 @@ namespace Addictol
88109
bool ModuleReferenceHandleLimitWarning::DoInstall([[maybe_unused]] F4SE::MessagingInterface::Message* a_msg) noexcept
89110
{
90111
if (!a_msg)
112+
{
113+
HandleManager::singleton = reinterpret_cast<HandleManager*>(REL::VariantID(665313, 2688741, 4796005).address());
114+
115+
*(uintptr_t*)&HandleManager::CreateHandle_orig = RELEX::DetourJump(
116+
RE::ID::BSPointerHandle::BSPointerHandleManagerInterface::CreateHandle.address(),
117+
(uintptr_t)&HandleManager::HkCreateHandle);
118+
91119
return true;
92-
93-
std::string eventName = GetMessagingInterfaceString(a_msg);
94-
CheckReferenceHandleLimit(eventName);
120+
}
121+
else if (a_msg->type == F4SE::MessagingInterface::kGameLoaded)
122+
{
123+
HandleManager::DebugInfo(GetMessagingInterfaceString(a_msg));
95124

96-
// FIXME: Add check to CreateHandle function
125+
return true;
126+
}
97127

98-
return true;
128+
return false;
99129
}
100130

101131
bool ModuleReferenceHandleLimitWarning::DoListener([[maybe_unused]] F4SE::MessagingInterface::Message* a_msg) noexcept
102132
{
103-
return true;
133+
if (a_msg && (a_msg->type == F4SE::MessagingInterface::kPostLoadGame))
134+
{
135+
HandleManager::DebugInfo(GetMessagingInterfaceString(a_msg));
136+
137+
return true;
138+
}
139+
140+
return false;
104141
}
105142

106143
bool ModuleReferenceHandleLimitWarning::DoPapyrusListener([[maybe_unused]] RE::BSScript::IVirtualMachine* a_vm) noexcept

VC/Addictol.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
213213
<AdditionalIncludeDirectories>$(SolutionDir)..\Depends\commonlibf4\include;$(SolutionDir)..\Depends;$(SolutionDir)..\Depends\detours;$(SolutionDir)..\Depends\commonlibf4\lib\commonlib-shared\include;$(SolutionDir)..\Version;$(SolutionDir)..\Addictol\Include;$(SolutionDir)..\Depends\vmm\include;$(SolutionDir)..\Depends\tbb\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
214214
<CallingConvention>FastCall</CallingConvention>
215-
<DisableSpecificWarnings>4200;4996;26444;26495</DisableSpecificWarnings>
215+
<DisableSpecificWarnings>4200;4996;26444;26495;4806</DisableSpecificWarnings>
216216
<ForcedIncludeFiles>
217217
</ForcedIncludeFiles>
218218
</ClCompile>

Version/build_version.txt

0 Bytes
Binary file not shown.

Version/resource_version2.h

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)