Skip to main content

openrtc_netcode_core/
lib.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4pub const NETCODE_PROTOCOL: &str = "openrtc-netcode";
5pub const NETCODE_PROTOCOL_VERSION: u8 = 1;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9pub enum EnvelopeKind {
10    Hello,
11    Peer,
12    User,
13    #[serde(rename = "state.snapshot")]
14    StateSnapshot,
15    #[serde(rename = "state.patch")]
16    StatePatch,
17    #[serde(rename = "lobby.meta")]
18    LobbyMeta,
19    #[serde(rename = "member.meta")]
20    MemberMeta,
21    Ping,
22    Pong,
23}
24
25#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct NetcodeEnvelope<T = Value> {
27    pub protocol: String,
28    pub v: u8,
29    pub kind: EnvelopeKind,
30    #[serde(rename = "lobbyId")]
31    pub lobby_id: String,
32    pub from: String,
33    pub seq: u64,
34    #[serde(rename = "sentAt")]
35    pub sent_at: u64,
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub target: Option<String>,
38    pub body: T,
39}
40
41impl<T> NetcodeEnvelope<T> {
42    pub fn new(
43        kind: EnvelopeKind,
44        lobby_id: impl Into<String>,
45        from: impl Into<String>,
46        seq: u64,
47        sent_at: u64,
48        body: T,
49    ) -> Self {
50        Self {
51            protocol: NETCODE_PROTOCOL.to_string(),
52            v: NETCODE_PROTOCOL_VERSION,
53            kind,
54            lobby_id: lobby_id.into(),
55            from: from.into(),
56            seq,
57            sent_at,
58            target: None,
59            body,
60        }
61    }
62
63    pub fn with_target(mut self, target: impl Into<String>) -> Self {
64        self.target = Some(target.into());
65        self
66    }
67
68    pub fn validate(&self) -> Result<(), NetcodeProtocolError> {
69        if self.protocol != NETCODE_PROTOCOL {
70            return Err(NetcodeProtocolError::ProtocolMismatch);
71        }
72        if self.v != NETCODE_PROTOCOL_VERSION {
73            return Err(NetcodeProtocolError::VersionMismatch(self.v));
74        }
75        if self.lobby_id.is_empty() {
76            return Err(NetcodeProtocolError::MissingLobbyId);
77        }
78        if self.from.is_empty() {
79            return Err(NetcodeProtocolError::MissingSender);
80        }
81        Ok(())
82    }
83}
84
85#[derive(Debug, Clone, PartialEq, Eq)]
86pub enum NetcodeProtocolError {
87    ProtocolMismatch,
88    VersionMismatch(u8),
89    MissingLobbyId,
90    MissingSender,
91}
92
93impl std::fmt::Display for NetcodeProtocolError {
94    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        match self {
96            Self::ProtocolMismatch => write!(formatter, "netcode protocol mismatch"),
97            Self::VersionMismatch(version) => {
98                write!(formatter, "unsupported netcode protocol version {version}")
99            }
100            Self::MissingLobbyId => write!(formatter, "netcode envelope missing lobby id"),
101            Self::MissingSender => write!(formatter, "netcode envelope missing sender"),
102        }
103    }
104}
105
106impl std::error::Error for NetcodeProtocolError {}
107
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct HelloBody {
110    #[serde(rename = "peerId")]
111    pub peer_id: String,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    #[serde(rename = "displayName")]
114    pub display_name: Option<String>,
115}
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118pub struct UserMessageBody {
119    #[serde(rename = "type")]
120    pub message_type: String,
121    pub payload: Value,
122}
123
124#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125pub struct StateSnapshotBody {
126    pub values: std::collections::BTreeMap<String, Value>,
127    pub owners: std::collections::BTreeMap<String, String>,
128}
129
130#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
131pub struct StatePatchBody {
132    pub key: String,
133    pub value: Value,
134    pub owner: String,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct MetadataBody {
139    pub values: std::collections::BTreeMap<String, String>,
140}
141
142pub fn encode_envelope<T: Serialize>(envelope: &NetcodeEnvelope<T>) -> serde_json::Result<String> {
143    serde_json::to_string(envelope)
144}
145
146pub fn decode_envelope(json: &str) -> serde_json::Result<NetcodeEnvelope<Value>> {
147    serde_json::from_str(json)
148}