perf: fix deferred divide precision, threadlocal TLS, MSVC target
Removed deferred divide from both ray-tri functions -- the det-scaled epsilon comparisons lose precision for near-parallel rays, potentially accepting triangles the original rejects. Downstream SetupBoxFrustum then writes to wrong globals near 0xCE6738 (SGroupPtr), causing ERROR #124 on exit. inflate_hook: replaced manual FS:0x24 thread ID pool with Zig native threadlocal. Fixes potential crash on Wine where FS segment layout may differ. Build: switched from GNU to MSVC ABI (4-5K smaller per DLL, no .eh_frame unwind tables). Added setjmp.h stub for libdeflate MSVC build. Added noperf build variant (zig build noperf).
This commit is contained in:
+30
-5
@@ -38,7 +38,21 @@ git push origin main
|
||||
|
||||
After that, the remote `main` only needs updating when `DLL_README.md` changes.
|
||||
|
||||
## 1. Build DLLs
|
||||
## 1. Bump module versions
|
||||
|
||||
Each module has a `version` field in `build.zig`'s `module_list`. This is what
|
||||
`GetWeirdUtilsVersion()` reports to Lua addons. Before building, bump the
|
||||
version for any module that has changed since the last release:
|
||||
|
||||
```zig
|
||||
// build.zig — module_list
|
||||
.{ .name = "minimapicons", .version = "1.1", ... },
|
||||
```
|
||||
|
||||
Only bump modules that actually changed. Use `git log --oneline -- src/<module>/`
|
||||
to check what changed since the last release tag.
|
||||
|
||||
## 2. Build DLLs
|
||||
|
||||
Decide which modules to include in this release. Check `build.zig` for the
|
||||
current list of module flags (`b.option(bool, ...)` declarations) and their
|
||||
@@ -71,6 +85,16 @@ zig build all-variants -Doptimize=ReleaseSmall
|
||||
|
||||
This builds all variants - you only attach the ones for this release.
|
||||
|
||||
### No-performance variant
|
||||
|
||||
```sh
|
||||
zig build noperf -Doptimize=ReleaseSmall
|
||||
```
|
||||
|
||||
Builds `weirdutils_noperf.dll` - the combined DLL with all default modules
|
||||
except `weirdperformance`. Provided as a release asset for users who experience
|
||||
issues with the performance module (e.g. Wine compatibility).
|
||||
|
||||
### Output locations
|
||||
|
||||
| Artifact | Path |
|
||||
@@ -84,7 +108,7 @@ Verify:
|
||||
ls -lh zig-out/bin/weirdutils.dll zig-out/variants/*.dll
|
||||
```
|
||||
|
||||
## 2. Update the remote README
|
||||
## 3. Update the remote README
|
||||
|
||||
The remote README should match the features in this release. Start from
|
||||
`DLL_README.md` and remove the sections for modules not being released -
|
||||
@@ -103,7 +127,7 @@ git commit -m "Update README for vX.Y.Z"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
## 3. Write the release notes
|
||||
## 4. Write the release notes
|
||||
|
||||
Use this template - fill in the sections that apply, delete the rest.
|
||||
Use `-` (not em dash) anywhere a dash would be used.
|
||||
@@ -132,7 +156,7 @@ of what they're getting:
|
||||
- `weirdutils.dll` includes all features; individual DLLs are also provided
|
||||
```
|
||||
|
||||
## 4. Create the release and upload DLLs
|
||||
## 5. Create the release and upload DLLs
|
||||
|
||||
Uses `tea` (Gitea/Forgejo CLI) which handles release creation, tagging, and
|
||||
asset upload in one command. The tag is created on the remote automatically.
|
||||
@@ -167,7 +191,7 @@ tea release assets delete --repo MarcelineVQ/WeirdUtils -y v0.4.0 minimapicons.d
|
||||
tea release assets create --repo MarcelineVQ/WeirdUtils v0.4.0 zig-out/variants/minimapicons.dll
|
||||
```
|
||||
|
||||
## 5. Hide source archives
|
||||
## 6. Hide source archives
|
||||
|
||||
Codeberg attaches empty source tar/zip by default. Hide them via API:
|
||||
|
||||
@@ -200,6 +224,7 @@ print(r[0]['id']) if r else print('not found')
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Module versions bumped in `build.zig` for changed modules
|
||||
- [ ] Check `RELEASE_NOTES.md` for unreleased changes — move into release notes
|
||||
- [ ] Built with `ReleaseSmall` (both default and `all-variants`)
|
||||
- [ ] Remote README updated — no unreleased module sections or names
|
||||
|
||||
@@ -16,38 +16,43 @@ const ModuleDesc = struct {
|
||||
/// Single source of truth for all modules. Adding a module here is enough
|
||||
/// to wire up the build option, build_options passthrough, and DLL variant.
|
||||
const module_list = [_]ModuleDesc{
|
||||
.{ .name = "pngscreenshots", .desc = "Enable screenshot module", .src_dir = "screenshot" },
|
||||
.{ .name = "interact", .desc = "Enable interact module", .addon_name = "Interact" },
|
||||
.{ .name = "outline", .desc = "Enable outline module", .default = false, .addon_name = "Outline" },
|
||||
.{ .name = "worldmarkers", .desc = "Enable world markers module", .addon_name = "WorldMarkers", .addon_hidden = true },
|
||||
.{ .name = "framecrash", .desc = "Enable framecrash fix", .default = false },
|
||||
.{ .name = "logsessions", .desc = "Enable log session rotation", .addon_name = "LogSessions" },
|
||||
.{ .name = "minimapicons", .desc = "Enable custom minimap icons", .addon_name = "MinimapIcons" },
|
||||
.{ .name = "transmogfix", .desc = "Enable transmog update coalescing" },
|
||||
.{ .name = "customassets", .desc = "Enable loose file loading & permissive patch glob" },
|
||||
.{ .name = "healtextfix", .desc = "Enable SuperWoW heal text fix" },
|
||||
.{ .name = "bigcursor", .desc = "Enable big cursor module" },
|
||||
.{ .name = "clickthrough", .desc = "Enable GO click-through (enlarge GO model bounds)" },
|
||||
.{ .name = "dpslog", .desc = "Enable structured combat log events for addons", .default = false },
|
||||
.{ .name = "transform44", .desc = "Enable transform44 profiling/A/B testing (dev only)", .default = false },
|
||||
.{ .name = "addonperf", .desc = "Enable addon memory/CPU profiling API", .default = false },
|
||||
.{ .name = "ssemaths", .desc = "Enable UnitXP x87 math polyfill replacements (SSE)", .default = false },
|
||||
.{ .name = "silicon", .desc = "Enable SSE2 math replacements (ported from libSiliconPatch)", .default = false },
|
||||
.{ .name = "weirdperformance", .desc = "Enable production performance optimizations (SSE, inflate, filecache, timer)", .default = true },
|
||||
.{ .name = "guidcache", .desc = "Enable GUID lookup cache (standalone test)", .default = false },
|
||||
.{ .name = "pngscreenshots", .version = "1.0", .desc = "Enable screenshot module", .src_dir = "screenshot" },
|
||||
.{ .name = "interact", .version = "1.0", .desc = "Enable interact module", .addon_name = "Interact" },
|
||||
.{ .name = "outline", .version = "1.0", .desc = "Enable outline module", .default = false, .addon_name = "Outline" },
|
||||
.{ .name = "worldmarkers", .version = "1.0", .desc = "Enable world markers module", .addon_name = "WorldMarkers", .addon_hidden = true },
|
||||
.{ .name = "framecrash", .version = "1.0", .desc = "Enable framecrash fix", .default = false },
|
||||
.{ .name = "logsessions", .version = "1.0", .desc = "Enable log session rotation", .addon_name = "LogSessions" },
|
||||
.{ .name = "minimapicons", .version = "1.0", .desc = "Enable custom minimap icons", .addon_name = "MinimapIcons" },
|
||||
.{ .name = "transmogfix", .version = "1.0", .desc = "Enable transmog update coalescing" },
|
||||
.{ .name = "customassets", .version = "1.0", .desc = "Enable loose file loading & permissive patch glob" },
|
||||
.{ .name = "healtextfix", .version = "1.0", .desc = "Enable SuperWoW heal text fix" },
|
||||
.{ .name = "bigcursor", .version = "1.0", .desc = "Enable big cursor module" },
|
||||
.{ .name = "clickthrough", .version = "1.0", .desc = "Enable GO click-through (enlarge GO model bounds)" },
|
||||
.{ .name = "dpslog", .version = "0.1", .desc = "Enable structured combat log events for addons", .default = false },
|
||||
.{ .name = "transform44", .version = "1.0", .desc = "Enable transform44 profiling/A/B testing (dev only)", .default = false },
|
||||
.{ .name = "addonperf", .version = "1.0", .desc = "Enable addon memory/CPU profiling API", .default = false },
|
||||
.{ .name = "ssemaths", .version = "1.0", .desc = "Enable UnitXP x87 math polyfill replacements (SSE)", .default = false },
|
||||
.{ .name = "silicon", .version = "1.0", .desc = "Enable SSE2 math replacements (ported from libSiliconPatch)", .default = false },
|
||||
.{ .name = "weirdperformance", .version = "1.1", .desc = "Enable production performance optimizations (SSE, inflate, filecache, timer)", .default = true },
|
||||
};
|
||||
|
||||
pub fn build(b: *std.Build) void {
|
||||
const target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .x86,
|
||||
.os_tag = .windows,
|
||||
.abi = .gnu,
|
||||
.abi = .msvc,
|
||||
.cpu_features_add = std.Target.x86.featureSet(&.{ .sse, .sse2 }),
|
||||
});
|
||||
const optimize = b.option(std.builtin.OptimizeMode, "optimize", "Optimization mode (default: ReleaseFast)") orelse .ReleaseFast;
|
||||
|
||||
// Resolve module enable flags once (b.option can only be called once per name)
|
||||
var module_enabled: [module_list.len]bool = undefined;
|
||||
inline for (module_list, 0..) |m, i| {
|
||||
module_enabled[i] = b.option(bool, m.name, m.desc) orelse m.default;
|
||||
}
|
||||
|
||||
const build_options = b.addOptions();
|
||||
addModuleOptions(b, build_options);
|
||||
addModuleOptionsFromArray(b, build_options, &module_enabled);
|
||||
const build_options_module = build_options.createModule();
|
||||
|
||||
const zhook_dep = b.dependency("zhook", .{
|
||||
@@ -85,7 +90,7 @@ pub fn build(b: *std.Build) void {
|
||||
const bone_sse_target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .x86,
|
||||
.os_tag = .windows,
|
||||
.abi = .gnu,
|
||||
.abi = .msvc,
|
||||
.cpu_features_add = std.Target.x86.featureSet(&.{ .sse, .sse2, .sse3, .sse4_1, .fma, .avx }),
|
||||
});
|
||||
const bone_sse_obj = b.addObject(.{
|
||||
@@ -102,7 +107,7 @@ pub fn build(b: *std.Build) void {
|
||||
const ref_target = b.resolveTargetQuery(.{
|
||||
.cpu_arch = .x86,
|
||||
.os_tag = .windows,
|
||||
.abi = .gnu,
|
||||
.abi = .msvc,
|
||||
.cpu_features_sub = std.Target.x86.featureSet(&.{ .sse, .sse2 }),
|
||||
});
|
||||
const bone_sse_ref_obj = b.addObject(.{
|
||||
@@ -181,6 +186,7 @@ pub fn build(b: *std.Build) void {
|
||||
},
|
||||
.flags = &.{"-DLIBDEFLATE_ASSEMBLER_DOES_NOT_SUPPORT_AVX512VNNI"},
|
||||
});
|
||||
libdeflate.root_module.addIncludePath(b.path("src/performance/libdeflate/stubs"));
|
||||
libdeflate.root_module.addIncludePath(b.path("src/performance/libdeflate"));
|
||||
libdeflate.root_module.addIncludePath(b.path("src/performance/libdeflate/lib"));
|
||||
|
||||
@@ -204,12 +210,9 @@ pub fn build(b: *std.Build) void {
|
||||
if (comptime std.mem.eql(u8, module_name, "weirdperformance")) {
|
||||
mod.addObject(self.clip_sse);
|
||||
mod.addObject(self.cull_sse);
|
||||
mod.addObject(self.entity_sse);
|
||||
mod.addObject(self.bone_sse);
|
||||
mod.addObject(self.bone_sse_ref);
|
||||
mod.addObject(self.silicon_sse);
|
||||
mod.addObject(self.particle_sse);
|
||||
mod.addObject(self.particle_ref);
|
||||
mod.addObjectFile(self.libdeflate.getEmittedBin());
|
||||
}
|
||||
if (comptime std.mem.eql(u8, module_name, "transform44")) {
|
||||
@@ -357,8 +360,40 @@ pub fn build(b: *std.Build) void {
|
||||
run_step.dependOn(&run_bench.step);
|
||||
}
|
||||
|
||||
// Build combined DLL without performance module
|
||||
const noperf_install = noperf_blk: {
|
||||
const noperf_step = b.step("noperf", "Build weirdutils_noperf.dll (no performance module)");
|
||||
const noperf_opts = b.addOptions();
|
||||
var noperf_enabled = module_enabled;
|
||||
// Force weirdperformance off
|
||||
inline for (module_list, 0..) |m, i| {
|
||||
if (comptime std.mem.eql(u8, m.name, "weirdperformance")) noperf_enabled[i] = false;
|
||||
}
|
||||
addModuleOptionsFromArray(b, noperf_opts, &noperf_enabled);
|
||||
|
||||
const noperf_lib = b.addLibrary(.{
|
||||
.name = "weirdutils_noperf",
|
||||
.linkage = .dynamic,
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/main.zig"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
.imports = &.{
|
||||
.{ .name = "zhook", .module = zhook_mod },
|
||||
.{ .name = "build_options", .module = noperf_opts.createModule() },
|
||||
},
|
||||
}),
|
||||
});
|
||||
const noperf_install = b.addInstallArtifact(noperf_lib, .{
|
||||
.dest_dir = .{ .override = .{ .custom = "variants" } },
|
||||
});
|
||||
noperf_step.dependOn(&noperf_install.step);
|
||||
break :noperf_blk noperf_install;
|
||||
};
|
||||
|
||||
// Convenience step to build all single-module variants
|
||||
const build_all_step = b.step("all-variants", "Build all DLL variants");
|
||||
build_all_step.dependOn(&noperf_install.step);
|
||||
|
||||
inline for (module_list) |variant_mod| {
|
||||
@setEvalBranchQuota(10000);
|
||||
@@ -410,9 +445,9 @@ pub fn build(b: *std.Build) void {
|
||||
// Build options: enable flags + scanned file lists for each module
|
||||
// =============================================================================
|
||||
|
||||
fn addModuleOptions(b: *std.Build, opts: *std.Build.Step.Options) void {
|
||||
inline for (module_list) |mod| {
|
||||
opts.addOption(bool, "enable_" ++ mod.name, b.option(bool, mod.name, mod.desc) orelse mod.default);
|
||||
fn addModuleOptionsFromArray(b: *std.Build, opts: *std.Build.Step.Options, enabled: *const [module_list.len]bool) void {
|
||||
inline for (module_list, 0..) |mod, i| {
|
||||
opts.addOption(bool, "enable_" ++ mod.name, enabled[i]);
|
||||
}
|
||||
// Pass full module name/version lists so main.zig/addons.zig can use them at comptime
|
||||
const names: []const []const u8 = comptime blk: {
|
||||
|
||||
+2
-1
@@ -241,9 +241,10 @@ pub fn main() void {
|
||||
// AddToSpatialGrid -- linked list requires game state, A/B test in-game only
|
||||
// bench_addToSpatialGrid();
|
||||
|
||||
if (false) { // disabled: not testing these right now
|
||||
bench_collisionDetection();
|
||||
bench_rayTriIndexedInt();
|
||||
|
||||
if (false) { // disabled
|
||||
bench_entityUpdate();
|
||||
}
|
||||
|
||||
|
||||
@@ -174,34 +174,22 @@ export fn rayTriIntersectIndexedInt(
|
||||
|
||||
if (det > -1e-6 and det < 1e-6) return 0;
|
||||
|
||||
const inv_det = 1.0 / det;
|
||||
const tvec = ray_origin - v0;
|
||||
const u_raw = dot3(tvec, pvec);
|
||||
|
||||
const det_neg_eps = det * neg_eps;
|
||||
const det_one_plus = det * one_plus_eps;
|
||||
if (det > 0) {
|
||||
if (u_raw < det_neg_eps or u_raw > det_one_plus) return 0;
|
||||
} else {
|
||||
if (u_raw > det_neg_eps or u_raw < det_one_plus) return 0;
|
||||
}
|
||||
const u = dot3(tvec, pvec) * inv_det;
|
||||
if (u < neg_eps or u > one_plus_eps) return 0;
|
||||
|
||||
const qvec = cross(tvec, edge1);
|
||||
const v_raw = dot3(ray_dir, qvec);
|
||||
const v = dot3(ray_dir, qvec) * inv_det;
|
||||
if (v < neg_eps or (u + v) > one_plus_eps) return 0;
|
||||
|
||||
if (det > 0) {
|
||||
if (v_raw < det_neg_eps or (u_raw + v_raw) > det_one_plus) return 0;
|
||||
} else {
|
||||
if (v_raw > det_neg_eps or (u_raw + v_raw) < det_one_plus) return 0;
|
||||
}
|
||||
|
||||
// Hit confirmed -- only divide now
|
||||
const inv_det = 1.0 / det;
|
||||
if (t_out != 0) {
|
||||
@as(*align(1) f32, @ptrFromInt(t_out)).* = dot3(edge2, qvec) * inv_det;
|
||||
}
|
||||
if (normal_out != 0) {
|
||||
@as(*align(1) f32, @ptrFromInt(normal_out)).* = u_raw * inv_det;
|
||||
@as(*align(1) f32, @ptrFromInt(normal_out + 4)).* = v_raw * inv_det;
|
||||
@as(*align(1) f32, @ptrFromInt(normal_out)).* = u;
|
||||
@as(*align(1) f32, @ptrFromInt(normal_out + 4)).* = v;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@@ -482,39 +470,20 @@ export fn performCollisionDetectionSSE(this: u32, key_data: u32, key_size: u32)
|
||||
const pvec = cross(ray_dir, edge2);
|
||||
const det = dot3(edge1, pvec);
|
||||
|
||||
if (det <= 1e-7 and det >= -1e-7) continue;
|
||||
// Original thresholds: reject if det is in (-1e-6, 1e-6) dead zone
|
||||
if (det > -1e-6 and det < 1e-6) continue;
|
||||
|
||||
const inv_det = 1.0 / det;
|
||||
const tvec = ray_origin - v0;
|
||||
|
||||
// u_raw = dot(tvec, pvec) -- NOT multiplied by inv_det yet
|
||||
const u_raw = dot3(tvec, pvec);
|
||||
|
||||
// Compare u_raw against det-scaled epsilon bounds.
|
||||
// If det > 0: u = u_raw/det, so u < -eps iff u_raw < -eps*det, u > 1+eps iff u_raw > (1+eps)*det
|
||||
// If det < 0: division flips sign, so u < -eps iff u_raw > -eps*det (which is positive)
|
||||
// Trick: multiply both sides by sign(det) to normalize.
|
||||
// Or equivalently: if det>0 check u_raw in [det*neg_eps, det*one_plus_eps]
|
||||
// if det<0 check u_raw in [det*one_plus_eps, det*neg_eps]
|
||||
const det_neg_eps = det * neg_eps;
|
||||
const det_one_plus = det * one_plus_eps;
|
||||
if (det > 0) {
|
||||
if (u_raw < det_neg_eps or u_raw > det_one_plus) continue;
|
||||
} else {
|
||||
if (u_raw > det_neg_eps or u_raw < det_one_plus) continue;
|
||||
}
|
||||
const u = dot3(tvec, pvec) * inv_det;
|
||||
if (u < neg_eps or u > one_plus_eps) continue;
|
||||
|
||||
const qvec = cross(tvec, edge1);
|
||||
const v_raw = dot3(ray_dir, qvec);
|
||||
const v = dot3(ray_dir, qvec) * inv_det;
|
||||
if (v < neg_eps or (u + v) > one_plus_eps) continue;
|
||||
|
||||
// Same sign-aware bounds check for v
|
||||
if (det > 0) {
|
||||
if (v_raw < det_neg_eps or (u_raw + v_raw) > det_one_plus) continue;
|
||||
} else {
|
||||
if (v_raw > det_neg_eps or (u_raw + v_raw) < det_one_plus) continue;
|
||||
}
|
||||
|
||||
// Only divide for confirmed hits
|
||||
const t = dot3(edge2, qvec) / det;
|
||||
const t = dot3(edge2, qvec) * inv_det;
|
||||
|
||||
if (t >= 0.0 and t < readF32(this + 0x4C)) {
|
||||
// Update closest hit
|
||||
|
||||
@@ -4,10 +4,9 @@
|
||||
//! uses libdeflate (~2.2x faster than stock zlib). Falls back to original on failure.
|
||||
//!
|
||||
//! Thread-safety: the decompressor struct contains mutable decode tables rebuilt
|
||||
//! per block, so each thread gets its own cached decompressor via a thread-local
|
||||
//! pool keyed by Windows thread ID (FS:[0x24]). This avoids both the race condition
|
||||
//! (shared decompressor → wild writes) and the per-call alloc overhead (~8-9% gain
|
||||
//! over alloc/free each call).
|
||||
//! per block, so each thread gets its own cached decompressor via Zig's native
|
||||
//! threadlocal (OS-managed TLS). This avoids both the race condition
|
||||
//! (shared decompressor -> wild writes) and the per-call alloc overhead.
|
||||
//!
|
||||
//! Benchmark results (84k calls, heavy load):
|
||||
//! stock=2681ms | per-call-alloc=1304ms (2.05x) | tls-cached=1194ms (2.2x)
|
||||
@@ -24,43 +23,17 @@ extern fn libdeflate_zlib_decompress(?*anyopaque, [*]const u8, usize, [*]u8, usi
|
||||
var lib_available: bool = false;
|
||||
var log: logging.Logger = .{};
|
||||
|
||||
// --- Thread-local decompressor pool ---
|
||||
// Keyed by Windows thread ID. WoW has ~5-10 threads; only 2-3 call decompress.
|
||||
const TLS_SLOTS = 8;
|
||||
const TlsSlot = struct {
|
||||
thread_id: u32 = 0,
|
||||
decomp: ?*anyopaque = null,
|
||||
};
|
||||
var tls_pool: [TLS_SLOTS]TlsSlot = [_]TlsSlot{.{}} ** TLS_SLOTS;
|
||||
|
||||
fn getCurrentThreadId() u32 {
|
||||
return asm volatile ("movl %%fs:0x24, %[ret]"
|
||||
: [ret] "=r" (-> u32),
|
||||
);
|
||||
}
|
||||
// --- Thread-local decompressor ---
|
||||
// Each thread lazily allocates its own decompressor on first use.
|
||||
// OS-managed TLS via Zig's threadlocal -- works correctly on both
|
||||
// native Windows and Wine without manual FS segment access.
|
||||
threadlocal var tls_decomp: ?*anyopaque = null;
|
||||
|
||||
fn getTlsDecompressor() ?*anyopaque {
|
||||
const tid = getCurrentThreadId();
|
||||
for (&tls_pool) |*slot| {
|
||||
if (slot.thread_id == tid) return slot.decomp;
|
||||
}
|
||||
const decomp = libdeflate_alloc_decompressor() orelse return null;
|
||||
for (&tls_pool) |*slot| {
|
||||
if (slot.thread_id == 0) {
|
||||
slot.thread_id = tid;
|
||||
slot.decomp = decomp;
|
||||
return decomp;
|
||||
}
|
||||
}
|
||||
libdeflate_free_decompressor(decomp);
|
||||
return null;
|
||||
}
|
||||
|
||||
fn freeTlsPool() void {
|
||||
for (&tls_pool) |*slot| {
|
||||
if (slot.decomp) |d| libdeflate_free_decompressor(d);
|
||||
slot.* = .{};
|
||||
}
|
||||
if (tls_decomp) |d| return d;
|
||||
const d = libdeflate_alloc_decompressor() orelse return null;
|
||||
tls_decomp = d;
|
||||
return d;
|
||||
}
|
||||
|
||||
// --- Timing ---
|
||||
@@ -142,5 +115,4 @@ pub fn install() bool {
|
||||
pub fn remove() void {
|
||||
decompress_hook.detach();
|
||||
lib_available = false;
|
||||
freeTlsPool();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
/* Stub: libdeflate doesn't use setjmp, but MSVC intrin.h includes it */
|
||||
typedef int jmp_buf[16];
|
||||
@@ -333,8 +333,8 @@ pub fn installHooks() void {
|
||||
// if (findguid_hook.attach(0x464890, &findguidDetour) == .ok) installed += 1;
|
||||
// if (obj_delete_hook.attach(0x464920, &objDeleteDetour) == .ok) installed += 1;
|
||||
|
||||
// Flush GUID cache before object manager teardown
|
||||
if (destroy_objmgr_hook.attach(0x467700, &destroyObjMgrDetour) == .ok) installed += 1;
|
||||
// GUID cache disabled for now
|
||||
// if (destroy_objmgr_hook.attach(0x467700, &destroyObjMgrDetour) == .ok) installed += 1;
|
||||
|
||||
// Per-frame cache reset
|
||||
if (world_update_hook.attach(0x482EA0, &worldUpdateDetour) == .ok) installed += 1;
|
||||
|
||||
Reference in New Issue
Block a user