clientside rounds changes

This commit is contained in:
ntr 2019-06-06 00:03:27 +10:00
parent 1f5fba80f1
commit 6e513e3da7
9 changed files with 95 additions and 159 deletions

View File

@ -446,7 +446,7 @@ header {
flex: 1 1 50%;
}
.create-form input {
.create-form .login-input {
width: 50%;
}

View File

@ -23,8 +23,8 @@ document.fonts.load('16pt "Jura"').then(() => {
setupKeys(store);
const ws = createSocket(events);
store.dispatch(actions.setWs(ws));
ws.connect();
events.setWs(ws);
const App = () => (
<Provider store={store}>

View File

@ -65,7 +65,7 @@ function Instance(args) {
<tr key={i}
className={p.ready ? 'ready' : ''}>
<td>{p.name}</td>
<td>{p.score.wins} / {p.score.losses}</td>
<td>{p.wins} / {p.losses}</td>
<td>{pText}</td>
</tr>
);

View File

@ -6,9 +6,9 @@ const addState = connect(
function receiveState(state) {
const { ws, team } = state;
function sendInstanceNew(sConstructs, name, players) {
function sendInstanceNew(sConstructs, name, pve) {
if (sConstructs.length) {
return ws.sendInstanceNew(sConstructs, name, players);
return ws.sendInstanceNew(sConstructs, name, pve);
}
return false;
}
@ -24,19 +24,19 @@ class InstanceCreateForm extends Component {
constructor(props) {
super(props);
this.state = { players: 2, name: '' };
this.state = { pve: false, name: '' };
const { sendInstanceNew } = props;
this.sendInstanceNew = sendInstanceNew.bind(this);
this.nameInput = this.nameInput.bind(this);
this.playersChange = this.playersChange.bind(this);
this.pveChange = this.pveChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
playersChange(event) {
this.setState({ players: Number(event.target.value) });
pveChange() {
this.setState({ pve: !this.state.pve });
}
nameInput(event) {
@ -45,8 +45,8 @@ class InstanceCreateForm extends Component {
handleSubmit(event) {
event.preventDefault();
this.sendInstanceNew(this.props.team, this.state.name, this.state.players);
this.setState({ name: '', players: 2 });
this.sendInstanceNew(this.props.team, this.state.name, this.state.pve);
this.setState({ name: '', pve: false });
}
render() {
@ -62,21 +62,17 @@ class InstanceCreateForm extends Component {
type="text"
disabled={disabled}
value={this.state.name}
placeholder="name"
placeholder="game name"
onInput={this.nameInput}
/>
<label htmlFor="playerSelect">players</label>
<select id="playerSelect"
<label htmlFor="pveSelect">vs CPU</label>
<input id="pveSelect"
type="checkbox"
disabled={disabled}
value={this.state.players}
onChange={this.playersChange}
checked={this.state.pve}
onChange={this.pveChange}
>
<option value={1}>pve</option>
<option value={2}>2</option>
<option value={4}>4</option>
<option value={8}>8</option>
<option value={16}>16</option>
</select>
</input>
</form>
<button
onClick={this.handleSubmit}

View File

@ -56,7 +56,7 @@ function List(args) {
const instancePanels = instanceList.map(instance => {
const player = instance.players.find(p => p.id === account.id);
const scoreText = player
? `${player.score.wins} : ${player.score.losses}`
? `${player.wins} : ${player.losses}`
: '';
function instanceClick() {

View File

@ -125,8 +125,8 @@ function createSocket(events) {
send({ method: 'instance_join', params: { instance_id: instanceId, construct_ids: constructs } });
}
function sendInstanceNew(constructs, name, players) {
send({ method: 'instance_new', params: { construct_ids: constructs, name, players } });
function sendInstanceNew(constructs, name, pve) {
send({ method: 'instance_new', params: { construct_ids: constructs, name, pve } });
}
function sendInstanceReady(instanceId) {
@ -284,6 +284,7 @@ function createSocket(events) {
if (account) {
events.setAccount(account);
sendAccountInstances();
sendInstanceList();
sendAccountConstructs();
}

View File

@ -38,18 +38,23 @@ enum Format {
#[derive(Debug,Clone,Serialize,Deserialize)]
struct Round {
player_ids: Vec<Uuid>,
game_id: Option<Uuid>,
finished: bool,
}
impl Round {
fn new() -> Round {
Round { game_id: None, finished: false }
}
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Instance {
id: Uuid,
pub name: String,
players: Vec<Player>,
rounds: Vec<Vec<Round>>,
rounds: Vec<Round>,
open: bool,
max_players: usize,
@ -72,13 +77,13 @@ impl Instance {
phase: InstancePhase::Lobby,
open: true,
max_players: 2,
max_rounds: 16,
max_rounds: 5,
name: String::new(),
password: None,
phase_end: Utc::now(),
phase_start: Utc::now(),
format: Format::RoundRobin,
format: Format::Standard,
}
}
@ -90,7 +95,7 @@ impl Instance {
phase: InstancePhase::InProgress,
open: false,
max_players: 0,
max_rounds: 1,
max_rounds: 5,
name: "Global Matchmaking".to_string(),
password: None,
phase_start: Utc::now(),
@ -108,7 +113,6 @@ impl Instance {
self.players
.iter()
.filter(|p| !p.ready)
.filter(|p| self.current_game_id(p.id).is_none())
.map(|p| p.id)
.collect::<Vec<Uuid>>()
}
@ -131,15 +135,6 @@ impl Instance {
(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"));
@ -156,15 +151,17 @@ impl Instance {
fn add_bots(mut self) -> Instance {
self.open = false;
self.players = iter::repeat_with(|| {
iter::repeat_with(|| {
let bot_id = Uuid::new_v4();
let constructs = instance_mobs(bot_id);
let mut p = Player::new(bot_id, &name(), constructs).set_bot(true);
p.set_ready(true);
p
})
.take(15)
.collect::<Vec<Player>>();
// .take(self.max_players - self.players.len())
.take(1)
.for_each(|p| self.players.push(p));
self
}
@ -223,52 +220,47 @@ impl Instance {
// create a game object if both players are ready
// this should only happen once
let all_ready = self.round_ready_check(player_id);
let all_ready = self.round_ready_check();
if !all_ready {
return Ok(None);
}
let game = self.create_round_game(player_id);
let game = self.create_round_game();
{
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();
let current_round = self.rounds
.last_mut()
.expect("instance does not have any rounds");
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);
fn round_ready_check(&mut self) -> bool {
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);
fn create_round_game(&mut self) -> Game {
let current_round = self.rounds
.last_mut()
.expect("instance does not have any rounds");
let mut game = Game::new();
current_round.game_id = Some(game.id);
game
.set_player_num(2)
.set_player_constructs(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)) {
for player in self.players.clone().into_iter() {
game.player_add(player).unwrap();
}
@ -303,7 +295,7 @@ impl Instance {
p.vbox.fill();
});
self.generate_rounds();
self.rounds.push(Round::new());
self.bot_round_actions();
self
@ -313,7 +305,9 @@ impl Instance {
match self.format {
// bo5 standard
// OR condition is for forfeitures
Format::Standard => self.players.iter().any(|p| p.score.wins > 2) || self.rounds.len() == 5,
Format::Standard =>
self.players.iter().any(|p| p.wins as usize >= self.max_rounds / 2 + 1)
|| self.rounds.len() == self.max_rounds,
// everybody plays each other once
Format::RoundRobin => self.rounds.len() == self.players.len() - 1,
@ -353,54 +347,14 @@ impl Instance {
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> {
fn current_game_id(&self) -> Option<Uuid> {
if self.phase == InstancePhase::Lobby {
return None;
}
let current_round = self.current_round(player_id);
let current_round = self.rounds
.last()
.expect("instance does not have any rounds");
if current_round.finished || current_round.game_id.is_none() {
return None;
@ -410,13 +364,17 @@ impl Instance {
}
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;
{
let current_round = self.rounds
.last_mut()
.expect("instance does not have any rounds");
if current_round.game_id.unwrap() != game.id {
return Err(err_msg("instance does not have a round for this game"));
}
current_round.finished = true;
}
// if you don't win, you lose
// ties can happen if both players forfeit
@ -446,7 +404,7 @@ impl Instance {
fn all_games_finished(&self) -> bool {
match self.rounds.last() {
Some(r) => r.iter().all(|g| g.finished),
Some(r) => r.finished,
None => true,
}
}
@ -460,10 +418,15 @@ impl Instance {
}
pub fn vbox_action_allowed(&self, account: Uuid) -> Result<(), Error> {
if self.players.iter().find(|p| p.id == account).is_none() {
return Err(err_msg("player not in this instance"));
}
if self.phase == InstancePhase::Lobby {
return Err(err_msg("game not yet started"));
}
if self.current_game_id(account).is_some() {
if self.current_game_id().is_some() {
return Err(err_msg("you cannot perform vbox actions while in a game"));
}
@ -653,20 +616,13 @@ pub fn instances_need_upkeep(tx: &mut Transaction) -> Result<Vec<Instance>, Erro
}
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)?
let mut instance = match params.pve {
true => Instance::new()
.set_name(params.name)?
.add_bots(),
2 => Instance::new()
.set_max_players(params.players)?
false => Instance::new()
.set_name(params.name)?
.set_format(Format::Standard),
_ => Instance::new()
.set_max_players(params.players)?
.set_name(params.name)?,
};
instance = instance_create(tx, instance)?;
@ -705,10 +661,10 @@ pub fn instance_ready(params: InstanceReadyParams, tx: &mut Transaction, account
instance_update(tx, instance)
}
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)?;
if let Some(game_id) = instance.current_game_id(account.id) {
if let Some(game_id) = instance.current_game_id() {
let game = game_get(tx, game_id)?;
// return the game until it's finished
@ -736,24 +692,19 @@ mod tests {
#[test]
fn instance_pve_test() {
let mut instance = Instance::new()
.set_max_players(16).expect("unable to set max players")
.set_format(Format::RoundRobin)
.add_bots();
let mut instance = Instance::new().add_bots();
let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account);
let player = Player::new(player_account, &"test".to_string(), constructs).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();
instance.player_ready(player_account).unwrap();
assert_eq!(instance.phase, InstancePhase::Finished);
assert_eq!(instance.rounds[0].len(), 8);
assert_eq!(instance.rounds.len(), 15);
assert!(instance.players.iter().any(|p| p.wins as usize == instance.max_rounds / 2 + 1));
}
#[test]
@ -766,9 +717,7 @@ mod tests {
#[test]
fn instance_start_test() {
let mut instance = Instance::new()
.set_max_players(2)
.expect("could not create instance");
let mut instance = Instance::new();
assert_eq!(instance.max_players, 2);
@ -807,9 +756,7 @@ mod tests {
#[test]
fn instance_upkeep_test() {
let mut instance = Instance::new()
.set_max_players(2).expect("could not create instance")
.set_format(Format::Standard);
let mut instance = Instance::new();
let player_account = Uuid::new_v4();
let constructs = instance_mobs(player_account);

View File

@ -14,22 +14,17 @@ use skill::{Effect};
const DISCARD_COST: u16 = 5;
#[derive(Debug,Clone,Copy,Serialize,Deserialize)]
pub struct Score {
pub wins: u8,
pub losses: u8,
}
#[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Player {
pub id: Uuid,
pub name: String,
pub vbox: Vbox,
pub score: Score,
pub constructs: Vec<Construct>,
pub bot: bool,
pub ready: bool,
pub warnings: u8,
pub wins: u8,
pub losses: u8,
}
impl Player {
@ -38,7 +33,8 @@ impl Player {
id: account,
name: name.clone(),
vbox: Vbox::new(),
score: Score { wins: 0, losses: 0 },
wins: 0,
losses: 0,
constructs,
bot: false,
ready: false,
@ -69,13 +65,13 @@ impl Player {
}
pub fn add_win(&mut self) -> &mut Player {
self.score.wins += 1;
self.wins += 1;
self.vbox.balance_add(12);
self
}
pub fn add_loss(&mut self) -> &mut Player {
self.score.losses += 1;
self.losses += 1;
self.vbox.balance_add(9);
self
}

View File

@ -17,7 +17,6 @@ use game::{Game, game_state, game_skill, game_ready};
use account::{Account, account_create, account_login, account_from_token, account_constructs, account_instances};
use skill::{Skill};
use spec::{Spec};
use player::{Score};
use instance::{Instance, instance_state, instance_list, instance_new, instance_ready, instance_join};
use vbox::{vbox_accept, vbox_apply, vbox_discard, vbox_combine, vbox_reclaim, vbox_unequip};
use item::{Item, ItemInfoCtr, item_info};
@ -389,9 +388,6 @@ pub enum RpcResult {
ConstructList(Vec<Construct>),
GameState(Game),
ItemInfo(ItemInfoCtr),
InstanceScores(Vec<(String, Score)>),
// ZoneState(Zone),
// ZoneClose(()),
InstanceList(Vec<Instance>),
InstanceState(Instance),
@ -528,7 +524,7 @@ struct InstanceLobbyMsg {
pub struct InstanceLobbyParams {
pub construct_ids: Vec<Uuid>,
pub name: String,
pub players: usize,
pub pve: bool,
pub password: Option<String>,
}