The host's state leaves `_G`: the eleven `__benilla_*` globals, which any addon could read and overwrite, move into the host's model (and, for the error handler, the Lua registry, as the reference keeps it), and the getters over them are Rust bindings answering the reference's shapes. - `GetTime` returns one number (`0x515ea0`), and `GetGameTime` returns hour then minute (`0x515ee0`). The tick advances the model clock, and every cooldown and buff reader reads it. - `GetZoneText`, `GetRealZoneText`, `GetSubZoneText` and `GetMinimapZoneText` always answer a string, `""` when unset (`0x48a0a0`-`0x48a100`). `GetZonePVPInfo` answers three values (`0x48d540`). - `seterrorhandler`/`geterrorhandler` work over a registry slot that starts empty, as the reference's does (`0x7039e0`, `0x702950`, `0x702900`). A non-function raises `Usage: seterrorhandler(errfunc)`. The engine dispatches caught errors through the slot, not through the global (`0x703b40`). The stock `BasicControls.xml` installs `_ERRORMESSAGE` before any addon, so play is unchanged. - The engine's surface allowlist now holds only `_G`. `scripts/api-coverage.sh` no longer excuses `__` names. Verified: - New tests show each of the eleven names absent from `_G` and that writing it changes nothing. On main, `__benilla_now = 0` rewound `GetTime` and every buff and cooldown. - The getter shapes and the error-handler slot are pinned. The surface gate fails when a name is re-added. - `scripts/gates.sh` and `scripts/smoke.sh` are green. - A live run read the clock, zone texts, PvP info and game time, then showed that writing the old names changes nothing. Built on macOS; no platform seam touched. Fixes #576 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
59 lines
2.4 KiB
Rust
59 lines
2.4 KiB
Rust
//! The two clocks Lua reads: `GetTime()`, the session seconds [`UiScript::tick`] advances, and
|
|
//! `GetGameTime()`, the game clock the host pushes. Both live in the [`Model`], out of an addon's
|
|
//! reach, as the reference's live in the client.
|
|
//!
|
|
//! [`UiScript::tick`]: super::UiScript::tick
|
|
|
|
use mlua::Lua;
|
|
|
|
use super::Model;
|
|
|
|
impl super::UiScript {
|
|
/// The current `GetTime()` value in seconds; the app stamps absolute expiries with it, such as
|
|
/// an aura's `expirationTime`.
|
|
pub fn now(&self) -> f64 {
|
|
self.model_ref().now
|
|
}
|
|
|
|
/// Start this VM's `GetTime()` clock at `secs`, so a rebuilt VM (a relog, a `ReloadUI`) keeps
|
|
/// the process's clock. The reference's `GetTime` (`0x515ea0`, through `0x42c010` and
|
|
/// `0x42b790`) is `GetTickCount` scaled by 0.001, an OS clock that never restarts, which stock
|
|
/// `Cooldown.lua` relies on (`start > 0`). Set once, at construction; after that only
|
|
/// [`Self::tick`] moves it.
|
|
pub fn set_now(&mut self, secs: f64) {
|
|
self.model_mut().now = secs;
|
|
}
|
|
|
|
/// Push the game clock `GetGameTime()` answers, `(hour, minute)`; 0:00 until the first push.
|
|
pub fn set_game_time(&mut self, hour: u32, minute: u32) {
|
|
self.model_mut().game_time = (hour, minute);
|
|
}
|
|
}
|
|
|
|
/// `GetTime()`'s clock, for a caller holding no model borrow; one that holds it reads `model.now`.
|
|
pub(crate) fn now(lua: &Lua) -> f64 {
|
|
lua.app_data_ref::<Model>().expect("model app_data").now
|
|
}
|
|
|
|
/// Register `GetTime` and `GetGameTime`.
|
|
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
|
let g = lua.globals();
|
|
// GetTime() (`0x515ea0`): one number, `GetTickCount` times the f64 0.001 at `0x801608`. Ours is
|
|
// the session clock the tick advances, so `GetTime()` deltas and the `elapsed` handed to
|
|
// OnUpdate agree; reference FrameXML (CastingBarFrame and kin) anchors cast windows on it.
|
|
g.set("GetTime", lua.create_function(|lua, ()| Ok(now(lua)))?)?;
|
|
// GetGameTime() (`0x515ee0`): two numbers, the hour `[0xce853c]` then the minute `[0xce8538]`,
|
|
// the clock `SMSG_LOGIN_SETTIMESPEED` seeds; minute resolution, no seconds.
|
|
g.set(
|
|
"GetGameTime",
|
|
lua.create_function(|lua, ()| {
|
|
let (hour, minute) = lua
|
|
.app_data_ref::<Model>()
|
|
.expect("model app_data")
|
|
.game_time;
|
|
Ok((f64::from(hour), f64::from(minute)))
|
|
})?,
|
|
)?;
|
|
Ok(())
|
|
}
|