844 lines
24 KiB
Rust
844 lines
24 KiB
Rust
use uuid::Uuid;
|
|
|
|
use serde_cbor::{from_slice, to_vec};
|
|
|
|
use postgres::transaction::Transaction;
|
|
|
|
use failure::Error;
|
|
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};
|
|
use vbox::{Var};
|
|
use rpc::{RpcResult};
|
|
use names::{name};
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
enum InstancePhase {
|
|
Lobby,
|
|
InProgress,
|
|
Finished,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
|
struct Round {
|
|
player_ids: Vec<Uuid>,
|
|
game_id: Option<Uuid>,
|
|
finished: bool,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
|
pub struct Instance {
|
|
id: Uuid,
|
|
players: Vec<Player>,
|
|
phase: InstancePhase,
|
|
rounds: Vec<Vec<Round>>,
|
|
open: bool,
|
|
max_players: usize,
|
|
max_rounds: usize,
|
|
password: Option<String>,
|
|
pub name: String,
|
|
phase_end: DateTime<Utc>,
|
|
}
|
|
|
|
impl Instance {
|
|
fn new() -> Instance {
|
|
Instance {
|
|
id: Uuid::new_v4(),
|
|
players: vec![],
|
|
rounds: vec![],
|
|
phase: InstancePhase::Lobby,
|
|
open: true,
|
|
max_players: 2,
|
|
max_rounds: 16,
|
|
name: String::new(),
|
|
password: None,
|
|
phase_end: Utc::now(),
|
|
}
|
|
}
|
|
|
|
pub fn global(player: Player) -> Instance {
|
|
Instance {
|
|
id: Uuid::nil(),
|
|
players: vec![player],
|
|
rounds: vec![],
|
|
phase: InstancePhase::InProgress,
|
|
open: false,
|
|
max_players: 0,
|
|
max_rounds: 1,
|
|
name: "Global Matchmaking".to_string(),
|
|
password: None,
|
|
phase_end: Utc::now(),
|
|
}
|
|
}
|
|
|
|
fn phase_timed_out(&self) -> bool {
|
|
Utc::now().signed_duration_since(self.phase_end).num_milliseconds() > 0
|
|
}
|
|
|
|
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> {
|
|
if max > 16 || max % 2 != 0 {
|
|
return Err(err_msg("max players must be divisible by 2 and less than 16"));
|
|
}
|
|
|
|
self.max_players = max;
|
|
Ok(self)
|
|
}
|
|
|
|
fn set_name(mut self, name: String) -> Result<Instance, Error> {
|
|
if name.len() == 0 {
|
|
return Err(err_msg("name must have a length"));
|
|
}
|
|
|
|
self.name = name;
|
|
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 {
|
|
self.open = false;
|
|
self.players = iter::repeat_with(|| {
|
|
let bot_id = Uuid::new_v4();
|
|
let cryps = instance_mobs(bot_id);
|
|
let mut p = Player::new(bot_id, &name(), cryps).set_bot(true);
|
|
p.set_ready(true);
|
|
p
|
|
})
|
|
.take(15)
|
|
.collect::<Vec<Player>>();
|
|
self
|
|
}
|
|
|
|
fn add_player(&mut self, player: Player) -> Result<&mut Instance, Error> {
|
|
if self.players.len() >= self.max_players {
|
|
return Err(err_msg("game full"))
|
|
}
|
|
|
|
match self.players.iter().find(|p| p.id == player.id) {
|
|
Some(_p) => return Err(err_msg("already joined")),
|
|
None => (),
|
|
};
|
|
|
|
self.players.push(player);
|
|
Ok(self)
|
|
}
|
|
|
|
fn player_ready(&mut self, player_id: Uuid) -> Result<Option<Game>, Error> {
|
|
if ![InstancePhase::InProgress, InstancePhase::Lobby].contains(&self.phase) {
|
|
return Err(err_msg("instance not in start or vbox phase"));
|
|
}
|
|
|
|
// LOBBY CHECKS
|
|
if self.phase == InstancePhase::Lobby {
|
|
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;
|
|
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;
|
|
self.players[i].set_ready(v);
|
|
|
|
// start the game even if afk noobs have no skills
|
|
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"));
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
let game = self.create_round_game(player_id);
|
|
|
|
{
|
|
let round_num = self.rounds.len() - 1;
|
|
let current_round = self.rounds[round_num]
|
|
.iter_mut()
|
|
.find(|g| g.player_ids.contains(&player_id))
|
|
.unwrap();
|
|
|
|
current_round.game_id = Some(game.id);
|
|
}
|
|
|
|
return Ok(Some(game));
|
|
|
|
}
|
|
|
|
fn round_ready_check(&mut self, player_id: Uuid) -> bool {
|
|
let current_round = self.current_round(player_id);
|
|
self.players
|
|
.iter()
|
|
.filter(|p| current_round.player_ids.contains(&p.id))
|
|
.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();
|
|
|
|
game
|
|
.set_player_num(2)
|
|
.set_player_cryps(3)
|
|
.set_instance(self.id);
|
|
|
|
// create the initiators player
|
|
for player in self.players
|
|
.clone()
|
|
.into_iter()
|
|
.filter(|p| current_round.player_ids.contains(&p.id)) {
|
|
game.player_add(player).unwrap();
|
|
}
|
|
|
|
assert!(game.can_start());
|
|
return game.start();
|
|
}
|
|
|
|
fn can_start(&self) -> bool {
|
|
self.players.len() == self.max_players && self.all_ready()
|
|
}
|
|
|
|
fn start(&mut self) -> &mut Instance {
|
|
// self.players.sort_unstable_by_key(|p| p.id);
|
|
self.open = false;
|
|
self.next_round()
|
|
}
|
|
|
|
fn next_round(&mut self) -> &mut Instance {
|
|
self.phase = InstancePhase::InProgress;
|
|
self.phase_end = Utc::now()
|
|
.checked_add_signed(Duration::seconds(120))
|
|
.expect("could not set phase end");
|
|
|
|
|
|
if self.rounds.len() >= self.max_rounds {
|
|
return self.finish();
|
|
}
|
|
|
|
self.players.iter_mut().for_each(|p| {
|
|
p.set_ready(false);
|
|
p.vbox.fill();
|
|
});
|
|
|
|
self.generate_rounds();
|
|
self.bot_round_actions();
|
|
|
|
self
|
|
}
|
|
|
|
fn finish(&mut self) -> &mut Instance {
|
|
self.phase = InstancePhase::Finished;
|
|
self
|
|
}
|
|
|
|
fn finished(&self) -> bool {
|
|
self.phase == InstancePhase::Finished
|
|
}
|
|
|
|
fn bot_round_actions(&mut self) -> &mut Instance {
|
|
for bot in self.players.iter_mut().filter(|p| p.bot) {
|
|
bot.vbox.fill();
|
|
bot.autobuy();
|
|
}
|
|
|
|
let games = self.players
|
|
.clone()
|
|
.iter()
|
|
.filter(|b| b.bot)
|
|
.filter_map(|b| self.player_ready(b.id).unwrap())
|
|
.collect::<Vec<Game>>();
|
|
|
|
for game in games {
|
|
if game.finished() {
|
|
self.game_finished(&game).unwrap();
|
|
} else {
|
|
println!("{:?} unfishededes", game);
|
|
}
|
|
}
|
|
|
|
self
|
|
}
|
|
|
|
fn generate_rounds(&mut self) -> &mut Instance {
|
|
let round_num = self.rounds.len();
|
|
let mut matched_players = self.players
|
|
.iter()
|
|
.map(|p| p.id)
|
|
.collect::<Vec<Uuid>>();
|
|
|
|
let np = matched_players.len();
|
|
|
|
if round_num > 0 {
|
|
matched_players.rotate_right(round_num % np);
|
|
matched_players.swap(0,1);
|
|
}
|
|
|
|
// only set up for even player numbers atm
|
|
// no byes
|
|
let current_round = matched_players[0..(np / 2)]
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, id)| Round {
|
|
player_ids: vec![*id, matched_players[np - (i + 1)]],
|
|
game_id: None,
|
|
finished: false,
|
|
})
|
|
.collect::<Vec<Round>>();
|
|
|
|
self.rounds.push(current_round);
|
|
|
|
self
|
|
}
|
|
|
|
fn current_round(&self, player_id: Uuid) -> &Round {
|
|
let round_num = self.rounds.len() - 1;
|
|
|
|
let current_round = self.rounds[round_num]
|
|
.iter()
|
|
.find(|g| g.player_ids.contains(&player_id))
|
|
.unwrap();
|
|
|
|
current_round
|
|
}
|
|
|
|
fn current_game_id(&self, player_id: Uuid) -> Option<Uuid> {
|
|
if self.phase == InstancePhase::Lobby {
|
|
return None;
|
|
}
|
|
|
|
let current_round = self.current_round(player_id);
|
|
|
|
if current_round.finished || current_round.game_id.is_none() {
|
|
return None;
|
|
}
|
|
|
|
return current_round.game_id;
|
|
}
|
|
|
|
fn game_finished(&mut self, game: &Game) -> Result<&mut Instance, Error> {
|
|
let round_num = self.rounds.len() - 1;
|
|
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,
|
|
}
|
|
}
|
|
|
|
// PLAYER ACTIONS
|
|
fn account_player(&mut self, account: Uuid) -> Result<&mut Player, Error> {
|
|
self.players
|
|
.iter_mut()
|
|
.find(|p| p.id == account)
|
|
.ok_or(err_msg("account not in instance"))
|
|
}
|
|
|
|
pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> {
|
|
if self.phase == InstancePhase::Lobby {
|
|
return Err(err_msg("game not yet started"));
|
|
}
|
|
if self.current_game_id(account).is_some() {
|
|
return Err(err_msg("you cannot perform vbox actions while in a game"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn vbox_discard(mut self, account: Uuid) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_discard()?;
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn vbox_accept(mut self, account: Uuid, group: usize, index: usize) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_accept(group, index)?;
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn vbox_combine(mut self, account: Uuid, indices: Vec<usize>) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_combine(indices)?;
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn vbox_reclaim(mut self, account: Uuid, index: usize) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_reclaim(index)?;
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn vbox_apply(mut self, account: Uuid, index: usize, cryp_id: Uuid) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_apply(index, cryp_id)?;
|
|
Ok(self)
|
|
}
|
|
|
|
pub fn vbox_unequip(mut self, account: Uuid, target: Var, cryp_id: Uuid) -> Result<Instance, Error> {
|
|
self.vbox_action_allowed(account)?;
|
|
self.account_player(account)?
|
|
.vbox_unequip(target, cryp_id)?;
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
pub fn instance_create(tx: &mut Transaction, instance: Instance) -> Result<Instance, Error> {
|
|
let instance_bytes = to_vec(&instance)?;
|
|
|
|
let query = "
|
|
INSERT INTO instances (id, data, open)
|
|
VALUES ($1, $2, $3)
|
|
RETURNING id;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[&instance.id, &instance_bytes, &instance.open])?;
|
|
|
|
result.iter().next().ok_or(format_err!("no instances written"))?;
|
|
|
|
return Ok(instance);
|
|
}
|
|
|
|
pub fn instance_update(tx: &mut Transaction, instance: Instance) -> Result<Instance, Error> {
|
|
let instance_bytes = to_vec(&instance)?;
|
|
|
|
let query = "
|
|
UPDATE instances
|
|
SET data = $1, open = $2, finished = $3, updated_at = now()
|
|
WHERE id = $4
|
|
RETURNING id, data;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[&instance_bytes, &instance.open, &instance.finished(), &instance.id])?;
|
|
|
|
result.iter().next().ok_or(err_msg("no instance row returned"))?;
|
|
|
|
// println!("{:?} wrote instance", instance.id);
|
|
|
|
return Ok(instance);
|
|
}
|
|
|
|
pub fn instance_get(tx: &mut Transaction, instance_id: Uuid) -> Result<Instance, Error> {
|
|
let query = "
|
|
SELECT *
|
|
FROM instances
|
|
WHERE id = $1
|
|
FOR UPDATE;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[&instance_id])?;
|
|
|
|
let returned = match result.iter().next() {
|
|
Some(row) => row,
|
|
None => return Err(err_msg("instance not found")),
|
|
};
|
|
|
|
let instance_bytes: Vec<u8> = returned.get("data");
|
|
let instance = from_slice::<Instance>(&instance_bytes)?;
|
|
|
|
return Ok(instance);
|
|
}
|
|
|
|
pub fn instance_delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
|
|
let query = "
|
|
DELETE
|
|
FROM instances
|
|
WHERE id = $1;
|
|
";
|
|
|
|
let result = tx
|
|
.execute(query, &[&id])?;
|
|
|
|
if result != 1 {
|
|
return Err(format_err!("unable to delete instance {:?}", id));
|
|
}
|
|
|
|
println!("instance deleted {:?}", id);
|
|
|
|
return Ok(());
|
|
}
|
|
|
|
pub fn instance_get_open(tx: &mut Transaction) -> Result<Instance, Error> {
|
|
let query = "
|
|
SELECT *
|
|
FROM instances
|
|
WHERE open = true;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[])?;
|
|
|
|
let returned = match result.iter().next() {
|
|
Some(row) => row,
|
|
None => return Err(err_msg("instance not found")),
|
|
};
|
|
|
|
let instance_bytes: Vec<u8> = returned.get("data");
|
|
let instance = from_slice::<Instance>(&instance_bytes)?;
|
|
|
|
return Ok(instance);
|
|
}
|
|
|
|
pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Error> {
|
|
let query = "
|
|
SELECT data, id
|
|
FROM instances
|
|
WHERE finished = false
|
|
AND id != '00000000-0000-0000-0000-000000000000'
|
|
FOR UPDATE;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[])?;
|
|
|
|
let mut list = vec![];
|
|
|
|
for row in result.into_iter() {
|
|
let bytes: Vec<u8> = row.get(0);
|
|
let id = row.get(1);
|
|
|
|
match from_slice::<Instance>(&bytes) {
|
|
Ok(i) => list.push(i),
|
|
Err(_e) => {
|
|
instance_delete(tx, id)?;
|
|
}
|
|
};
|
|
}
|
|
|
|
return Ok(list);
|
|
}
|
|
|
|
pub fn instance_new(params: InstanceLobbyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
|
let mut instance = match params.players {
|
|
1 => Instance::new()
|
|
.set_max_players(16)?
|
|
.set_name(params.name)?
|
|
.add_bots(),
|
|
_ => Instance::new()
|
|
.set_max_players(params.players)?
|
|
.set_name(params.name)?,
|
|
};
|
|
|
|
instance = instance_create(tx, instance)?;
|
|
let join_params = InstanceJoinParams { instance_id: instance.id, cryp_ids: params.cryp_ids };
|
|
|
|
instance_join(join_params, tx, account)
|
|
}
|
|
|
|
pub fn instance_join(params: InstanceJoinParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
|
let mut instance = instance_get(tx, params.instance_id)?;
|
|
|
|
let cryps = params.cryp_ids
|
|
.iter()
|
|
.map(|id| cryp_get(tx, *id, account.id))
|
|
.collect::<Result<Vec<Cryp>, Error>>()?;
|
|
|
|
if cryps.len() != 3 {
|
|
return Err(format_err!("incorrect player size. ({:})", 3));
|
|
}
|
|
|
|
let player = player_create(tx, Player::new(account.id, &account.name, cryps), instance.id, account)?;
|
|
|
|
instance.add_player(player)?;
|
|
|
|
instance_update(tx, instance)
|
|
}
|
|
|
|
// pub fn instance_ready_global(tx: &mut Transaction, _account: &Account, player: Player) -> Result<Game, Error> {
|
|
// // get the game
|
|
// let game = match game_global_get(tx) {
|
|
// Ok(g) => {
|
|
// println!("received global game {:?}", g.id);
|
|
// // if there is one try to join
|
|
// match game_instance_join(tx, player.clone(), g.id) {
|
|
// Ok(g) => g,
|
|
// // if fails make a new one
|
|
// Err(_e) => game_instance_new(tx, vec![player], Uuid::new_v4(), Uuid::nil())?,
|
|
// }
|
|
// },
|
|
// // if not found make a new one
|
|
// Err(_) => game_instance_new(tx, vec![player], Uuid::new_v4(), Uuid::nil())?,
|
|
// };
|
|
|
|
// // set the current game
|
|
// game_global_set(tx, &game)?;
|
|
// Ok(game)
|
|
// }
|
|
|
|
pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
|
|
let mut instance = instance_get(tx, params.instance_id)?;
|
|
let player_id = instance.account_player(account.id)?.id;
|
|
|
|
if let Some(game) = instance.player_ready(player_id)? {
|
|
game_write(tx, &game)?;
|
|
}
|
|
|
|
instance_update(tx, instance)
|
|
}
|
|
|
|
pub fn instance_state(params: InstanceStateParams, tx: &mut Transaction, account: &Account) -> Result<RpcResult, Error> {
|
|
let instance = instance_get(tx, params.instance_id)?;
|
|
|
|
if let Some(game_id) = instance.current_game_id(account.id) {
|
|
let game = game_get(tx, game_id)?;
|
|
|
|
// return the game until it's finished
|
|
if game.phase != Phase::Finish {
|
|
return Ok(RpcResult::GameState(game))
|
|
}
|
|
}
|
|
|
|
Ok(RpcResult::InstanceState(instance))
|
|
}
|
|
|
|
pub fn global_game_finished(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
|
|
let winner = game.winner().ok_or(err_msg("game not finished"))?;
|
|
|
|
for player in game.players.iter() {
|
|
let mut player = player_get(tx, player.id, Uuid::nil())?;
|
|
match player.id == winner.id {
|
|
true => player.add_win(),
|
|
false => player.add_loss(),
|
|
};
|
|
player.vbox.fill();
|
|
player_global_update(tx, player, true)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn instance_game_finished(tx: &mut Transaction, game: &Game, instance_id: Uuid) -> Result<(), Error> {
|
|
let mut instance = instance_get(tx, instance_id)?;
|
|
instance.game_finished(game)?;
|
|
// println!("{:?}", instance_get(tx, instance_id)?);
|
|
|
|
instance_update(tx, instance)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn instance_pve_test() {
|
|
let mut instance = Instance::new()
|
|
.set_max_players(16).expect("unable to set max players")
|
|
.set_max_rounds(2).expect("max rounds failure")
|
|
.add_bots();
|
|
|
|
let player_account = Uuid::new_v4();
|
|
let cryps = instance_mobs(player_account);
|
|
let player = Player::new(player_account, &"test".to_string(), cryps).set_bot(true);
|
|
let player_id = player.id;
|
|
|
|
instance.add_player(player).expect("could not add player");
|
|
|
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
|
instance.player_ready(player_id).unwrap();
|
|
|
|
assert_eq!(instance.phase, InstancePhase::Finished);
|
|
assert_eq!(instance.rounds[0].len(), 8);
|
|
assert_eq!(instance.rounds.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn instance_bot_vbox_test() {
|
|
let instance = Instance::new();
|
|
let player_account = Uuid::new_v4();
|
|
let cryps = instance_mobs(player_account);
|
|
let _player = Player::new(player_account, &"test".to_string(), cryps).set_bot(true);
|
|
}
|
|
|
|
#[test]
|
|
fn instance_start_test() {
|
|
let mut instance = Instance::new()
|
|
.set_max_players(2)
|
|
.expect("could not create instance");
|
|
|
|
assert_eq!(instance.max_players, 2);
|
|
|
|
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");
|
|
|
|
assert_eq!(instance.phase, InstancePhase::Lobby);
|
|
instance.player_ready(a_id).expect("a ready");
|
|
assert!(!instance.can_start());
|
|
|
|
instance.player_ready(b_id).expect("b ready");
|
|
assert_eq!(instance.phase, InstancePhase::InProgress);
|
|
|
|
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_end = 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);
|
|
}
|
|
}
|