Merge spectate and history views; increase bot delay to 1 second.

This commit is contained in:
yukirij 2024-08-25 12:28:51 -07:00
parent d4d578a5a7
commit 762f2abab2
15 changed files with 300 additions and 488 deletions

View File

@ -202,12 +202,6 @@ impl App {
)).await.ok();
}
QRPacketData::RGameHistory(response) => {
socket.send(Message::Binary(
encode_response(CODE_GAME_HISTORY, response.encode())
)).await.ok();
}
_ => { }
}
}

View File

@ -380,9 +380,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
app.sessions.set(&token, session);
app.session_time.set(chain_id, token);
// Set player to Dawn.
response.mode = 0;
response.status = STATUS_OK;
response.token = token;
}
@ -419,10 +416,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
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 {
@ -444,7 +439,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
else {
println!("User resumes session.");
response.status = STATUS_OK;
response.mode = (session.p_dusk.user == user_id) as u8;
true
}
} else { response.status = STATUS_NOAUTH; false }
@ -455,14 +449,20 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
println!("User spectates session.");
response.status = STATUS_OK;
response.mode = 2;
true
} {
// Associate session and connection on join
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
conn.session = Some(session.token);
}
session.add_connection(response.mode, qr.id);
let mode = if user_id == session.p_dawn.user {
0
} else if user_id == session.p_dusk.user {
1
} else {
2
};
session.add_connection(mode, qr.id);
}
}
@ -618,21 +618,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
}
}
QRPacketData::QGameHistory(request) => {
let mut response = PacketGameHistoryResponse::new();
println!("Request: Game History");
if let Some(session) = app.sessions.get(&request.token) {
response.status = STATUS_OK;
response = generate_game_history(&app, &session);
} else {
response.status = STATUS_ERROR;
}
Some(QRPacket::new(qr.id, QRPacketData::RGameHistory(response)))
}
_ => { Some(QRPacket::new(0, QRPacketData::None)) }
}
}
@ -644,59 +629,12 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
fn generate_game_state(app:&App, session:&Session) -> protocol::PacketGameStateResponse
{
use protocol::{PacketGameStateResponse, PacketGameStateResponsePiece};
use protocol::PacketGameStateResponse;
let mut response = PacketGameStateResponse::new();
response.player = 2;
response.turn = session.game.turn;
if session.game.history.len() > 0 {
response.play = session.game.history[session.game.history.len() - 1];
}
// Get Dawn handle
if let Some(id) = session.p_dawn.user {
if let Some(user) = app.get_user_by_id(id) {
response.dawn_handle = user.handle.clone();
}
}
// Get Dusk handle
if let Some(id) = session.p_dusk.user {
if let Some(user) = app.get_user_by_id(id) {
response.dusk_handle = user.handle.clone();
}
}
// Get pool sizes
response.dawn_pool = session.game.pool[0];
response.dusk_pool = session.game.pool[1];
// Get list of pieces
for i in 0..session.game.board.pieces.len() {
response.pieces[i] = if let Some(piece) = &session.game.board.pieces[i] {
PacketGameStateResponsePiece {
valid:true,
piece:piece.class,
promoted:piece.promoted,
player:piece.player,
tile:piece.tile,
}
} else {
PacketGameStateResponsePiece::new()
};
}
response
}
fn generate_game_history(app:&App, session:&Session) -> protocol::PacketGameHistoryResponse
{
use protocol::PacketGameHistoryResponse;
let mut response = PacketGameHistoryResponse::new();
response.token = session.token;
response.player = 2;
// Get Dawn handle
if let Some(id) = session.p_dawn.user {
@ -712,6 +650,7 @@ fn generate_game_history(app:&App, session:&Session) -> protocol::PacketGameHist
}
}
// Get history
response.history = session.game.history.clone();
response

View File

