Skip to main content

flatland_protocol/
types.rs

1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4pub type EntityId = u64;
5pub type Tick = u64;
6pub type Seq = u32;
7pub type SessionId = u64;
8
9/// World position `(x, y, z, w, t)` — `t` reserved at 0.
10#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
11pub struct WorldCoord {
12    pub x: f32,
13    pub y: f32,
14    pub z: f32,
15    pub w: u32,
16    pub t: u32,
17}
18
19impl WorldCoord {
20    pub fn surface(x: f32, y: f32) -> Self {
21        Self {
22            x,
23            y,
24            z: 0.0,
25            w: 0,
26            t: 0,
27        }
28    }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
32pub struct Velocity2D {
33    pub vx: f32,
34    pub vy: f32,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
38pub struct Transform {
39    pub position: WorldCoord,
40    pub yaw: f32,
41    pub velocity: Velocity2D,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
45pub struct EntityState {
46    pub id: EntityId,
47    pub transform: Transform,
48}
49
50/// Client → server gameplay input (reliable, sequenced per entity).
51#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
52pub enum Intent {
53    Move {
54        entity_id: EntityId,
55        forward: f32,
56        strafe: f32,
57        seq: Seq,
58    },
59    Stop {
60        entity_id: EntityId,
61        seq: Seq,
62    },
63}
64
65/// Server → client AOI-filtered entity updates for one sim tick.
66#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67pub struct TickDelta {
68    pub tick: Tick,
69    pub entities: Vec<EntityState>,
70}
71
72/// Full state on region enter or reconnect.
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74pub struct Snapshot {
75    pub tick: Tick,
76    pub chunk_rev: u64,
77    pub entities: Vec<EntityState>,
78}
79
80/// Every on-wire payload is wrapped for versioning and codec uniformity.
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct Envelope<T> {
83    pub protocol_version: u16,
84    pub payload: T,
85}
86
87impl<T> Envelope<T> {
88    pub fn new(payload: T) -> Self {
89        Self {
90            protocol_version: crate::PROTOCOL_VERSION,
91            payload,
92        }
93    }
94}
95
96/// Session handshake after transport connect.
97#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98pub struct Hello {
99    pub client_name: String,
100    pub protocol_version: u16,
101    #[serde(default)]
102    pub auth: AuthCredential,
103    /// Required for session auth; embedded in `ApiToken` variant otherwise.
104    #[serde(default)]
105    pub character_id: Option<Uuid>,
106}
107
108/// How the client authenticates to the game gateway.
109/// Uses default serde enum encoding (postcard-compatible; not internally tagged).
110#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
111#[serde(rename_all = "snake_case")]
112pub enum AuthCredential {
113    DevLocal,
114    Session { token: String },
115    ApiToken { token: String, character_id: Uuid },
116}
117
118impl Default for AuthCredential {
119    fn default() -> Self {
120        Self::DevLocal
121    }
122}
123
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct Welcome {
126    pub session_id: SessionId,
127    pub entity_id: EntityId,
128    pub snapshot: Snapshot,
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132pub enum ServerMessage {
133    Welcome(Welcome),
134    Tick(TickDelta),
135    IntentAck { entity_id: EntityId, seq: Seq, tick: Tick },
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub enum ClientMessage {
140    Hello(Hello),
141    Intent(Intent),
142    Disconnect,
143}