about to change teams -> player

This commit is contained in:
ntr 2019-04-29 15:31:12 +10:00
parent 618f366796
commit 15ae93e70f
8 changed files with 124 additions and 94 deletions

View File

@ -67,10 +67,11 @@ class InstanceCreateForm extends Component {
<label htmlFor="playerSelect">players</label>
<select id="playerSelect"
disabled={disabled}
value={this.state.players}
onChange={this.playersChange}
>
<option value={1}>pve</option>
<option selected value={2}>2</option>
<option value={2}>2</option>
<option value={4}>4</option>
<option value={8}>8</option>
<option value={16}>16</option>

View File

@ -6,6 +6,10 @@ exports.up = async knex => {
table.index('id');
table.timestamps(true, true);
table.binary('data').notNullable();
table.boolean('finished')
.defaultTo(false)
.notNullable()
.index();
});
await knex.schema.createTable('instances', async table => {
@ -14,7 +18,7 @@ exports.up = async knex => {
table.timestamps(true, true);
table.binary('data').notNullable();
table.boolean('finished')
table.boolean('open')
.defaultTo(false)
.notNullable()
.index();

View File

@ -89,6 +89,8 @@ make strike *really* hit first / resolve at same time?
* not sure hwo to get sets of player games
* set joined_games_$account [game_id]
* move team -> player
* chat
* notifications
* elo + leaderboards

View File

@ -1,7 +1,9 @@
use rand::prelude::*;
use uuid::Uuid;
use std::time::Instant;
// timekeeping
use chrono::prelude::*;
use chrono::Duration;
// Db Commons
use serde_cbor::{from_slice, to_vec};
@ -22,6 +24,7 @@ pub struct Team {
pub player: Option<Uuid>,
pub bot: bool,
pub cryps: Vec<Cryp>,
pub ready: bool,
}
impl Team {
@ -31,6 +34,7 @@ impl Team {
player: None,
cryps: vec![],
bot: false,
ready: false,
};
}
@ -95,6 +99,7 @@ pub struct Game {
pub log: Vec<String>,
pub instance: Option<Uuid>,
pub mode: GameMode,
phase_start: DateTime<Utc>,
}
impl Game {
@ -110,6 +115,7 @@ impl Game {
log: vec![],
instance: None,
mode: GameMode::Normal,
phase_start: Utc::now(),
};
}
@ -218,6 +224,7 @@ impl Game {
}
fn skill_phase_start(mut self) -> Game {
self.phase_start = Utc::now();
self.log.push("<Skill Phase>".to_string());
if ![Phase::Start, Phase::Resolve].contains(&self.phase) {
@ -349,14 +356,15 @@ impl Game {
}
fn skill_phase_finished(&self) -> bool {
self.teams.iter()
// for every team
.all(|t| self.stack.iter()
// the number of skills they have cast
.filter(|s| s.source_team_id == t.id).collect::<Vec<&Cast>>()
// should equal the number required this turn
.len() == t.skills_required()
)
self.teams.iter().all(|t| t.ready)
// self.teams.iter()
// // for every team
// .all(|t| self.stack.iter()
// // the number of skills they have cast
// .filter(|s| s.source_team_id == t.id).collect::<Vec<&Cast>>()
// // should equal the number required this turn
// .len() == t.skills_required()
// )
}
// requires no input
@ -590,75 +598,24 @@ impl Game {
self
}
fn phase_timed_out(&self) -> bool {
Utc::now().signed_duration_since(self.phase_start).num_seconds() > 30
}
pub fn upkeep(mut self) -> Game {
// give teams a ready status
// on time out if not enough skills selected
// add a warning
// on 3 warnings forfeit
if self.phase == Phase::Skill && self.phase_timed_out() {
self = self.resolve_phase_start();
}
self
}
}
pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
let query = "
SELECT *
FROM games
WHERE id = $1
";
let result = tx
.query(query, &[&params.game_id])?;
let returned = match result.iter().next() {
Some(row) => row,
None => return Err(err_msg("game not found")),
};
// tells from_slice to cast into a cryp
let game_bytes: Vec<u8> = returned.get("data");
let mut game = from_slice::<Game>(&game_bytes)?;
if game.phase != Phase::Skill {
return Err(err_msg("game not in skill phase"))
}
game.add_skill(account.id, params.cryp_id, params.target_cryp_id, params.skill)?;
if game.skill_phase_finished() {
game = game.resolve_phase_start();
}
game_update(tx, &game)?;
Ok(game)
}
// pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
// let query = "
// SELECT *
// FROM games
// WHERE id = $1
// ";
// let result = tx
// .query(query, &[&params.game_id])?;
// let returned = match result.iter().next() {
// Some(row) => row,
// None => return Err(err_msg("game not found")),
// };
// // tells from_slice to cast into a cryp
// let game_bytes: Vec<u8> = returned.get("data");
// let mut game = from_slice::<Game>(&game_bytes)?;
// game.add_target(account.id, params.cryp_id, params.skill_id)?;
// if game.target_phase_finished() {
// game.resolve_phase_start();
// }
// game_update(game, tx)?;
// Ok(game)
// }
pub fn game_write(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
let game_bytes = to_vec(&game)?;
@ -847,7 +804,7 @@ pub fn game_update(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
let query = "
UPDATE games
SET data = $1, complete = $2, updated_at = now()
SET data = $1, finished = $2, updated_at = now()
WHERE id = $3
RETURNING id, data;
";
@ -869,6 +826,34 @@ pub fn game_update(tx: &mut Transaction, game: &Game) -> Result<(), Error> {
return Ok(());
}
pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
let mut game = game_get(tx, params.game_id)?;
game.add_skill(account.id, params.cryp_id, params.target_cryp_id, params.skill)?;
if game.skill_phase_finished() {
game = game.resolve_phase_start();
}
game_update(tx, &game)?;
Ok(game)
}
pub fn game_ready(params: GameSkillParams, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
let mut game = game_get(tx, params.game_id)?;
game.player_ready(account.id, params.cryp_id, params.target_cryp_id, params.skill)?;
if game.skill_phase_finished() {
game = game.resolve_phase_start();
}
game_update(tx, &game)?;
Ok(game)
}
// pub fn game_pve_new(cryp_ids: Vec<Uuid>, mode: GameMode, tx: &mut Transaction, account: &Account) -> Result<Game, Error> {
// if cryp_ids.len() == 0 {
// return Err(err_msg("no cryps selected"));
@ -1325,4 +1310,12 @@ mod tests {
assert!(game.team_by_id(x_team.id).skills_required() == 2);
return;
}
#[test]
fn upkeep_test() {
let mut game = create_2v2_test_game();
game.phase_start = Utc::now().checked_sub_signed(Duration::seconds(31)).unwrap();
println!("{:?}", game.upkeep());
}
}

View File

@ -375,7 +375,7 @@ impl Instance {
current_round
}
fn current_game(&mut self, player_id: Uuid) -> Option<Uuid> {
fn current_game(&self, player_id: Uuid) -> Option<Uuid> {
if self.phase == InstancePhase::Lobby {
return None;
}
@ -388,7 +388,10 @@ impl Instance {
.all(|p| p.ready);
match can_start {
true => Some(current_round.game_id),
true => match current_round.finished {
true => None,
false => Some(current_round.game_id),
},
false => None,
}
}
@ -401,37 +404,54 @@ impl Instance {
.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(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)
@ -541,7 +561,8 @@ pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Erro
let query = "
SELECT data, id
FROM instances
WHERE updated_at < now() - interval '5 seconds';
WHERE updated_at < now() - interval '5 seconds'
AND id != '00000000-0000-0000-0000-000000000000';
";
let result = tx

View File

@ -7,7 +7,8 @@ use std::net::{TcpListener, TcpStream};
use serde_cbor::{to_vec};
use std::env;
use std::thread::spawn;
use std::thread::{spawn, sleep};
use std::time::Duration;
use r2d2::{Pool};
use r2d2::{PooledConnection};
@ -72,7 +73,16 @@ pub fn start() {
let server = TcpListener::bind("0.0.0.0:40000").unwrap();
let warden_pool = pool.clone();
spawn(move || warden(warden_pool));
spawn(move || {
loop {
let db_connection = warden_pool.get().expect("unable to get db connection");
if let Err(e) = warden(db_connection) {
println!("{:?}", e);
}
sleep(Duration::new(5, 0));
}
});
for stream in server.incoming() {
let db = pool.clone();

View File

@ -558,19 +558,19 @@ pub fn vbox_combine(params: VboxCombineParams, tx: &mut Transaction, account: &A
}
pub fn vbox_reclaim(params: VboxReclaimParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let mut instance = instance_get(tx, params.instance_id)?
let instance = instance_get(tx, params.instance_id)?
.vbox_reclaim(account.id, params.index)?;
return instance_update(tx, instance);
}
pub fn vbox_apply(params: VboxApplyParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let mut instance = instance_get(tx, params.instance_id)?
let instance = instance_get(tx, params.instance_id)?
.vbox_apply(account.id, params.index, params.cryp_id)?;
return instance_update(tx, instance);
}
pub fn vbox_unequip(params: VboxUnequipParams, tx: &mut Transaction, account: &Account) -> Result<Instance, Error> {
let mut instance = instance_get(tx, params.instance_id)?
let instance = instance_get(tx, params.instance_id)?
.vbox_unequip(account.id, params.target, params.cryp_id)?;
return instance_update(tx, instance);
}

View File

@ -11,10 +11,13 @@ use r2d2_postgres::{PostgresConnectionManager};
use game::{Game, games_need_upkeep, game_update};
use instance::{Instance, instances_need_upkeep, instance_update};
use net::{Db};
fn fetch_games(mut tx: Transaction) -> Result<Transaction, Error> {
let games = games_need_upkeep(&mut tx)?;
println!("warden: {:?} games active", games.len());
for mut game in games {
game_update(&mut tx, &game.upkeep())?;
}
@ -32,16 +35,12 @@ fn fetch_instances(mut tx: Transaction) -> Result<Transaction, Error> {
Ok(tx)
}
pub fn warden(pool: Pool<PostgresConnectionManager>) -> Result<(), Error> {
loop {
let db_connection = pool.get().expect("unable to get db connection");
fetch_games(db_connection.transaction()?)?
pub fn warden(db: Db) -> Result<(), Error> {
fetch_games(db.transaction()?)?
.commit()?;
fetch_instances(db_connection.transaction()?)?
fetch_instances(db.transaction()?)?
.commit()?;
sleep(Duration::new(1, 0));
}
Ok(())
}