1pub mod err;
21pub mod prelude;
22
23use crate::err::HostError;
24use datagram_chunker::DatagramChunker;
25use flood_rs::prelude::OutOctetStream;
26use flood_rs::{Deserialize, Serialize};
27use hexify::format_hex;
28use log::{debug, trace};
29use monotonic_time_rs::Millis;
30use nimble_host_logic::{
31 connection::Connection, session::GameSession, GameStateProvider, HostLogic,
32};
33use nimble_layer::NimbleLayer;
34use nimble_protocol::prelude::ClientToHostCommands;
35use std::collections::HashMap;
36use std::fmt::{Debug, Display};
37use tick_id::TickId;
38
39#[derive(Default, Debug)]
41pub struct HostConnection {
42 layer: NimbleLayer,
43}
44
45impl HostConnection {
46 #[must_use]
47 pub fn new() -> Self {
48 Self {
49 layer: NimbleLayer::default(),
50 }
51 }
52}
53
54pub struct ConnectionId(u8);
56
57impl ConnectionId {
58 #[must_use]
60 pub const fn inner(&self) -> u8 {
61 self.0
62 }
63}
64
65pub struct Host<StepT: Clone + Debug + Eq + Deserialize + Serialize + Display> {
73 logic: HostLogic<StepT>,
74 connections: HashMap<u8, HostConnection>,
75}
76
77impl<StepT: Clone + Deserialize + Serialize + Eq + Debug + Display> Host<StepT> {
78 #[must_use]
85 pub fn new(app_version: app_version::Version, tick_id: TickId) -> Self {
86 Self {
87 logic: HostLogic::<StepT>::new(tick_id, app_version),
88 connections: HashMap::new(),
89 }
90 }
91
92 #[must_use]
94 pub const fn debug_logic(&self) -> &HostLogic<StepT> {
95 &self.logic
96 }
97
98 #[must_use]
100 pub fn debug_get_logic(
101 &self,
102 connection_id: nimble_host_logic::HostConnectionId,
103 ) -> Option<&Connection<StepT>> {
104 self.logic.get(connection_id)
105 }
106
107 #[must_use]
109 pub const fn session(&self) -> &GameSession<StepT> {
110 self.logic.session()
111 }
112
113 pub fn update(
132 &mut self,
133 connection_id: nimble_host_logic::HostConnectionId,
134 now: Millis,
135 datagram: &[u8],
136 state_provider: &impl GameStateProvider,
137 ) -> Result<Vec<Vec<u8>>, HostError> {
138 trace!(
139 "time:{now}: host received for connection:{} payload:\n{}",
140 connection_id.0,
141 format_hex(datagram)
142 );
143
144 let found_connection = self
145 .connections
146 .get_mut(&connection_id.0)
147 .ok_or(HostError::ConnectionNotFound(connection_id.0))?;
148
149 let datagram_without_layer = found_connection.layer.receive(datagram)?;
150
151 let deserialized_commands = datagram_chunker::deserialize_datagram::<
152 ClientToHostCommands<StepT>,
153 >(datagram_without_layer)?;
154
155 let mut all_commands_to_send = Vec::new();
156 for deserialized_command in deserialized_commands {
157 let commands_to_send =
158 self.logic
159 .update(connection_id, now, &deserialized_command, state_provider)?;
160
161 all_commands_to_send.extend(commands_to_send);
162 }
163
164 self.logic.post_update();
165
166 let mut datagram_chunker = DatagramChunker::new(1024);
167 for cmd in all_commands_to_send {
168 let mut out_stream = OutOctetStream::new();
169 cmd.serialize(&mut out_stream)?;
170 datagram_chunker.push(out_stream.octets_ref())?;
171 }
172
173 let outgoing_datagrams = datagram_chunker.finalize();
174
175 let out_datagrams = found_connection.layer.send(&outgoing_datagrams)?;
176
177 for (index, datagram) in out_datagrams.iter().enumerate() {
178 trace!(
179 "host sending index {} payload:\n{}",
180 index,
181 format_hex(datagram)
182 );
183 }
184
185 Ok(out_datagrams)
186 }
187
188 #[must_use]
194 pub fn get(
195 &self,
196 connection_id: nimble_host_logic::HostConnectionId,
197 ) -> Option<&HostConnection> {
198 self.connections.get(&connection_id.0)
199 }
200
201 pub fn create_connection(&mut self) -> Option<nimble_host_logic::HostConnectionId> {
207 if let Some(connection_id) = self.logic.create_connection() {
208 self.connections
209 .insert(connection_id.0, HostConnection::new());
210 debug!("Created connection {:?}", connection_id);
211 Some(connection_id)
212 } else {
213 None
214 }
215 }
216
217 pub fn destroy_connection(
227 &mut self,
228 connection_id: nimble_host_logic::HostConnectionId,
229 ) -> Result<(), HostError> {
230 debug!("destroying connection {:?}", connection_id);
231 self.connections.remove(&connection_id.0);
232 self.logic.destroy_connection(connection_id)?;
233 Ok(())
234 }
235}