evoke_core/client_server/
client.rs

1use std::error::Error;
2
3use alkahest::{Schema, Unpacked};
4use scoped_arena::Scope;
5
6use crate::channel::Channel;
7
8use super::*;
9
10#[derive(Debug, thiserror::Error)]
11pub enum ClientError<E: Error + 'static> {
12    #[error("Client channel error: {source}")]
13    ChannelError {
14        #[from]
15        source: E,
16    },
17
18    #[error("Unexpected server message")]
19    UnexpectedMessage,
20}
21pub struct Updates<'a, U: Schema> {
22    pub server_step: u64,
23    pub updates: Unpacked<'a, U>,
24}
25
26pub struct ClientSession<C> {
27    channel: C,
28    current_step: u64,
29    next_update_step: u64,
30}
31
32impl<C> ClientSession<C>
33where
34    C: Channel,
35{
36    /// Create new client session via specified channel.
37    pub async fn new(channel: C, scope: &Scope<'_>) -> Result<Self, ClientError<C::Error>> {
38        let mut channel = channel;
39        channel
40            .send_reliable::<ClientMessage, _>(ClientMessageConnectPack { token: "evoke" }, scope)
41            .await?;
42
43        loop {
44            match channel.recv::<ServerMessage>(scope) {
45                Ok(Some(ServerMessageUnpacked::Connected { step })) => {
46                    return Ok(ClientSession {
47                        channel,
48                        current_step: step,
49                        next_update_step: 0,
50                    });
51                }
52                Ok(Some(ServerMessageUnpacked::Updates { .. })) => {}
53                Ok(Some(ServerMessageUnpacked::PlayerJoined { .. })) => {
54                    return Err(ClientError::UnexpectedMessage)
55                }
56                Ok(None) => {
57                    channel.recv_ready().await?;
58                }
59                Err(err) => return Err(err.into()),
60            }
61        }
62    }
63
64    pub fn current_step(&self) -> u64 {
65        self.current_step
66    }
67
68    /// Adds new player to the session.
69    pub async fn add_player<'a, P, J, K>(
70        &mut self,
71        player: K,
72        scope: &'a Scope<'a>,
73    ) -> Result<Unpacked<'a, J>, ClientError<C::Error>>
74    where
75        P: Schema,
76        J: Schema,
77        K: Pack<P>,
78    {
79        self.channel
80            .send_reliable::<ClientMessage<P>, _>(ClientMessageAddPlayerPack { player }, scope)
81            .await?;
82
83        loop {
84            match self.channel.recv::<ServerMessage<J>>(scope) {
85                Ok(Some(ServerMessageUnpacked::PlayerJoined { info })) => return Ok(info),
86                Ok(Some(ServerMessageUnpacked::Connected { .. })) => {
87                    return Err(ClientError::UnexpectedMessage)
88                }
89                Ok(Some(ServerMessageUnpacked::Updates { .. })) => {}
90                Ok(None) => {
91                    self.channel.recv_ready().await?;
92                }
93                Err(err) => return Err(err.into()),
94            }
95        }
96    }
97
98    /// Sends input from players to the server.
99    pub async fn send_inputs<T, K, I>(
100        &mut self,
101        inputs: I,
102        scope: &Scope<'_>,
103    ) -> Result<(), C::Error>
104    where
105        T: Schema,
106        K: Pack<T>,
107        I: IntoIterator,
108        I::IntoIter: ExactSizeIterator<Item = (PlayerId, K)>,
109    {
110        self.channel
111            .send_reliable::<ClientMessage<(), T>, _>(
112                ClientMessageInputsPack {
113                    step: self.current_step,
114                    next_update_step: self.next_update_step,
115                    inputs: inputs.into_iter(),
116                },
117                scope,
118            )
119            .await?;
120
121        Ok(())
122    }
123
124    /// Advances client-side simulation by one step.
125    pub fn advance<'a, U>(
126        &mut self,
127        scope: &'a Scope<'_>,
128    ) -> Result<Option<Updates<'a, U>>, ClientError<C::Error>>
129    where
130        U: Schema,
131    {
132        let updates = match self.channel.recv::<ServerMessage<(), U>>(scope) {
133            Ok(Some(ServerMessageUnpacked::Updates {
134                server_step,
135                updates,
136            })) => {
137                self.next_update_step = server_step + 1;
138                Some(Updates {
139                    server_step,
140                    updates,
141                })
142            }
143            Ok(Some(_)) => return Err(ClientError::UnexpectedMessage),
144            Ok(None) => None,
145            Err(err) => return Err(err.into()),
146        };
147
148        self.current_step += 1;
149        Ok(updates)
150    }
151}