warden working
This commit is contained in:
parent
d3c301a3ac
commit
5d7095a06d
@ -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() {
|
function scoreBoard() {
|
||||||
const players = instance.players.map((p, i) =>
|
const players = instance.players.map((p, i) => {
|
||||||
|
const pText = playerText(p);
|
||||||
|
console.log(pText);
|
||||||
|
return (
|
||||||
<tr key={i}
|
<tr key={i}
|
||||||
className={p.ready ? 'ready' : ''}>
|
className={p.ready ? 'ready' : ''}>
|
||||||
<td>{p.name}</td>
|
<td>{p.name}</td>
|
||||||
<td>{p.score.wins} / {p.score.losses}</td>
|
<td>{p.score.wins} / {p.score.losses}</td>
|
||||||
<td>{p.ready ? 'ready' : ''}</td>
|
<td>{pText}</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
@ -26,16 +26,6 @@ pub enum Phase {
|
|||||||
Finish,
|
Finish,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
|
|
||||||
pub enum GameMode {
|
|
||||||
Normal,
|
|
||||||
Pvp,
|
|
||||||
Zone3v2Attack,
|
|
||||||
Zone2v2Caster,
|
|
||||||
Zone3v3MeleeMiniboss,
|
|
||||||
Zone3v3HealerBoss,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
@ -47,7 +37,6 @@ pub struct Game {
|
|||||||
pub resolved: Vec<Resolution>,
|
pub resolved: Vec<Resolution>,
|
||||||
pub log: Vec<String>,
|
pub log: Vec<String>,
|
||||||
pub instance: Option<Uuid>,
|
pub instance: Option<Uuid>,
|
||||||
pub mode: GameMode,
|
|
||||||
phase_start: DateTime<Utc>,
|
phase_start: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +52,6 @@ impl Game {
|
|||||||
resolved: vec![],
|
resolved: vec![],
|
||||||
log: vec![],
|
log: vec![],
|
||||||
instance: None,
|
instance: None,
|
||||||
mode: GameMode::Normal,
|
|
||||||
phase_start: Utc::now(),
|
phase_start: Utc::now(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -83,16 +71,11 @@ impl Game {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_mode(&mut self, mode: GameMode) -> &mut Game {
|
|
||||||
self.mode = mode;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn joinable(&self) -> bool {
|
pub fn joinable(&self) -> bool {
|
||||||
self.can_start()
|
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 {
|
if self.players.len() == self.player_num {
|
||||||
return Err(err_msg("maximum number of players"));
|
return Err(err_msg("maximum number of players"));
|
||||||
}
|
}
|
||||||
@ -102,11 +85,12 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if player.cryps.iter().all(|c| c.skills.len() == 0) {
|
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::<Vec<String>>().join(", ");
|
let player_description = player.cryps.iter().map(|c| c.name.clone()).collect::<Vec<String>>().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);
|
self.players.push(player);
|
||||||
|
|
||||||
@ -162,13 +146,19 @@ impl Game {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_start(&self) -> bool {
|
pub fn can_start(&self) -> bool {
|
||||||
return self.players.len() == self.player_num
|
return self.players.len() == self.player_num
|
||||||
&& self.players.iter().all(|t| t.cryps.len() == self.player_cryps)
|
&& self.players.iter().all(|t| t.cryps.len() == self.player_cryps)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(mut self) -> Game {
|
pub fn start(mut self) -> Game {
|
||||||
self.log.push("Game starting...".to_string());
|
self.log.push("Game starting...".to_string());
|
||||||
|
|
||||||
|
// forfeit
|
||||||
|
if self.finished() {
|
||||||
|
return self.finish();
|
||||||
|
}
|
||||||
|
|
||||||
self.skill_phase_start()
|
self.skill_phase_start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +180,7 @@ impl Game {
|
|||||||
self.phase = Phase::Skill;
|
self.phase = Phase::Skill;
|
||||||
|
|
||||||
self.pve_add_skills();
|
self.pve_add_skills();
|
||||||
|
|
||||||
if self.skill_phase_finished() {
|
if self.skill_phase_finished() {
|
||||||
return self.resolve_phase_start()
|
return self.resolve_phase_start()
|
||||||
}
|
}
|
||||||
@ -317,6 +308,10 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn player_ready(&mut self, player_id: Uuid) -> Result<&mut Game, Error> {
|
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)?
|
self.player_by_id(player_id)?
|
||||||
.set_ready(true);
|
.set_ready(true);
|
||||||
|
|
||||||
@ -571,10 +566,12 @@ impl Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn upkeep(mut self) -> Game {
|
pub fn upkeep(mut self) -> Game {
|
||||||
if self.phase != Phase::Skill {
|
if self.phase == Phase::Finish {
|
||||||
panic!("{:?} game not in skill phase during upkeep", self);
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("upkeep beginning: {:?} vs {:?}", self.players[0].name, self.players[1].name);
|
||||||
|
|
||||||
if !self.phase_timed_out() {
|
if !self.phase_timed_out() {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -583,8 +580,10 @@ impl Game {
|
|||||||
if !player.ready {
|
if !player.ready {
|
||||||
player.set_ready(true);
|
player.set_ready(true);
|
||||||
player.add_warning();
|
player.add_warning();
|
||||||
|
println!("upkeep: {:?} warned", player.name);
|
||||||
if player.warnings >= 3 {
|
if player.warnings >= 3 {
|
||||||
player.forfeit();
|
player.forfeit();
|
||||||
|
println!("upkeep: {:?} forfeited", player.name);
|
||||||
self.log.push(format!("{:?} forfeited.", player.name));
|
self.log.push(format!("{:?} forfeited.", player.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -891,8 +890,7 @@ pub fn game_instance_new(tx: &mut Transaction, players: Vec<Player>, game_id: Uu
|
|||||||
game
|
game
|
||||||
.set_player_num(2)
|
.set_player_num(2)
|
||||||
.set_player_cryps(3)
|
.set_player_cryps(3)
|
||||||
.set_instance(instance_id)
|
.set_instance(instance_id);
|
||||||
.set_mode(GameMode::Pvp);
|
|
||||||
|
|
||||||
// create the initiators player
|
// create the initiators player
|
||||||
for player in players {
|
for player in players {
|
||||||
|
|||||||
@ -9,12 +9,16 @@ use failure::err_msg;
|
|||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
|
// timekeeping
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use chrono::Duration;
|
||||||
|
|
||||||
use rpc::{InstanceLobbyParams, InstanceJoinParams, InstanceReadyParams, InstanceStateParams};
|
use rpc::{InstanceLobbyParams, InstanceJoinParams, InstanceReadyParams, InstanceStateParams};
|
||||||
use account::Account;
|
use account::Account;
|
||||||
use player::{Player, player_create, player_get, player_global_update};
|
use player::{Player, player_create, player_get, player_global_update};
|
||||||
use cryp::{Cryp, cryp_get};
|
use cryp::{Cryp, cryp_get};
|
||||||
use mob::{instance_mobs};
|
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 vbox::{Var};
|
||||||
use rpc::{RpcResult};
|
use rpc::{RpcResult};
|
||||||
use names::{name};
|
use names::{name};
|
||||||
@ -29,7 +33,7 @@ enum InstancePhase {
|
|||||||
#[derive(Debug,Clone,Serialize,Deserialize)]
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
||||||
struct Round {
|
struct Round {
|
||||||
player_ids: Vec<Uuid>,
|
player_ids: Vec<Uuid>,
|
||||||
game_id: Uuid,
|
game_id: Option<Uuid>,
|
||||||
finished: bool,
|
finished: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,8 +45,10 @@ pub struct Instance {
|
|||||||
rounds: Vec<Vec<Round>>,
|
rounds: Vec<Vec<Round>>,
|
||||||
open: bool,
|
open: bool,
|
||||||
max_players: usize,
|
max_players: usize,
|
||||||
|
max_rounds: usize,
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
phase_start: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance {
|
impl Instance {
|
||||||
@ -54,8 +60,10 @@ impl Instance {
|
|||||||
phase: InstancePhase::Lobby,
|
phase: InstancePhase::Lobby,
|
||||||
open: true,
|
open: true,
|
||||||
max_players: 2,
|
max_players: 2,
|
||||||
|
max_rounds: 16,
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
password: None,
|
password: None,
|
||||||
|
phase_start: Utc::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,13 +75,42 @@ impl Instance {
|
|||||||
phase: InstancePhase::InProgress,
|
phase: InstancePhase::InProgress,
|
||||||
open: false,
|
open: false,
|
||||||
max_players: 0,
|
max_players: 0,
|
||||||
|
max_rounds: 1,
|
||||||
name: "Global Matchmaking".to_string(),
|
name: "Global Matchmaking".to_string(),
|
||||||
password: None,
|
password: None,
|
||||||
|
phase_start: Utc::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upkeep(mut self) -> Instance {
|
fn phase_timed_out(&self) -> bool {
|
||||||
self
|
Utc::now().signed_duration_since(self.phase_start).num_seconds() > 60
|
||||||
|
}
|
||||||
|
|
||||||
|
fn timed_out_players(&self) -> Vec<Uuid> {
|
||||||
|
self.players
|
||||||
|
.iter()
|
||||||
|
.filter(|p| !p.ready)
|
||||||
|
.filter(|p| self.current_game_id(p.id).is_none())
|
||||||
|
.map(|p| p.id)
|
||||||
|
.collect::<Vec<Uuid>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn upkeep(mut self) -> (Instance, Vec<Game>) {
|
||||||
|
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::<Vec<Game>>();
|
||||||
|
|
||||||
|
(self, new_games)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_max_players(mut self, max: usize) -> Result<Instance, Error> {
|
fn set_max_players(mut self, max: usize) -> Result<Instance, Error> {
|
||||||
@ -94,6 +131,15 @@ impl Instance {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_max_rounds(mut self, rounds: usize) -> Result<Instance, Error> {
|
||||||
|
if rounds == 0 {
|
||||||
|
return Err(err_msg("max rounds must be nonzero"));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.max_rounds = rounds;
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
fn add_bots(mut self) -> Instance {
|
fn add_bots(mut self) -> Instance {
|
||||||
self.open = false;
|
self.open = false;
|
||||||
self.players = iter::repeat_with(|| {
|
self.players = iter::repeat_with(|| {
|
||||||
@ -118,79 +164,98 @@ impl Instance {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn player_update(mut self, player: Player, ignore_phase: bool) -> Result<Instance, Error> {
|
fn player_ready(&mut self, player_id: Uuid) -> Result<Option<Game>, Error> {
|
||||||
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> {
|
|
||||||
if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) {
|
if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) {
|
||||||
return Err(err_msg("instance not in start or vbox phase"));
|
return Err(err_msg("instance not in start or vbox phase"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LOBBY CHECKS
|
||||||
|
if self.phase == InstancePhase::Lobby {
|
||||||
let i = self.players
|
let i = self.players
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.position(|p| p.id == player_id)
|
.position(|p| p.id == player_id)
|
||||||
.ok_or(err_msg("player_id not found"))?;
|
.ok_or(err_msg("player_id not found"))?;
|
||||||
|
|
||||||
if self.phase != InstancePhase::Lobby && self.players[i].cryps.iter().all(|c| c.skills.len() == 0) {
|
let v = !self.players[i].ready;
|
||||||
return Err(err_msg("your cryps have no skills"));
|
self.players[i].set_ready(v);
|
||||||
|
|
||||||
|
match self.can_start() {
|
||||||
|
true => {
|
||||||
|
self.start();
|
||||||
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
false => return Ok(None),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// GAME PHASE READY
|
||||||
|
let i = self.players
|
||||||
|
.iter_mut()
|
||||||
|
.position(|p| p.id == player_id)
|
||||||
|
.ok_or(err_msg("player_id not found"))?;
|
||||||
|
|
||||||
let v = !self.players[i].ready;
|
let v = !self.players[i].ready;
|
||||||
self.players[i].set_ready(v);
|
self.players[i].set_ready(v);
|
||||||
|
|
||||||
if self.phase == InstancePhase::Lobby && self.can_start() {
|
// start the game even if afk noobs have no skills
|
||||||
self.start();
|
if !self.phase_timed_out() && self.players[i].cryps.iter().all(|c| c.skills.len() == 0) {
|
||||||
|
return Err(err_msg("your cryps have no skills"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self)
|
// create a game object if both players are ready
|
||||||
|
// this should only happen once
|
||||||
|
|
||||||
|
let all_ready = self.round_ready_check(player_id);
|
||||||
|
|
||||||
|
if !all_ready {
|
||||||
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn player_has_pve_game(&self, player_id: Uuid) -> bool {
|
let game = self.create_round_game(player_id);
|
||||||
let opponent_id = self.current_round(player_id).player_ids
|
|
||||||
.iter()
|
|
||||||
.find(|p| **p != player_id)
|
|
||||||
.expect("unable to find opponent");
|
|
||||||
|
|
||||||
return self.players
|
{
|
||||||
.iter()
|
let round_num = self.rounds.len() - 1;
|
||||||
.find(|p| p.id == *opponent_id)
|
let current_round = self.rounds[round_num]
|
||||||
.expect("unable to find opponent")
|
.iter_mut()
|
||||||
.bot;
|
.find(|g| g.player_ids.contains(&player_id))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
current_round.game_id = Some(game.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bot_vs_player_game(&self, player_id: Uuid) -> Result<Game, Error> {
|
return Ok(Some(game));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn round_ready_check(&mut self, player_id: Uuid) -> bool {
|
||||||
let current_round = self.current_round(player_id);
|
let current_round = self.current_round(player_id);
|
||||||
let bot_id = current_round.player_ids.iter().find(|id| **id != player_id).unwrap();
|
self.players
|
||||||
|
.iter()
|
||||||
let plr = self.players.clone().into_iter().find(|p| p.id == player_id).unwrap();
|
.filter(|p| current_round.player_ids.contains(&p.id))
|
||||||
let bot = self.players.clone().into_iter().find(|p| p.id == *bot_id).unwrap();
|
.all(|p| p.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe just embed the games in the instance
|
||||||
|
// but seems hella inefficient
|
||||||
|
fn create_round_game(&self, player_id: Uuid) -> Game {
|
||||||
|
let current_round = self.current_round(player_id);
|
||||||
let mut game = Game::new();
|
let mut game = Game::new();
|
||||||
game.id = current_round.game_id;
|
|
||||||
game
|
game
|
||||||
.set_player_num(2)
|
.set_player_num(2)
|
||||||
.set_player_cryps(3)
|
.set_player_cryps(3)
|
||||||
.set_instance(self.id);
|
.set_instance(self.id);
|
||||||
|
|
||||||
game
|
// create the initiators player
|
||||||
.player_add(plr)?
|
for player in self.players
|
||||||
.player_add(bot)?;
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|p| current_round.player_ids.contains(&p.id)) {
|
||||||
|
game.player_add(player).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
game = game.start();
|
assert!(game.can_start());
|
||||||
|
return game.start();
|
||||||
Ok(game)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_start(&self) -> bool {
|
fn can_start(&self) -> bool {
|
||||||
@ -205,119 +270,46 @@ impl Instance {
|
|||||||
|
|
||||||
fn next_round(&mut self) -> &mut Instance {
|
fn next_round(&mut self) -> &mut Instance {
|
||||||
self.phase = InstancePhase::InProgress;
|
self.phase = InstancePhase::InProgress;
|
||||||
|
self.phase_start = Utc::now();
|
||||||
|
|
||||||
|
if self.rounds.len() >= self.max_rounds {
|
||||||
|
return self.finish();
|
||||||
|
}
|
||||||
|
|
||||||
self.players.iter_mut().for_each(|p| {
|
self.players.iter_mut().for_each(|p| {
|
||||||
p.ready = false;
|
p.set_ready(false);
|
||||||
p.vbox.fill();
|
p.vbox.fill();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.generate_rounds();
|
self.generate_rounds();
|
||||||
self.bot_vbox_phase();
|
self.bot_round_actions();
|
||||||
self.bot_games_phase();
|
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_ready(&self) -> bool {
|
fn finish(&mut self) -> &mut Instance {
|
||||||
self.players.iter().all(|p| p.ready)
|
self.phase = InstancePhase::Finished;
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
|
fn bot_round_actions(&mut self) -> &mut Instance {
|
||||||
let round_num = self.rounds.len() - 1;
|
|
||||||
self.rounds[round_num]
|
|
||||||
.iter_mut()
|
|
||||||
.find(|r| r.game_id == game.id)
|
|
||||||
.ok_or(err_msg("could not find matchup in current round"))?
|
|
||||||
.finished = true;
|
|
||||||
|
|
||||||
// if you don't win, you lose
|
|
||||||
// ties can happen if both players forfeit
|
|
||||||
let winner_id = match game.winner() {
|
|
||||||
Some(w) => w.id,
|
|
||||||
None => Uuid::nil(),
|
|
||||||
};
|
|
||||||
|
|
||||||
for player in game.players.iter() {
|
|
||||||
let mut player = self.account_player(player.id)?;
|
|
||||||
match player.id == winner_id {
|
|
||||||
true => player.add_win(),
|
|
||||||
false => player.add_loss(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.all_games_finished() {
|
|
||||||
self.next_round();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_games_finished(&self) -> bool {
|
|
||||||
match self.rounds.last() {
|
|
||||||
Some(r) => r.iter().all(|g| g.finished),
|
|
||||||
None => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bot_vbox_phase(&mut self) -> &mut Instance {
|
|
||||||
for bot in self.players.iter_mut().filter(|p| p.bot) {
|
for bot in self.players.iter_mut().filter(|p| p.bot) {
|
||||||
bot.vbox.fill();
|
bot.vbox.fill();
|
||||||
bot.autobuy();
|
bot.autobuy();
|
||||||
bot.set_ready(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
let games = self.players
|
||||||
}
|
.clone()
|
||||||
|
|
||||||
fn bot_games_phase(&mut self) -> &mut Instance {
|
|
||||||
if self.phase != InstancePhase::InProgress {
|
|
||||||
panic!("instance not in progress phase");
|
|
||||||
}
|
|
||||||
|
|
||||||
let r = self.rounds.len() - 1;
|
|
||||||
// println!("round num {:?}", r);
|
|
||||||
// println!("{:?}", self.rounds[r]);
|
|
||||||
for mut round in self.rounds[r].iter_mut() {
|
|
||||||
if self.players
|
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|p| round.player_ids.contains(&p.id) && p.bot && p.ready)
|
.filter(|b| b.bot)
|
||||||
.count() == 2 {
|
.filter_map(|b| self.player_ready(b.id).unwrap())
|
||||||
// println!("should play a game between {:?}", round.player_ids);
|
.collect::<Vec<Game>>();
|
||||||
let a = self.players.clone().into_iter().find(|p| p.id == round.player_ids[0]).unwrap();
|
|
||||||
let b = self.players.clone().into_iter().find(|p| p.id == round.player_ids[1]).unwrap();
|
|
||||||
|
|
||||||
// println!("{:?} vs {:?}", a.name, b.name);
|
|
||||||
|
|
||||||
let mut game = Game::new();
|
|
||||||
game
|
|
||||||
.set_player_num(2)
|
|
||||||
.set_player_cryps(3);
|
|
||||||
|
|
||||||
game
|
|
||||||
.player_add(a).unwrap()
|
|
||||||
.player_add(b).unwrap();
|
|
||||||
|
|
||||||
game = game.start();
|
|
||||||
|
|
||||||
if !game.finished() {
|
|
||||||
panic!("game not finished {:?}", game)
|
|
||||||
}
|
|
||||||
|
|
||||||
let winner = match game.winner() {
|
|
||||||
Some(w) => w,
|
|
||||||
None => panic!("game has no winner {:?}", game),
|
|
||||||
};
|
|
||||||
|
|
||||||
round.finished = true;
|
|
||||||
|
|
||||||
for player in game.players.iter() {
|
|
||||||
let mut player = self.players.iter_mut().find(|p| p.id == player.id).unwrap();
|
|
||||||
match player.id == winner.id {
|
|
||||||
true => player.add_win(),
|
|
||||||
false => player.add_loss(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for game in games {
|
||||||
|
if game.finished() {
|
||||||
|
self.game_finished(&game).unwrap();
|
||||||
|
} else {
|
||||||
|
println!("{:?} unfishededes", game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,7 +337,7 @@ impl Instance {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, id)| Round {
|
.map(|(i, id)| Round {
|
||||||
player_ids: vec![*id, matched_players[np - (i + 1)]],
|
player_ids: vec![*id, matched_players[np - (i + 1)]],
|
||||||
game_id: Uuid::new_v4(),
|
game_id: None,
|
||||||
finished: false,
|
finished: false,
|
||||||
})
|
})
|
||||||
.collect::<Vec<Round>>();
|
.collect::<Vec<Round>>();
|
||||||
@ -366,24 +358,59 @@ impl Instance {
|
|||||||
current_round
|
current_round
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_game(&self, player_id: Uuid) -> Option<Uuid> {
|
fn current_game_id(&self, player_id: Uuid) -> Option<Uuid> {
|
||||||
if self.phase == InstancePhase::Lobby {
|
if self.phase == InstancePhase::Lobby {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_round = self.current_round(player_id);
|
let current_round = self.current_round(player_id);
|
||||||
|
|
||||||
let can_start = self.players
|
if current_round.finished || current_round.game_id.is_none() {
|
||||||
.iter()
|
return None;
|
||||||
.filter(|p| current_round.player_ids.contains(&p.id))
|
}
|
||||||
.all(|p| p.ready);
|
|
||||||
|
|
||||||
match can_start {
|
return current_round.game_id;
|
||||||
true => match current_round.finished {
|
}
|
||||||
true => None,
|
|
||||||
false => Some(current_round.game_id),
|
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
|
||||||
},
|
let round_num = self.rounds.len() - 1;
|
||||||
false => None,
|
self.rounds[round_num]
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|r| r.game_id.is_some())
|
||||||
|
.find(|r| r.game_id.unwrap() == game.id)
|
||||||
|
.ok_or(err_msg("could not find matchup in current round"))?
|
||||||
|
.finished = true;
|
||||||
|
|
||||||
|
// if you don't win, you lose
|
||||||
|
// ties can happen if both players forfeit
|
||||||
|
let winner_id = match game.winner() {
|
||||||
|
Some(w) => w.id,
|
||||||
|
None => Uuid::nil(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for player in game.players.iter() {
|
||||||
|
let mut player = self.account_player(player.id)?;
|
||||||
|
match player.id == winner_id {
|
||||||
|
true => player.add_win(),
|
||||||
|
false => player.add_loss(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.all_games_finished() {
|
||||||
|
self.next_round();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_ready(&self) -> bool {
|
||||||
|
self.players.iter().all(|p| p.ready)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn all_games_finished(&self) -> bool {
|
||||||
|
match self.rounds.last() {
|
||||||
|
Some(r) => r.iter().all(|g| g.finished),
|
||||||
|
None => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +426,7 @@ impl Instance {
|
|||||||
if self.phase == InstancePhase::Lobby {
|
if self.phase == InstancePhase::Lobby {
|
||||||
return Err(err_msg("game not yet started"));
|
return Err(err_msg("game not yet started"));
|
||||||
}
|
}
|
||||||
if self.current_game(account).is_some() {
|
if self.current_game_id(account).is_some() {
|
||||||
return Err(err_msg("you cannot perform vbox actions while in a game"));
|
return Err(err_msg("you cannot perform vbox actions while in a game"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,8 +579,7 @@ pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Erro
|
|||||||
let query = "
|
let query = "
|
||||||
SELECT data, id
|
SELECT data, id
|
||||||
FROM instances
|
FROM instances
|
||||||
WHERE updated_at < now() - interval '5 seconds'
|
WHERE id != '00000000-0000-0000-0000-000000000000';
|
||||||
AND id != '00000000-0000-0000-0000-000000000000';
|
|
||||||
";
|
";
|
||||||
|
|
||||||
let result = tx
|
let result = tx
|
||||||
@ -636,32 +662,9 @@ pub fn instance_join(params: InstanceJoinParams, tx: &mut Transaction, account:
|
|||||||
pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
||||||
let mut instance = instance_get(tx, params.instance_id)?;
|
let mut instance = instance_get(tx, params.instance_id)?;
|
||||||
let player_id = instance.account_player(account.id)?.id;
|
let player_id = instance.account_player(account.id)?.id;
|
||||||
instance.player_ready(player_id)?;
|
|
||||||
|
|
||||||
if let Some(game_id) = instance.current_game(player_id) {
|
if let Some(game) = instance.player_ready(player_id)? {
|
||||||
match instance.player_has_pve_game(player_id) {
|
|
||||||
true => match game_get(tx, game_id) {
|
|
||||||
Ok(g) => g,
|
|
||||||
Err(_) => {
|
|
||||||
let game = instance.bot_vs_player_game(player_id)?;
|
|
||||||
game_write(tx, &game)?;
|
game_write(tx, &game)?;
|
||||||
game
|
|
||||||
},
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
let opponent_id = *instance
|
|
||||||
.current_round(account.id)
|
|
||||||
.player_ids
|
|
||||||
.iter()
|
|
||||||
.find(|p| **p != account.id)
|
|
||||||
.expect("could not find opponent");
|
|
||||||
|
|
||||||
let a = instance.account_player(account.id)?.clone();
|
|
||||||
let b = instance.account_player(opponent_id)?.clone();
|
|
||||||
let players = vec![a, b];
|
|
||||||
game_instance_new(tx, players, game_id, instance.id)?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
instance_update(tx, instance)
|
instance_update(tx, instance)
|
||||||
@ -670,11 +673,11 @@ pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account
|
|||||||
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<RpcResult, Error> {
|
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<RpcResult, Error> {
|
||||||
let instance = instance_get(tx, params.instance_id)?;
|
let instance = instance_get(tx, params.instance_id)?;
|
||||||
|
|
||||||
if let Some(game_id) = instance.current_game(account.id) {
|
if let Some(game_id) = instance.current_game_id(account.id) {
|
||||||
let game = game_get(tx, game_id)?;
|
let game = game_get(tx, game_id)?;
|
||||||
|
|
||||||
|
// return the game until it's finished
|
||||||
if game.phase != Phase::Finish {
|
if game.phase != Phase::Finish {
|
||||||
|
|
||||||
return Ok(RpcResult::GameState(game))
|
return Ok(RpcResult::GameState(game))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -716,6 +719,7 @@ mod tests {
|
|||||||
fn instance_pve_test() {
|
fn instance_pve_test() {
|
||||||
let mut instance = Instance::new()
|
let mut instance = Instance::new()
|
||||||
.set_max_players(16).expect("unable to set max players")
|
.set_max_players(16).expect("unable to set max players")
|
||||||
|
.set_max_rounds(2).expect("max rounds failure")
|
||||||
.add_bots();
|
.add_bots();
|
||||||
|
|
||||||
let player_account = Uuid::new_v4();
|
let player_account = Uuid::new_v4();
|
||||||
@ -728,21 +732,9 @@ mod tests {
|
|||||||
assert_eq!(instance.phase, InstancePhase::Lobby);
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
||||||
instance.player_ready(player_id).unwrap();
|
instance.player_ready(player_id).unwrap();
|
||||||
|
|
||||||
assert_eq!(instance.phase, InstancePhase::InProgress);
|
assert_eq!(instance.phase, InstancePhase::Finished);
|
||||||
assert_eq!(instance.rounds[0].len(), 8);
|
assert_eq!(instance.rounds[0].len(), 8);
|
||||||
|
assert_eq!(instance.rounds.len(), 2);
|
||||||
instance.player_ready(player_id).unwrap();
|
|
||||||
|
|
||||||
assert!(instance.all_games_finished());
|
|
||||||
instance.next_round();
|
|
||||||
|
|
||||||
instance.player_ready(player_id).unwrap();
|
|
||||||
|
|
||||||
instance.next_round();
|
|
||||||
|
|
||||||
instance.player_ready(player_id).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(instance.rounds.len(), 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -784,5 +776,55 @@ mod tests {
|
|||||||
assert_eq!(instance.phase, InstancePhase::InProgress);
|
assert_eq!(instance.phase, InstancePhase::InProgress);
|
||||||
|
|
||||||
assert!(!instance.can_start());
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
instance.players[0].autobuy();
|
||||||
|
instance.players[1].autobuy();
|
||||||
|
|
||||||
|
instance.player_ready(a_id).expect("a ready");
|
||||||
|
let game = instance.player_ready(b_id).expect("b ready");
|
||||||
|
|
||||||
|
assert!(game.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn instance_upkeep_test() {
|
||||||
|
let mut instance = Instance::new()
|
||||||
|
.set_max_players(2)
|
||||||
|
.expect("could not create instance");
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let cryps = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"a".to_string(), cryps);
|
||||||
|
let a_id = player.id;
|
||||||
|
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
assert!(!instance.can_start());
|
||||||
|
|
||||||
|
let player_account = Uuid::new_v4();
|
||||||
|
let cryps = instance_mobs(player_account);
|
||||||
|
let player = Player::new(player_account, &"b".to_string(), cryps);
|
||||||
|
let b_id = player.id;
|
||||||
|
instance.add_player(player).expect("could not add player");
|
||||||
|
|
||||||
|
instance.players[0].autobuy();
|
||||||
|
|
||||||
|
instance.player_ready(a_id).expect("a ready");
|
||||||
|
instance.player_ready(b_id).expect("b ready");
|
||||||
|
|
||||||
|
instance.phase_start = Utc::now().checked_sub_signed(Duration::seconds(61)).unwrap();
|
||||||
|
|
||||||
|
let (mut instance, new_games) = instance.upkeep();
|
||||||
|
|
||||||
|
assert_eq!(new_games.len(), 1);
|
||||||
|
|
||||||
|
let game = &new_games[0];
|
||||||
|
assert!(game.finished());
|
||||||
|
|
||||||
|
instance.game_finished(game).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(instance.rounds.len(), 2);
|
||||||
|
assert!(instance.players.iter().all(|p| !p.ready));
|
||||||
|
|
||||||
|
println!("{:#?}", instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -73,14 +73,12 @@ impl Player {
|
|||||||
|
|
||||||
pub fn add_win(&mut self) -> &mut Player {
|
pub fn add_win(&mut self) -> &mut Player {
|
||||||
self.score.wins += 1;
|
self.score.wins += 1;
|
||||||
self.set_ready(false);
|
|
||||||
self.vbox.balance_add(12);
|
self.vbox.balance_add(12);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_loss(&mut self) -> &mut Player {
|
pub fn add_loss(&mut self) -> &mut Player {
|
||||||
self.score.losses += 1;
|
self.score.losses += 1;
|
||||||
self.set_ready(false);
|
|
||||||
self.vbox.balance_add(9);
|
self.vbox.balance_add(9);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,24 @@
|
|||||||
use std::time::{Duration};
|
|
||||||
use std::thread::sleep;
|
|
||||||
|
|
||||||
// Db Commons
|
// Db Commons
|
||||||
use postgres::transaction::Transaction;
|
use postgres::transaction::Transaction;
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use failure::err_msg;
|
|
||||||
|
|
||||||
use r2d2::{Pool};
|
use game::{games_need_upkeep, game_update, game_write, game_delete};
|
||||||
use r2d2_postgres::{PostgresConnectionManager};
|
use instance::{instances_need_upkeep, instance_update};
|
||||||
|
|
||||||
use game::{Game, games_need_upkeep, game_update};
|
|
||||||
use instance::{Instance, instances_need_upkeep, instance_update};
|
|
||||||
use net::{Db};
|
use net::{Db};
|
||||||
|
|
||||||
fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> {
|
fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> {
|
||||||
let games = games_need_upkeep(&mut tx)?;
|
let games = games_need_upkeep(&mut tx)?;
|
||||||
|
|
||||||
println!("warden: {:?} games active", games.len());
|
|
||||||
|
|
||||||
for mut game in games {
|
for mut game in games {
|
||||||
game_update(&mut tx, &game.upkeep())?;
|
let game = game.upkeep();
|
||||||
|
match game_update(&mut tx, &game) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("{:?}", e);
|
||||||
|
game_delete(&mut tx, game.id)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
@ -29,18 +28,26 @@ fn fetch_instances(mut tx: Transaction) -> Result<Transaction, Error> {
|
|||||||
let instances = instances_need_upkeep(&mut tx)?;
|
let instances = instances_need_upkeep(&mut tx)?;
|
||||||
|
|
||||||
for mut instance in instances {
|
for mut instance in instances {
|
||||||
instance_update(&mut tx, instance.upkeep())?;
|
let (instance, new_games) = instance.upkeep();
|
||||||
|
println!("{:?} new games", new_games.len());
|
||||||
|
for game in new_games {
|
||||||
|
game_write(&mut tx, &game)?;
|
||||||
|
}
|
||||||
|
instance_update(&mut tx, instance)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tx)
|
Ok(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn warden(db: Db) -> Result<(), Error> {
|
pub fn warden(db: Db) -> Result<(), Error> {
|
||||||
|
println!("upkeep beginning");
|
||||||
fetch_games(db.transaction()?)?
|
fetch_games(db.transaction()?)?
|
||||||
.commit()?;
|
.commit()?;
|
||||||
|
|
||||||
fetch_instances(db.transaction()?)?
|
fetch_instances(db.transaction()?)?
|
||||||
.commit()?;
|
.commit()?;
|
||||||
|
|
||||||
|
println!("upkeep done");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user