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
use crate::game::{GameControlMsg, GameSettings, GameState, PlayerInput};

use bincode::{deserialize, serialize};
use std::time::{Duration, Instant};
use zmq;

#[doc(hidden)]
pub const PLAYER_INPUT_PORT: i32 = 8001;
#[doc(hidden)]
pub const GAME_STATE_PORT: i32 = 8002;
#[doc(hidden)]
pub const GAME_CONTROL_PORT: i32 = 8003;

const PLAYER_INPUT_INTERVAL: Duration = Duration::from_millis(15);

/// This is your client's network connection to the server. The methods abstract away all the actual
/// object serialization and network communication. Hooray for encapsulation!
pub struct ConnectionToServer {
    _context: zmq::Context,
    game_control_socket: zmq::Socket,
    game_state_socket: zmq::Socket,
    player_input_socket: zmq::Socket,
    last_player_input_sent: Instant,
}

impl ConnectionToServer {
    /// Create a new connection to a server.  `host` is the IP address or domain name of the server.
    /// If you run the server on the same machine, you should pass `localhost` or `127.0.0.1` as
    /// the host.  This neets to be `mut` since it needs to track state internally.
    pub fn new(host: &str) -> Self {
        let context = zmq::Context::new();

        let game_control_socket = context.socket(zmq::REQ).unwrap();
        game_control_socket
            .connect(&format!("tcp://{}:{}", host, GAME_CONTROL_PORT))
            .unwrap();

        let game_state_socket = context.socket(zmq::SUB).unwrap();
        game_state_socket.set_rcvtimeo(0).unwrap();
        game_state_socket
            .connect(&format!("tcp://{}:{}", host, GAME_STATE_PORT))
            .unwrap();
        game_state_socket.set_subscribe(&[]).unwrap();

        let player_input_socket = context.socket(zmq::PUSH).unwrap();
        player_input_socket
            .connect(&format!("tcp://{}:{}", host, PLAYER_INPUT_PORT))
            .unwrap();

        Self {
            _context: context,
            game_control_socket,
            game_state_socket,
            player_input_socket,
            last_player_input_sent: Instant::now(),
        }
    }

    /// Join a game.  If successful, this returns an `Ok(u8)` representing your
    /// player id. Save the player id, because it is how you will be able to tell which of the
    /// players the server tells you about later is YOU! If unsuccessful then this returns an
    /// `Err(String)` that you can unwrap and print out to see an informative error message.
    pub fn join(&mut self, name: &str) -> Result<u8, String> {
        let msg = GameControlMsg::Join {
            name: name.to_string(),
        };
        self.game_control_socket
            .send(&serialize(&msg).unwrap(), 0)
            .unwrap();
        let bytes = self.game_control_socket.recv_bytes(0).unwrap();
        let result: Result<u8, String> = deserialize(&bytes[..]).unwrap();
        result
    }

    /// Get the current `GameSettings`.  You should look at the version number and make sure that
    /// you are connecting to a version of the server you expect.
    // TODO: Do the version check here.
    pub fn get_game_settings(&mut self) -> GameSettings {
        let msg = GameControlMsg::Fetch;
        self.game_control_socket
            .send(&serialize(&msg).unwrap(), 0)
            .unwrap();
        let bytes = self.game_control_socket.recv_bytes(0).unwrap();
        let game_settings: GameSettings = deserialize(&bytes[..]).unwrap();
        game_settings
    }

    /// Cause the selected player id to leave the game.  You should pass in your own player id,
    /// obviously.  Passing in someone else's player id would be really mean.
    pub fn leave(&mut self, id: u8) -> bool {
        let msg = GameControlMsg::Leave { id };
        self.game_control_socket.set_rcvtimeo(1500).unwrap();
        self.game_control_socket
            .send(&serialize(&msg).unwrap(), 0)
            .unwrap();
        if let Ok(bytes) = self.game_control_socket.recv_bytes(0) {
            let succeeded: bool = deserialize(&bytes[..]).unwrap();
            return succeeded;
        }
        false
    }

    /// Gets all available unprocessed game states.  Game states arrive in order.  You should call
    /// this every time around your game loop.
    pub fn poll_game_states(&mut self) -> Vec<GameState> {
        let mut game_states = Vec::<GameState>::new();
        while let Ok(bytes) = self.game_state_socket.recv_bytes(0) {
            game_states.push(deserialize(&bytes[..]).unwrap());
        }
        game_states
    }

    /// Send player input to the server. This method only actually sends input to the server if it
    /// has been >= 15ms since the last time it sent input (to avoid overwhelming the server with
    /// too many input packets per client).  Otherwise, it just does nothing.  You should maintain
    /// a mutable PlayerInput in your game loop and try to send it every time around your loop.
    pub fn send_player_input(&mut self, player_input: &PlayerInput) {
        if self.last_player_input_sent.elapsed() >= PLAYER_INPUT_INTERVAL {
            self.player_input_socket
                .send(&serialize(player_input).unwrap(), 0)
                .unwrap();
            self.last_player_input_sent = Instant::now();
        }
    }
}