diff --git a/client/src/components/info.component.jsx b/client/src/components/info.component.jsx
index 9d395a9c..6919ed49 100644
--- a/client/src/components/info.component.jsx
+++ b/client/src/components/info.component.jsx
@@ -153,15 +153,39 @@ function Info(args) {
);
}
+ function playerRound(id) {
+ if (!instance.rounds.length) return null;
+ return instance.rounds[instance.rounds.length - 1].find(r => r.player_ids.includes(id));
+ }
+
+ function playerText(player) {
+ const round = playerRound(player.id);
+ if (!round) {
+ return player.ready
+ ? 'ready'
+ : '';
+ }
+
+ if (round.finished) return 'finished';
+
+ return player.ready
+ ? 'ready'
+ : '';
+ }
+
function scoreBoard() {
- const players = instance.players.map((p, i) =>
-
- | {p.name} |
- {p.score.wins} / {p.score.losses} |
- {p.ready ? 'ready' : ''} |
-
- );
+ const players = instance.players.map((p, i) => {
+ const pText = playerText(p);
+ console.log(pText);
+ return (
+
+ | {p.name} |
+ {p.score.wins} / {p.score.losses} |
+ {pText} |
+
+ );
+ });
return (
diff --git a/server/src/game.rs b/server/src/game.rs
index dcb146a0..93e73327 100644
--- a/server/src/game.rs
+++ b/server/src/game.rs
@@ -26,16 +26,6 @@ pub enum Phase {
Finish,
}
-#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
-pub enum GameMode {
- Normal,
- Pvp,
- Zone3v2Attack,
- Zone2v2Caster,
- Zone3v3MeleeMiniboss,
- Zone3v3HealerBoss,
-}
-
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Game {
pub id: Uuid,
@@ -47,7 +37,6 @@ pub struct Game {
pub resolved: Vec,
pub log: Vec,
pub instance: Option,
- pub mode: GameMode,
phase_start: DateTime,
}
@@ -63,7 +52,6 @@ impl Game {
resolved: vec![],
log: vec![],
instance: None,
- mode: GameMode::Normal,
phase_start: Utc::now(),
};
}
@@ -83,16 +71,11 @@ impl Game {
self
}
- pub fn set_mode(&mut self, mode: GameMode) -> &mut Game {
- self.mode = mode;
- self
- }
-
pub fn joinable(&self) -> bool {
self.can_start()
}
- pub fn player_add(&mut self, player: Player) -> Result<&mut Game, Error> {
+ pub fn player_add(&mut self, mut player: Player) -> Result<&mut Game, Error> {
if self.players.len() == self.player_num {
return Err(err_msg("maximum number of players"));
}
@@ -102,11 +85,12 @@ impl Game {
}
if player.cryps.iter().all(|c| c.skills.len() == 0) {
- return Err(err_msg("your cryps have no skills"));
+ println!("WARNING: {:?} has no skills and has forfeited {:?}", player.name, self.id);
+ player.forfeit();
}
let player_description = player.cryps.iter().map(|c| c.name.clone()).collect::>().join(", ");
- self.log.push(format!("{:} has joined the game.", player_description));
+ self.log.push(format!("{:} has joined the game. [{:?}]", player.name, player_description));
self.players.push(player);
@@ -162,13 +146,19 @@ impl Game {
self
}
- fn can_start(&self) -> bool {
+ pub fn can_start(&self) -> bool {
return self.players.len() == self.player_num
&& self.players.iter().all(|t| t.cryps.len() == self.player_cryps)
}
pub fn start(mut self) -> Game {
self.log.push("Game starting...".to_string());
+
+ // forfeit
+ if self.finished() {
+ return self.finish();
+ }
+
self.skill_phase_start()
}
@@ -190,6 +180,7 @@ impl Game {
self.phase = Phase::Skill;
self.pve_add_skills();
+
if self.skill_phase_finished() {
return self.resolve_phase_start()
}
@@ -317,6 +308,10 @@ impl Game {
}
fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Game, Error> {
+ if self.phase != Phase::Skill {
+ return Err(err_msg("game not in skill phase"));
+ }
+
self.player_by_id(player_id)?
.set_ready(true);
@@ -571,10 +566,12 @@ impl Game {
}
pub fn upkeep(mut self) -> Game {
- if self.phase != Phase::Skill {
- panic!("{:?} game not in skill phase during upkeep", self);
+ if self.phase == Phase::Finish {
+ return self;
}
+ println!("upkeep beginning: {:?} vs {:?}", self.players[0].name, self.players[1].name);
+
if !self.phase_timed_out() {
return self;
}
@@ -583,8 +580,10 @@ impl Game {
if !player.ready {
player.set_ready(true);
player.add_warning();
+ println!("upkeep: {:?} warned", player.name);
if player.warnings >= 3 {
player.forfeit();
+ println!("upkeep: {:?} forfeited", player.name);
self.log.push(format!("{:?} forfeited.", player.name));
}
}
@@ -891,8 +890,7 @@ pub fn game_instance_new(tx: &mut Transaction, players: Vec, game_id: Uu
game
.set_player_num(2)
.set_player_cryps(3)
- .set_instance(instance_id)
- .set_mode(GameMode::Pvp);
+ .set_instance(instance_id);
// create the initiators player
for player in players {
diff --git a/server/src/instance.rs b/server/src/instance.rs
index 6c732f1f..eca9ffb6 100644
--- a/server/src/instance.rs
+++ b/server/src/instance.rs
@@ -9,12 +9,16 @@ use failure::err_msg;
use std::iter;
+// timekeeping
+use chrono::prelude::*;
+use chrono::Duration;
+
use rpc::{InstanceLobbyParams, InstanceJoinParams, InstanceReadyParams, InstanceStateParams};
use account::Account;
use player::{Player, player_create, player_get, player_global_update};
use cryp::{Cryp, cryp_get};
use mob::{instance_mobs};
-use game::{Game, Phase, game_get, game_write, game_instance_new, game_instance_join};
+use game::{Game, Phase, game_get, game_write, game_instance_new};
use vbox::{Var};
use rpc::{RpcResult};
use names::{name};
@@ -29,7 +33,7 @@ enum InstancePhase {
#[derive(Debug,Clone,Serialize,Deserialize)]
struct Round {
player_ids: Vec,
- game_id: Uuid,
+ game_id: Option,
finished: bool,
}
@@ -41,8 +45,10 @@ pub struct Instance {
rounds: Vec>,
open: bool,
max_players: usize,
+ max_rounds: usize,
password: Option,
pub name: String,
+ phase_start: DateTime,
}
impl Instance {
@@ -54,8 +60,10 @@ impl Instance {
phase: InstancePhase::Lobby,
open: true,
max_players: 2,
+ max_rounds: 16,
name: String::new(),
password: None,
+ phase_start: Utc::now(),
}
}
@@ -67,13 +75,42 @@ impl Instance {
phase: InstancePhase::InProgress,
open: false,
max_players: 0,
+ max_rounds: 1,
name: "Global Matchmaking".to_string(),
password: None,
+ phase_start: Utc::now(),
}
}
- pub fn upkeep(mut self) -> Instance {
- self
+ fn phase_timed_out(&self) -> bool {
+ Utc::now().signed_duration_since(self.phase_start).num_seconds() > 60
+ }
+
+ fn timed_out_players(&self) -> Vec {
+ self.players
+ .iter()
+ .filter(|p| !p.ready)
+ .filter(|p| self.current_game_id(p.id).is_none())
+ .map(|p| p.id)
+ .collect::>()
+ }
+
+ pub fn upkeep(mut self) -> (Instance, Vec) {
+ if self.phase != InstancePhase::InProgress {
+ return (self, vec![]);
+ }
+
+ if !self.phase_timed_out() {
+ return (self, vec![]);
+ }
+
+ let new_games = self
+ .timed_out_players()
+ .iter()
+ .filter_map(|p| self.player_ready(*p).unwrap())
+ .collect::>();
+
+ (self, new_games)
}
fn set_max_players(mut self, max: usize) -> Result {
@@ -94,6 +131,15 @@ impl Instance {
Ok(self)
}
+ fn set_max_rounds(mut self, rounds: usize) -> Result {
+ if rounds == 0 {
+ return Err(err_msg("max rounds must be nonzero"));
+ }
+
+ self.max_rounds = rounds;
+ Ok(self)
+ }
+
fn add_bots(mut self) -> Instance {
self.open = false;
self.players = iter::repeat_with(|| {
@@ -118,79 +164,98 @@ impl Instance {
Ok(self)
}
- pub fn player_update(mut self, player: Player, ignore_phase: bool) -> Result {
- if !ignore_phase && self.phase != InstancePhase::InProgress {
- return Err(format_err!("instance not in vbox phase ({:?})", self.phase));
- }
-
- let i = self.players
- .iter()
- .position(|p| p.id == player.id)
- .ok_or(err_msg("player_id not found"))?;
-
- self.players[i] = player;
-
- Ok(self)
- }
-
- fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Instance, Error> {
+ fn player_ready(&mut self, player_id: Uuid) -> Result