Merge remote-tracking branch 'origin/ci'

# Conflicts:
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_0.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_127.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b31m1.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_2b32.msgpack
#	client/tests/fixtures/msgpack/snapshot_boundary_tick_32767.msgpack
#	client/tests/fixtures/msgpack/snapshot_empty.msgpack
#	client/tests/fixtures/msgpack/snapshot_multi_entity.msgpack
#	client/tests/fixtures/msgpack/snapshot_one_npc.msgpack
#	client/tests/fixtures/msgpack/snapshot_player.msgpack
#	client/tests/fixtures/msgpack/snapshot_v2_full.msgpack
#	server/Cargo.toml
#	server/src/bridge/text_renderer.rs
#	server/src/bridge/types.rs
#	server/src/perception/observer/mod.rs
#	server/src/simulation/path_follow.rs
#	server/tests/bridge_ipc.rs
#	server/tests/bridge_tcp.rs
#	server/tests/gen_fixtures.rs
#	server/tests/serialization.rs
This commit is contained in:
2026-02-19 15:35:56 +01:00
38 changed files with 307 additions and 50 deletions
+24 -8
View File
@@ -6,7 +6,7 @@
use bevy_ecs::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::collections::BTreeMap;
use crate::bridge::types::MovementStance;
use crate::knowledge::types::SoundRange;
@@ -27,7 +27,20 @@ pub struct PlayerCharacter;
///
/// Examples: a Standing character can walk past a Seated NPC at a console,
/// a Fixture (terminal) shares a tile with someone Seated at it.
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[derive(
Component,
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
Default,
Serialize,
Deserialize,
)]
pub enum TilePresence {
/// Upright position — walking, standing, sprinting. Default for all entities.
#[default]
@@ -44,7 +57,9 @@ pub enum TilePresence {
/// Tile position component for grid-based movement.
/// Discrete integer coordinates used in simulation; converted to f32
/// at the bridge boundary for VisibleEntity wire format.
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(
Component, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
pub struct TilePosition {
pub x: i32,
pub y: i32,
@@ -121,7 +136,7 @@ impl TilePosition {
}
/// Chunk coordinate for chunk-based map storage (D-012).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ChunkCoord {
pub cx: i32,
pub cy: i32,
@@ -166,14 +181,14 @@ impl ChunkData {
/// Unloaded chunks are treated as unwalkable.
#[derive(Resource, Debug, Clone)]
pub struct WalkabilityMap {
chunks: HashMap<ChunkCoord, ChunkData>,
chunks: BTreeMap<ChunkCoord, ChunkData>,
}
impl WalkabilityMap {
/// Create a walkability map covering a rectangular area with all tiles walkable.
/// Generates chunks to cover the specified dimensions on z-level 0..z_levels.
pub fn new(width: i32, height: i32, z_levels: i32) -> Self {
let mut chunks = HashMap::new();
let mut chunks = BTreeMap::new();
let cx_max = (width + CHUNK_SIZE - 1) / CHUNK_SIZE;
let cy_max = (height + CHUNK_SIZE - 1) / CHUNK_SIZE;
for z in 0..z_levels {
@@ -188,7 +203,7 @@ impl WalkabilityMap {
/// Create a walkability map covering a rectangular area with all tiles blocked.
pub fn new_blocked(width: i32, height: i32, z_levels: i32) -> Self {
let mut chunks = HashMap::new();
let mut chunks = BTreeMap::new();
let cx_max = (width + CHUNK_SIZE - 1) / CHUNK_SIZE;
let cy_max = (height + CHUNK_SIZE - 1) / CHUNK_SIZE;
for z in 0..z_levels {
@@ -263,6 +278,7 @@ pub struct MoveIntent {
/// entities without intents, then resolve movers in order — first valid claim
/// to a layer slot wins.
/// Always removes MoveIntent component after processing.
#[tracing::instrument(level = "debug", skip_all)]
pub fn validate_movement(
mut commands: Commands,
walkability: Option<Res<WalkabilityMap>>,
@@ -285,7 +301,7 @@ pub fn validate_movement(
// Collect layer slots occupied by stationary entities (no MoveIntent).
// Key: (position, layer) — two entities can share a tile if different layers.
let mut occupied: HashMap<(TilePosition, TilePresence), Entity> = HashMap::new();
let mut occupied: BTreeMap<(TilePosition, TilePresence), Entity> = BTreeMap::new();
for (entity, pos, presence) in stationary.iter() {
let layer = presence.copied().unwrap_or_default();
occupied.insert((*pos, layer), entity);