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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
use super::timer::Timer;
use super::VERSION;

use rand::prelude::Rng;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::ops::Add;
use std::ops::Mul;
use std::time::Duration;


/// 2D Vector (x, y) that can represent coordinates in OpenGL space that fill your window, or
/// velocity, or whatever other two f32 values you need.  The OpenGL window is (-1.0, -1.0) in
/// the bottom left to (1.0, 1.0) in the top right.
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct Vector2 {
    pub x : f32,
    pub y : f32,
}

impl Vector2 {
    /// New Vector2D at (0.0, 0.0)
    pub fn new() -> Self {
        Self { x: 0.0, y: 0.0 }
    }
    /// Create a random Vector2D with x and y both in `[-dimension, dimension]`
    pub fn new_in_square<T: Rng>(dimension : f32, rng : &mut T) -> Self {
        Self {
            x: rng.gen_range(-dimension, dimension),
            y: rng.gen_range(-dimension, dimension),
        }
    }
    /// Calculate the distance between two Vector2's -- useful when they represent coordinates
    pub fn distance_between(&self, other : Self) -> f32 {
        ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
    }
    /// Calculate the angle between two Vector2's -- useful when they represent coordinates
//    pub fn angle_between(&self, other : Self) -> f32 {
//        (self.x - other.x).atan2(self.y - other.y)
//    }
    pub fn angle_between(&self, other : Self) -> f32 {
        (other.y - self.y).atan2(other.x - self.x)
    }
    /// Calculate the magnitude of the Vector2 -- useful when it represents a vector (such as
    /// velocity)
    pub fn magnitude(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2)).sqrt()
    }
    /// Create a new Vector2D, normalized to be of unit length (length 1).
    pub fn normalized(&self) -> Self {
        let magnitude = self.magnitude();
        Self {
            x: self.x / magnitude,
            y: self.y / magnitude,
        }
    }
    /// Create a new Vector2D that is clamped to a magnitude of `1.0`
    pub fn clamped_to_normal(&self) -> Self {
        if self.magnitude() > 1.0 {
            self.normalized()
        } else {
            self.clone()
        }
    }
    /// Create a new Vector2D clamped to `magnitude`
    pub fn clamped_to(&self, magnitude : f32) -> Self {
        if self.magnitude() > magnitude {
            let ratio = magnitude / self.magnitude();
            Self {
                x: self.x * ratio,
                y: self.y * ratio,
            }
        } else {
            self.clone()
        }
    }
}

/// Do docs for trait impls show up???
impl PartialOrd for Vector2 {
    fn partial_cmp(&self, other: &Vector2) -> Option<Ordering>{
        let magnitude = self.magnitude();
        let other_magnitude = other.magnitude();
        return if magnitude < other_magnitude {
            Some(Ordering::Less)
        } else if magnitude == other_magnitude {
            Some(Ordering::Equal)
        } else {
            Some(Ordering::Greater)
        };
    }
}

impl Add for Vector2 {
    type Output = Vector2;
    fn add(self, other : Vector2) -> Vector2 {
        Vector2 {
            x: self.x + other.x,
            y: self.y + other.y,
        }
    }
}

impl Mul<f32> for Vector2 {
    type Output = Vector2;
    fn mul(self, other : f32) -> Vector2 {
        Vector2 {
            x: self.x * other,
            y: self.y * other,
        }
    }
}

/// Convenience trait that adds an `.f32()` method that returns a 32-bit float representation of
/// something.  Implemented for std::time::Duration and rusty_sword_arena::timer::Timer.
pub trait Floatable {
    fn f32(&self) -> f32;
}

impl Floatable for Duration {
    fn f32(&self) -> f32 {
        self.as_secs() as f32 + self.subsec_nanos() as f32 * 1e-9
    }
}

/// Abstracted button values you may receive (arrow keys and WASD keys combined into directions, for example)
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum ButtonValue {
    /// Arrow Up, W, Comma (Dvorak)
    Up,
    /// Arrow Down, S, O (Dvorak)
    Down,
    /// Arrow Left, A
    Left,
    /// Arrow Right, D, E (Dvorak)
    Right,
    /// Left Mouse Button, Space Bar, Backspace (Kinesis Advantage Keyboard)
    Attack,
    /// Escape
    Quit,
}

/// Whether a button was pressed or released
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum ButtonState {
    Pressed,
    Released,
}

/// Client Events that may occur
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq)]
pub enum Event {
    /// The window was closed somehow, so we better quit
    WindowClosed,
    /// The mouse is now at this location (OpenGL coordinates - can extend past what's viewable if
    /// the mouse is outside the window)
    MouseMoved { position : Vector2 },
    Button {
        button_value : ButtonValue,
        button_state : ButtonState
    },
}

/// Various game control actions. Join a game, leave a game, just fetch updated game settings.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum GameControlMsg {
    Join  { name : String },
    Leave { id : u8 },
    Fetch,
}

