Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,17 @@ permissions:
jobs:
fmt:
runs-on: ubuntu-latest
# Non-blocking: 61 files on main already fail this. Reformatting them is a separate change.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Check formatting
continue-on-error: true
run: cargo fmt --all -- --check

clippy:
runs-on: ubuntu-latest
# Non-blocking: lyracore-module, -gateway and -importer are red on main today. lyracore-shared
# and -package-delta are clean, so this flips to blocking once the other three are fixed.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- name: Run clippy
continue-on-error: true
run: cargo clippy --workspace --all-targets --all-features -- -D warnings

test:
Expand Down
4 changes: 3 additions & 1 deletion crates/lyracore-shared/src/spatial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,9 @@ mod tests {
let side = (2 * BOX_HALF_SPAN + 1) as usize;
assert_eq!(got.len(), side * side);
assert_eq!(
got.iter().copied().collect::<std::collections::HashSet<_>>()
got.iter()
.copied()
.collect::<std::collections::HashSet<_>>()
.len(),
got.len(),
"a duplicate id inside one box would double-deliver a cell"
Expand Down
10 changes: 6 additions & 4 deletions gateway/src/codec/breath.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Breath mirror-timer and drowning combat-log packets.

use wow_world_messages::vanilla::opcodes::ServerOpcodeMessage;
use wow_world_messages::vanilla::{
EnvironmentalDamageType, SMSG_ENVIRONMENTAL_DAMAGE_LOG, SMSG_START_MIRROR_TIMER,
SMSG_STOP_MIRROR_TIMER, TimerType,
EnvironmentalDamageType, TimerType, SMSG_ENVIRONMENTAL_DAMAGE_LOG, SMSG_START_MIRROR_TIMER,
SMSG_STOP_MIRROR_TIMER,
};
use wow_world_messages::vanilla::opcodes::ServerOpcodeMessage;

/// The only timer this relay currently mirrors: underwater breath.
const BREATH_TIMER: TimerType = TimerType::Breath;
Expand All @@ -25,7 +25,9 @@ pub fn build_breath_timer_start(time_remaining_ms: u32, duration_ms: u32) -> Ser

/// Client-side countdown stop on the surface edge.
pub fn build_breath_timer_stop() -> ServerOpcodeMessage {
ServerOpcodeMessage::SMSG_STOP_MIRROR_TIMER(SMSG_STOP_MIRROR_TIMER { timer: BREATH_TIMER })
ServerOpcodeMessage::SMSG_STOP_MIRROR_TIMER(SMSG_STOP_MIRROR_TIMER {
timer: BREATH_TIMER,
})
}

/// Combat-log line for exactly the health amount the server's drowning resolver removed.
Expand Down
29 changes: 18 additions & 11 deletions gateway/src/codec/gameobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,16 @@ pub fn build_gameobject_create_object(go: &GameObjectView) -> SMSG_UPDATE_OBJECT
/// matching vanilla's Z-axis-only yaw convention — rot0/rot1 stay 0, i.e. no terrain pitch/roll, which
/// is the best a bare `orientation` float can express) instead.
pub fn build_gameobject_rotation_values(go: &GameObjectView) -> (u16, Vec<u8>) {
let (rot0, rot1, rot2, rot3) =
if go.rotation_0 == 0.0 && go.rotation_1 == 0.0 && go.rotation_2 == 0.0 && go.rotation_3 == 0.0 {
let half = go.orientation * 0.5;
(0.0, 0.0, half.sin(), half.cos())
} else {
(go.rotation_0, go.rotation_1, go.rotation_2, go.rotation_3)
};
let (rot0, rot1, rot2, rot3) = if go.rotation_0 == 0.0
&& go.rotation_1 == 0.0
&& go.rotation_2 == 0.0
&& go.rotation_3 == 0.0
{
let half = go.orientation * 0.5;
(0.0, 0.0, half.sin(), half.cos())
} else {
(go.rotation_0, go.rotation_1, go.rotation_2, go.rotation_3)
};
let mut mask = update_mask::UpdateMaskValues::new();
mask.set_f32(update_mask::idx::GAMEOBJECT_ROTATION, rot0);
mask.set_f32(update_mask::idx::GAMEOBJECT_ROTATION + 1, rot1);
Expand Down Expand Up @@ -196,6 +199,7 @@ pub fn build_dynamicobject_create_object(
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::FRAC_1_SQRT_2;
use wow_world_messages::vanilla::opcodes::ServerOpcodeMessage;

#[test]
Expand Down Expand Up @@ -282,7 +286,10 @@ mod tests {
let block_count = body[mask_start] as usize;
let values_start = mask_start + 1 + block_count * 4;
let mut out = [0.0f32; 4];
for (i, chunk) in body[values_start..values_start + 16].chunks_exact(4).enumerate() {
for (i, chunk) in body[values_start..values_start + 16]
.chunks_exact(4)
.enumerate()
{
out[i] = f32::from_le_bytes(chunk.try_into().unwrap());
}
out
Expand All @@ -305,14 +312,14 @@ mod tests {
display_id: 259,
rotation_0: 0.1,
rotation_1: 0.2,
rotation_2: 0.70710678,
rotation_3: 0.70710678,
rotation_2: FRAC_1_SQRT_2,
rotation_3: FRAC_1_SQRT_2,
size: 0.0,
};
let (opcode, body) = build_gameobject_rotation_values(&go);
assert_eq!(opcode, 0x00A9, "SMSG_UPDATE_OBJECT opcode");
let floats = decode_rotation_floats(go.guid, &body);
assert_eq!(floats, [0.1, 0.2, 0.70710678, 0.70710678]);
assert_eq!(floats, [0.1, 0.2, FRAC_1_SQRT_2, FRAC_1_SQRT_2]);
}

#[test]
Expand Down
10 changes: 5 additions & 5 deletions gateway/src/codec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ pub mod social;
pub mod update_mask;

// Packet-family submodules.
mod char;
mod auction;
mod breath;
mod char;
mod combat;
mod corpse;
mod duel;
Expand All @@ -36,8 +36,8 @@ mod movement;
mod npc;
mod pet;
mod quest;
mod trainer;
mod taxi;
mod trainer;
mod values;
mod weather;

Expand All @@ -50,9 +50,9 @@ pub use social::{
};

// Re-export packet-family surfaces through `crate::codec::<sym>`.
pub use char::*;
pub use auction::*;
pub use breath::*;
pub use char::*;
pub use combat::*;
pub use corpse::*;
pub use duel::*;
Expand All @@ -66,8 +66,8 @@ pub use movement::*;
pub use npc::*;
pub use pet::*;
pub use quest::*;
pub use trainer::*;
pub use taxi::*;
pub use trainer::*;
pub use values::*;
pub use weather::*;

Expand All @@ -80,9 +80,9 @@ use lyracore_shared::opcodes::movement as movement_opcodes;
use lyracore_shared::packing::unpack4;
use wow_world_messages::vanilla::opcodes::{ClientOpcodeMessage, ServerOpcodeMessage};
use wow_world_messages::vanilla::{
Area,
AllowedClass,
AllowedRace,
Area,
BagFamily,
// Item binding: "Binds when picked up/equipped" tooltip line.
Bonding,
Expand Down
9 changes: 6 additions & 3 deletions gateway/src/codec/movement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,12 @@ pub fn build_taxi_move_raw(
duration_ms: u32,
spline_id: u32,
) -> Option<(u16, Vec<u8>)> {
if points.is_empty() || points.iter().chain([&start]).any(|p| {
!p.x.is_finite() || !p.y.is_finite() || !p.z.is_finite()
}) {
if points.is_empty()
|| points
.iter()
.chain([&start])
.any(|p| !p.x.is_finite() || !p.y.is_finite() || !p.z.is_finite())
{
return None;
}
let mut body = Vec::with_capacity(34 + points.len() * 12);
Expand Down
2 changes: 1 addition & 1 deletion gateway/src/codec/npc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub fn option_condition_holds(cond_type: u32, quest_taken: bool, quest_rewarded:
gossip_condition::QUEST_TAKEN => quest_taken,
gossip_condition::QUEST_REWARDED => quest_rewarded,
gossip_condition::NEVER => false, // fail-closed placeholder (seasonal events don't exist here)
_ => true, // unrecognized → fail open, never silently hide an option
_ => true, // unrecognized → fail open, never silently hide an option
}
}

Expand Down
40 changes: 31 additions & 9 deletions gateway/src/codec/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
// Only the loot byte-match test needs gtker's typed loot response (the runtime path is raw).
use wow_world_messages::vanilla::ServerMessage;
use wow_world_messages::vanilla::{
EnvironmentalDamageType, SMSG_LOOT_RESPONSE_LootMethod, SMSG_LOOT_RESPONSE, TimerType,
EnvironmentalDamageType, SMSG_LOOT_RESPONSE_LootMethod, TimerType, SMSG_LOOT_RESPONSE,
};
// Group loot methods: the vote-kind byte constants + wire RollVote enum.
use lyracore_shared::loot_roll::vote_kind;
Expand Down Expand Up @@ -793,7 +793,9 @@ fn breath_mirror_timer_edges_roundtrip_on_the_vanilla_wire() {
let mut stop_bytes = Vec::new();
stop.write_unencrypted_server(&mut stop_bytes).unwrap();
match ServerOpcodeMessage::read_unencrypted(&mut stop_bytes.as_slice()).unwrap() {
ServerOpcodeMessage::SMSG_STOP_MIRROR_TIMER(msg) => assert_eq!(msg.timer, TimerType::Breath),
ServerOpcodeMessage::SMSG_STOP_MIRROR_TIMER(msg) => {
assert_eq!(msg.timer, TimerType::Breath)
}
other => panic!("expected SMSG_STOP_MIRROR_TIMER, got {other}"),
}
}
Expand Down Expand Up @@ -2064,7 +2066,11 @@ fn gossip_message_renders_imported_options_verbatim_with_a_trailing_farewell() {
let opts = vec![
opt(0, "Care for a chat?", gossip_option::GOSSIP),
opt(1, "Let me see your goods.", gossip_option::VENDOR),
opt(0, "I'd like to stay here a while.", gossip_option::INNKEEPER),
opt(
0,
"I'd like to stay here a while.",
gossip_option::INNKEEPER,
),
];
let m = build_gossip_message(
1,
Expand Down Expand Up @@ -3078,8 +3084,8 @@ fn active_taxi_mount_is_present_in_the_player_create_mask() {
other => panic!("expected a Player CreateObject2, got {other:?}"),
}

let unmounted = build_create_object(&warrior_entity(), CreateKind::SelfPlayer, &[], &[])
.unwrap();
let unmounted =
build_create_object(&warrior_entity(), CreateKind::SelfPlayer, &[], &[]).unwrap();
match &unmounted.objects[0] {
Object::CreateObject2 {
mask2: UpdateMask::Player(player),
Expand Down Expand Up @@ -3443,11 +3449,27 @@ fn monster_move_facing_carries_the_angle_and_a_degenerate_single_point_spline()

#[test]
fn taxi_move_writes_absolute_catmull_rom_points_byte_for_byte() {
let start = Vector3d { x: -9465.0, y: -1300.0, z: 50.0 };
let start = Vector3d {
x: -9465.0,
y: -1300.0,
z: 50.0,
};
let points = vec![
Vector3d { x: -9460.0, y: -1298.0, z: 52.0 },
Vector3d { x: -9450.0, y: -1290.0, z: 54.0 },
Vector3d { x: -9440.0, y: -1280.0, z: 56.0 },
Vector3d {
x: -9460.0,
y: -1298.0,
z: 52.0,
},
Vector3d {
x: -9450.0,
y: -1290.0,
z: 54.0,
},
Vector3d {
x: -9440.0,
y: -1280.0,
z: 56.0,
},
];
let (opcode, bytes) = build_taxi_move_raw(0xF00D, start, points.clone(), 12_345, 77).unwrap();
assert_eq!(opcode, 0x00DD);
Expand Down
1 change: 0 additions & 1 deletion gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -733,7 +733,6 @@ pub fn synthetic_owner_identity(account_id: u64) -> [u8; 32] {
id
}


/// A crash-diagnosis probe for the SMSG_COMPRESSED_MOVES corruption at crowd scale:
/// `LYRACORE_WRITER_TRACE=1` turns on a per-session black-box ring inside `spawn_writer` — the
/// last 32 (opcode, declared size, rolling checksum) triples for the exact bytes handed to the
Expand Down
4 changes: 2 additions & 2 deletions gateway/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
mod accept;
mod codec;
mod config;
mod fd_limit;
#[cfg(test)]
#[allow(dead_code)] // Shared commands are also used by Module integration tests.
#[path = "../../module/tests/support/mod.rs"]
mod durable_test_support;
mod fd_limit;
mod load_sample;
mod logon;
mod movement_batch_metrics;
Expand Down Expand Up @@ -151,7 +151,7 @@ async fn run() -> Result<()> {
tokio::time::sleep(std::time::Duration::from_secs(secs)).await;
logon.abort();
world.abort();
return Ok(());
Ok(())
}
#[cfg(not(feature = "dhat-heap"))]
{
Expand Down
1 change: 0 additions & 1 deletion gateway/src/stdb/account_sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ impl SessionEpochs {
}
}


#[cfg(test)]
mod session_epoch_tests {
use super::SessionEpochs;
Expand Down
6 changes: 5 additions & 1 deletion gateway/src/stdb/aoi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,11 @@ mod frozen_sql_oracle {
/// ONE point-probe query per (table, cell) — the shape the missing-AOI-index fix landed. Kept alongside the range
/// form because a future change has to satisfy BOTH or it has narrowed visibility on one of
/// them.
fn cell_queries(map_id: u32, instance_id: u64, cells: impl Iterator<Item = i64>) -> Vec<String> {
fn cell_queries(
map_id: u32,
instance_id: u64,
cells: impl Iterator<Item = i64>,
) -> Vec<String> {
let cells: Vec<i64> = cells.collect();
let mut out = Vec::with_capacity(BOX_SCOPED_TABLES.len() * cells.len());
for table in BOX_SCOPED_TABLES {
Expand Down
5 changes: 4 additions & 1 deletion gateway/src/stdb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
//! - `subscriptions`: `PlayerSubscriptions`, viewer setup, and shared packet builders.
//! - `views`: row→view converters + the thin `RealmRow`/`AccountRow` mirrors.

// The SpacetimeDB codegen writes one function per table subscription; several run past any
// reasonable length ceiling and no edit here survives a regeneration.
#[allow(clippy::too_many_lines)]
pub mod bindings;

mod account_sessions; // per-account session-epoch + live-socket registry, split out of `connection`
Expand All @@ -24,8 +27,8 @@ mod reads;
mod reducers;
pub(crate) mod subscriptions;
mod views;
mod world_store; // impl WorldStore for Coordinator (replaces the former WorldCoordinatorStore newtype)
pub(crate) mod world_index;
mod world_store; // impl WorldStore for Coordinator (replaces the former WorldCoordinatorStore newtype)
pub(crate) mod world_view; // shared per-shard spatial, broadcast, private, and owner dispatch

pub use connection::Coordinator;
Expand Down
1 change: 0 additions & 1 deletion gateway/src/stdb/reads/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,5 +355,4 @@ mod tests {
);
}
}

}
1 change: 0 additions & 1 deletion gateway/src/stdb/reads/talent_reputation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,5 +263,4 @@ mod tests {
let sworn = ft(3, 14, 8, 0, 1, 0);
assert!(super::faction_template_hostile(&sworn, &player));
}

}
2 changes: 2 additions & 0 deletions gateway/src/stdb/reducers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,8 @@ impl Coordinator {
/// Cast a GROUND-TARGETED spell at a clicked world point (`CMSG_CAST_SPELL` with a DEST_LOCATION —
/// Flamestrike/Blizzard/Rain of Fire). Same per-account attribution as `cast_spell`; the `(x,y,z)` is
/// the ground click so the module anchors the AoE/patch there.
// The ground point rides flat because it mirrors the `gw_cast_spell_at` reducer signature.
#[allow(clippy::too_many_arguments)]
pub fn cast_spell_at(
&self,
_account_id: u64,
Expand Down
Loading