1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! A battle client.

use crate::battle::{Battle, BattleController, BattleRules, EventCallback};
use crate::error::WeaselResult;
use crate::event::{
    EventProcessor, EventPrototype, EventReceiver, MultiClientSink, MultiClientSinkHandle,
    MultiClientSinkHandleMut, ServerSink, VersionedEventWrapper,
};
use crate::player::PlayerId;

/// A client event processor.
///
/// Clients can accept any kind of event from a remote server.
/// Local events are sent to the server to which the client is connected.
///
/// One or more client sinks can be connected to a client. Events received from
/// the server are propagated to these sinks.
pub struct Client<R: BattleRules> {
    battle: Battle<R>,
    server_sink: Box<dyn ServerSink<R> + Send>,
    client_sinks: MultiClientSink<R>,
    player: Option<PlayerId>,
}

impl<R: BattleRules + 'static> Client<R> {
    /// Returns a client builder.
    pub fn builder(
        battle: Battle<R>,
        server_sink: Box<dyn ServerSink<R> + Send>,
    ) -> ClientBuilder<R> {
        ClientBuilder {
            battle,
            server_sink,
            player: None,
        }
    }

    /// Returns whether or not client events authentication is enabled.
    pub fn authentication(&self) -> bool {
        self.player.is_some()
    }

    /// Returns the player id associated to this client.
    pub fn player(&self) -> &Option<PlayerId> {
        &self.player
    }

    /// Returns a reference to the server sink to which all event prototypes
    /// initiated by this client are sent.
    pub fn server_sink(&self) -> &(dyn ServerSink<R> + Send) {
        &*self.server_sink
    }

    /// Disconnects the current server sink and sets a new one.
    pub fn set_server_sink(&mut self, sink: Box<dyn ServerSink<R> + Send>) {
        self.server_sink.on_disconnect();
        self.server_sink = sink;
    }

    /// Returns a handle to access the client sinks of this client.
    pub fn client_sinks(&self) -> MultiClientSinkHandle<'_, R> {
        MultiClientSinkHandle::new(&self.client_sinks)
    }

    /// Returns a mutable handle to manage the client sinks of this client.
    pub fn client_sinks_mut(&mut self) -> MultiClientSinkHandleMut<'_, R> {
        MultiClientSinkHandleMut::new(&mut self.client_sinks, &self.battle)
    }
}

impl<R: BattleRules> BattleController<R> for Client<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 Client<R> {
    type ProcessOutput = WeaselResult<(), R>;

    fn process(&mut self, event: EventPrototype<R>) -> Self::ProcessOutput {
        self.battle.verify_prototype(&event)?;
        // Decorate the prototype with additional information.
        let event = event.client_prototype(self.battle().rules().version().clone(), self.player);
        // Send the event to the server.
        self.server_sink.send(&event)
    }
}

impl<R: BattleRules + 'static> EventReceiver<R> for Client<R> {
    fn receive(&mut self, event: VersionedEventWrapper<R>) -> WeaselResult<(), R> {
        // Verify the event.
        self.battle.verify_wrapper(&event)?;
        // Apply the event on the battle.
        self.battle.apply(&event.wrapper(), &mut None);
        // Send the event to all client sinks.
        self.client_sinks.send_all(&event);
        Ok(())
    }
}

/// A builder object to create a client.
pub struct ClientBuilder<R: BattleRules> {
    battle: Battle<R>,
    server_sink: Box<dyn ServerSink<R> + Send>,
    player: Option<PlayerId>,
}

impl<R: BattleRules> ClientBuilder<R> {
    /// Enable authentication on the new client.
    /// All produced events will be authenticated with `player`.
    pub fn enable_authentication(mut self, player: PlayerId) -> Self {
        self.player = Some(player);
        self
    }

    /// Creates a new client.
    pub fn build(self) -> Client<R> {
        Client {
            battle: self.battle,
            server_sink: self.server_sink,
            client_sinks: MultiClientSink::new(),
            player: self.player,
        }
    }
}