/// A color with 32-bit float parts from `[0.0, 1.0]` suitable for OpenGL.
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Color {
    /// Red
    pub r : f32,
    /// Green
    pub g : f32,
    /// Blue
    pub b : f32,
}

impl Color {
    pub fn new(r : f32, g : f32, b : f32) -> Self {
        Self { r, g, b }
    }
}

impl Hash for Color {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (self.r as u32).hash(state);
        (self.g as u32).hash(state);
        (self.b as u32).hash(state);
    }
}

impl Eq for Color {}

/// Server returns a GameSetting in response to receiving a PlayerSync
/// Game settings and player names and colors (including your own) are all in there.  You will
/// need to re-parse this every time someone joins or leaves the game.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct GameSetting {
    /// Version number of the server you are connecting to. Compare to rusty_sword_arena::version
    pub version : String,
    /// The maximum amount of players this server will allow
    pub max_players : u8,
    /// How quickly a player can get moving (magnitude in OpenGL units per second^2)
    pub acceleration : f32,
    /// Maximum velocity of a player (magnitude in OpenGL units per second)
    pub max_velocity : f32,
    /// How much drag there is on movement when the player is _not_ trying to move (how quick you
    /// stop)
    pub drag : f32,
    /// Move threshold. Magnitude of Vector2 below which a move_speed will be considered 0.
    pub move_threshold : f32,
    /// Milliseconds. How long the server will wait to respawn a player who dies.
    pub respawn_delay : u64,
    /// Milliseconds. How long the server will allow not receiving input before dropping a player.
    pub drop_delay : u64,
}

impl GameSetting {
    pub fn new() -> Self {
        GameSetting {
            version : VERSION.to_string(),
            max_players : 32,
            acceleration : 1.5,
            max_velocity : 0.25,
            drag : 5.0,
            move_threshold : 0.05,
            respawn_delay : 5000,
            drop_delay : 4000,
        }
    }
    pub fn get_hash(&self) -> u64 {
        let mut hasher = DefaultHasher::new();
        self.hash(&mut hasher);
        hasher.finish()
    }
}

impl Hash for GameSetting {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.version.hash(state);
        self.max_players.hash(state);
        (self.acceleration as u32).hash(state);
        (self.drag as u32).hash(state);
        (self.move_threshold as u32).hash(state);
        self.respawn_delay.hash(state);
        self.drop_delay.hash(state);
    }
}

/// An event that has happened to your player this frame!  Note that it's possible to receive a
/// whole bunch of events in the same frame.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum PlayerEvent {
    /// Player has attacked and hit player id.
    AttackHit { id : u8 },
    /// Player has attacked, but not hit anyone.
    AttackMiss,
    /// Player has changed to a new weapon
    ChangeWeapon,
    /// Player has died
    Die,
    /// Player has stopped healing
    HealEnd,
    /// Player has started healing
    HealStart,
    /// Player has stopped moving
    MoveEnd,
    /// Player has started moving
    MoveStart,
    /// Player has spawned
    Spawn,
    /// Player has received damage
    TookDamage,
    /// Player has joined the game
    Join,
    /// Player has left the game
    Leave,
}

/// A weapon a player may hold
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct Weapon {
    /// Something like "Rusty Sword", "Shiny Sword", "Rusty Spear", etc.
    pub description : String,
    /// How much damage the weapon can cause
    pub damage : f32,
    /// How long until the player can attack again
    pub attack_timer : Timer,
    /// How far attacks reach from your player, in OpenGL units.
    pub radius : f32,
}

impl Weapon {
    pub fn new() -> Self {
        Self {
            description : "Rusty Sword".to_string(),
            damage : 17.0,
            radius : 0.1,
            attack_timer : Timer::from_millis(500),
        }
    }
}

/// The state of a player on the server. The server broadcasts these to all clients every frame as
/// part of a FrameState.  Note that you can receive `PlayerState`s before you have gotten a
/// corresponding GameSetting telling you their name and color!
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PlayerState {
    /// The ID of the player
    pub id : u8,
    /// The name of the player
    pub name : String,
    /// The color of the player
    pub color : Color,
    /// The position of the player in OpenGL units.
    pub pos : Vector2,
    /// The direction the player is facing, in radians
    pub direction : f32,
    /// Your player occupies a circle of this radius, in OpenGL units.
    pub radius : f32,
    /// Current velocity of the player
    pub velocity : Vector2,
    /// Current health of the player [0.0, 100.0]
    pub health : f32,
    // Private! Does it show up in docs?
    starting_health : f32,
    /// Current weapon of the player
    pub weapon : Weapon,
    /// Any player events that have occurred to the player this frame
    pub player_events : Vec<PlayerEvent>,
    /// How long the server will wait to get input from you before disconnecting you
    pub drop_timer : Timer,
    /// How long until the player respawns.  If respawn_timer.ready == false, then the player is
    /// dead and you should seriously consider indicating that visually somehow, even if only by not
    /// displaying the player.
    pub respawn_timer : Timer,
    /// Are you dead?  Untangling health/respawn_timer dynamics is a pain, so we'll use this much
    /// more convenient boolean.
    pub dead : bool,
}

