From af9f2c479d1c1d2cdfc0719f99cec99a5c821bbe Mon Sep 17 00:00:00 2001 From: ntr Date: Mon, 22 Oct 2018 20:00:51 +1100 Subject: [PATCH] pvp init --- client/src/components/body.component.jsx | 2 + client/src/components/cryp.list.container.js | 6 +- client/src/components/cryp.list.jsx | 11 +- client/src/components/game.join.button.jsx | 42 +++++ client/src/components/game.jsx | 5 +- client/src/socket.jsx | 17 +- server/WORKLOG.md | 2 - server/src/cryp.rs | 2 +- server/src/game.rs | 167 ++++++++++++++----- server/src/main.rs | 2 +- server/src/rpc.rs | 61 ++++++- 11 files changed, 263 insertions(+), 54 deletions(-) create mode 100644 client/src/components/game.join.button.jsx diff --git a/client/src/components/body.component.jsx b/client/src/components/body.component.jsx index 6c41dfb0..f031f99b 100644 --- a/client/src/components/body.component.jsx +++ b/client/src/components/body.component.jsx @@ -3,6 +3,7 @@ const preact = require('preact'); const ItemListContainer = require('./item.list.container'); const CrypSpawnContainer = require('./cryp.spawn.container'); +const GameJoinButton = require('./game.join.button'); const CrypListContainer = require('./cryp.list.container'); const GameContainer = require('./game.container'); @@ -12,6 +13,7 @@ function renderBody() {
+
diff --git a/client/src/components/cryp.list.container.js b/client/src/components/cryp.list.container.js index 5fcd16ba..78a4cf1a 100644 --- a/client/src/components/cryp.list.container.js +++ b/client/src/components/cryp.list.container.js @@ -9,6 +9,10 @@ const addState = connect( return ws.sendGamePve(crypId); } + function sendGamePvp(crypIds) { + return ws.sendGamePvp(crypIds); + } + function sendItemUse(targetId) { if (activeItem) { return ws.sendItemUse(activeItem, targetId); @@ -16,7 +20,7 @@ const addState = connect( return false; } - return { cryps, sendGamePve, activeItem, sendItemUse }; + return { cryps, sendGamePve, sendGamePvp, activeItem, sendItemUse }; } ); diff --git a/client/src/components/cryp.list.jsx b/client/src/components/cryp.list.jsx index 404ee80b..a8cbb4f8 100755 --- a/client/src/components/cryp.list.jsx +++ b/client/src/components/cryp.list.jsx @@ -3,7 +3,7 @@ const preact = require('preact'); const { stringSort } = require('./../utils'); const nameSort = stringSort('name'); -function CrypList({ cryps, activeItem, sendGamePve, sendItemUse }) { +function CrypList({ cryps, activeItem, sendGamePve, sendGamePvp, sendItemUse }) { if (!cryps) return
not ready
; const crypPanels = cryps.sort(nameSort).map(cryp => ( @@ -38,6 +38,15 @@ function CrypList({ cryps, activeItem, sendGamePve, sendItemUse }) { onClick={() => sendGamePve(cryp.id)}> Start PVE + + +
)); return ( diff --git a/client/src/components/game.join.button.jsx b/client/src/components/game.join.button.jsx new file mode 100644 index 00000000..302c779f --- /dev/null +++ b/client/src/components/game.join.button.jsx @@ -0,0 +1,42 @@ +const preact = require('preact'); +const { connect } = require('preact-redux'); + +const addState = connect( + function receiveState(state) { + const { ws, cryps } = state; + function sendGameJoin(gameId) { + return ws.sendGameJoin(gameId, [cryps[0].id]); + } + + return { account: state.account, sendGameJoin }; + } +); + +function GameJoinButton({ account, sendGameJoin }) { + let gameId = ''; + + if (!account) return
...
; + + return ( +
+
+ (gameId = e.target.value)} + /> +
+
+ +
+
+ ); +} + +module.exports = addState(GameJoinButton); diff --git a/client/src/components/game.jsx b/client/src/components/game.jsx index 39416b19..56e15b36 100644 --- a/client/src/components/game.jsx +++ b/client/src/components/game.jsx @@ -126,8 +126,9 @@ function GamePanel(props) {
{otherTeams.map(OpponentTeam)}
-
-
{game.phase}
+
+
{game.phase}
+
{game.id}
{incoming} diff --git a/client/src/socket.jsx b/client/src/socket.jsx index 0d547e60..0a9994cd 100755 --- a/client/src/socket.jsx +++ b/client/src/socket.jsx @@ -24,7 +24,7 @@ function createSocket(store) { // Connection opened ws.addEventListener('open', function wsOpen(event) { - send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } }); + // send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } }); }); // Listen for messages @@ -113,8 +113,19 @@ function createSocket(store) { send({ method: 'game_pve', params: { id } }); } + function sendGamePvp(crypIds) { + send({ method: 'game_pvp', params: { cryp_ids: crypIds } }); + } + + function sendGameJoin(gameId, crypIds) { + send({ method: 'game_join', params: { game_id: gameId, cryp_ids: crypIds } }); + } + function sendGameSkill(gameId, crypId, targetTeamId, skill) { - send({ method: 'game_skill', params: { game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill } }); + send({ + method: 'game_skill', + params: { game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill } + }); store.dispatch(actions.setActiveSkill(null)); } @@ -164,6 +175,8 @@ function createSocket(store) { sendAccountLogin, sendAccountRegister, sendGamePve, + sendGamePvp, + sendGameJoin, sendGameSkill, sendGameTarget, sendCrypSpawn, diff --git a/server/WORKLOG.md b/server/WORKLOG.md index 10f97b64..6dbfd1ae 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -30,8 +30,6 @@ * teach cyps skills * can you attack yourself? * fetch existing battles - * check for cryp skill already used - * check for cryp skill ownership * check for game participation * write players row for every team+cryp added * return results<> diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 6d24138f..9637b2dd 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -219,7 +219,7 @@ pub fn cryp_get(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result = result.get(0); let cryp = from_slice::(&cryp_bytes)?; diff --git a/server/src/game.rs b/server/src/game.rs index 529c7b99..2df81302 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -8,8 +8,8 @@ use failure::Error; use failure::err_msg; use account::Account; -use rpc::{GameSkillParams, GamePveParams, GameTargetParams}; -use cryp::{Cryp, CrypStat, Stat}; +use rpc::{GameSkillParams, GamePveParams, GamePvpParams, GameTargetParams, GameJoinParams}; +use cryp::{Cryp, cryp_get}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Roll { @@ -142,7 +142,7 @@ pub struct Game { } impl Game { - pub fn new() -> Game { + fn new() -> Game { return Game { id: Uuid::new_v4(), team_size: 0, @@ -154,38 +154,40 @@ impl Game { }; } - pub fn set_team_num(&mut self, size: usize) -> &mut Game { + fn set_team_num(&mut self, size: usize) -> &mut Game { self.team_num = size; self } - pub fn set_team_size(&mut self, size: usize) -> &mut Game { + fn set_team_size(&mut self, size: usize) -> &mut Game { self.team_size = size; self } - pub fn set_pve(&mut self, pve: bool) -> &mut Game { + fn set_pve(&mut self, pve: bool) -> &mut Game { self.is_pve = pve; self } - pub fn add_team(&mut self, team: Team) -> &mut Game { + // check team not already in + fn add_team(&mut self, team: Team) -> Result<&mut Game, Error> { if self.teams.len() == self.team_num { - panic!("maximum number of teams"); + return Err(err_msg("maximum number of teams")); } self.teams.push(team); - self + + Ok(self) } // handle missing team properly - pub fn team_by_id(&mut self, id: Uuid) -> &mut Team { + fn team_by_id(&mut self, id: Uuid) -> &mut Team { match self.teams.iter_mut().find(|t| t.id == id) { Some(t) => t, None => panic!("id not in game {:?}", id), } } - pub fn cryp_by_id(&mut self, id: Uuid) -> &mut Cryp { + fn cryp_by_id(&mut self, id: Uuid) -> &mut Cryp { for team in self.teams.iter_mut() { let in_team = team.cryp_by_id(id); if in_team.is_some() { @@ -195,16 +197,16 @@ impl Game { panic!("cryp not in game"); } - fn update_team(&mut self, updated: Team) -> &mut Game { - match self.teams.iter().position(|t| t.id == updated.id) { - Some(index) => { - self.teams.remove(index); - self.teams.push(updated); - self - } - None => panic!("team not in game"), - } - } + // fn update_team(&mut self, updated: Team) -> &mut Game { + // match self.teams.iter().position(|t| t.id == updated.id) { + // Some(index) => { + // self.teams.remove(index); + // self.teams.push(updated); + // self + // } + // None => panic!("team not in game"), + // } + // } fn update_cryp(&mut self, updated: Cryp) -> &mut Game { for team in self.teams.iter_mut() { @@ -217,16 +219,16 @@ impl Game { self } - pub fn can_start(&self) -> bool { + fn can_start(&self) -> bool { self.teams.len() == self.team_num } - pub fn start(&mut self) -> &mut Game { + fn start(&mut self) -> &mut Game { self.skill_phase_start(); self } - pub fn skill_phase_start(&mut self) -> &mut Game { + fn skill_phase_start(&mut self) -> &mut Game { if ![Phase::Start, Phase::Damage].contains(&self.phase) { panic!("game not in damage or start phase"); } @@ -261,7 +263,7 @@ impl Game { // skills can target any team, but we have to check if the caller is the owner of the cryp // and that the cryp has the skill they are trying to add - pub fn add_skill(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result { + fn add_skill(&mut self, team_id: Uuid, cryp_id: Uuid, target_team_id: Uuid, skill: Skill) -> Result { if self.phase != Phase::Skill { return Err(err_msg("game not in skill phase")); } @@ -291,12 +293,12 @@ impl Game { return Ok(skill.id); } - pub fn skill_phase_finished(&self) -> bool { + fn skill_phase_finished(&self) -> bool { self.teams.iter().all(|t| t.skills.len() == self.team_size) } // move all skills into their target team's targets list - pub fn target_phase_start(&mut self) -> &mut Game { + fn target_phase_start(&mut self) -> &mut Game { if self.phase != Phase::Skill { panic!("game not in skill phase"); } @@ -335,7 +337,7 @@ impl Game { // targets can only be added by the owner of the team - pub fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut GameSkill, Error> { + fn add_target(&mut self, team_id: Uuid, cryp_id: Uuid, skill_id: Uuid) -> Result<&mut GameSkill, Error> { // whose team is this? let team = self.team_by_id(team_id); @@ -350,13 +352,13 @@ impl Game { Ok(skill.set_target(cryp_id)) } - pub fn target_phase_finished(&self) -> bool { + fn target_phase_finished(&self) -> bool { self.teams.iter().all(|t| t.incoming.iter().all(|i| i.target_cryp_id.is_some())) } // requires no input // just do it - pub fn damage_phase_start(&mut self) -> &mut Game { + fn damage_phase_start(&mut self) -> &mut Game { if self.phase != Phase::Target { panic!("game not in target phase"); } @@ -392,7 +394,7 @@ impl Game { self } - pub fn is_finished(&self) -> bool { + fn is_finished(&self) -> bool { self.teams.iter().any(|t| t.cryps.iter().all(|c| c.is_ko())) } @@ -433,7 +435,9 @@ pub fn game_skill(params: GameSkillParams, tx: &mut Transaction, account: &Accou game.target_phase_start(); } - return game_write(game, tx); + game_update(&game, tx)?; + + Ok(game) } pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Account) -> Result { @@ -461,7 +465,9 @@ pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Acc game.damage_phase_start(); } - return game_write(game, tx); + game_update(&game, tx)?; + + Ok(game) } pub fn game_new(game: &Game, tx: &mut Transaction) -> Result<(), Error> { @@ -476,13 +482,35 @@ pub fn game_new(game: &Game, tx: &mut Transaction) -> Result<(), Error> { let result = tx .query(query, &[&game.id, &game_bytes])?; - let _returned = result.iter().next().expect("no row returned"); + result.iter().next().ok_or(format_err!("no game written"))?; println!("{:?} wrote game", game.id); return Ok(()); } +pub fn game_get(tx: &mut Transaction, id: Uuid) -> Result { + let query = " + SELECT * + FROM games + WHERE id = $1 + "; + + let result = tx + .query(query, &[&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 = returned.get("data"); + let game = from_slice::(&game_bytes)?; + + return Ok(game); +} + /// write a row for every cryp in a team when added to a battle pub fn players_write(team: &Team, game_id: Uuid, tx: &mut Transaction) -> Result<(), Error> { // pve @@ -508,7 +536,7 @@ pub fn players_write(team: &Team, game_id: Uuid, tx: &mut Transaction) -> Result return Ok(()); } -pub fn game_write(game: Game, tx: &mut Transaction) -> Result { +pub fn game_update(game: &Game, tx: &mut Transaction) -> Result<(), Error> { let game_bytes = to_vec(&game)?; let query = " @@ -521,11 +549,11 @@ pub fn game_write(game: Game, tx: &mut Transaction) -> Result { let result = tx .query(query, &[&game_bytes, &game.id])?; - let returned = result.iter().next().expect("no row returned"); + result.iter().next().ok_or(format_err!("game {:?} could not be written", game))?; println!("{:?} wrote game", game.id); - return Ok(game); + return Ok(()); } fn generate_mob(plr: &Cryp) -> Cryp { @@ -589,8 +617,8 @@ pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account) .set_cryps(vec![mob]); game - .add_team(plr_team) - .add_team(mob_team); + .add_team(plr_team)? + .add_team(mob_team)?; game.start(); @@ -601,6 +629,59 @@ pub fn game_pve(params: GamePveParams, tx: &mut Transaction, account: &Account) Ok(game) } +pub fn game_pvp(params: GamePvpParams, tx: &mut Transaction, account: &Account) -> Result { + let cryps = params.cryp_ids + .iter() + .map(|id| cryp_get(tx, *id, account.id)) + .collect::, Error>>()?; + + // create the game + let mut game = Game::new(); + let game_id = game.id; + + game + .set_pve(false) + .set_team_num(2) + .set_team_size(1); + + // create the initiators team + let mut team = Team::new(account.id); + team.set_cryps(cryps); + + game.add_team(team)?; + + // persist + game_new(&game, tx)?; + players_write(&game.team_by_id(account.id), game_id, tx)?; + + Ok(game) +} + +pub fn game_join(params: GameJoinParams, tx: &mut Transaction, account: &Account) -> Result { + let mut game = game_get(tx, params.game_id)?; + let game_id = game.id; + + let cryps = params.cryp_ids + .iter() + .map(|id| cryp_get(tx, *id, account.id)) + .collect::, Error>>()?; + + let mut team = Team::new(account.id); + team.set_cryps(cryps); + + game.add_team(team)?; + + if game.can_start() { + game.start(); + } + + game_update(&game, tx)?; + players_write(&game.team_by_id(account.id), game_id, tx)?; + + Ok(game) +} + + #[cfg(test)] mod tests { use game::*; @@ -640,8 +721,8 @@ mod tests { .set_cryps(vec![y]); game - .add_team(x_team) - .add_team(y_team); + .add_team(x_team).unwrap() + .add_team(y_team).unwrap(); assert!(game.can_start()); @@ -656,8 +737,8 @@ mod tests { println!("{:?}", game); - game.add_target(x_team_id, x_id, y_attack_id.unwrap()); - game.add_target(y_team_id, y_id, x_attack_id.unwrap()); + game.add_target(x_team_id, x_id, y_attack_id.unwrap()).unwrap(); + game.add_target(y_team_id, y_id, x_attack_id.unwrap()).unwrap(); assert!(game.target_phase_finished()); diff --git a/server/src/main.rs b/server/src/main.rs index 709a5e77..60895e7e 100755 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -14,7 +14,7 @@ extern crate serde_cbor; #[macro_use] extern crate serde_derive; -extern crate failure; +#[macro_use] extern crate failure; // #[macro_use] extern crate failure_derive; mod cryp; diff --git a/server/src/rpc.rs b/server/src/rpc.rs index 76529743..72d622e3 100755 --- a/server/src/rpc.rs +++ b/server/src/rpc.rs @@ -11,7 +11,7 @@ use failure::err_msg; use net::Db; use cryp::{Cryp, cryp_spawn}; -use game::{Game, Skill, game_pve, game_skill, game_target}; +use game::{Game, Skill, game_pve, game_pvp, game_join, game_skill, game_target}; use account::{Account, account_create, account_login, account_from_token, account_cryps}; use item::{Item, items_list, item_use}; @@ -39,6 +39,8 @@ impl Rpc { let response = match v.method.as_ref() { "cryp_spawn" => Rpc::cryp_spawn(data, &mut tx, account, client), "game_pve" => Rpc::game_pve(data, &mut tx, account, client), + "game_pvp" => Rpc::game_pvp(data, &mut tx, account, client), + "game_join" => Rpc::game_join(data, &mut tx, account, client), "game_skill" => Rpc::game_skill(data, &mut tx, account, client), "game_target" => Rpc::game_target(data, &mut tx, account, client), "account_create" => Rpc::account_create(data, &mut tx, account, client), @@ -87,6 +89,40 @@ impl Rpc { return Ok(game_response); } + fn game_pvp(data: Vec, tx: &mut Transaction, account: Option, _client: &mut WebSocket) -> Result { + let a = match account { + Some(a) => a, + None => return Err(err_msg("auth required")), + }; + + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let game_response = RpcResponse { + method: "game_state".to_string(), + params: RpcResult::GameState(game_pvp(msg.params, tx, &a)?) + }; + + return Ok(game_response); + } + + + fn game_join(data: Vec, tx: &mut Transaction, account: Option, _client: &mut WebSocket) -> Result { + let a = match account { + Some(a) => a, + None => return Err(err_msg("auth required")), + }; + + let msg = from_slice::(&data).or(Err(err_msg("invalid params")))?; + + let game_response = RpcResponse { + method: "game_state".to_string(), + params: RpcResult::GameState(game_join(msg.params, tx, &a)?) + }; + + return Ok(game_response); + } + + fn game_skill(data: Vec, tx: &mut Transaction, account: Option, _client: &mut WebSocket) -> Result { let a = match account { Some(a) => a, @@ -250,6 +286,29 @@ pub struct GamePveParams { pub id: Uuid, } +#[derive(Debug,Clone,Serialize,Deserialize)] +struct GamePvpMsg { + method: String, + params: GamePvpParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct GamePvpParams { + pub cryp_ids: Vec, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +struct GameJoinMsg { + method: String, + params: GameJoinParams, +} + +#[derive(Debug,Clone,Serialize,Deserialize)] +pub struct GameJoinParams { + pub game_id: Uuid, + pub cryp_ids: Vec, +} + #[derive(Debug,Clone,Serialize,Deserialize)] struct GameTargetMsg { method: String,