diff --git a/game/src/game/mod.rs b/game/src/game/mod.rs index 0f51396..4cbca3f 100644 --- a/game/src/game/mod.rs +++ b/game/src/game/mod.rs @@ -7,17 +7,9 @@ use crate::{ pub type Pool = [u8; 7]; -#[derive(Clone, Copy, PartialEq)] -pub enum GameState { - None, - Joinable, - Ongoing, - Complete, -} - pub struct Game { + pub complete:bool, pub turn:u16, - pub state:GameState, pub board:Board, pub pool:[Pool; 2], @@ -27,7 +19,7 @@ impl Game { pub fn new() -> Self { Self { - state:GameState::Joinable, + complete:false, turn:0, diff --git a/server/src/app/session.rs b/server/src/app/session.rs index aae525b..6a0c58a 100644 --- a/server/src/app/session.rs +++ b/server/src/app/session.rs @@ -3,6 +3,7 @@ use game::Game; pub type SessionToken = [u8; 8]; pub type SessionSecret = [u8; 8]; +#[derive(Clone)] pub struct Player { pub user:Option, pub connections:Vec, @@ -23,13 +24,19 @@ pub struct Session { pub chain_id:usize, } impl Session { - pub fn get_connections(&self) -> Vec + pub fn get_connections(&self) -> Vec<(u32, u8)> { - [ - self.p_dawn.connections.clone(), - self.p_dusk.connections.clone(), - self.connections.clone(), - ].concat() + let mut result = Vec::new(); + for conn in &self.p_dawn.connections { + result.push((*conn, 0)); + } + for conn in &self.p_dusk.connections { + result.push((*conn, 1)); + } + for conn in &self.connections { + result.push((*conn, 2)); + } + result } pub fn add_connection(&mut self, source:u8, id:u32) diff --git a/server/src/manager/data.rs b/server/src/manager/data.rs index 56f10e0..a2a8487 100644 --- a/server/src/manager/data.rs +++ b/server/src/manager/data.rs @@ -261,8 +261,6 @@ pub async fn thread_system(mut app:App, bus:Bus) } QRPacketData::QSessionList(request) => { - use game::game::GameState; - println!("Request: Session List"); let mut response = PacketSessionListResponse::new(); @@ -280,8 +278,12 @@ pub async fn thread_system(mut app:App, bus:Bus) // - IsPlayer must have the current user in either player slot. // - IsLive must have both users connected to the session. - let mut valid = request.game_state == GameState::None || request.game_state == session.game.state; - valid &= request.game_state != GameState::Joinable || session.p_dawn.user.is_none() || session.p_dusk.user.is_none(); + let mut valid = match request.game_state { + 1 => session.p_dawn.user.is_none() || session.p_dusk.user.is_none(), + 2 => session.p_dawn.user.is_some() && session.p_dusk.user.is_some(), + 3 => session.game.complete, + _ => true, + }; valid &= !request.is_player || session.p_dawn.user == user_id || session.p_dusk.user == user_id; valid &= !request.is_live || (session.p_dawn.connections.len() > 0 && session.p_dusk.connections.len() > 0); @@ -402,14 +404,19 @@ pub async fn thread_system(mut app:App, bus:Bus) // User must not already be player if session.p_dawn.user != user_id && session.p_dusk.user != user_id { - // Add user to empty player slot - if if session.p_dawn.user.is_none() { - session.p_dawn.user = Some(uid); - response.mode = 0; - true - } else if session.p_dusk.user.is_none() { - session.p_dusk.user = Some(uid); - response.mode = 1; + // Add user to session and randomize seats. + if if session.p_dusk.user.is_none() { + let mut r = [0u8; 1]; + rng.fill(&mut r).ok(); + if (r[0] & 1) == 0 { + session.p_dusk = session.p_dawn.clone(); + session.p_dawn.user = Some(uid); + session.p_dawn.connections.clear(); + response.mode = 0; + } else { + session.p_dusk.user = Some(uid); + response.mode = 1; + } true } else { // Session is not empty @@ -458,12 +465,11 @@ pub async fn thread_system(mut app:App, bus:Bus) let mut packets = Vec::new(); let game_state = generate_gamestate(&app, session); - for cid in &session.get_connections() { - if *cid != qr.id { - packets.push(QRPacket::new( - *cid, - QRPacketData::RGameState(game_state.clone()), - )); + for (cid, player) in session.get_connections() { + if cid != qr.id { + let mut gs = game_state.clone(); + gs.player = player; + packets.push(QRPacket::new(cid, QRPacketData::RGameState(gs))); } } for packet in packets { @@ -509,6 +515,11 @@ pub async fn thread_system(mut app:App, bus:Bus) if let Some(session) = app.sessions.get(&request.token) { response = generate_gamestate(&app, &session); + + if user_id.is_some() { + if user_id == session.p_dawn.user { response.player = 0; } + else if user_id == session.p_dusk.user { response.player = 1; } + } } else { response.status = STATUS_ERROR; } @@ -540,9 +551,9 @@ pub async fn thread_system(mut app:App, bus:Bus) app.filesystem.session_history_push(session.id, request.play).ok(); // Forward play to all clients - for cid in &session.get_connections() { + for (cid, _) in session.get_connections() { packets.push(QRPacket::new( - *cid, + cid, QRPacketData::QGamePlay(request.clone()) )); } @@ -583,6 +594,8 @@ fn generate_gamestate(app:&App, session:&Session) -> protocol::PacketGameStateRe use protocol::{PacketGameStateResponse, PacketGameStateResponsePiece}; let mut response = PacketGameStateResponse::new(); + + response.player = 2; response.turn = session.game.turn; // Get Dawn handle diff --git a/server/src/protocol/packet/game_state.rs b/server/src/protocol/packet/game_state.rs index eb22d56..b4ae755 100644 --- a/server/src/protocol/packet/game_state.rs +++ b/server/src/protocol/packet/game_state.rs @@ -64,6 +64,7 @@ impl PacketGameStateResponsePiece { pub struct PacketGameStateResponse { pub status:u16, pub turn:u16, + pub player:u8, pub dawn_handle:String, pub dusk_handle:String, pub dawn_pool:Pool, @@ -76,6 +77,7 @@ impl PacketGameStateResponse { Self { status:0, turn:0, + player:0, dawn_handle:String::new(), dusk_handle:String::new(), dawn_pool:Pool::default(), @@ -89,6 +91,9 @@ impl Packet for PacketGameStateResponse { fn encode(&self) -> Vec { + let mut flags = 0u16; + flags |= self.player as u16; + let mut piece_bytes = Vec::new(); for piece in &self.pieces { let piece_data: u16 = piece.valid as u16 @@ -118,6 +123,7 @@ impl Packet for PacketGameStateResponse { [ pack_u16(self.status), + pack_u16(flags), pack_u16(self.turn), pack_u16(self.dawn_handle.len() as u16), self.dawn_handle.as_bytes().to_vec(), diff --git a/server/src/protocol/packet/session_list.rs b/server/src/protocol/packet/session_list.rs index 6d842b5..0c38a8a 100644 --- a/server/src/protocol/packet/session_list.rs +++ b/server/src/protocol/packet/session_list.rs @@ -2,17 +2,14 @@ use crate::{ app::session::SessionToken, util::pack::{pack_u16, pack_u32, unpack_u16}, }; -use game::{ - game::GameState, - util::mask, -}; +use game::util::mask; use super::Packet; #[derive(Clone)] pub struct PacketSessionList { pub page:u16, - pub game_state:GameState, + pub game_state:u8, pub is_player:bool, pub is_live:bool, } @@ -21,7 +18,7 @@ impl PacketSessionList { { Self { page:0, - game_state:GameState::Joinable, + game_state:0, is_player:false, is_live:false, } @@ -41,12 +38,7 @@ impl Packet for PacketSessionList { */ if data.len() - *index == 4 { let flags = unpack_u16(data, index); - result.game_state = match flags & mask(2, 0) as u16 { - 1 => GameState::Joinable, - 2 => GameState::Ongoing, - 3 => GameState::Complete, - _ => GameState::None, - }; + result.game_state = (flags & mask(2, 0) as u16) as u8; result.is_player = (flags & mask(1, 2) as u16) != 0; result.is_live = (flags & mask(1, 3) as u16) != 0; diff --git a/www/js/interface.js b/www/js/interface.js index 24b6cdd..3ee09db 100644 --- a/www/js/interface.js +++ b/www/js/interface.js @@ -717,6 +717,8 @@ const INTERFACE = { message(code, data) { switch(code) { case OpCode.GameState: { + console.log(data.player); + INTERFACE_DATA.player = data.player; GAME_DATA.turn = data.turn; if(data.dawn.length > 0) { INTERFACE_DATA.handles[0] = data.dawn; } diff --git a/www/js/scene.js b/www/js/scene.js index 19acb7e..c527828 100644 --- a/www/js/scene.js +++ b/www/js/scene.js @@ -207,7 +207,7 @@ const SCENES = { return true; }, refresh() { - MESSAGE_SESSION_LIST(0, 0, false, false); + MESSAGE_SESSION_LIST(0, 2, false, false); }, message(code, data) { switch(code) { @@ -266,7 +266,7 @@ const SCENES = { return true; }, refresh() { - MESSAGE_SESSION_LIST(0, 0, true, false); + MESSAGE_SESSION_LIST(0, 2, true, false); }, message(code, data) { switch(code) { @@ -334,7 +334,7 @@ const SCENES = { UI.clear(table); if(data !== null) { - table.appendChild(UI.session_table(data.records)); + table.appendChild(UI.session_table_join(data.records)); } } break; case OpCode.SessionCreate: diff --git a/www/js/system.js b/www/js/system.js index 72b1bcd..3590e07 100644 --- a/www/js/system.js +++ b/www/js/system.js @@ -205,6 +205,7 @@ function MESSAGE(event) { //if(bytes.length - index >= 22) { data = { status:0, + player:2, turn:0, dawn:"", dusk:"", @@ -218,6 +219,13 @@ function MESSAGE(event) { index = result.index; data.status = result.data; + // Flags + result = UNPACK.u16(bytes, index); + index = result.index; + let flags = result.data; + + data.player = flags & 0x3; + // Turn result = UNPACK.u16(bytes, index); index = result.index; diff --git a/www/js/ui.js b/www/js/ui.js index 0b3aba6..6d3f9f2 100644 --- a/www/js/ui.js +++ b/www/js/ui.js @@ -185,6 +185,39 @@ const UI = { return tbody; }, + session_table_join(records) { + let rows = [ ]; + + for(let r = 0; r < records.length; ++r) { + let buttons = [ ]; + let join_callback = function() { + MESSAGE_SESSION_JOIN(this.token, true); + }; + join_callback = join_callback.bind({token: records[r].token}); + + if(records[r].player) { + buttons.push(UI.button("View", join_callback)); + } else { + buttons.push(UI.button("Join", join_callback)); + } + + let host = UI.text(records[r].dawn); + if(records[r].dawn == "") { dawn = UI.span([UI.text("Vacant")], "text-system"); } + + rows.push([ + host, + buttons, + ]); + } + + let tbody = UI.table_content( + [ "Host", "" ], + rows, + ); + + return tbody; + }, + clear(dom) { while(dom.lastChild !== null) { dom.removeChild(document.body.lastChild); } },