/// Represents the state of the player on the server for the current frame.  Always delivered by the
/// server to the client inside a [GameState](game/struct.GameState.html).  The server always
/// creates `PlayeState`s, updates them, and sends them to the client each frame. The client is free
/// to modify its local copy (for example, to remove [PlayerEvent](game/enum.PlayerEvent.html)s it
/// has processed) -- a new set of `PlayerStates` will be delivered the next frame. Clients
/// typically look at the fields and events, and don't use any of the methods.
impl PlayerState {
    /// The client should never create a `PlayerState` -- the server will do that.
    pub fn new(game_setting : &GameSetting, id : u8, name : String, color : Color, pos : Vector2, radius : f32) -> Self {
        let mut respawn_timer = Timer::from_millis(game_setting.respawn_delay);
        respawn_timer.set_millis_transient(1000); // spawn more quickly on initial connect
        Self {
            id,
            name,
            color,
            pos,
            direction : 0.0,
            radius,
            velocity : Vector2::new(),
            health : 100.0,
            starting_health : 100.0,
            weapon : Weapon::new(),
            player_events : Vec::<PlayerEvent>::new(),
            drop_timer: Timer::from_millis(game_setting.drop_delay),
            respawn_timer,
            dead : true,
        }
    }
    /// Clients never need to call update. The server calls it each time it processes some amount of
    /// time.
    pub fn update(&mut self, delta : Duration) {
        self.weapon.attack_timer.update(delta);
        self.drop_timer.update(delta);
        self.respawn_timer.update(delta);
    }
    /// Used by the server to reset things that have been taken care of last frame.
    pub fn new_frame(&mut self) {
        self.player_events.clear();
    }
    /// Used by the server when a player needs to die
    pub fn die(&mut self, msg : &str) {
        println!("{}", msg);
        self.health = -1.0;
        self.respawn_timer.reset();
        self.player_events.push(PlayerEvent::Die);
        self.dead = true;
    }
    /// Used by the server when a player needs to spawn
    pub fn respawn(&mut self, pos : Vector2, msg : &str) {
        println!("{}", msg);
        self.pos = pos;
        self.health = self.starting_health;
        self.player_events.push(PlayerEvent::Spawn);
        self.dead = false;
    }
}

/// Once per frame, the server will broadcast a GameState to all clients.  IMPORTANT: If you don't
/// receive a GameState for 2 full seconds, the client MUST DROP its ServerConnection (or just
/// exit entirely).  The underlying networking that we're currently using hides network disconnects,
/// which can leave the networking in a funky state if one end drops.  So we need to rely on
/// detecting this heartbeat and shutting down the clients to ensure networking is clean when the
/// server restarts.  (In the future, lets switch to a protocol that can detect disconnects...)
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct GameState {
    /// Which frame we're on.  Starts at zero and increments by 1 each frame.
    pub frame_number : u64,
    /// The actual time the server measured since the previous frame.
    pub delta : Duration,
    /// The hash of the current game setting. Your client should store this somewhere. If it changes
    /// then something has changed (most likely a player has joined or disconnected), so you should
    /// send a GameControlMsg::Fetch to get the new GameSetting from the server and update your
    /// client state.
    pub game_setting_hash : u64,
    /// All of the player's states, including your own!
    pub player_states : HashMap<u8, PlayerState>,
}
/// Clients should send `PlayerInput`s to the server ASAP.  The quicker the server gets inputs, the
/// more accurate the simulation will be.  But of course, you also shouldn't overload the server
/// with too much traffic, because that's bad too.  Good rule of thumb: Coalesce 4 milliseconds
/// worth of input together, and send that.  That's about 4 times faster than frames are sent by the
/// server (60fps = 16.7ms).  The server should be able to handle ~250 pkts/sec per client.  I hope.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PlayerInput {
    /// The ID of your player
    pub id : u8,
    /// Whether you are attempting to attack (actual attack will occur if the server-side attack
    /// timer has reached 0)
    pub attack : bool,
    /// How much your player is attempting to move horizontally (x) and vertically (y) [-1.0, 1.0].
    /// Positive is right and up for x and y, respectively.
    pub move_amount : Vector2,
    /// What direction your player is facing. You can turn instantly, you lucky dog.
    pub direction: f32,
}

impl PlayerInput {
    pub fn new() -> Self {
        Self {
            id : 0,
            attack : false,
            move_amount : Vector2::new(),
            direction: 0.0,
        }
    }
    // Combine successive inputs into one input
    pub fn coalesce(&mut self, new : PlayerInput) {
        // Any attack sticks
        self.attack = self.attack || new.attack;
        // Anything else the new value wins
        self.move_amount = new.move_amount;
        self.direction = new.direction;
    }
}