Skip to content

Commit e2ea37d

Browse files
committed
Rejection code mith077, rework patch, read data handle manager from game, terminate game if out of entries
1 parent 36d6597 commit e2ea37d

9 files changed

Lines changed: 72 additions & 78 deletions

File tree

.Build/F4SE/Plugins/Addictol.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ bImageSpaceAdapter = true
159159
# Warns if you have two AddonNode forms with the same index in your load order, which will cause errors with visual effects.
160160
bDuplicateAddonNodeIndex = true
161161

162-
# Warns if you are approaching the reference handle limoit or exceed the reference handle limit, which can cause CTDs.
162+
# Warns if you are approaching the reference handle limit or exceed the reference handle limit.
163+
# Terminate process if out of entries!
163164
bReferenceHandleLimit = true
164165

165166

Addictol/Include/AdGameUtils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
#include <string_view>
44
#include <string>
55

6+
#include <F4SE/F4SE.h>
7+
68
namespace RE
79
{
810
class TESForm;
911
class TESObjectREFR;
1012
}
1113

12-
1314
namespace Addictol
1415
{
1516
using namespace std::literals;
@@ -19,4 +20,6 @@ namespace Addictol
1920
// easy + formatted way to log form details like formid, editorid (if loaded), and plugin:
2021
// {FormID: 0x123456, EditorID: "ExampleForm", Plugin: "Example.esp"}
2122
std::string GetFormInfo(RE::TESForm* a_form);
23+
24+
std::string GetMessagingInterfaceString(F4SE::MessagingInterface::Message* a_msg) noexcept;
2225
}

Addictol/Source/AdGameUtils.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
// Causes crashes if put above RE/S/Script.h
1010
#include <RE/C/ConsoleLog.h>
1111

12-
1312
namespace Addictol
1413
{
1514
bool ExecuteCommand(std::string_view a_command, RE::TESObjectREFR *a_targetRef, bool a_silent)
@@ -59,4 +58,14 @@ namespace Addictol
5958
(editorID != ""sv) ? editorID : "EDITORID_NOT_LOADED"sv,
6059
file ? file->GetFilename() : "PLUGIN_NOT_FOUND"sv);
6160
}
61+
62+
extern std::array<std::string_view, 11> g_msgName;
63+
64+
std::string GetMessagingInterfaceString(F4SE::MessagingInterface::Message* a_msg) noexcept
65+
{
66+
if (!a_msg)
67+
return "ERROR_NULL_MESSAGE";
68+
69+
return g_msgName.size() > a_msg->type ? g_msgName[a_msg->type].data() : "ERROR_NO_TYPE";
70+
}
6271
}

Addictol/Source/AdRegisterModules.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ void AdRegisterModules()
180180
modules.Register(sModuleActorCauseSaveBloat);
181181
//modules.Register(sModuleAnimSignedCrash);
182182
//modules.Register(sModuleBethesdaNetCrash);
183-
//modules.Register(sModuleReferenceHandleLimitWarning);
184183

185184
// Registers other patches
186185
modules.Register(sModuleThreads, kGameDataReady);
@@ -204,5 +203,5 @@ void AdRegisterModules()
204203

205204
// Profiler - registered at load stage, listener at GameDataReady for report generation
206205
modules.Register(sModuleProfiler);
207-
modules.Register(sModuleProfiler, kGameDataReady);
206+
modules.Register(sModuleProfiler, kGameDataReady);
208207
}

Addictol/Source/Modules/AdModuleReferenceHandleLimitWarning.cpp

Lines changed: 48 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,105 +3,79 @@
33
#include <AdGameUtils.h>
44

55
#include <RE/C/ConsoleLog.h>
6-
7-
// the ratio at which we start warning in console log.
8-
// this can be set to anything, i just set it to 80% for right now
9-
#define WARNING_MIN_RATIO 0.80f
6+
#include <RE/B/BSSpinLock.h>
7+
#include <RE/B/BSPointerHandle.h>
108

