aetheris_protocol/events.rs
1use serde::{Deserialize, Serialize};
2
3use crate::types::{ClientId, ComponentKind, NetworkId};
4
5/// An event representing a fragment of a larger message.
6/// Used for MTU stability to prevent packet drops and enable reassembly.
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct FragmentedEvent {
9 /// Unique identifier for the fragmented message.
10 pub message_id: u32,
11 /// The index of this fragment (0-based).
12 pub fragment_index: u16,
13 /// Total number of fragments for this message.
14 pub total_fragments: u16,
15 /// The raw payload of this fragment.
16 #[serde(with = "serde_bytes")]
17 pub payload: Vec<u8>,
18}
19
20/// An event representing a change to a single component on a single entity.
21/// Produced by `WorldState::extract_deltas()` on the server.
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct ReplicationEvent {
24 /// Which entity changed.
25 pub network_id: NetworkId,
26 /// Which component type changed.
27 pub component_kind: ComponentKind,
28 /// The serialized delta payload (only the changed fields).
29 /// In Phase 1, this is a full snapshot per component for simplicity.
30 #[serde(with = "serde_bytes")]
31 pub payload: Vec<u8>,
32 /// The server tick at which this change was recorded.
33 pub tick: u64,
34}
35
36/// An inbound update to be applied to the ECS.
37/// Produced by `Encoder::decode()` on the receiving end.
38#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
39pub struct ComponentUpdate {
40 /// The entity to update.
41 pub network_id: NetworkId,
42 /// Which component type to update.
43 pub component_kind: ComponentKind,
44 /// The deserialized field values.
45 #[serde(with = "serde_bytes")]
46 pub payload: Vec<u8>,
47 /// The tick this update originated from.
48 pub tick: u64,
49}
50
51/// Events produced by `GameTransport::poll_events()`.
52#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
53pub enum NetworkEvent {
54 /// A new client has connected and been assigned a `ClientId`.
55 ClientConnected(ClientId),
56 /// A client has disconnected (graceful or timeout).
57 ClientDisconnected(ClientId),
58 /// Raw unreliable data received from a client.
59 UnreliableMessage {
60 /// The client that sent the message.
61 client_id: ClientId,
62 /// The raw message bytes.
63 #[serde(with = "serde_bytes")]
64 data: Vec<u8>,
65 },
66 /// Raw reliable data received from a client.
67 ReliableMessage {
68 /// The client that sent the message.
69 client_id: ClientId,
70 /// The raw message bytes.
71 #[serde(with = "serde_bytes")]
72 data: Vec<u8>,
73 },
74 /// A heartbeat ping from a client.
75 Ping {
76 /// The client that sent the ping.
77 client_id: ClientId,
78 /// The client's tick/timestamp when the ping was sent.
79 tick: u64,
80 },
81 /// A heartbeat pong from the server.
82 Pong {
83 /// The original tick/timestamp from the ping.
84 tick: u64,
85 },
86 /// A session authentication request from the client.
87 Auth {
88 /// The session token obtained from the Control Plane.
89 session_token: String,
90 },
91 /// A WebTransport session was closed by the remote or due to error.
92 SessionClosed(ClientId),
93 /// A WebTransport stream was reset.
94 StreamReset(ClientId),
95 /// A fragment of a larger message.
96 Fragment {
97 /// The client that sent the fragment.
98 client_id: ClientId,
99 /// The fragment data.
100 fragment: FragmentedEvent,
101 },
102 /// A testing command to trigger a stress test (Phase 1/Playground only).
103 StressTest {
104 /// The client that requested the stress test.
105 client_id: ClientId,
106 /// Number of entities to spawn.
107 count: u16,
108 /// Whether spawned entities should rotate.
109 rotate: bool,
110 },
111 /// A testing command to spawn a specific entity (Phase 1/Playground only).
112 Spawn {
113 /// The client that requested the spawn.
114 client_id: ClientId,
115 /// Which entity type to spawn.
116 entity_type: u16,
117 /// Position X
118 x: f32,
119 /// Position Y
120 y: f32,
121 /// Initial rotation
122 rot: f32,
123 },
124 /// A command to clear all entities from the world (Phase 1/Playground only).
125 ClearWorld {
126 /// The client that requested the clear.
127 client_id: ClientId,
128 },
129}
130
131/// A restricted view of `NetworkEvent` for over-the-wire transport.
132/// Prevents local-only variants (like `ClientConnected`) from being sent/received.
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub enum WireEvent {
135 /// A heartbeat ping.
136 Ping {
137 /// The client's tick/timestamp when the ping was sent.
138 tick: u64,
139 },
140 /// A heartbeat pong.
141 Pong {
142 /// The original tick/timestamp from the ping.
143 tick: u64,
144 },
145 /// A session authentication request.
146 Auth {
147 /// The session token.
148 session_token: String,
149 },
150 /// A fragment of a larger message.
151 Fragment(FragmentedEvent),
152 /// A testing command to trigger a stress test.
153 StressTest {
154 /// Number of entities to spawn.
155 count: u16,
156 /// Whether spawned entities should rotate.
157 rotate: bool,
158 },
159 /// A testing command to spawn a specific entity.
160 Spawn {
161 /// Which entity type to spawn.
162 entity_type: u16,
163 /// Position X
164 x: f32,
165 /// Position Y
166 y: f32,
167 /// Initial rotation
168 rot: f32,
169 },
170 /// A command to clear all entities from the world.
171 ClearWorld,
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn test_event_sizes_and_derives() {
180 let ev = NetworkEvent::ClientConnected(ClientId(1));
181 assert_eq!(ev, NetworkEvent::ClientConnected(ClientId(1)));
182
183 let re = ReplicationEvent {
184 network_id: NetworkId(1),
185 component_kind: ComponentKind(0),
186 payload: vec![1, 2, 3],
187 tick: 0,
188 };
189 assert_eq!(re.payload.len(), 3);
190 assert_eq!(
191 re,
192 ReplicationEvent {
193 network_id: NetworkId(1),
194 component_kind: ComponentKind(0),
195 payload: vec![1, 2, 3],
196 tick: 0,
197 }
198 );
199 }
200}