use crate::battle::{Battle, BattleController, BattleRules, EventCallback};
use crate::error::{WeaselError, WeaselResult};
use crate::event::{
ClientEventPrototype, EventProcessor, EventPrototype, EventQueue, EventReceiver, EventRights,
EventServer, EventWrapper, MultiClientSink, MultiClientSinkHandle, MultiClientSinkHandleMut,
VersionedEventWrapper,
};
use crate::player::{PlayerId, RightsHandle, RightsHandleMut};
use crate::team::TeamId;
pub struct Server<R: BattleRules> {
pub(crate) battle: Battle<R>,
client_sinks: MultiClientSink<R>,
authentication: bool,
}
impl<R: BattleRules + 'static> Server<R> {
pub fn builder(battle: Battle<R>) -> ServerBuilder<R> {
ServerBuilder {
battle,
authentication: false,
}
}
pub fn authentication(&self) -> bool {
self.authentication
}
pub fn rights(&self) -> RightsHandle<R> {
self.battle.rights()
}
pub fn rights_mut<'a>(&'a mut self) -> RightsHandleMut<R, impl Iterator<Item = &'a TeamId<R>>> {
self.battle.rights_mut()
}
pub fn client_sinks(&self) -> MultiClientSinkHandle<'_, R> {
MultiClientSinkHandle::new(&self.client_sinks)
}
pub fn client_sinks_mut(&mut self) -> MultiClientSinkHandleMut<'_, R> {
MultiClientSinkHandleMut::new(&mut self.client_sinks, &self.battle)
}
fn apply_event(&mut self, event: EventWrapper<R>) -> WeaselResult<(), R> {
let mut event_queue = Some(EventQueue::<R>::new());
self.battle.apply(&event, &mut event_queue);
self.client_sinks
.send_all(&event.clone().version(self.battle.rules().version().clone()));
let mut errors = Vec::new();
if let Some(event_queue) = event_queue {
for mut prototype in event_queue {
if prototype.origin().is_none() {
prototype.set_origin(Some(event.id()));
}
let result = self.process(prototype);
if let Err(error) = result {
errors.push(error);
}
}
}
match errors.len() {
1 => Err(errors.swap_remove(0)),
x if x > 1 => Err(WeaselError::MultiError(errors)),
_ => Ok(()),
}
}
fn check_rights(&self, player: PlayerId, team_id: &TeamId<R>) -> WeaselResult<(), R> {
if !self.rights().check(player, team_id) {
Err(WeaselError::AuthenticationError(
Some(player),
team_id.clone(),
))
} else {
Ok(())
}
}
}
impl<R: BattleRules> BattleController<R> for Server<R> {
fn battle(&self) -> &Battle<R> {
&self.battle
}
fn event_callback(&self) -> &Option<EventCallback<R>> {
&self.battle.event_callback
}
fn set_event_callback(&mut self, callback: Option<EventCallback<R>>) {
self.battle.event_callback = callback;
}
}
impl<R: BattleRules + 'static> EventProcessor<R> for Server<R> {
type ProcessOutput = WeaselResult<(), R>;
fn process(&mut self, event: EventPrototype<R>) -> Self::ProcessOutput {
self.battle
.verify_prototype(&event)
.map_err(|e| WeaselError::InvalidEvent(event.event().clone(), e.into()))?;
let event = self.battle.promote(event);
self.apply_event(event)
}
}
impl<R: BattleRules + 'static> EventServer<R> for Server<R> {
fn process_client(&mut self, event: ClientEventPrototype<R>) -> WeaselResult<(), R> {
self.battle.verify_client(&event)?;
match event.rights(&self.battle) {
EventRights::Server => {
return Err(WeaselError::ServerOnlyEvent);
}
EventRights::Team(team_id) => {
if self.authentication {
if let Some(player) = event.player() {
self.check_rights(player, team_id)?;
} else {
return Err(WeaselError::MissingAuthentication);
}
}
}
EventRights::Teams(teams_ids) => {
if self.authentication {
if let Some(player) = event.player() {
for team_id in teams_ids {
self.check_rights(player, team_id)?;
}
} else {
return Err(WeaselError::MissingAuthentication);
}
}
}
EventRights::None => {}
}
let event = self.battle.promote(event.prototype());
self.apply_event(event)
}
}
impl<R: BattleRules + 'static> EventReceiver<R> for Server<R> {
fn receive(&mut self, event: VersionedEventWrapper<R>) -> WeaselResult<(), R> {
self.battle.verify_wrapper(&event)?;
self.battle.apply(&event.wrapper(), &mut None);
self.client_sinks.send_all(&event);
Ok(())
}
}
pub struct ServerBuilder<R: BattleRules> {
battle: Battle<R>,
authentication: bool,
}
impl<R: BattleRules> ServerBuilder<R> {
pub fn enforce_authentication(mut self) -> Self {
self.authentication = true;
self
}
pub fn build(self) -> Server<R> {
Server {
battle: self.battle,
client_sinks: MultiClientSink::new(),
authentication: self.authentication,
}
}
}