Fix release-mode crashes: inline asm register clobbers and fastcall ABI
Root cause: hook.fastcall used "r" constraints + explicit MOV to set
ECX/EDX. LLVM can allocate "r" inputs to clobbered registers, causing
cross-assignment (ecx_in→EDX, edx_in→ECX) or function address stomping
when func lands in ECX/EDX. Debug works by luck (trivial regalloc);
Release optimizes aggressively and hits the conflicts.
Fix: explicit "{ecx}", "{edx}", "{eax}" register constraints in zhook
fastcall — compiler places values directly, no MOV needed, no ambiguity.
Also fix 9 inline asm blocks across main.zig, interact.zig,
screenshot.zig, markers.zig missing ECX/EDX clobbers after CALL
instructions. Without clobbers the optimizer assumes registers retain
input values after the call — stale reuse in release builds.
Other changes in this commit:
- Rename markers→worldmarkers (build flag, DLL, Lua table)
- Rename assetfix→looseassets
- lua.zig: add .never_tail to pushcclosure, pcall, openlib, pushnumber
- Move internal marker functions into WorldMarkers Lua table via openlib
- Remove unused GetCurrentAreaId function
- Fix cleanup_file_handle_hook.original() → .callOriginal()
This commit is contained in:
+14
-16
@@ -5,15 +5,19 @@
|
||||
//! Markers persist across zone transitions via MarkerDef definitions.
|
||||
//! Entities are respawned automatically when the player approaches within 200y.
|
||||
//!
|
||||
//! Lua API:
|
||||
//! Lua API (globals):
|
||||
//! WorldMarker(index, x, y, z) — place marker at coordinates
|
||||
//! WorldMarker(index, "unit") — place marker at unit's position
|
||||
//! WorldMarker(index) — place marker at cursor terrain position
|
||||
//! ClearWorldMarker(index) — remove specific marker (1-5)
|
||||
//! ClearWorldMarker() — remove all markers
|
||||
//! SetMarkerDef(i, x, y, z, area) — store definition (no immediate spawn)
|
||||
//! ClearMarkerDef([index]) — clear definition (and entity)
|
||||
//! GetMarkerDef(index) — returns x, y, z, areaId or nil
|
||||
//! CanSetWorldMarkers() — returns 1 if leader/assist, nil otherwise
|
||||
//!
|
||||
//! Lua API (WorldMarkers table — internal, used by addon):
|
||||
//! WorldMarkers.SetMarkerDef(i, x, y, z, area, sender)
|
||||
//! WorldMarkers.SetMarkerDefSync(i, x, y, z, area, sender)
|
||||
//! WorldMarkers.ClearMarkerDef([index,] sender)
|
||||
//! WorldMarkers.GetMarkerDef(index) — returns x, y, z, areaId or nil
|
||||
|
||||
const std = @import("std");
|
||||
const hook = @import("zhook");
|
||||
@@ -151,7 +155,7 @@ fn getNameFromGUID(guid_lo: u32, guid_hi: u32) ?[*:0]const u8 {
|
||||
: [_] "{ecx}" (@as(u32, o.NAME_CACHE_OBJ)),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (@as(u32, o.FN_NAME_CACHE_LOOKUP)),
|
||||
: .{ .edx = true, .memory = true, .cc = true });
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return if (result != 0) @ptrFromInt(result) else null;
|
||||
}
|
||||
|
||||
@@ -340,7 +344,7 @@ fn createEntityInstance(path: [*:0]const u8, pos: *[3]f32, facing: f32, flags: u
|
||||
[_] "{edx}" (@intFromPtr(pos)),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (o.FN_CREATE_ENTITY_INSTANCE),
|
||||
: .{ .memory = true, .cc = true });
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
|
||||
return if (result != 0) @ptrFromInt(result) else null;
|
||||
}
|
||||
@@ -351,7 +355,7 @@ fn cleanupEntity(obj: *anyopaque) void {
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(obj)),
|
||||
[func] "r" (o.FN_CLEANUP_ENTITY),
|
||||
: .{ .eax = true, .edx = true, .memory = true, .cc = true });
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -389,7 +393,7 @@ fn playAnimation(entity: *anyopaque, anim_id: u32, queue: bool) void {
|
||||
: [_] "{ecx}" (model),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (o.FN_PLAY_BONE_ANIMATION),
|
||||
: .{ .eax = true, .edx = true, .memory = true, .cc = true });
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
|
||||
/// Clean up despawning entities whose Decay animation has finished.
|
||||
@@ -800,7 +804,7 @@ pub fn luaGetMarkerDef(L: lua.State) callconv(.c) u32 {
|
||||
return 4;
|
||||
}
|
||||
|
||||
/// Lua: local ok = CanSetMarkers()
|
||||
/// Lua: local ok = CanSetWorldMarkers()
|
||||
/// Returns 1 if the local player has permission (leader/assist), nil otherwise.
|
||||
/// Used by the addon for broadcast/sync decisions.
|
||||
pub fn luaCanSetMarkers(L: lua.State) callconv(.c) u32 {
|
||||
@@ -811,13 +815,7 @@ pub fn luaCanSetMarkers(L: lua.State) callconv(.c) u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Lua: local areaId = GetCurrentAreaId()
|
||||
/// Returns the current zone area ID from the game global.
|
||||
pub fn luaGetCurrentAreaId(L: lua.State) callconv(.c) u32 {
|
||||
const area_id = hook.readMem(u32, o.ZONE_AREA_ID);
|
||||
lua.pushnumber(L, @floatCast(@as(f64, @floatFromInt(area_id))));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// =============================================================================
|
||||
// World teardown hook
|
||||
|
||||
Reference in New Issue
Block a user