Linux and Windows get their sound back, and emotes refuse while moving
- every build off macOS has been silent since the 09-02 snapshot (B356): the non-macOS device layer was a stub that opened nothing, and nothing had ever compiled the client for another target; that layer is now the same output stream over cpal — one seam, not two stacks: the ring, the render thread and the rebuild-on-device-change loop are one code on every platform, only the device file differs; the audio thread never allocates, a lost or changed default device is noticed at kira's own cadence, and realtime is `SCHED_FIFO` on unix and MMCSS "Pro Audio" on Windows, either refusable, the ring turning a refusal into a degradation; and the two instruments this bug says were missing exist — a Linux and Windows cross-compile under `clippy -D warnings`, and a live output test that opens the real device and asserts the ring never ran dry, because compiling is not sounding (1920) - an emote that requires standing still now refuses on the move: `CheckEmoteEligible`'s `0x4000` arm writes an out-param that `DoEmote` turns into a red "You can't do that while moving!" and aborts; 20 emotes of 78 can raise the line, read off the shipped `Emotes.dbc`; SWIMMING is not in the mask, so a swimmer holding still emotes fine, and `IsSelfControlled` is TRUE for an ordinary player, so a feared or confused player emotes away happily (1904); and the cast path gains its equipped-item rung — the button greyed and pressing it still sent: the reference short-circuits to "fits" on four gates, not one, the miss being a zero `EquippedItemSubClassMask`, which means no item required at all, and the rung sits above the crowd-control validator, so a player both stunned and missing the weapon is told about the weapon; that ladder has six arms, not three — CHARMED, FLEEING and CONFUSED join (1925) - the quest reward's money is the reference's own widget: the tail of the shared `QuestFrameItems_Update` drives `questState.."MoneyFrame"` through `MoneyFrame_Update`, and the two hand-built coin trios become one `MoneyFrameTemplate` instance each at the reference's anchors, which our coins already used; the progress panel stays on coins, being the required-money flow, not the reward one (1928); and the flight master's `!` joins the questgiver sweep, because the green `!` and the gold `!` are the SAME slot, `unit+0xb2c`, torn down once before both queries re-issue; the mouseover re-ask is refuted — the only taxi-status sender has one live caller — and the two queries are not symmetric: the questgiver's goes out on every sweep, the taxi one only when the unit's key moves or on a full sweep, as the wire shows at Dungar Longdrink: 68 questgiver status queries against 1 taxi-node one (1918) - `GetSpellTabInfo` out of range answers `nil, nil, 0, 0` — the reference returns four values on every path that returns (`0x4b3ce0`), and ours answered a single nil copied from another binding's shape; it matters because stock `SpellBookFrame_OnLoad` calls it unconditionally and then does `id > (offset + numSpells)`, which a nil raises on and (0+0) makes true, hiding every button on the page — the behaviour to match; the argument raises the reference's usage string when absent, and the index truncates toward zero THEN decrements (1931); the perf probe gains a `WOW_PROBE_UNCAP=vsync` arm, because the uncapped present mode is a request, not a schedule — measured, it taxes the number it was meant to pin (11.80 `cpu_ms` uncapped against 14.65 under it), so the default stays uncapped; and the last three test temp paths are keyed on the pid, after concurrent runs raced on one fixed shared file Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -573,7 +573,7 @@ const TARGET_FLAG_UNIT: u16 = 0x0002;
|
||||
/// a ground-targeted `CMSG_CAST_SPELL` writes ([`super::spells::cast_spell_at_dest`]).
|
||||
const TARGET_FLAG_DEST_LOCATION: u16 = 0x0040;
|
||||
/// `TARGET_FLAG_ITEM` — a bound *item* target, the same bit and the same packed guid an
|
||||
/// item-targeted `CMSG_CAST_SPELL` writes ([`super::spells::cast_spell_on_item`]). One block
|
||||
/// item-targeted `CMSG_CAST_SPELL` writes (`super::spells::cast_spell_on_item`). One block
|
||||
/// builder, two opcodes (decision 0923).
|
||||
const TARGET_FLAG_ITEM: u16 = 0x0010;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ pub(super) const MOVEMENT_FLAG_SPLINE_ELEVATION: u32 = 0x400_0000;
|
||||
/// `flags`/`position`/`orientation`/`timestamp`/`fall_time`, the **transport pose** ([`TransportPose`],
|
||||
/// present iff `MOVEFLAG_ON_TRANSPORT` — this is how a boarded rider's `MSG_MOVE_*` heartbeat carries
|
||||
/// its local frame, decision 0438 "Riding is the mover's platform frame"), the **swim pitch**
|
||||
/// ([`Self::pitch`], present iff `MOVEFLAG_SWIMMING`), and the **jump tail** ([`JumpInfo`], so an
|
||||
/// (`Self::pitch`, present iff `MOVEFLAG_SWIMMING`), and the **jump tail** ([`JumpInfo`], so an
|
||||
/// observer can replay a jump arc); the spline-elevation tail is parsed to stay aligned but discarded.
|
||||
///
|
||||
/// VERIFIED byte-for-byte against vmangos `MovementInfo::Read` (build 1.12.1): note 1.12 has **no**
|
||||
@@ -265,7 +265,7 @@ pub struct TransportPose {
|
||||
/// (decision 0438 phase 2). The transport, swim-pitch, and jump tails are conditional
|
||||
/// outbound, gated on their flags. Inbound, every conditional tail is parsed (see
|
||||
/// [`read_movement_info`]) — the transport pose into [`Self::transport`], the swim pitch into
|
||||
/// [`Self::pitch`], the jump tail into [`Self::jump`], and the spline-elevation float to stay aligned
|
||||
/// `Self::pitch`, the jump tail into [`Self::jump`], and the spline-elevation float to stay aligned
|
||||
/// only (no consumer needs it).
|
||||
pub struct MovementInfo {
|
||||
pub flags: u32,
|
||||
|
||||
@@ -586,7 +586,7 @@ pub struct PlayerSkillSlot {
|
||||
/// one of these per object and [`Self::merge`]s each `Values` delta into it; the codec itself
|
||||
/// stays stateless (decision 0006).
|
||||
///
|
||||
/// **Absent-field semantics** hinge on [`Self::descriptor_end`]: a CREATE block is a *complete*
|
||||
/// **Absent-field semantics** hinge on `Self::descriptor_end`: a CREATE block is a *complete*
|
||||
/// snapshot — the server masks in only non-zero fields (vmangos `Object::_SetCreateBits`: bit set
|
||||
/// iff `value != 0`), and the real client reads it into a zero-initialized descriptor buffer — so
|
||||
/// on a create-seeded store an absent field reads `Some(0)`, the truth. A bare `Values` delta
|
||||
|
||||
@@ -45,7 +45,7 @@ impl WorldWriter {
|
||||
|
||||
/// Open an item by bag position (`CMSG_OPEN_ITEM`, layout in [`messages::open_item`]) — crack
|
||||
/// the clam, empty the picked lockbox, unwrap the gift. The right-click fork for an
|
||||
/// [`crate::ItemInfo::openable`] item; the server answers with `SMSG_LOOT_RESPONSE` on the
|
||||
/// `crate::ItemInfo::openable` item; the server answers with `SMSG_LOOT_RESPONSE` on the
|
||||
/// item's **own** guid (so the loot window opens over a thing in the bag), or an equip error
|
||||
/// on a refusal (still locked, dead, flying).
|
||||
pub fn open_item(&mut self, bag_index: u8, slot: u8) -> Result<()> {
|
||||
|
||||
Reference in New Issue
Block a user