1use crate::serialize::{
7 CombinedSteps, InternalAllParticipantVectors, InternalAuthoritativeStepRange,
8};
9use crate::{ClientRequestId, SessionConnectionSecret};
10use flood_rs::{Deserialize, ReadOctetStream, Serialize, WriteOctetStream};
11use log::trace;
12use nimble_blob_stream::prelude::SenderToReceiverFrontCommands;
13use nimble_participant::ParticipantId;
14use std::fmt::{Debug, Display, Formatter};
15use std::io;
16use tick_id::TickId;
17
18#[repr(u8)]
19#[allow(clippy::module_name_repetitions)] pub enum HostToClientCommand {
21 GameStep = 0x08,
22 JoinGame = 0x09,
23 DownloadGameState = 0x0B,
24 BlobStreamChannel = 0x0C,
25 Connect = 0x0D,
26 Pong = 0x0E,
27}
28
29impl TryFrom<u8> for HostToClientCommand {
30 type Error = io::Error;
31
32 fn try_from(value: u8) -> io::Result<Self> {
33 Ok(match value {
34 0x09 => Self::JoinGame,
35 0x08 => Self::GameStep,
36 0x0B => Self::DownloadGameState,
37 0x0C => Self::BlobStreamChannel,
38 0x0D => Self::Connect,
39 0x0E => Self::Pong,
40 _ => Err(io::Error::new(
41 io::ErrorKind::InvalidData,
42 format!("Unknown host to client command 0x{value:0X}"),
43 ))?,
44 })
45 }
46}
47
48#[derive(Debug, Eq, PartialEq, Copy, Clone)]
49pub struct TickIdUtil;
50
51impl TickIdUtil {
52 pub fn to_stream(tick_id: TickId, stream: &mut impl WriteOctetStream) -> io::Result<()> {
56 stream.write_u32(tick_id.0)
57 }
58
59 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<TickId> {
63 Ok(TickId(stream.read_u32()?))
64 }
65}
66
67#[derive(Debug, PartialEq, Eq)]
68pub struct DownloadGameStateResponse {
69 pub client_request: u8,
70 pub tick_id: TickId,
71 pub blob_stream_channel: u16,
72}
73
74impl Display for DownloadGameStateResponse {
75 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
76 write!(
77 f,
78 "download game state response {} {} {}",
79 self.client_request, self.tick_id, self.blob_stream_channel
80 )
81 }
82}
83
84impl DownloadGameStateResponse {
85 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
89 stream.write_u8(self.client_request)?;
90 TickIdUtil::to_stream(self.tick_id, stream)?;
91 stream.write_u16(self.blob_stream_channel)
92 }
93
94 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
98 Ok(Self {
99 client_request: stream.read_u8()?,
100 tick_id: TickIdUtil::from_stream(stream)?,
101 blob_stream_channel: stream.read_u16()?,
102 })
103 }
104}
105
106#[derive(Debug, PartialEq, Eq)]
107pub struct GameStatePart {
108 pub blob_stream_command: SenderToReceiverFrontCommands,
109}
110
111#[derive(Debug)]
112pub struct ConnectResponse {
113 pub flags: u8,
114 pub client_request_id: ClientRequestId,
115}
116
117impl ConnectResponse {
118 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
122 stream.write_u8(self.flags)?;
123 stream.write_u8(self.client_request_id.0)?;
124 Ok(())
125 }
126
127 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
131 Ok(Self {
132 flags: stream.read_u8()?,
133 client_request_id: ClientRequestId(stream.read_u8()?),
134 })
135 }
136}
137
138#[derive(Debug, PartialEq, Eq)]
139pub struct ConnectionAccepted {
140 pub flags: u8,
141 pub response_to_request: ClientRequestId,
142}
143
144impl Display for ConnectionAccepted {
145 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
146 write!(
147 f,
148 "connection accepted {} {}",
149 self.flags, self.response_to_request
150 )
151 }
152}
153
154impl ConnectionAccepted {
155 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
159 stream.write_u8(self.flags)?;
160 self.response_to_request.serialize(stream)?;
161 Ok(())
162 }
163
164 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
168 Ok(Self {
169 flags: stream.read_u8()?,
170 response_to_request: ClientRequestId::deserialize(stream)?,
171 })
172 }
173}
174
175#[derive(Debug)]
176pub struct PongInfo {
177 pub lower_millis: u16,
178}
179
180impl Serialize for PongInfo {
181 fn serialize(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
182 stream.write_u16(self.lower_millis)
183 }
184}
185
186impl Deserialize for PongInfo {
187 fn deserialize(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
188 Ok(Self {
189 lower_millis: stream.read_u16()?,
190 })
191 }
192}
193
194#[derive(Debug)]
195#[allow(clippy::module_name_repetitions)] pub enum HostToClientCommands<StepT: Deserialize + Serialize + Debug + Clone + Display> {
197 JoinGame(JoinGameAccepted),
198 GameStep(GameStepResponse<StepT>),
199 DownloadGameState(DownloadGameStateResponse),
200 BlobStreamChannel(SenderToReceiverFrontCommands),
201 ConnectType(ConnectionAccepted),
202 Pong(PongInfo),
203}
204
205impl<StepT: Clone + Debug + Serialize + Deserialize + Display> Serialize
206 for HostToClientCommands<StepT>
207{
208 fn serialize(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
209 stream.write_u8(self.into())?;
210 match self {
211 Self::JoinGame(join_game_response) => join_game_response.to_stream(stream),
212 Self::GameStep(game_step_response) => game_step_response.to_stream(stream),
213 Self::DownloadGameState(download_game_state_response) => {
214 download_game_state_response.to_stream(stream)
215 }
216 Self::BlobStreamChannel(blob_stream_command) => blob_stream_command.to_stream(stream),
217 Self::ConnectType(connect_response) => connect_response.to_stream(stream),
218 Self::Pong(pong_info) => pong_info.serialize(stream),
219 }
220 }
221}
222
223impl<StepT: Clone + Debug + Serialize + Deserialize + Display> Display
224 for HostToClientCommands<StepT>
225{
226 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
227 match self {
228 Self::JoinGame(join_game_response) => {
229 write!(f, "JoinGameResponse({join_game_response})")
230 }
231 Self::GameStep(game_step_response) => {
232 write!(f, "GameStepResponse({game_step_response})")
233 }
234 Self::DownloadGameState(download_game_state_response) => {
235 write!(f, "DownloadGameState({download_game_state_response})")
236 }
237 Self::BlobStreamChannel(blob_stream_command) => {
238 write!(f, "BlobStreamChannel({blob_stream_command})")
239 }
240 Self::ConnectType(connect_response) => {
241 write!(f, "ConnectResponse({connect_response})")
242 }
243 Self::Pong(pong_info) => write!(f, "Pong({pong_info:?})"),
244 }
245 }
246}
247
248impl<StepT: Clone + Debug + Serialize + Deserialize + Display> Deserialize
249 for HostToClientCommands<StepT>
250{
251 fn deserialize(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
252 let command_value = stream.read_u8()?;
253 let command = HostToClientCommand::try_from(command_value)?;
254 Ok(match command {
255 HostToClientCommand::JoinGame => Self::JoinGame(JoinGameAccepted::from_stream(stream)?),
256 HostToClientCommand::GameStep => Self::GameStep(GameStepResponse::from_stream(stream)?),
257 HostToClientCommand::DownloadGameState => {
258 Self::DownloadGameState(DownloadGameStateResponse::from_stream(stream)?)
259 }
260 HostToClientCommand::BlobStreamChannel => {
261 Self::BlobStreamChannel(SenderToReceiverFrontCommands::from_stream(stream)?)
262 }
263 HostToClientCommand::Connect => {
264 Self::ConnectType(ConnectionAccepted::from_stream(stream)?)
265 }
266 HostToClientCommand::Pong => Self::Pong(PongInfo::deserialize(stream)?),
267 })
268 }
269}
270
271impl<StepT: Deserialize + Serialize + Debug + Display + Clone> From<&HostToClientCommands<StepT>>
272 for u8
273{
274 fn from(command: &HostToClientCommands<StepT>) -> Self {
275 match command {
276 HostToClientCommands::JoinGame(_) => HostToClientCommand::JoinGame as Self,
277 HostToClientCommands::GameStep(_) => HostToClientCommand::GameStep as Self,
278 HostToClientCommands::DownloadGameState(_) => {
279 HostToClientCommand::DownloadGameState as Self
280 }
281 HostToClientCommands::BlobStreamChannel(_) => {
282 HostToClientCommand::BlobStreamChannel as Self
283 }
284 HostToClientCommands::ConnectType(_) => HostToClientCommand::Connect as Self,
285 HostToClientCommands::Pong(_) => HostToClientCommand::Pong as Self,
286 }
287 }
288}
289
290#[derive(Debug, PartialEq, Eq)]
291pub struct PartyAndSessionSecret {
292 pub session_secret: SessionConnectionSecret,
293 pub party_id: u8,
294}
295
296impl PartyAndSessionSecret {
297 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
301 self.session_secret.to_stream(stream)?;
302 stream.write_u8(self.party_id)
303 }
304
305 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
309 Ok(Self {
310 session_secret: SessionConnectionSecret::from_stream(stream)?,
311 party_id: stream.read_u8()?,
312 })
313 }
314}
315
316#[derive(Debug, PartialEq, Eq)]
317pub struct JoinGameParticipant {
318 pub local_index: u8,
319 pub participant_id: ParticipantId,
320}
321
322impl JoinGameParticipant {
323 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
327 stream.write_u8(self.local_index)?;
328 self.participant_id.serialize(stream)?;
329 Ok(())
330 }
331
332 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
336 Ok(Self {
337 local_index: stream.read_u8()?,
338 participant_id: ParticipantId::deserialize(stream)?,
339 })
340 }
341}
342
343#[derive(Debug)]
344pub struct JoinGameParticipants(pub Vec<JoinGameParticipant>);
345
346impl JoinGameParticipants {
347 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
351 stream.write_u8(self.0.len() as u8)?;
352 for join_game_participant in &self.0 {
353 join_game_participant.to_stream(stream)?;
354 }
355 Ok(())
356 }
357
358 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
362 let count = stream.read_u8()?;
363 let mut vec = Vec::<JoinGameParticipant>::with_capacity(count as usize);
364 for _ in 0..count {
365 vec.push(JoinGameParticipant::from_stream(stream)?);
366 }
367
368 Ok(Self(vec))
369 }
370}
371
372#[derive(Debug)]
373pub struct JoinGameAccepted {
374 pub client_request_id: ClientRequestId,
375 pub party_and_session_secret: PartyAndSessionSecret,
376 pub participants: JoinGameParticipants,
377}
378
379impl Display for JoinGameAccepted {
380 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
381 write!(
382 f,
383 "JoinGameAccepted {} {:?} {:?}",
384 self.client_request_id, self.party_and_session_secret, self.participants
385 )
386 }
387}
388
389impl JoinGameAccepted {
390 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
394 self.client_request_id.serialize(stream)?;
395 self.party_and_session_secret.to_stream(stream)?;
396 self.participants.to_stream(stream)
397 }
398
399 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
403 Ok(Self {
404 client_request_id: ClientRequestId::deserialize(stream)?,
405 party_and_session_secret: PartyAndSessionSecret::from_stream(stream)?,
406 participants: JoinGameParticipants::from_stream(stream)?,
407 })
408 }
409}
410
411#[derive(Debug, PartialEq, Eq)]
412pub struct GameStepResponseHeader {
413 pub connection_buffer_count: u8,
414 pub delta_buffer: i8,
415 pub next_expected_tick_id: TickId,
416}
417
418impl Display for GameStepResponseHeader {
419 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
420 write!(
421 f,
422 "game_step_response: count:{} expected:{} delta-buf:{}",
423 self.connection_buffer_count, self.next_expected_tick_id, self.delta_buffer
424 )
425 }
426}
427
428impl GameStepResponseHeader {
429 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
433 stream.write_u8(self.connection_buffer_count)?;
434 stream.write_i8(self.delta_buffer)?;
435 TickIdUtil::to_stream(self.next_expected_tick_id, stream)
436 }
437
438 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
442 Ok(Self {
443 connection_buffer_count: stream.read_u8()?,
444 delta_buffer: stream.read_i8()?,
445 next_expected_tick_id: TickIdUtil::from_stream(stream)?,
446 })
447 }
448}
449
450impl<StepT: Deserialize + Serialize + Debug + Clone + Display>
451 InternalAuthoritativeStepRange<StepT>
452{
453 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
457 stream.write_u8(self.delta_tick_id_from_previous)?;
458
459 self.authoritative_steps.serialize(stream)?;
460
461 Ok(())
462 }
463
464 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
468 let delta_steps = stream.read_u8()?;
469
470 let authoritative_combined_step = InternalAllParticipantVectors::deserialize(stream)?;
471
472 Ok(Self {
473 delta_tick_id_from_previous: delta_steps,
474 authoritative_steps: authoritative_combined_step,
475 })
476 }
477}
478
479#[derive(Debug)]
480pub struct AuthoritativeStepRanges<StepT: Deserialize + Serialize + Debug + Clone + Display> {
481 pub ranges: Vec<CombinedSteps<StepT>>,
482}
483
484impl<StepT: Deserialize + Serialize + Debug + Clone + Display> Display
485 for AuthoritativeStepRanges<StepT>
486{
487 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
488 write!(f, "auth_steps range-count:{} ranges:", self.ranges.len())?;
489
490 for range in &self.ranges {
491 write!(f, "\n{range}")?;
492 }
493
494 Ok(())
495 }
496}
497
498impl<StepT: Deserialize + Serialize + Debug + Clone + Display> Serialize
499 for AuthoritativeStepRanges<StepT>
500{
501 fn serialize(&self, stream: &mut impl WriteOctetStream) -> io::Result<()>
502 where
503 Self: Sized,
504 {
505 let mut converted_ranges = Vec::new();
506
507 let root_tick_id = if self.ranges.is_empty() {
508 TickId(0)
509 } else {
510 self.ranges[0].tick_id
511 };
512 let mut tick_id = root_tick_id;
513 for auth_range in &self.ranges {
514 let delta_ticks_from_previous = u8::try_from(auth_range.tick_id - tick_id)
515 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "ranges are incorrect"))?;
516 tick_id = auth_range.tick_id + auth_range.steps.len() as u32;
517
518 let internal = auth_range.to_internal();
519
520 let range = InternalAuthoritativeStepRange {
521 delta_tick_id_from_previous: delta_ticks_from_previous,
522 authoritative_steps: internal,
523 };
524 converted_ranges.push(range);
525 }
526
527 let all_ranges = InternalAuthoritativeStepRanges {
528 root_tick_id,
529 ranges: converted_ranges,
530 };
531
532 all_ranges.to_stream(stream)
533 }
534}
535
536impl<StepT: Deserialize + Serialize + Debug + Clone + Display> Deserialize
537 for AuthoritativeStepRanges<StepT>
538{
539 fn deserialize(stream: &mut impl ReadOctetStream) -> io::Result<Self>
540 where
541 Self: Sized,
542 {
543 let internal_auth_step_ranges =
544 InternalAuthoritativeStepRanges::<StepT>::from_stream(stream)?;
545 let mut tick_id = internal_auth_step_ranges.root_tick_id;
546
547 let mut converted_ranges = Vec::new();
548 for internal_step_range in &internal_auth_step_ranges.ranges {
549 tick_id += internal_step_range.delta_tick_id_from_previous as u32;
550
551 let combined_steps =
552 CombinedSteps::from_internal(&internal_step_range.authoritative_steps, tick_id);
553
554 converted_ranges.push(combined_steps);
555 }
556
557 Ok(Self {
558 ranges: converted_ranges,
559 })
560 }
561}
562
563#[derive(Debug)]
564pub struct InternalAuthoritativeStepRanges<StepT: Deserialize + Serialize + Debug + Clone + Display>
565{
566 pub root_tick_id: TickId,
567 pub ranges: Vec<InternalAuthoritativeStepRange<StepT>>,
568}
569
570impl<StepT: Deserialize + Serialize + Debug + Clone + Display>
571 InternalAuthoritativeStepRanges<StepT>
572{
573 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
577 TickIdUtil::to_stream(self.root_tick_id, stream)?;
578 stream.write_u8(self.ranges.len() as u8)?;
579 trace!(
580 "tick_id: {} range_count: {}",
581 self.root_tick_id,
582 self.ranges.len()
583 );
584 for range in &self.ranges {
585 range.to_stream(stream)?;
586 }
587 Ok(())
588 }
589
590 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
594 let root_tick_id = TickIdUtil::from_stream(stream)?;
595 let range_count = stream.read_u8()?;
596
597 let mut authoritative_step_ranges =
598 Vec::<InternalAuthoritativeStepRange<StepT>>::with_capacity(range_count as usize);
599
600 for _ in 0..range_count {
601 authoritative_step_ranges.push(InternalAuthoritativeStepRange::from_stream(stream)?);
602 }
603
604 Ok(Self {
605 root_tick_id,
606 ranges: authoritative_step_ranges,
607 })
608 }
609}
610
611#[derive(Debug)]
612pub struct GameStepResponse<StepT: Serialize + Deserialize + Debug + Clone + Display> {
613 pub response_header: GameStepResponseHeader,
614 pub authoritative_steps: AuthoritativeStepRanges<StepT>,
615}
616
617impl<StepT: Serialize + Deserialize + Debug + Clone + Display> Display for GameStepResponse<StepT> {
618 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
619 write!(
620 f,
621 "response: {} auth-steps: {}",
622 self.response_header, self.authoritative_steps
623 )
624 }
625}
626
627impl<StepT: Deserialize + Serialize + Debug + Clone + Display> GameStepResponse<StepT> {
628 pub fn to_stream(&self, stream: &mut impl WriteOctetStream) -> io::Result<()> {
632 self.response_header.to_stream(stream)?;
633 self.authoritative_steps.serialize(stream)
634 }
635
636 pub fn from_stream(stream: &mut impl ReadOctetStream) -> io::Result<Self> {
640 Ok(Self {
641 response_header: GameStepResponseHeader::from_stream(stream)?,
642 authoritative_steps: AuthoritativeStepRanges::deserialize(stream)?,
643 })
644 }
645}