119
namespace Addictol
1210
{
13-
// credit: Warnings::ReferenceHandleCap in Daytripper 4 by mith077 under MIT
14-
// https://www.nexusmods.com/fallout4/mods/91141?tab=files
15-
16-
// data
17-
static inline constexpr uint32_t MAX_HANDLE_LIMIT = 1 << 21; // 21 bits for handle, 5 for age, 1 for active = 26 bit test
18-
static const std::string WarningMessage_ReferenceHandleLimitExceeded = "Addictol::ReferenceHandleLimitWarning: "
19-
"Reference Handle Limit Exceeded! This can cause CTDs. Consider trimming your load order for your next save.";
20-
static const std::string WarningMessage_ReferenceHandleLimitAlmostExceeded = "Addictol::ReferenceHandleLimitWarning: "
21-
"TODO";
11+
// the ratio at which we start warning in console log.
12+
// this can be set to anything, i just set it to 90% for right now
13+
constexpr auto WARNING_MIN_RATIO = 0.90f;
2214

15+
// 21 bits for handle, 5 for age, 1 for active = 26 bit test
16+
static constexpr uint32_t MAX_HANDLE_LIMIT = 1 << 21;
2317
// cached values
2418
static uint32_t lastCount = 0;
25-
static float lastRatio = 0.0f;
26-
27-
// tested on AE, but should be fine on OG/NG since daytripper's addresses work on OG/NG
28-
static const auto ReferenceHandleArray_addresses = REL::VariantID(1103816, 2688744).address();
29-
static const auto ReferenceHandleArray = reinterpret_cast<uint64_t*>(ReferenceHandleArray_addresses);
19+
static double lastRatio = 0.0f;
3020

3121
static REX::TOML::Bool<> bWarningsReferenceHandleLimit{ "Warnings"sv, "bReferenceHandleLimit"sv, true };
3222

23+
struct HandleManager :
24+
public RE::BSPointerHandleManagerInterface<RE::TESObjectREFR>
25+
{
26+
struct Entries
27+
{
28+
RE::BSPointerHandle<RE::TESObjectREFR> handle;
29+
void* addressPointerInREFR;
30+
};
31+
32+
uint32_t freeListHead;
33+
uint32_t freeListTail;
34+
RE::BSReadWriteLock handleManagerLock;
35+
Entries* handleEntries;
36+
const RE::BSPointerHandle<RE::TESObjectREFR> nullHandle{};
37+
};
38+
3339
ModuleReferenceHandleLimitWarning::ModuleReferenceHandleLimitWarning() :
3440
Module("Reference Handle Limit Warning", &bWarningsReferenceHandleLimit)
3541
{}
3642

37-
// note: could be useful to have in AdGameUtils but compiler was being annoying
38-
std::string GetMessagingInterfaceString(F4SE::MessagingInterface::Message* a_msg)
43+
static uint32_t GetReferenceHandleCount() noexcept
3944
{
40-
if (!a_msg)
41-
return "ERROR_NULL_MESSAGE";
42-
43-
switch (a_msg->type) {
44-
case F4SE::MessagingInterface::kPostLoad:
45-
return "kPostLoad";
46-
case F4SE::MessagingInterface::kPostPostLoad:
47-
return "kPostPostLoad";
48-
case F4SE::MessagingInterface::kPreLoadGame:
49-
return "kPreLoadGame";
50-
case F4SE::MessagingInterface::kPostLoadGame:
51-
return "kPostLoadGame";
52-
case F4SE::MessagingInterface::kPreSaveGame:
53-
return "kPreSaveGame";
54-
case F4SE::MessagingInterface::kPostSaveGame:
55-
return "kPostSaveGame";
56-
case F4SE::MessagingInterface::kDeleteGame:
57-
return "kDeleteGame";
58-
case F4SE::MessagingInterface::kInputLoaded:
59-
return "kInputLoaded";
60-
case F4SE::MessagingInterface::kNewGame:
61-
return "kNewGame";
62-
case F4SE::MessagingInterface::kGameLoaded:
63-
return "kGameLoaded";
64-
case F4SE::MessagingInterface::kGameDataReady:
65-
return "kGameDataReady";
66-
default:
67-
return "ERROR_NO_TYPE";
68-
}
69-
70-
return "ERROR_IDEK_HONESTLY";
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;
47+
return manager ? manager->freeListHead : 0;
7148
}
7249

73-
uint32_t GetReferenceHandleCount() noexcept
74-
{
75-
uint32_t count = 0;
76-
for (uint32_t i = 0; i < MAX_HANDLE_LIMIT; i++) {
77-
if ((ReferenceHandleArray[i] >> 26) & 1) {
78-
count++;
79-
}
80-
}
81-
82-
return count;
83-
}
84-
85-
void CheckReferenceHandleLimit(std::string_view eventName, bool cacheResults = true) noexcept
50+
static void CheckReferenceHandleLimit(std::string_view eventName, bool cacheResults = true) noexcept
8651
{
8752
uint32_t count = GetReferenceHandleCount();
88-
float ratio = static_cast<float>(count) / static_cast<float>(MAX_HANDLE_LIMIT);
53+
double ratio = static_cast<double>(count) / static_cast<float>(MAX_HANDLE_LIMIT);
8954

90-
if (cacheResults) {
55+
if (cacheResults)
56+
{
9157
// we can cache it so we dont have to iterate again unless we need to measure it again
9258
lastCount = count;
9359
lastRatio = ratio;
9460
}
9561

96-
REX::INFO("Reference Handle Count ({}): {}. Ratio: {:.4}%"sv, eventName, count, ratio);
62+
REX::INFO("Reference Handle Count ({}): {}. Ratio: {:.1f}%"sv, eventName, count, ratio * 100.f);
9763

9864
// warn if needed
99-
if (ratio >= WARNING_MIN_RATIO) {
100-
REX::WARN(std::string_view(WarningMessage_ReferenceHandleLimitExceeded));
65+
if (ratio >= WARNING_MIN_RATIO)
66+
{
67+
if (ratio >= 0.999f)
68+
{
69+
REX::CRITICAL("OUT OF HANDLE ARRAY ENTRIES!TERMINATE GAME FORCIBLY!"sv);
70+
REX::W32::TerminateProcess(REX::W32::GetCurrentProcess(), ERANGE);
71+
}
72+
else
73+
{
74+
REX::WARN("HANDLE ARRAY ENTRIES ALMOST EXCEEDED"sv);
10175

102-
auto* consoleLog = RE::ConsoleLog::GetSingleton();
103-
if (consoleLog) {
104-
consoleLog->AddString(WarningMessage_ReferenceHandleLimitExceeded.c_str());
76+
auto* consoleLog = RE::ConsoleLog::GetSingleton();
77+
if (consoleLog)
78+
consoleLog->AddString("Addictol::ReferenceHandleLimitWarning: HANDLE ARRAY ENTRIES ALMOST EXCEEDED");
10579
}
10680
}
10781
}
@@ -118,6 +92,8 @@ namespace Addictol
11892

11993
std::string eventName = GetMessagingInterfaceString(a_msg);
12094
CheckReferenceHandleLimit(eventName);
95+
96+
// FIXME: Add check to CreateHandle function
12197

12298
return true;
12399
}

VC/Addictol.vcxproj.filters

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@
217217
<ClCompile Include="..\Addictol\Source\Modules\AdModuleUtilityShader.cpp">
218218
<Filter>Source\Modules</Filter>
219219
</ClCompile>
220+
<ClCompile Include="..\Addictol\Source\Modules\AdModuleReferenceHandleLimitWarning.cpp">
221+
<Filter>Source\Modules</Filter>
222+
</ClCompile>
220223
</ItemGroup>
221224
<ItemGroup>
222225
<ClInclude Include="..\Version\resource_version2.h" />
@@ -427,6 +430,9 @@
427430
<ClInclude Include="..\Addictol\Include\Modules\AdModuleUtilityShader.h">
428431
<Filter>Include\Modules</Filter>
429432
</ClInclude>
433+
<ClInclude Include="..\Addictol\Include\Modules\AdModuleReferenceHandleLimitWarning.h">
434+
<Filter>Include\Modules</Filter>
435+
</ClInclude>
430436
</ItemGroup>
431437
<ItemGroup>
432438
<ResourceCompile Include="..\Version\resource_version.rc" />

Version/build_version.txt

0 Bytes
Binary file not shown.

Version/resource_version2.h

0 Bytes
Binary file not shown.

Version/resource_version2.tmp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#define STRINGIZE(s) STRINGIZE2(s)
1111

1212
#define VERSION_MAJOR 1
13-
#define VERSION_MINOR 3
13+
#define VERSION_MINOR 4
1414
#define VERSION_REVISION 0
1515
#define VERSION_BUILD <BUILD>
1616

0 commit comments

Comments
 (0)