1use crate::host_to_client::TickIdUtil;
6use crate::serialize::CombinedSteps;
7use crate::{ClientRequestId, SessionConnectionSecret, Version};
8use flood_rs::{Deserialize, ReadOctetStream, Serialize, WriteOctetStream};
9use nimble_blob_stream::prelude::ReceiverToSenderFrontCommands;
10use nimble_participant::ParticipantId;
11use std::fmt::{Debug, Display};
12use std::{fmt, io};
13use tick_id::TickId;
14
15#[repr(u8)]
16enum ClientToHostCommand {
17 JoinGame = 0x01,
18 Steps = 0x02,
19 DownloadGameState = 0x03,
20 BlobStreamChannel = 0x04,
21 Connect = 0x05,
22 Ping = 0x06,
23}
24
25impl TryFrom<u8> for ClientToHostCommand {
26 type Error = io::Error;
27
28 fn try_from(value: u8) -> io::Result<Self> {
29 Ok(match value {
30 0x01 => Self::JoinGame,
31 0x02 => Self::Steps,
32 0x03 => Self::DownloadGameState,
33 0x04 => Self::BlobStreamChannel,
34 0x05 => Self::Connect,
35 0x06 => Self::Ping,
36 _ => Err(io::Error::new(
37 io::ErrorKind::InvalidData,
38 format!("Unknown ClientToHostCommand {value}"),
39 ))?,
40 })
41 }
42}
43
44#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45pub struct ConnectRequest {
46 pub nimble_version: Version,
47 pub use_debug_stream: bool,
48 pub application_version: Version,
49 pub client_request_id: ClientRequestId,
50}
51impl ConnectRequest {
52 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
56 self.nimble_version.to_stream(stream)?;
57 stream.write_u8(u8::from(self.use_debug_stream))?;
58 self.application_version.to_stream(stream)?;
59 self.client_request_id.serialize(stream)?;
60 Ok(())
61 }
62
63 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
67 Ok(Self {
68 nimble_version: Version::from_stream(stream)?,
69 use_debug_stream: stream.read_u8()? != 0,
70 application_version: Version::from_stream(stream)?,
71 client_request_id: ClientRequestId::deserialize(stream)?,
72 })
73 }
74}
75
76#[derive(Clone, Debug, Eq, PartialEq)]
77pub struct DownloadGameStateRequest {
78 pub request_id: u8,
79}
80
81impl DownloadGameStateRequest {
82 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
86 stream.write_u8(self.request_id)
87 }
88
89 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
93 Ok(Self {
94 request_id: stream.read_u8()?,
95 })
96 }
97}
98
99#[derive(Debug, Clone)]
100#[allow(clippy::module_name_repetitions)] pub enum ClientToHostCommands<StepT: Clone + Debug + Serialize + Deserialize + Display> {
102 JoinGameType(JoinGameRequest),
103 Steps(StepsRequest<StepT>),
104 DownloadGameState(DownloadGameStateRequest),
105 BlobStreamChannel(ReceiverToSenderFrontCommands),
106 ConnectType(ConnectRequest),
107 Ping(u16),
108}
109
110impl<StepT: Clone + Debug + Serialize + Deserialize + Display> Serialize
111 for ClientToHostCommands<StepT>
112{
113 fn serialize(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
114 stream.write_u8(self.into())?;
115 match self {
116 Self::Steps(predicted_steps_and_ack) => predicted_steps_and_ack.to_stream(stream),
117 Self::JoinGameType(join_game_request) => join_game_request.to_stream(stream),
118 Self::DownloadGameState(download_game_state) => download_game_state.to_stream(stream),
119 Self::BlobStreamChannel(blob_stream_command) => blob_stream_command.to_stream(stream),
120 Self::ConnectType(connect_request) => connect_request.to_stream(stream),
121 Self::Ping(ping_time) => stream.write_u16(*ping_time),
122 }
123 }
124}
125
126impl<StepT: Clone + Debug + Serialize + Deserialize + Display> Deserialize
127 for ClientToHostCommands<StepT>
128{
129 fn deserialize(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
130 let command_value = stream.read_u8()?;
131 let command = ClientToHostCommand::try_from(command_value)?;
132 let x = match command {
133 ClientToHostCommand::JoinGame => {
134 Self::JoinGameType(JoinGameRequest::from_stream(stream)?)
135 }
136 ClientToHostCommand::Steps => Self::Steps(StepsRequest::from_stream(stream)?),
137 ClientToHostCommand::DownloadGameState => {
138 Self::DownloadGameState(DownloadGameStateRequest::from_stream(stream)?)
139 }
140 ClientToHostCommand::BlobStreamChannel => {
141 Self::BlobStreamChannel(ReceiverToSenderFrontCommands::from_stream(stream)?)
142 }
143 ClientToHostCommand::Connect => Self::ConnectType(ConnectRequest::from_stream(stream)?),
144 ClientToHostCommand::Ping => Self::Ping(stream.read_u16()?),
145 };
146 Ok(x)
147 }
148}
149
150impl<StepT: Deserialize + Serialize + Debug + Display + Clone> From<&ClientToHostCommands<StepT>>
151 for u8
152{
153 fn from(command: &ClientToHostCommands<StepT>) -> Self {
154 match command {
155 ClientToHostCommands::Steps(_) => ClientToHostCommand::Steps as Self,
156 ClientToHostCommands::JoinGameType(_) => ClientToHostCommand::JoinGame as Self,
157 ClientToHostCommands::DownloadGameState(_) => {
158 ClientToHostCommand::DownloadGameState as Self
159 }
160 ClientToHostCommands::BlobStreamChannel(_) => {
161 ClientToHostCommand::BlobStreamChannel as Self
162 }
163 ClientToHostCommands::ConnectType(_) => ClientToHostCommand::Connect as Self,
164 ClientToHostCommands::Ping(_) => ClientToHostCommand::Ping as Self,
165 }
166 }
167}
168
169impl<StepT: Clone + Debug + Eq + PartialEq + Serialize + Deserialize + Display> Display
170 for ClientToHostCommands<StepT>
171{
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 match self {
174 Self::JoinGameType(join) => write!(f, "join {join:?}"),
175 Self::Steps(predicted_steps_and_ack) => {
176 write!(f, "steps {predicted_steps_and_ack}")
177 }
178 Self::DownloadGameState(download_game_state) => {
179 write!(f, "download game state {download_game_state:?}")
180 }
181 Self::BlobStreamChannel(blob_command) => {
182 write!(f, "blob stream channel {blob_command:?}")
183 }
184 &Self::ConnectType(connect_request) => write!(f, "connect {connect_request:?}"),
185 Self::Ping(_) => write!(f, "ping"),
186 }
187 }
188}
189
190#[repr(u8)]
193pub enum JoinGameTypeValue {
194 NoSecret,
195 SessionSecret,
196 HostMigrationParticipantId,
197}
198
199impl JoinGameTypeValue {
200 #[must_use]
201 pub const fn to_octet(&self) -> u8 {
202 match self {
203 Self::NoSecret => Self::NoSecret as u8,
204 Self::SessionSecret => Self::SessionSecret as u8,
205 Self::HostMigrationParticipantId => Self::HostMigrationParticipantId as u8,
206 }
207 }
208 pub fn to_stream(self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
212 stream.write_u8(self.to_octet())?;
213 Ok(())
214 }
215
216 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
220 let join_game_type_value_raw = stream.read_u8()?;
221 Self::try_from(join_game_type_value_raw)
222 }
223}
224
225#[derive(Debug, PartialEq, Eq, Clone, Copy)]
226pub enum JoinGameType {
227 NoSecret,
228 UseSessionSecret(SessionConnectionSecret),
229 HostMigrationParticipantId(ParticipantId),
230}
231
232impl TryFrom<u8> for JoinGameTypeValue {
233 type Error = io::Error;
234
235 fn try_from(value: u8) -> io::Result<Self> {
236 Ok(match value {
237 0x00 => Self::NoSecret,
238 0x01 => Self::SessionSecret,
239 0x02 => Self::HostMigrationParticipantId,
240 _ => Err(io::Error::new(
241 io::ErrorKind::InvalidData,
242 format!("Unknown join game type {value}"),
243 ))?,
244 })
245 }
246}
247
248impl JoinGameType {
249 #[must_use]
250 pub const fn to_octet(&self) -> u8 {
251 match self {
252 Self::NoSecret => JoinGameTypeValue::NoSecret as u8,
253 Self::UseSessionSecret(_) => JoinGameTypeValue::SessionSecret as u8,
254 Self::HostMigrationParticipantId(_) => {
255 JoinGameTypeValue::HostMigrationParticipantId as u8
256 }
257 }
258 }
259
260 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
264 stream.write_u8(self.to_octet())?;
265 match self {
266 Self::NoSecret => {}
267 Self::UseSessionSecret(session_secret) => session_secret.to_stream(stream)?,
268 Self::HostMigrationParticipantId(participant_id) => participant_id.serialize(stream)?,
269 }
270 Ok(())
271 }
272
273 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
277 let join_game_type_value_raw = stream.read_u8()?;
278 let value = JoinGameTypeValue::try_from(join_game_type_value_raw)?;
279 let join_game_type = match value {
280 JoinGameTypeValue::NoSecret => Self::NoSecret,
281 JoinGameTypeValue::SessionSecret => {
282 Self::UseSessionSecret(SessionConnectionSecret::from_stream(stream)?)
283 }
284 JoinGameTypeValue::HostMigrationParticipantId => {
285 Self::HostMigrationParticipantId(ParticipantId::deserialize(stream)?)
286 }
287 };
288 Ok(join_game_type)
289 }
290}
291
292#[derive(Debug, Copy, Clone, PartialEq, Eq)]
293pub struct JoinPlayerRequest {
294 pub local_index: u8,
295}
296
297impl JoinPlayerRequest {
298 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
302 stream.write_u8(self.local_index)
303 }
304
305 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
309 Ok(Self {
310 local_index: stream.read_u8()?,
311 })
312 }
313}
314
315#[derive(Debug, PartialEq, Eq, Clone)]
316pub struct JoinPlayerRequests {
317 pub players: Vec<JoinPlayerRequest>,
318}
319
320impl JoinPlayerRequests {
321 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
325 stream.write_u8(u8::try_from(self.players.len()).map_err(|err| {
326 io::Error::new(
327 io::ErrorKind::InvalidData,
328 format!("too many players {err}"),
329 )
330 })?)?;
331 for player in &self.players {
332 player.to_stream(stream)?;
333 }
334 Ok(())
335 }
336
337 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
341 let count = stream.read_u8()?;
342 let mut vec = Vec::<JoinPlayerRequest>::with_capacity(count as usize);
343 for _ in 0..count {
344 vec.push(JoinPlayerRequest::from_stream(stream)?);
345 }
346
347 Ok(Self { players: vec })
348 }
349}
350
351#[derive(Debug, PartialEq, Eq, Clone)]
352pub struct JoinGameRequest {
353 pub client_request_id: ClientRequestId,
354 pub join_game_type: JoinGameType,
355 pub player_requests: JoinPlayerRequests,
356}
357
358impl JoinGameRequest {
359 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
363 self.client_request_id.serialize(stream)?;
364 self.join_game_type.to_stream(stream)?;
365 self.player_requests.to_stream(stream)?;
367 Ok(())
368 }
369
370 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
374 Ok(Self {
375 client_request_id: ClientRequestId::deserialize(stream)?,
376 join_game_type: JoinGameType::from_stream(stream)?,
377 player_requests: JoinPlayerRequests::from_stream(stream)?,
378 })
379 }
380}
381
382#[derive(Debug, PartialEq, Eq, Clone)]
383pub struct StepsAck {
384 pub waiting_for_tick_id: TickId,
385}
386
387impl Display for StepsAck {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 write!(f, "waiting:{}", self.waiting_for_tick_id)
390 }
391}
392
393impl StepsAck {
394 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
398 TickIdUtil::to_stream(self.waiting_for_tick_id, stream)?;
399 Ok(())
400 }
401
402 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
406 Ok(Self {
407 waiting_for_tick_id: TickIdUtil::from_stream(stream)?,
408 })
409 }
410}
411
412#[derive(Debug, Clone)]
413pub struct StepsRequest<StepT: Clone + Serialize + Deserialize + Debug + Display> {
414 pub ack: StepsAck,
415 pub combined_predicted_steps: CombinedSteps<StepT>,
416}
417
418impl<StepT: Clone + Serialize + Deserialize + Debug + Display> Display for StepsRequest<StepT> {
419 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420 write!(
421 f,
422 "steps-request ack:{}, steps:{}",
423 self.ack, self.combined_predicted_steps
424 )
425 }
426}
427
428impl<StepT: Clone + Serialize + Deserialize + Debug + Display> StepsRequest<StepT> {
429 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
433 self.ack.to_stream(stream)?;
434
435 self.combined_predicted_steps.serialize(stream)?;
436 Ok(())
437 }
438
439 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
443 Ok(Self {
444 ack: StepsAck::from_stream(stream)?,
445 combined_predicted_steps: CombinedSteps::deserialize(stream)?,
446 })
447 }
448}