Add DLL-side permission gate and return values for WorldMarker/ClearWorldMarker

- WorldMarker/ClearWorldMarker now return 1 on success, nil on permission denied
- LuaCFn calling convention fixed to x86_fastcall (matches WoW Lua C functions)
- ClearWorldMarker handles nil arg (clear all) via lua_type check
- Addon wraps DLL functions: shows deny message on nil return, broadcasts on success
- Permission denial feedback with cooldown (5s) and max 3 messages per login
- Removed redundant canSetMarkers() Lua-side check from broadcast path
This commit is contained in:
MarcelineVQ
2026-03-02 20:59:55 -08:00
parent 3c15c8650a
commit 0c2b038e45
2 changed files with 57 additions and 42 deletions
+38 -22
View File
@@ -119,43 +119,59 @@ local function broadcastAllDefs()
log("syncAll: " .. count .. " on " .. ch)
end
-- =============================================================================
-- Permission denial feedback (cooldown + max 3 per login, reset on success)
-- =============================================================================
local denyCount = 0
local denyLastTime = 0
local DENY_COOLDOWN = 5
local DENY_MAX = 3
local function showDenyMessage()
if denyCount >= DENY_MAX then return end
local now = GetTime()
if now - denyLastTime < DENY_COOLDOWN then return end
denyLastTime = now
denyCount = denyCount + 1
if GetNumRaidMembers() > 0 or GetNumPartyMembers() > 0 then
DEFAULT_CHAT_FRAME:AddMessage("|cffffff00You must be leader or assist to use world markers.|r")
else
DEFAULT_CHAT_FRAME:AddMessage("|cffffff00You must be in a group to use world markers.|r")
end
end
-- =============================================================================
-- Wrap DLL functions to broadcast on group placement/clear
-- =============================================================================
local RawWorldMarker = WorldMarker
function WorldMarker(index, ...)
log("WorldMarker(" .. tostring(index) .. ")")
-- Cancel pending sync request — we're actively placing marks
if syncTimer and syncTimer.pending then
syncTimer:Hide()
syncTimer.pending = false
log("sync timer cancelled (local mark placed)")
local ok = RawWorldMarker(index, unpack(arg))
if not ok then
showDenyMessage()
return
end
RawWorldMarker(index, unpack(arg))
denyCount = 0
local ch = getChannel()
if ch then
if canSetMarkers() then
local x, y, z, areaId = GetMarkerDef(index)
if x then
log("def ok, broadcasting")
broadcastPlace(index, x, y, z, areaId)
else
log("GetMarkerDef(" .. index .. ")=nil")
end
else
log("no perm to broadcast")
local x, y, z, areaId = GetMarkerDef(index)
if x then
broadcastPlace(index, x, y, z, areaId)
end
else
log("solo, no broadcast")
end
end
local RawClearWorldMarker = ClearWorldMarker
function ClearWorldMarker(index)
log("ClearWorldMarker(" .. tostring(index) .. ")")
RawClearWorldMarker(index)
if canSetMarkers() and getChannel() then
local ok = RawClearWorldMarker(index)
if not ok then
showDenyMessage()
return
end
denyCount = 0
local ch = getChannel()
if ch then
if index then
broadcastClear(index)
else
+19 -20
View File
@@ -115,8 +115,8 @@ const sc = std.builtin.CallingConvention{ .x86_stdcall = .{} };
// Permission check — leader or raid officer required
// =============================================================================
/// WoW Lua C functions: ECX=L, return count of pushed values.
const LuaCFn = *const fn (lua.State) callconv(.c) u32;
/// WoW Lua C functions: ECX=L, plain ret, return count of pushed values.
const LuaCFn = *const fn (lua.State) callconv(fc) u32;
const FN_IS_PARTY_LEADER: LuaCFn = @ptrFromInt(0x004e9130);
const FN_IS_RAID_OFFICER: LuaCFn = @ptrFromInt(0x004bb910);
@@ -394,14 +394,12 @@ fn clearAllMarkers() void {
// Lua API
// =============================================================================
/// Lua: WorldMarker(index [, x, y, z | "unitId"])
/// WorldMarker(1, x, y, z) — place at coordinates
/// WorldMarker(1, "target") — place at unit's current position
/// WorldMarker(1) — place at cursor terrain position
/// Lua: local ok = WorldMarker(index [, x, y, z | "unitId"])
/// Returns 1 on success, nil on permission denied.
pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 {
if (!canSetMarkers(L)) {
con.print("[markers] WorldMarker: no permission (need leader/assist)\n");
return 0;
con.print("[markers] WorldMarker: no permission\n");
return 0; // nil — addon shows user message
}
const nargs = lua.gettop(L);
@@ -419,13 +417,11 @@ pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 {
const index: usize = @intCast(raw_index - 1);
if (nargs >= 4 and lua.isnumber(L, 2)) {
// WorldMarker(index, x, y, z)
const x: f32 = @floatCast(lua.tonumber(L, 2));
const y: f32 = @floatCast(lua.tonumber(L, 3));
const z: f32 = @floatCast(lua.tonumber(L, 4));
_ = placeMarker(index, .{ .x = x, .y = y, .z = z });
} else if (nargs >= 2 and lua.isstring(L, 2)) {
// WorldMarker(index, "unitId")
const unit_id = lua.tostring(L, 2) orelse {
con.print("[markers] WorldMarker: invalid unit string\n");
return 0;
@@ -436,7 +432,6 @@ pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 {
};
_ = placeMarker(index, pos);
} else {
// WorldMarker(index) — cursor terrain position
const pos = getCursorTerrainPosition() orelse {
con.print("[markers] no terrain under cursor\n");
return 0;
@@ -444,27 +439,30 @@ pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 {
_ = placeMarker(index, pos);
}
return 0;
lua.pushnumber(L, 1.0);
return 1;
}
/// Lua: ClearWorldMarker([index])
/// ClearWorldMarker(1) — remove marker 1
/// ClearWorldMarker() — remove all markers
/// Lua: local ok = ClearWorldMarker([index])
/// Returns 1 on success, nil on permission denied.
pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 {
if (!canSetMarkers(L)) {
con.print("[markers] ClearWorldMarker: no permission (need leader/assist)\n");
con.print("[markers] ClearWorldMarker: no permission\n");
return 0;
}
const nargs = lua.gettop(L);
const lua_type: *const fn (lua.State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3400);
if (nargs == 0) {
if (nargs == 0 or lua_type(L, 1) == 0) {
// No args or nil — clear all
clearAllMarkers();
return 0;
lua.pushnumber(L, 1.0);
return 1;
}
if (!lua.isnumber(L, 1)) {
con.print("[markers] ClearWorldMarker: expected index (1-5) or no args\n");
con.print("[markers] ClearWorldMarker: expected index (1-5) or nil\n");
return 0;
}
@@ -475,7 +473,8 @@ pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 {
}
clearMarker(@intCast(raw_index - 1));
return 0;
lua.pushnumber(L, 1.0);
return 1;
}
// Delay before queuing Hold after Stand starts. Too short (<1s) causes the