Skip to main content

aetheris_client_wasm/
world_state.rs

1//! Client-side world state and interpolation buffer.
2//!
3//! This module implements the `WorldState` trait for the WASM client,
4//! providing the foundation for the two-tick interpolation buffer.
5
6use crate::shared_world::SabSlot;
7use aetheris_protocol::error::WorldError;
8use aetheris_protocol::events::{ComponentUpdate, ReplicationEvent};
9use aetheris_protocol::traits::WorldState;
10use aetheris_protocol::types::{ClientId, ComponentKind, LocalId, NetworkId, Transform};
11use std::collections::HashMap;
12
13/// A simplified client-side world that tracks entity states using `SabSlot`.
14#[derive(Debug)]
15pub struct ClientWorld {
16    /// Map of `NetworkId` to the last known authoritative state.
17    pub entities: HashMap<NetworkId, SabSlot>,
18    /// The latest tick received from the server.
19    pub latest_tick: u64,
20}
21
22impl Default for ClientWorld {
23    fn default() -> Self {
24        Self::new()
25    }
26}
27
28impl ClientWorld {
29    /// Creates a new, empty `ClientWorld`.
30    #[must_use]
31    pub fn new() -> Self {
32        Self {
33            entities: HashMap::new(),
34            latest_tick: 0,
35        }
36    }
37}
38
39impl WorldState for ClientWorld {
40    fn get_local_id(&self, network_id: NetworkId) -> Option<LocalId> {
41        Some(LocalId(network_id.0))
42    }
43
44    fn get_network_id(&self, local_id: LocalId) -> Option<NetworkId> {
45        Some(NetworkId(local_id.0))
46    }
47
48    fn extract_deltas(&mut self) -> Vec<ReplicationEvent> {
49        Vec::new()
50    }
51
52    fn apply_updates(&mut self, updates: &[(ClientId, ComponentUpdate)]) {
53        for (_, update) in updates {
54            if update.tick > self.latest_tick {
55                self.latest_tick = update.tick;
56            }
57
58            // ComponentKind(1) == Transform
59            if update.component_kind == ComponentKind(1)
60                && let Ok(transform) = rmp_serde::from_slice::<Transform>(&update.payload)
61            {
62                let entry = self.entities.entry(update.network_id).or_insert(SabSlot {
63                    network_id: update.network_id.0,
64                    x: transform.x,
65                    y: transform.y,
66                    z: transform.z,
67                    rotation: transform.rotation,
68                    dx: 0.0,
69                    dy: 0.0,
70                    dz: 0.0,
71                    hp: 100,
72                    shield: 0,
73                    entity_type: transform.entity_type,
74                    flags: 1, // ALIVE
75                    padding: [0; 5],
76                });
77
78                entry.x = transform.x;
79                entry.y = transform.y;
80                entry.z = transform.z;
81                entry.rotation = transform.rotation;
82                entry.entity_type = transform.entity_type;
83            }
84        }
85    }
86
87    fn simulate(&mut self) {}
88
89    fn spawn_networked(&mut self) -> NetworkId {
90        NetworkId(0)
91    }
92
93    fn spawn_networked_for(&mut self, _client_id: ClientId) -> NetworkId {
94        self.spawn_networked()
95    }
96
97    fn despawn_networked(&mut self, network_id: NetworkId) -> Result<(), WorldError> {
98        self.entities
99            .remove(&network_id)
100            .map(|_| ())
101            .ok_or(WorldError::EntityNotFound(network_id))
102    }
103
104    fn stress_test(&mut self, _count: u16, _rotate: bool) {}
105
106    fn spawn_kind(&mut self, _kind: u16, _x: f32, _y: f32, _rot: f32) -> NetworkId {
107        NetworkId(1)
108    }
109
110    fn clear_world(&mut self) {
111        self.entities.clear();
112    }
113}