use std::error::Error;
use alkahest::{Schema, Unpacked};
use scoped_arena::Scope;
use crate::channel::Channel;
use super::*;
#[derive(Debug, thiserror::Error)]
pub enum ClientError<E: Error + 'static> {
#[error("Client channel error: {source}")]
ChannelError {
#[from]
source: E,
},
#[error("Unexpected server message")]
UnexpectedMessage,
}
pub struct Updates<'a, U: Schema> {
pub server_step: u64,
pub updates: Unpacked<'a, U>,
}
pub struct ClientSession<C> {
channel: C,
current_step: u64,
next_update_step: u64,
}
impl<C> ClientSession<C>
where
C: Channel,
{
pub async fn new(channel: C, scope: &Scope<'_>) -> Result<Self, ClientError<C::Error>> {
let mut channel = channel;
channel
.send_reliable::<ClientMessage, _>(ClientMessageConnectPack { token: "evoke" }, scope)
.await?;
loop {
match channel.recv::<ServerMessage>(scope) {
Ok(Some(ServerMessageUnpacked::Connected { step })) => {
return Ok(ClientSession {
channel,
current_step: step,
next_update_step: 0,
});
}
Ok(Some(ServerMessageUnpacked::Updates { .. })) => {}
Ok(Some(ServerMessageUnpacked::PlayerJoined { .. })) => {
return Err(ClientError::UnexpectedMessage)
}
Ok(None) => {
channel.recv_ready().await?;
}
Err(err) => return Err(err.into()),
}
}
}
pub fn current_step(&self) -> u64 {
self.current_step
}
pub async fn add_player<'a, P, J, K>(
&mut self,
player: K,
scope: &'a Scope<'a>,
) -> Result<Unpacked<'a, J>, ClientError<C::Error>>
where
P: Schema,
J: Schema,
K: Pack<P>,
{
self.channel
.send_reliable::<ClientMessage<P>, _>(ClientMessageAddPlayerPack { player }, scope)
.await?;
loop {
match self.channel.recv::<ServerMessage<J>>(scope) {
Ok(Some(ServerMessageUnpacked::PlayerJoined { info })) => return Ok(info),
Ok(Some(ServerMessageUnpacked::Connected { .. })) => {
return Err(ClientError::UnexpectedMessage)
}
Ok(Some(ServerMessageUnpacked::Updates { .. })) => {}
Ok(None) => {
self.channel.recv_ready().await?;
}
Err(err) => return Err(err.into()),
}
}
}
pub async fn send_inputs<T, K, I>(
&mut self,
inputs: I,
scope: &Scope<'_>,
) -> Result<(), C::Error>
where
T: Schema,
K: Pack<T>,
I: IntoIterator,
I::IntoIter: ExactSizeIterator<Item = (PlayerId, K)>,
{
self.channel
.send_reliable::<ClientMessage<(), T>, _>(
ClientMessageInputsPack {
step: self.current_step,
next_update_step: self.next_update_step,
inputs: inputs.into_iter(),
},
scope,
)
.await?;
Ok(())
}
pub fn advance<'a, U>(
&mut self,
scope: &'a Scope<'_>,
) -> Result<Option<Updates<'a, U>>, ClientError<C::Error>>
where
U: Schema,
{
let updates = match self.channel.recv::<ServerMessage<(), U>>(scope) {
Ok(Some(ServerMessageUnpacked::Updates {
server_step,
updates,
})) => {
self.next_update_step = server_step + 1;
Some(Updates {
server_step,
updates,
})
}
Ok(Some(_)) => return Err(ClientError::UnexpectedMessage),
Ok(None) => None,
Err(err) => return Err(err.into()),
};
self.current_step += 1;
Ok(updates)
}
}