use crate::{
context::GameContext,
game::GameState,
multiplayer::{GameMultiplayer, GameNetwork},
};
use std::any::Any;
use tehuti::{
event::{Receiver, unbounded},
peer::{Peer, PeerId},
};
use tehuti_client_server::authority::{AuthorityUserData, PureAuthority};
use tehuti_timeline::time::TimeStamp;
pub struct ClientServerPrepareFrameEvent {
pub current_tick: TimeStamp,
}
pub struct ClientServerHandleInputsEvent {
pub current_tick: TimeStamp,
}
pub struct ClientServerTickEvent {
pub current_tick: TimeStamp,
pub delta_time: f32,
}
pub struct ClientServerCommitFrameEvent {
pub current_tick: TimeStamp,
}
pub trait ClientServerGameState {
fn is(payload: &dyn Any) -> bool {
payload.is::<ClientServerPrepareFrameEvent>()
|| payload.is::<ClientServerHandleInputsEvent>()
|| payload.is::<ClientServerTickEvent>()
|| payload.is::<ClientServerCommitFrameEvent>()
}
fn custom_event(&mut self, context: GameContext, payload: &mut dyn Any) {
if let Some(payload) = payload.downcast_ref::<ClientServerPrepareFrameEvent>() {
self.prepare_frame(context, payload.current_tick);
} else if let Some(payload) = payload.downcast_ref::<ClientServerHandleInputsEvent>() {
self.handle_inputs(context, payload.current_tick);
} else if let Some(payload) = payload.downcast_ref::<ClientServerTickEvent>() {
self.tick(context, payload.current_tick, payload.delta_time);
} else if let Some(payload) = payload.downcast_ref::<ClientServerCommitFrameEvent>() {
self.commit_frame(context, payload.current_tick);
}
}
#[allow(unused_variables)]
fn prepare_frame(&mut self, context: GameContext, current_tick: TimeStamp) {}
#[allow(unused_variables)]
fn handle_inputs(&mut self, context: GameContext, current_tick: TimeStamp) {}
#[allow(unused_variables)]
fn tick(&mut self, context: GameContext, current_tick: TimeStamp, delta_time: f32) {}
#[allow(unused_variables)]
fn commit_frame(&mut self, context: GameContext, current_tick: TimeStamp) {}
}
pub struct ClientServerMultiplayer {
pub authority: PureAuthority,
added_peers_receiver: Receiver<Peer>,
removed_peers_receiver: Receiver<PeerId>,
current_tick: TimeStamp,
time_accumulator: f32,
pub process_lifecycle_events: bool,
pub tick_delta_time: f32,
pub ticks_limit_per_frame: usize,
}
impl ClientServerMultiplayer {
pub fn new(network: &GameNetwork) -> Option<Self> {
let is_server = network
.meeting
.meeting_factory()
.user_data
.access::<AuthorityUserData>()
.ok()?
.is_server;
let (added_peers_sender, added_peers_receiver) = unbounded();
let (removed_peers_sender, removed_peers_receiver) = unbounded();
let authority = PureAuthority::new(
is_server,
network.interface.clone(),
added_peers_sender,
removed_peers_sender,
)
.ok()?;
Some(Self {
authority,
added_peers_receiver,
removed_peers_receiver,
current_tick: TimeStamp::default(),
time_accumulator: 0.0,
process_lifecycle_events: false,
tick_delta_time: 0.05,
ticks_limit_per_frame: usize::MAX,
})
}
pub fn with_process_lifecycle_events(mut self, value: bool) -> Self {
self.process_lifecycle_events = value;
self
}
pub fn with_tick_delta_time(mut self, value: f32) -> Self {
self.tick_delta_time = value;
self
}
pub fn with_ticks_per_second(mut self, value: usize) -> Self {
self.tick_delta_time = 1.0 / value.max(1) as f32;
self
}
pub fn with_ticks_limit_per_frame(mut self, value: usize) -> Self {
self.ticks_limit_per_frame = value;
self
}
pub fn is_initialized(&self) -> bool {
self.authority.is_initialized()
}
pub fn is_server(&self) -> bool {
self.authority.is_server()
}
pub fn is_client(&self) -> bool {
self.authority.is_client()
}
}
impl GameMultiplayer for ClientServerMultiplayer {
fn current_tick(&self) -> TimeStamp {
self.current_tick
}
fn maintain(&mut self, state: &mut dyn GameState, mut context: GameContext, delta_time: f32) {
if let Err(error) = self.authority.maintain() {
tracing::event!(
target: "quaso::multiplayer::client_server",
tracing::Level::ERROR,
"Failed to maintain authority: {}",
error
);
}
if !self.process_lifecycle_events || !self.authority.is_initialized() {
return;
}
let added_peers_receiver = self.added_peers_receiver.clone();
for peer in added_peers_receiver.iter() {
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.multiplayer_peer_added(context, peer);
}
let removed_peers_receiver = self.removed_peers_receiver.clone();
for peer_id in removed_peers_receiver.iter() {
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.multiplayer_peer_removed(context, peer_id);
}
{
let current_tick = self.current_tick;
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.custom_event(context, &mut ClientServerPrepareFrameEvent { current_tick });
}
{
let current_tick = self.current_tick;
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.custom_event(context, &mut ClientServerHandleInputsEvent { current_tick });
}
self.time_accumulator += delta_time;
let mut ticks_this_frame = 0;
while self.time_accumulator >= self.tick_delta_time
&& ticks_this_frame < self.ticks_limit_per_frame
{
self.time_accumulator -= self.tick_delta_time;
let current_tick = self.current_tick;
let tick_delta_time = self.tick_delta_time;
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.custom_event(
context,
&mut ClientServerTickEvent {
current_tick,
delta_time: tick_delta_time,
},
);
ticks_this_frame += 1;
self.current_tick += 1;
}
{
let current_tick = self.current_tick;
let mut context = unsafe { context.fork() };
context.multiplayer = Some(self);
state.custom_event(context, &mut ClientServerCommitFrameEvent { current_tick });
}
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}