@ -161,20 +161,13 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
Err(_) => { println!("error: packet decode failed."); }
}
CODE_GAME_HISTORY => match PacketGameHistory::decode(&data, &mut index) {
Ok(packet) => {
args.bus.send(
bus_ds,
QRPacket::new(conn_id, QRPacketData::QGameHistory(packet))
).ok();
}
Err(_) => { println!("error: packet decode failed."); }
}
_ => { }
}
true
}
Message::Close(_) => { true }
_ => {
println!("notice: received unexpected websocket data.");
true

View File

@ -31,4 +31,3 @@ pub const CODE_SESSION_LEAVE :u16 = 0x002F;
pub const CODE_GAME_STATE :u16 = 0x0030;
pub const CODE_GAME_PLAY :u16 = 0x0031;
pub const CODE_GAME_HISTORY :u16 = 0x0032;

View File

@ -23,6 +23,9 @@ pub enum QRPacketData {
QAuthRevoke,
QUserList(PacketUserList),
RUserList(PacketUserListResponse),
QSessionList(PacketSessionList),
RSessionList(PacketSessionListResponse),
@ -40,9 +43,6 @@ pub enum QRPacketData {
RGameState(PacketGameStateResponse),
QGamePlay(PacketGamePlay),
QGameHistory(PacketGameHistory),
RGameHistory(PacketGameHistoryResponse),
}
#[derive(Clone)]

View File

@ -42,6 +42,7 @@ impl Packet for PacketGameHistory {
pub struct PacketGameHistoryResponse {
pub status:u16,
pub token:SessionToken,
pub player:u8,
pub dawn_handle:String,
pub dusk_handle:String,
pub history:Vec<Play>,

View File

@ -3,7 +3,7 @@ use crate::{
util::pack::pack_u16,
};
use game::{game::Pool, history::Play};
use game::history::Play;
use super::Packet;
@ -38,53 +38,25 @@ impl Packet for PacketGameState {
}
}
#[derive(Clone, Copy)]
pub struct PacketGameStateResponsePiece {
pub valid:bool,
pub piece:u8,
pub promoted:bool,
pub player:u8,
pub tile:u8,
}
impl PacketGameStateResponsePiece {
pub fn new() -> Self
{
Self {
valid:false,
piece:0,
promoted:false,
player:0,
tile:0,
}
}
}
#[derive(Clone)]
pub struct PacketGameStateResponse {
pub status:u16,
pub turn:u16,
pub token:SessionToken,
pub player:u8,
pub play:Play,
pub dawn_handle:String,
pub dusk_handle:String,
pub dawn_pool:Pool,
pub dusk_pool:Pool,
pub pieces:[PacketGameStateResponsePiece; game::consts::PIECES_TOTAL],
pub history:Vec<Play>,
}
impl PacketGameStateResponse {
pub fn new() -> Self
{
Self {
status:0,
turn:0,
player:0,
play:Play::new(),
token:SessionToken::default(),
player:2,
dawn_handle:String::new(),
dusk_handle:String::new(),
dawn_pool:Pool::default(),
dusk_pool:Pool::default(),
pieces:[PacketGameStateResponsePiece::new(); game::consts::PIECES_TOTAL],
history:Vec::new(),
}
}
}
@ -93,53 +65,28 @@ impl Packet for PacketGameStateResponse {
fn encode(&self) -> Vec<u8>
{
let mut history_bytes = Vec::new();
for play in &self.history {
let mut data = 0;
data |= play.source as u16;
data |= (play.from as u16) << 4;
data |= (play.to as u16) << 10;
history_bytes.append(&mut pack_u16(data));
}
let mut flags = 0u16;
flags |= self.player as u16;
let mut play = 0;
play |= self.play.source as u16;
play |= (self.play.from as u16) << 4;
play |= (self.play.to as u16) << 10;
let mut piece_bytes = Vec::new();
for piece in &self.pieces {
let piece_data: u16 = piece.valid as u16
| ((piece.piece as u16) << 1)
| ((piece.promoted as u16) << 4)
| ((piece.player as u16) << 5)
| ((piece.tile as u16) << 6);
piece_bytes.append(&mut pack_u16(piece_data));
}
let dawn_pool_bytes :u16 = self.dawn_pool[0] as u16
| ((self.dawn_pool[1] as u16) << 5)
| ((self.dawn_pool[2] as u16) << 7)
| ((self.dawn_pool[3] as u16) << 9)
| ((self.dawn_pool[4] as u16) << 11)
| ((self.dawn_pool[5] as u16) << 13)
| ((self.dawn_pool[6] as u16) << 14);
let dusk_pool_bytes :u16 = self.dusk_pool[0] as u16
| ((self.dusk_pool[1] as u16) << 5)
| ((self.dusk_pool[2] as u16) << 7)
| ((self.dusk_pool[3] as u16) << 9)
| ((self.dusk_pool[4] as u16) << 11)
| ((self.dusk_pool[5] as u16) << 13)
| ((self.dusk_pool[6] as u16) << 14);
[
pack_u16(self.status),
self.token.to_vec(),
pack_u16(flags),
pack_u16(play),
pack_u16(self.turn),
pack_u16(self.dawn_handle.len() as u16),
self.dawn_handle.as_bytes().to_vec(),
pack_u16(self.dusk_handle.len() as u16),
self.dusk_handle.as_bytes().to_vec(),
pack_u16(dawn_pool_bytes),
pack_u16(dusk_pool_bytes),
piece_bytes,
pack_u16(self.history.len() as u16),
history_bytes,
].concat()
}
}

View File

@ -4,6 +4,8 @@ mod register; pub use register::*;
mod auth; pub use auth::*;
mod resume; pub use resume::*;
mod user_list; pub use user_list::*;
mod session_list; pub use session_list::*;
mod session_create; pub use session_create::*;
mod session_join; pub use session_join::*;
@ -11,7 +13,7 @@ mod session_retire; pub use session_retire::*;
mod game_state; pub use game_state::*;
mod game_play; pub use game_play::*;
mod game_history; pub use game_history::*;
//mod game_history; pub use game_history::*;
mod prelude {
pub trait Packet {

View File

@ -1,6 +1,6 @@
use crate::{
app::session::SessionToken,
util::pack::{pack_u8, pack_u16},
util::pack::pack_u16,
};
use super::Packet;
@ -29,7 +29,6 @@ impl Packet for PacketSessionCreate {
pub struct PacketSessionCreateResponse {
pub status:u16,
pub token:SessionToken,
pub mode:u8,
}
impl PacketSessionCreateResponse {
pub fn new() -> Self
@ -37,7 +36,6 @@ impl PacketSessionCreateResponse {
Self {
status:0,
token:SessionToken::default(),
mode:0,
}
}
}
@ -48,7 +46,6 @@ impl Packet for PacketSessionCreateResponse {
{
[
pack_u16(self.status),
pack_u8(self.mode),
self.token.to_vec(),
].concat()
}

View File

@ -1,6 +1,6 @@
use crate::{
app::session::SessionToken,
util::pack::{pack_u8, pack_u16},
util::pack::pack_u16,
};
use super::Packet;
@ -41,7 +41,6 @@ impl Packet for PacketSessionJoin {
pub struct PacketSessionJoinResponse {
pub status:u16,
pub token:SessionToken,
pub mode:u8,
}
impl PacketSessionJoinResponse {
pub fn new() -> Self
@ -49,7 +48,6 @@ impl PacketSessionJoinResponse {
Self {
status:0,
token:SessionToken::default(),
mode:0,
}
}
}
@ -60,7 +58,6 @@ impl Packet for PacketSessionJoinResponse {
{
[
pack_u16(self.status),
pack_u8(self.mode),
self.token.to_vec(),
].concat()
}

View File

@ -0,0 +1,76 @@
use crate::util::pack::{pack_u16, unpack_u16};
use super::Packet;
#[derive(Clone)]
pub struct PacketUserList {
pub page:u16,
}
impl PacketUserList {
pub fn new() -> Self
{
Self {
page:0,
}
}
}
impl Packet for PacketUserList {
type Data = Self;
fn decode(data:&Vec<u8>, index:&mut usize) -> Result<Self::Data, ()>
{
let mut result = Self::new();
if data.len() - *index == 2 {
result.page = unpack_u16(data, index);
Ok(result)
} else {
Err(())
}
}
}
#[derive(Clone)]
pub struct PacketUserListResponseRecord {
//pub token:UserToken,
pub handle:String,
}
#[derive(Clone)]
pub struct PacketUserListResponse {
pub status:u16,
pub records:Vec<PacketUserListResponseRecord>,
}
impl PacketUserListResponse {
pub fn new() -> Self
{
Self {
status:0,
records:Vec::new(),
}
}
}
impl Packet for PacketUserListResponse {
type Data = Self;
fn encode(&self) -> Vec<u8>
{
let mut result = pack_u16(self.status as u16);
result.append(&mut pack_u16(self.records.len() as u16));
for record in &self.records {
let mut chunk = Vec::new(); //record.token.to_vec();
// Handle
let mut bytes = record.handle.as_bytes().to_vec();
chunk.append(&mut pack_u16(bytes.len() as u16));
if bytes.len() > 0 { chunk.append(&mut bytes); }
result.append(&mut chunk);
}
result
}
}

View File

@ -3,8 +3,8 @@ let INTERFACE_DATA = null;
const INTERFACE = {
Mode: {
Local: 0,
Online: 1,
Replay: 2,
Player: 1,
Review: 2,
},
Color: {
@ -290,16 +290,17 @@ const INTERFACE = {
draw() {
if(INTERFACE_DATA === null) return;
if(INTERFACE_DATA.mode == INTERFACE.Mode.Replay) {
document.getElementById("ind_turn").innerText = INTERFACE_DATA.replay_turn + " of " + INTERFACE_DATA.history.length;
}
let canvas = INTERFACE_DATA.canvas;
let ctx = INTERFACE_DATA.context;
INTERFACE.resize();
INTERFACE.resolve_board();
let play = null;
if(INTERFACE_DATA.replay_turn > 0) {
play = INTERFACE_DATA.history[INTERFACE_DATA.replay_turn - 1];
}
let width = canvas.width;
let height = canvas.height;
@ -369,7 +370,7 @@ const INTERFACE = {
case 1: ctx.fillStyle = INTERFACE.Color.TileLight; break;
case 2: ctx.fillStyle = INTERFACE.Color.TileDark; break;
}
if(GAME_DATA.turn > 0 && INTERFACE_DATA.play.source < 2 && (INTERFACE_DATA.play.to == i || (INTERFACE_DATA.play.source == 0 && INTERFACE_DATA.play.from == i))) {
if(GAME_DATA.turn > 0 && play.source < 2 && (play.to == i || (play.source == 0 && play.from == i))) {
ctx.fillStyle = INTERFACE.Color.HintPlay;
} else if(GAME_DATA.state.check != 0 && piece !== null && piece.piece == GAME.Const.PieceId.Omen && piece.player == (GAME_DATA.turn & 1)) {
ctx.fillStyle = INTERFACE.Color.HintCheck;
@ -704,35 +705,18 @@ const INTERFACE = {
},
},
init(data, mode) {
init(token, mode) {
GAME.init();
let token = null;
let player = 0;
let player = 2;
if(mode == INTERFACE.Mode.Local) { player = 0; }
let history = [ ];
let dawn = null;
let dusk = null;
if(data !== null) {
switch(mode) {
case INTERFACE.Mode.Online: {
token = data.token;
player = data.mode;
} break;
case INTERFACE.Mode.Replay: {
history = data.history;
player = 2;
} break;
}
if(data.dawn !== undefined) { dawn = data.dawn; }
if(data.dusk !== undefined) { dusk = data.dusk; }
}
INTERFACE_DATA = {
mode: mode,
token: token,
canvas: document.getElementById("game"),
@ -747,15 +731,14 @@ const INTERFACE = {
handles: [dawn, dusk],
board_state: [ ],
play: null,
retire:false,
retire_warn:false,
auto_mode: null,
history: history,
replay_turn: 0,
replay_auto: false,
history: history,
auto_mode: null,
Ui: {
scale: 0,
@ -780,24 +763,18 @@ const INTERFACE = {
window.addEventListener("resize", INTERFACE.draw);
switch(INTERFACE_DATA.mode) {
case INTERFACE.Mode.Replay:
case INTERFACE.Mode.Local: {
INTERFACE.draw();
} break;
case INTERFACE.Mode.Online: {
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameState),
INTERFACE_DATA.token,
]);
} break;
}
} else {
LOAD(SCENES.Browse);
}
},
uninit() {
if(INTERFACE_DATA !== null) {
if(INTERFACE_DATA.mode == INTERFACE.Mode.Online) {
if(INTERFACE_DATA.mode != INTERFACE.Mode.Local) {
MESSAGE_COMPOSE([
PACK.u16(OpCode.SessionLeave),
]);
@ -820,44 +797,51 @@ const INTERFACE = {
if(data === null) { return; }
switch(code) {
case OpCode.GameState: {
INTERFACE_DATA.player = data.player;
GAME_DATA.turn = data.turn;
INTERFACE_DATA.play = data.play;
if(INTERFACE_DATA.play.source == 2) {
case OpCode.SessionCreate:
case OpCode.SessionJoin: {
if(data.status != Status.Ok) {
LOAD(SCENES.Browse);
}
switch(INTERFACE_DATA.mode) {
case INTERFACE.Mode.Review:
case INTERFACE.Mode.Player: {
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameState),
INTERFACE_DATA.token,
]);
} break;
}
} break;
case OpCode.GameState: {
if(INTERFACE_DATA.mode == INTERFACE.Mode.Player) {
INTERFACE_DATA.player = data.player;
}
INTERFACE_DATA.history = [ ];
for(let i = 0; i < data.history.length; ++i) {
INTERFACE.history_push(data.history[i]);
}
let turn = INTERFACE_DATA.history.length;
if(INTERFACE_DATA.history.length > 0) {
if(INTERFACE_DATA.history[INTERFACE_DATA.history.length-1].source == 2) {
GAME_DATA.state.code = 2;
turn = 0;
}
}
if(data.dawn.length > 0) { INTERFACE_DATA.handles[0] = data.dawn; }
if(data.dusk.length > 0) { INTERFACE_DATA.handles[1] = data.dusk; }
// Clear piece placement.
for(let i = 0; i < GAME_DATA.board.tiles.length; ++i) {
GAME_DATA.board.tiles[i].piece = null;
}
// Update pools.
GAME_DATA.pools[0].pieces = data.pool_dawn;
GAME_DATA.pools[1].pieces = data.pool_dusk;
// Replace pieces list.
for(let i = 0; i < GAME_DATA.board.pieces.length; ++i) {
GAME_DATA.board.pieces[i] = data.pieces[i];
if(data.pieces[i] !== null) {
GAME_DATA.board.tiles[data.pieces[i].tile].piece = i;
}
}
GAME_DATA.update_board();
INTERFACE.draw();
INTERFACE.replay_jump(turn);
} break;
case OpCode.GamePlay: {
if(data.status == Status.Ok && data.turn == GAME_DATA.turn) {
INTERFACE_DATA.play = data.play;
GAME_DATA.process(data.play);
INTERFACE.draw();
if(data.status == Status.Ok && data.turn == INTERFACE_DATA.history.length) {
INTERFACE.history_push(data.play);
}
} break;
}
@ -868,8 +852,7 @@ const INTERFACE = {
switch(INTERFACE_DATA.mode) {
// Apply action and change turn for local game.
case INTERFACE.Mode.Local: {
INTERFACE_DATA.play = play;
GAME_DATA.process(play);
INTERFACE.history_push(play);
INTERFACE_DATA.player = +(!INTERFACE_DATA.player);
INTERFACE_DATA.rotate = +(!INTERFACE_DATA.rotate);
@ -877,12 +860,12 @@ const INTERFACE = {
INTERFACE.draw();
if(INTERFACE_DATA.auto_mode !== null && INTERFACE_DATA.auto_mode == (GAME_DATA.turn & 1)) {
setTimeout(INTERFACE.auto_play, 500);
setTimeout(INTERFACE.auto_play, 1000);
}
} break;
// Send action to server for validation.
case INTERFACE.Mode.Online: {
case INTERFACE.Mode.Player: {
let move_data = play.source | (play.from << 1) | (play.to << 7);
MESSAGE_COMPOSE([
PACK.u16(OpCode.GamePlay),
@ -891,11 +874,6 @@ const INTERFACE = {
PACK.u16(move_data),
]);
} break;
// Branch into local game from here.
case INTERFACE.Mode.Replay: {
} break;
}
},
@ -910,7 +888,7 @@ const INTERFACE = {
},
retire() {
if(INTERFACE_DATA.mode == INTERFACE.Mode.Online) {
if(INTERFACE_DATA.mode == INTERFACE.Mode.Player) {
let button_retire = document.getElementById("button-retire");
if(INTERFACE_DATA.retire_warn) {
@ -939,25 +917,40 @@ const INTERFACE = {
}
},
history_push(play) {
INTERFACE_DATA.history.push(play);
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.replay_turn + " of " + INTERFACE_DATA.history.length;
document.getElementById("turn-slider").setAttribute("max", INTERFACE_DATA.history.length);
}
if(INTERFACE_DATA.replay_turn == INTERFACE_DATA.history.length - 1) {
INTERFACE.replay_next();
}
},
replay_jump(turn) {
turn = +turn;
if(INTERFACE_DATA.mode == INTERFACE.Mode.Replay) {
if(turn >= 0 && turn <= INTERFACE_DATA.history.length) {
INTERFACE_DATA.replay_turn = turn;
if(turn >= 0 && turn <= INTERFACE_DATA.history.length) {
if(turn < INTERFACE_DATA.replay_turn) {
GAME.init();
for(let i = 0; i < INTERFACE_DATA.replay_turn; ++i) {
for(let i = 0; i < turn; ++i) {
GAME_DATA.process(INTERFACE_DATA.history[i]);
}
INTERFACE_DATA.play = null;
if(INTERFACE_DATA.replay_turn > 0) {
INTERFACE_DATA.play = INTERFACE_DATA.history[INTERFACE_DATA.replay_turn - 1];
} else {
while(INTERFACE_DATA.replay_turn < turn) {
GAME_DATA.process(INTERFACE_DATA.history[INTERFACE_DATA.replay_turn]);
INTERFACE_DATA.replay_turn++;
}
}
INTERFACE_DATA.replay_turn = turn;
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.replay_turn + " of " + INTERFACE_DATA.history.length;
document.getElementById("turn-slider").value = INTERFACE_DATA.replay_turn;
INTERFACE.draw();
}
INTERFACE.draw();
}
},
replay_first() {

View File

@ -229,12 +229,6 @@ const SCENES = {
table.appendChild(UI.session_table(data.records));
}
} break;
case OpCode.SessionCreate:
case OpCode.SessionJoin: {
if(data.status == Status.Ok) {
LOAD(SCENES.Game, data);
}
} break;
}
},
disconnect() {
@ -398,8 +392,7 @@ const SCENES = {
table.appendChild(UI.session_table(data.records));
}
} break;
case OpCode.SessionCreate:
case OpCode.SessionJoin: {
{
if(data.status == Status.Ok) {
LOAD(SCENES.Game, data);
}
@ -453,11 +446,6 @@ const SCENES = {
table.appendChild(UI.session_table_history(data.records));
}
} break;
case OpCode.GameHistory: {
if(data.status == Status.Ok) {
LOAD(SCENES.GameHistory, data);
}
} break;
}
},
disconnect() {
@ -525,10 +513,8 @@ const SCENES = {
Game:{
load(data) {
if(data === null) { return false; }
let buttons_bottom = [ ];
if(data.mode != 2) {
if(data.mode != INTERFACE.Mode.Review) {
let button_retire = UI.button("Surrender", () => { INTERFACE.retire(); });
button_retire.setAttribute("id", "button-retire");
buttons_bottom.push(button_retire);
@ -540,11 +526,34 @@ const SCENES = {
UI.button("Mirror", () => { INTERFACE.mirror(); }),
], buttons_bottom);
if(data.mode == INTERFACE.Mode.Review) {
let ind_turn = UI.div([UI.text("0 of 0")]);
ind_turn.setAttribute("id", "indicator-turn");
UI.mainnav(
[ ],
[
UI.button("Auto", () => { INTERFACE.replay_toggle_auto(); }),
UI.button("◀", () => { INTERFACE.replay_first(); }),
UI.button("◁", () => { INTERFACE.replay_prev(); }),
ind_turn,
UI.button("▷", () => { INTERFACE.replay_next(); }),
UI.button("▶", () => { INTERFACE.replay_last(); }),
]
);
let slider = UI.slider((event) => { INTERFACE.replay_jump(event.target.value); });
slider.setAttribute("id", "turn-slider");
slider.setAttribute("min", "0");
slider.setAttribute("max", "0");
MAIN.appendChild(UI.div([ slider ], "turn-slider-padding"));
}
let canvas = document.createElement("canvas");
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
INTERFACE.init(data, INTERFACE.Mode.Online);
INTERFACE.init(data.token, data.mode);
history.pushState(null, "Omen - Game", "/game/" + PACK.base64(data.token).slice(0, -1));
return true;
@ -554,13 +563,9 @@ const SCENES = {
},
message(code, data) {
switch(code) {
case OpCode.GameState: {
if(data.status == Status.Ok) {
INTERFACE.message(code, data);
} else {
LOAD(SCENES.Browse);
}
} break;
case OpCode.SessionCreate:
case OpCode.SessionJoin:
case OpCode.GameState:
case OpCode.GamePlay: {
INTERFACE.message(code, data);
} break;
@ -587,7 +592,7 @@ const SCENES = {
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
INTERFACE.init(data, INTERFACE.Mode.Local);
INTERFACE.init(null, INTERFACE.Mode.Local);
history.pushState(null, "Omen - Practice", "/practice/");
return true;
@ -596,51 +601,6 @@ const SCENES = {
INTERFACE.uninit();
},
},
GameHistory:{
load(data) {
let buttons_bottom = [ ];
buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.History) }));
UI.nav([
UI.button("Rotate", () => { INTERFACE.rotate(); }),
UI.button("Mirror", () => { INTERFACE.mirror(); }),
], buttons_bottom);
let ind_turn = UI.div([UI.text("0 of 0")]);
ind_turn.setAttribute("id", "ind_turn");
UI.mainnav(
[ ],
[
UI.button("Auto", () => { INTERFACE.replay_toggle_auto(); }),
UI.button("◀", () => { INTERFACE.replay_first(); }),
UI.button("◁", () => { INTERFACE.replay_prev(); }),
ind_turn,
UI.button("▷", () => { INTERFACE.replay_next(); }),
UI.button("▶", () => { INTERFACE.replay_last(); }),
]
);
let slider = UI.slider((event) => { INTERFACE.replay_jump(event.target.value); });
slider.setAttribute("id", "turn-slider");
slider.setAttribute("min", "0");
slider.setAttribute("max", data.history.length);
MAIN.appendChild(UI.div([ slider ], "turn-slider-padding"));
let canvas = document.createElement("canvas");
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
INTERFACE.init(data, INTERFACE.Mode.Replay);
history.pushState(null, "Omen - History", "/history/" + PACK.base64(data.token).slice(0, -1));
return true;
},
unload() {
INTERFACE.uninit();
},
},
};
function LOAD(scene, data=null) {
@ -665,17 +625,6 @@ function LOAD_URL() {
case "guide": LOAD(SCENES.Guide); break;
case "practice": LOAD(SCENES.About); break;
case "history": {
LOAD(SCENES.History);
if(parts[2]) {
let token = UNPACK.base64(parts[2] + "=");
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameHistory),
token,
]);
}
} break;
case "join": {
LOAD(SCENES.Join);
if(parts[2]) {

View File

@ -185,21 +185,16 @@ function MESSAGE(event) {
case OpCode.SessionJoin: {
console.log("RECV SessionCreate/Join");
if(bytes.length - index == 11) {
if(bytes.length - index == 10) {
data = {
status:0,
token:new Uint8Array(8),
mode:2,
};
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
result = UNPACK.u8(bytes, index);
index = result.index;
data.mode = result.data;
for(let i = 0; i < 8; ++i) { data.token[i] = bytes[index++]; }
}
} break;
@ -207,17 +202,13 @@ function MESSAGE(event) {
case OpCode.GameState: {
console.log("RECV GameState");
//if(bytes.length - index >= 22) {
data = {
status:0,
token:new Uint8Array(8),
player:2,
turn:0,
play:new GAME.Play(),
dawn:"",
dusk:"",
pool_dawn:[ ],
pool_dusk:[ ],
pieces:[ ],
history:[ ],
};
// Status
@ -225,6 +216,9 @@ function MESSAGE(event) {
index = result.index;
data.status = result.data;
// Token
for(let i = 0; i < 8; ++i) { data.token[i] = bytes[index++]; }
// Flags
result = UNPACK.u16(bytes, index);
index = result.index;
@ -232,18 +226,6 @@ function MESSAGE(event) {
data.player = flags & 0x3;
// Last Play
result = UNPACK.u16(bytes, index);
index = result.index;
data.play.source = result.data & 0xF;
data.play.from = (result.data >> 4) & 0x3F;
data.play.to = (result.data >> 10) & 0x3F;
// Turn
result = UNPACK.u16(bytes, index);
index = result.index;
data.turn = result.data;
// Handles
result = UNPACK.string(bytes, index);
index = result.index;
@ -253,49 +235,21 @@ function MESSAGE(event) {
index = result.index;
data.dusk = result.data;
// Dawn pool
// History
result = UNPACK.u16(bytes, index);
index = result.index;
let dawn_pool_bits = result.data;
let history_length = result.data;
data.pool_dawn.push(dawn_pool_bits & 0x1F);
data.pool_dawn.push((dawn_pool_bits >> 5) & 0x3);
data.pool_dawn.push((dawn_pool_bits >> 7) & 0x3);
data.pool_dawn.push((dawn_pool_bits >> 9) & 0x3);
data.pool_dawn.push((dawn_pool_bits >> 11) & 0x3);
data.pool_dawn.push((dawn_pool_bits >> 13) & 0x1);
data.pool_dawn.push((dawn_pool_bits >> 14) & 0x1);
// Dusk pool
for(let i = 0; i < history_length; ++i) {
result = UNPACK.u16(bytes, index);
index = result.index;
let dusk_pool_bits = result.data;
data.pool_dusk.push(dusk_pool_bits & 0x1f);
data.pool_dusk.push((dusk_pool_bits >> 5) & 0x3);
data.pool_dusk.push((dusk_pool_bits >> 7) & 0x3);
data.pool_dusk.push((dusk_pool_bits >> 9) & 0x3);
data.pool_dusk.push((dusk_pool_bits >> 11) & 0x3);
data.pool_dusk.push((dusk_pool_bits >> 13) & 0x1);
data.pool_dusk.push((dusk_pool_bits >> 14) & 0x1);
// Pieces
for(let i = 0; i < GAME.Const.Count.Pieces; ++i) {
result = UNPACK.u16(bytes, index);
index = result.index;
let piece_data = result.data;
if((piece_data & 1) != 0) {
let piece = new GAME.Piece((piece_data >> 1) & 0x7, (piece_data >> 5) & 1);
piece.promoted = ((piece_data >> 4) & 1) != 0;
piece.tile = (piece_data >> 6) & 0x3F;
data.pieces.push(piece);
} else {
data.pieces.push(null);
data.history.push(new GAME.Play(
result.data & 0xF,
(result.data >> 4) & 0x3F,
(result.data >> 10) & 0x3F,
));
}
}
//}
} break;
case OpCode.GamePlay: {
@ -324,55 +278,12 @@ function MESSAGE(event) {
data.play.to = (result.data >> 10) & 0x3F;
} break;
case OpCode.GameHistory: {
console.log("RECV GameHistory");
data = {
status:0,
token:new Uint8Array(8),
dawn:"",
dusk:"",
history:[ ],
};
// Status
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
// Token
for(let i = 0; i < 8; ++i) { data.token[i] = bytes[index++]; }
// Handles
result = UNPACK.string(bytes, index);
index = result.index;
data.dawn = result.data;
result = UNPACK.string(bytes, index);
index = result.index;
data.dusk = result.data;
// Pieces
result = UNPACK.u16(bytes, index);
index = result.index;
let history_length = result.data;
for(let i = 0; i < history_length; ++i) {
result = UNPACK.u16(bytes, index);
index = result.index;
data.history.push(new GAME.Play(
result.data & 0xF,
(result.data >> 4) & 0x3F,
(result.data >> 10) & 0x3F,
));
}
} break;
default:
console.log("RECV Undefined " + code);
return;
}
console.log(data);
if(SCENE.message !== undefined) { SCENE.message(code, data) };
}

View File

@ -163,22 +163,31 @@ const UI = {
for(let r = 0; r < records.length; ++r) {
let buttons = [ ];
let join_callback = function() {
LOAD(SCENES.Game, {
token:this.token,
mode:INTERFACE.Mode.Player,
});
MESSAGE_SESSION_JOIN(this.token, true);
};
join_callback = join_callback.bind({token: records[r].token});
let spectate_callback = function() {
LOAD(SCENES.Game, {
token:this.token,
mode:INTERFACE.Mode.Review,
});
MESSAGE_SESSION_JOIN(this.token, false);
};
spectate_callback = spectate_callback.bind({token: records[r].token});
if(records[r].player) {
buttons.push(UI.button("Resume", join_callback));
buttons.push(UI.button("Review", spectate_callback));
} else {
if(sessionStorage.getItem("auth") !== null && (records[r].dawn == "" || records[r].dusk == "")) {
buttons.push(UI.button("Join", join_callback));
}
buttons.push(UI.button("Spectate", spectate_callback));
buttons.push(UI.button("View", spectate_callback));
}
let dawn = UI.text(records[r].dawn);
@ -210,6 +219,10 @@ const UI = {
for(let r = 0; r < records.length; ++r) {
let buttons = [ ];
let join_callback = function() {
LOAD(SCENES.Game, {
token:this.token,
mode:INTERFACE.Mode.Player,
});
MESSAGE_SESSION_JOIN(this.token, true);
};
join_callback = join_callback.bind({token: records[r].token});
@ -243,10 +256,11 @@ const UI = {
for(let r = 0; r < records.length; ++r) {
let buttons = [ ];
let view_callback = function() {
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameHistory),
this.token,
]);
LOAD(SCENES.Game, {
token:this.token,
mode:INTERFACE.Mode.Review,
});
MESSAGE_SESSION_JOIN(this.token, false);
};
view_callback = view_callback.bind({token: records[r].token});