449 lines
12 KiB
Rust
449 lines
12 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;
|
|
|
|
use rpc::{InstanceJoinParams, InstanceReadyParams};
|
|
use account::Account;
|
|
use player::{Player, player_create, player_get, player_update};
|
|
use cryp::{Cryp, cryp_get};
|
|
use mob::{instance_mobs};
|
|
use game::{Game, Team, game_get, game_write, game_instance_new, game_instance_join};
|
|
|
|
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
|
|
enum InstancePhase {
|
|
Open,
|
|
Vbox,
|
|
Games,
|
|
Finished,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
|
struct Round {
|
|
player_ids: Vec<Uuid>,
|
|
game_id: Uuid,
|
|
outcome: Option<(Uuid, Uuid)>,
|
|
}
|
|
|
|
#[derive(Debug,Clone,Serialize,Deserialize)]
|
|
pub struct Instance {
|
|
id: Uuid,
|
|
players: Vec<Player>,
|
|
phase: InstancePhase,
|
|
rounds: Vec<Vec<Round>>,
|
|
open: bool,
|
|
pve: bool,
|
|
}
|
|
|
|
impl Instance {
|
|
fn new() -> Instance {
|
|
Instance {
|
|
id: Uuid::new_v4(),
|
|
players: vec![],
|
|
rounds: vec![],
|
|
phase: InstancePhase::Open,
|
|
open: true,
|
|
pve: false,
|
|
}
|
|
}
|
|
|
|
fn add_bots(mut self) -> Instance {
|
|
self.pve = true;
|
|
self.players = iter::repeat_with(|| {
|
|
let bot_id = Uuid::new_v4();
|
|
let cryps = instance_mobs(bot_id);
|
|
Player::new(bot_id, self.id, cryps).set_bot(true)
|
|
})
|
|
.take(15)
|
|
.collect::<Vec<Player>>();
|
|
self
|
|
}
|
|
|
|
fn add_player(&mut self, player: Player) -> &mut Instance {
|
|
self.players.push(player);
|
|
self
|
|
}
|
|
|
|
fn player_ready(mut self, mut player: Player) -> Result<Instance, Error> {
|
|
if self.phase != InstancePhase::Vbox {
|
|
panic!("instance not in vbox phase");
|
|
}
|
|
|
|
let i = self.players
|
|
.iter()
|
|
.position(|p| p.id == player.id)
|
|
.ok_or(err_msg("player_id not found"))?;
|
|
|
|
player.set_ready(true);
|
|
|
|
self.players[i] = player;
|
|
|
|
Ok(self)
|
|
}
|
|
|
|
fn bot_vs_player_game(&self, player: &Player) -> Game {
|
|
let current_round = self.current_round(player);
|
|
|
|
let plr = self.players.clone().into_iter().find(|p| p.id == player.id).unwrap();
|
|
let bot = self.players.clone().into_iter().find(|p| p.id != player.id).unwrap();
|
|
|
|
let mut game = Game::new();
|
|
game.id = current_round.game_id;
|
|
game
|
|
.set_pve(true)
|
|
.set_team_num(2)
|
|
.set_team_size(3);
|
|
|
|
// add the players
|
|
let mut plr_team = Team::new(plr.account);
|
|
plr_team.set_cryps(plr.cryps);
|
|
|
|
let mut bot_team = Team::new(bot.account);
|
|
bot_team.set_cryps(bot.cryps);
|
|
bot_team.set_bot();
|
|
|
|
game
|
|
.team_add(plr_team).unwrap()
|
|
.team_add(bot_team).unwrap();
|
|
|
|
game.start();
|
|
|
|
game
|
|
}
|
|
|
|
fn can_start(&self) -> bool {
|
|
match self.pve {
|
|
true => self.players.len() == 16,
|
|
false => self.players.len() == 2,
|
|
}
|
|
}
|
|
|
|
fn start(&mut self) -> &mut Instance {
|
|
// self.players.sort_unstable_by_key(|p| p.id);
|
|
self.generate_rounds();
|
|
self.open = false;
|
|
self.phase = InstancePhase::Vbox;
|
|
|
|
self.vbox_phase_start()
|
|
}
|
|
|
|
fn vbox_phase_start(&mut self) -> &mut Instance {
|
|
// match self.rounds.last() {
|
|
// Some(r) => {
|
|
// for round in r {
|
|
// {
|
|
// let winner = self.players.iter_mut().find(|p| p.account == round.outcome.unwrap().0).unwrap();
|
|
// winner.add_win();
|
|
// }
|
|
// {
|
|
// let loser = self.players.iter_mut().find(|p| p.account == round.outcome.unwrap().1).unwrap();
|
|
// loser.add_loss();
|
|
// }
|
|
// }
|
|
// }
|
|
// None => (),
|
|
// }
|
|
|
|
self.bot_vbox_phase();
|
|
|
|
self
|
|
}
|
|
|
|
fn vbox_phase_finished(&self) -> bool {
|
|
self.players.iter().all(|p| p.ready)
|
|
}
|
|
|
|
// requires no input
|
|
// just do it
|
|
fn games_phase_start(&mut self) -> &mut Instance {
|
|
if self.phase != InstancePhase::Vbox {
|
|
panic!("instance not in vbox phase");
|
|
}
|
|
assert!(self.vbox_phase_finished());
|
|
|
|
self.phase = InstancePhase::Games;
|
|
|
|
self.bot_games_phase();
|
|
|
|
self
|
|
}
|
|
|
|
fn games_phase_finished(&self) -> bool {
|
|
match self.rounds.last() {
|
|
Some(r) => r.iter().all(|g| g.outcome.is_some()),
|
|
None => true,
|
|
}
|
|
}
|
|
|
|
fn bot_vbox_phase(&mut self) -> &mut Instance {
|
|
for bot in self.players.iter_mut().filter(|p| p.bot) {
|
|
bot.set_ready(true);
|
|
}
|
|
|
|
self
|
|
}
|
|
|
|
fn bot_games_phase(&mut self) -> &mut Instance {
|
|
if self.phase != InstancePhase::Games {
|
|
panic!("instance not in games phase");
|
|
}
|
|
|
|
if self.pve {
|
|
let r = self.rounds.len() - 1;
|
|
for mut round in self.rounds[r].iter_mut() {
|
|
if self.players
|
|
.iter()
|
|
.filter(|p| round.player_ids.contains(&p.id) && p.bot)
|
|
.count() == 2 {
|
|
println!("should play a game between {:?}", round.player_ids);
|
|
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();
|
|
|
|
let mut game = Game::new();
|
|
game
|
|
.set_pve(true)
|
|
.set_team_num(2)
|
|
.set_team_size(3);
|
|
|
|
// add the players
|
|
let mut a_team = Team::new(a.id);
|
|
a_team.set_cryps(a.cryps);
|
|
a_team.set_bot();
|
|
|
|
let mut b_team = Team::new(b.id);
|
|
b_team.set_cryps(b.cryps);
|
|
b_team.set_bot();
|
|
|
|
game
|
|
.team_add(a_team).unwrap()
|
|
.team_add(b_team).unwrap();
|
|
|
|
game.start();
|
|
|
|
assert!(game.finished());
|
|
round.outcome = Some((game.winner().unwrap().id, Uuid::new_v4()));
|
|
}
|
|
}
|
|
}
|
|
|
|
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>>();
|
|
|
|
if round_num > 0 {
|
|
matched_players.rotate_right(round_num);
|
|
matched_players.swap(0,1);
|
|
}
|
|
|
|
// only set up for even player numbers atm
|
|
// no byes
|
|
let np = matched_players.len();
|
|
println!("{:?} players in instance", np);
|
|
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: Uuid::new_v4(),
|
|
outcome: None,
|
|
})
|
|
.collect::<Vec<Round>>();
|
|
|
|
self.rounds.push(current_round);
|
|
|
|
self
|
|
}
|
|
|
|
fn current_round(&self, player: &Player) -> &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
|
|
}
|
|
}
|
|
|
|
pub fn instance_create(instance: Instance, tx: &mut Transaction) -> Result<Instance, Error> {
|
|
let instance_bytes = to_vec(&instance)?;
|
|
|
|
let query = "
|
|
INSERT INTO instances (id, data)
|
|
VALUES ($1, $2)
|
|
RETURNING id;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[&instance.id, &instance_bytes])?;
|
|
|
|
result.iter().next().ok_or(format_err!("no instances written"))?;
|
|
|
|
return Ok(instance);
|
|
}
|
|
|
|
pub fn instance_write(instance: Instance, tx: &mut Transaction) -> Result<Instance, Error> {
|
|
let instance_bytes = to_vec(&instance)?;
|
|
|
|
let query = "
|
|
UPDATE instances
|
|
SET data = $1, open = $2
|
|
WHERE id = $3
|
|
RETURNING id, data;
|
|
";
|
|
|
|
let result = tx
|
|
.query(query, &[&instance_bytes, &instance.open, &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;
|
|
";
|
|
|
|
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_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 instance_join(params: InstanceJoinParams, tx: &mut Transaction, account: &Account) -> Result<Player, Error> {
|
|
let mut instance = match params.pve {
|
|
true => instance_create(Instance::new().add_bots(), tx)?,
|
|
false => match instance_get_open(tx) {
|
|
Ok(i) => i,
|
|
Err(_) => instance_create(Instance::new(), tx)?,
|
|
},
|
|
};
|
|
|
|
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 team size. ({:})", 3));
|
|
}
|
|
|
|
let player = Player::new(account.id, instance.id, cryps);
|
|
player_create(tx, &player, account)?;
|
|
|
|
instance.add_player(player.clone());
|
|
|
|
if instance.can_start() {
|
|
instance.start();
|
|
}
|
|
|
|
instance_write(instance, tx)?;
|
|
|
|
return Ok(player);
|
|
}
|
|
|
|
pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
|
|
let player = player_get(tx, account.id, params.instance_id)?;
|
|
|
|
let instance = instance_get(tx, params.instance_id)?
|
|
.player_ready(player.clone())?;
|
|
|
|
let game_id = instance.current_round(&player).game_id;
|
|
|
|
let game = match instance.pve {
|
|
true => match game_get(tx, game_id) {
|
|
Ok(g) => g,
|
|
Err(_) => {
|
|
let game = instance.bot_vs_player_game(&player);
|
|
game_write(&game, tx)?;
|
|
game
|
|
},
|
|
},
|
|
false => match game_get(tx, game_id) {
|
|
Ok(_g) => game_instance_join(tx, player.clone(), game_id)?,
|
|
Err(_) => game_instance_new(tx, player.clone(), game_id)?,
|
|
}
|
|
};
|
|
|
|
player_update(tx, player)?;
|
|
instance_write(instance, tx)?;
|
|
return Ok(game);
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn instance_pve_test() {
|
|
let mut instance = Instance::new().add_bots();
|
|
|
|
let player_id = Uuid::new_v4();
|
|
let cryps = instance_mobs(player_id);
|
|
let player = Player::new(player_id, instance.id, cryps).set_bot(true);
|
|
|
|
instance.add_player(player);
|
|
assert!(instance.can_start());
|
|
|
|
instance.start();
|
|
assert_eq!(instance.rounds[0].len(), 8);
|
|
|
|
let player = instance.players.clone().into_iter().find(|p| p.account == player_id).unwrap();
|
|
let mut instance = instance.player_ready(player).unwrap();
|
|
|
|
assert!(instance.vbox_phase_finished());
|
|
instance.games_phase_start();
|
|
|
|
println!("{:?}", instance.rounds);
|
|
|
|
assert!(instance.games_phase_finished());
|
|
}
|
|
} |