Add URL and authentication persistence.

This commit is contained in:
yukirij 2024-08-24 15:19:06 -07:00
parent 1df7dab092
commit a90ace5af9
13 changed files with 459 additions and 365 deletions

View File

@ -28,6 +28,8 @@ pub struct App {
pub auths:Trie<Authentication>,
pub sessions:Trie<Session>,
pub contests:Vec<u32>,
pub session_time:Chain<SessionToken>,
}
impl App {
@ -35,6 +37,8 @@ impl App {
{
if let Ok(mut filesystem) = FileSystem::init() {
let mut contests = Vec::new();
// Load salts
println!("Loading salts..");
let mut salts = Sparse::new();
@ -60,6 +64,15 @@ impl App {
let user_count = filesystem.user_count()?;
for id in 0..user_count {
let user = filesystem.user_fetch(id as u32).unwrap();
// Add user to contests if flag is set.
if (user.flags & user::F_CONTEST) != 0 {
match contests.binary_search(&user.id) {
Ok(_) => { }
Err(pos) => { contests.insert(pos, user.id); }
}
}
let user_local_id = users.add(user);
user_id.set(user_local_id as isize, id);
}
@ -106,6 +119,8 @@ impl App {
auths:Trie::new(),
sessions,
contests,
session_time,
})
} else {

View File

@ -1,6 +1,8 @@
pub const F_CONTEST :u32 = 0x0000_0001;
pub struct User {
pub id:u32,
pub handle_id:u32,
pub flags:u32,
pub handle:String,
pub secret:Vec<u8>,
pub na_key:u32,

View File

@ -53,21 +53,15 @@ async fn service_http(mut request:hyper::Request<hyper::body::Incoming>, args:Ht
.unwrap())
}
} else {
match request.uri().path() {
"/" => Ok(Response::builder()
match args.cache.fetch(request.uri().path()) {
Some(data) => Ok(Response::builder()
.header(CONTENT_TYPE, &data.mime)
.header(CACHE_CONTROL, "no-cache")
.body(Full::new(Bytes::from(data.data))).unwrap()),
None => Ok(Response::builder()
.header(CONTENT_TYPE, "text/html")
.header(CACHE_CONTROL, "no-cache")
.body(Full::new(Bytes::from(args.cache.fetch("/.html").unwrap().data))).unwrap()),
_ => match args.cache.fetch(request.uri().path()) {
Some(data) => Ok(Response::builder()
.header(CONTENT_TYPE, &data.mime)
.header(CACHE_CONTROL, "no-cache")
.body(Full::new(Bytes::from(data.data))).unwrap()),
None => Ok(Response::builder()
.status(404)
.body(Full::new(Bytes::new())).unwrap())
}
}
}
}

View File

@ -105,12 +105,12 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
let user_id = app.filesystem.user_count().unwrap() as u32;
// Register user pool id and handle
let handle_id = app.filesystem.handle_store(&handle, user_id).unwrap();
app.filesystem.handle_store(&handle, user_id).ok();
app.user_handle.set(handle.as_bytes(), user_id);
let user_data = User {
id:user_id,
handle_id,
flags:0,
handle:display_name,
secret,
na_key:salt_id,
@ -225,6 +225,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
}
QRPacketData::QAuthResume(request) => {
println!("Request: Auth Resume");
let mut response = PacketAuthResumeResponse::new();
response.status = STATUS_ERROR;
@ -250,6 +252,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
}
QRPacketData::QAuthRevoke => {
println!("Request: Auth Revoke");
if let Some(conn) = app.connections.get_mut(qr.id as usize) {
match conn.auth {
Some(auth) => {
@ -692,6 +696,8 @@ fn generate_game_history(app:&App, session:&Session) -> protocol::PacketGameHist
let mut response = PacketGameHistoryResponse::new();
response.token = session.token;
// Get Dawn handle
if let Some(id) = session.p_dawn.user {
if let Some(user) = app.get_user_by_id(id) {

View File

@ -64,7 +64,7 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
QRPacket::new(conn_id, QRPacketData::QRegister(packet))
).ok();
}
Err(_) => { }
Err(_) => { println!("error: packet decode failed."); }
}
CODE_AUTH => match PacketAuth::decode(&data, &mut index) {
@ -74,7 +74,7 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
QRPacket::new(conn_id, QRPacketData::QAuth(packet))
).ok();
}
Err(_) => { }
Err(_) => { println!("error: packet decode failed."); }
}
CODE_AUTH_RESUME => match PacketAuthResume::decode(&data, &mut index) {
@ -84,7 +84,7 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
QRPacket::new(conn_id, QRPacketData::QAuthResume(packet))
).ok();
}
Err(_) => { }
Err(_) => { println!("error: packet decode failed."); }
}
CODE_AUTH_REVOKE => {

View File

@ -41,6 +41,7 @@ impl Packet for PacketGameHistory {
#[derive(Clone)]
pub struct PacketGameHistoryResponse {
pub status:u16,
pub token:SessionToken,
pub dawn_handle:String,
pub dusk_handle:String,
pub history:Vec<Play>,
@ -50,6 +51,7 @@ impl PacketGameHistoryResponse {
{
Self {
status:0,
token:SessionToken::default(),
dawn_handle:String::new(),
dusk_handle:String::new(),
history:Vec::new(),
@ -72,6 +74,7 @@ impl Packet for PacketGameHistoryResponse {
[
pack_u16(self.status),
self.token.to_vec(),
pack_u16(self.dawn_handle.len() as u16),
self.dawn_handle.as_bytes().to_vec(),
pack_u16(self.dusk_handle.len() as u16),

View File

@ -308,7 +308,7 @@ impl FileSystem {
let handle = user.handle.as_bytes().to_vec();
// Write user information
file.write(&pack_u32(user.handle_id)).map_err(|_| ())?;
file.write(&pack_u32(user.flags)).map_err(|_| ())?;
file.write(&pack_u32(user.na_key)).map_err(|_| ())?;
file.write(&pack_u16(user.secret.len() as u16)).map_err(|_| ())?;
file.write(&user.secret).map_err(|_| ())?;
@ -340,7 +340,7 @@ impl FileSystem {
let mut buffer_u32 = [0u8; 4];
file.read_exact(&mut buffer_u32).map_err(|_| ())?;
let handle_id = unpack_u32(&buffer_u32, &mut 0);
let flags = unpack_u32(&buffer_u32, &mut 0);
file.read_exact(&mut buffer_u32).map_err(|_| ())?;
let na_key = unpack_u32(&buffer_u32, &mut 0);
@ -361,7 +361,7 @@ impl FileSystem {
Ok(User {
id,
handle_id,
flags,
handle,
secret,
na_key,

View File

@ -7,8 +7,8 @@
<meta name="application-name" content="Omen">
<meta name="description" content="Strategy board game played on a hexagon grid.">
<link rel="icon" href="/favicon.png">
<link rel="stylesheet" href=".css">
<script src=".js"></script>
<link rel="stylesheet" href="/.css">
<script src="/.js"></script>
</head>
<body>
<noscript>This application requires JavaScript to function.</noscript>

View File

@ -7,15 +7,15 @@ GAME_ASSET.load_image = (image) => {
};
GAME_ASSET.Image = {
Promote: GAME_ASSET.load_image("asset/promote.svg"),
Promote: GAME_ASSET.load_image("/asset/promote.svg"),
Piece: [
[ GAME_ASSET.load_image("asset/militia_dawn.svg"), GAME_ASSET.load_image("asset/militia_dusk.svg") ],
[ GAME_ASSET.load_image("asset/lance_dawn.svg"), GAME_ASSET.load_image("asset/lance_dusk.svg") ],
[ GAME_ASSET.load_image("asset/knight_dawn.svg"), GAME_ASSET.load_image("asset/knight_dusk.svg") ],
[ GAME_ASSET.load_image("asset/tower_dawn.svg"), GAME_ASSET.load_image("asset/tower_dusk.svg") ],
[ GAME_ASSET.load_image("asset/castle_dawn.svg"), GAME_ASSET.load_image("asset/castle_dusk.svg") ],
[ GAME_ASSET.load_image("asset/dragon_dawn.svg"), GAME_ASSET.load_image("asset/dragon_dusk.svg") ],
[ GAME_ASSET.load_image("asset/behemoth_dawn.svg"), GAME_ASSET.load_image("asset/behemoth_dusk.svg") ],
[ GAME_ASSET.load_image("asset/omen_dawn.svg"), GAME_ASSET.load_image("asset/omen_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/militia_dawn.svg"), GAME_ASSET.load_image("/asset/militia_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/lance_dawn.svg"), GAME_ASSET.load_image("/asset/lance_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/knight_dawn.svg"), GAME_ASSET.load_image("/asset/knight_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/tower_dawn.svg"), GAME_ASSET.load_image("/asset/tower_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/castle_dawn.svg"), GAME_ASSET.load_image("/asset/castle_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/dragon_dawn.svg"), GAME_ASSET.load_image("/asset/dragon_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/behemoth_dawn.svg"), GAME_ASSET.load_image("/asset/behemoth_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/omen_dawn.svg"), GAME_ASSET.load_image("/asset/omen_dusk.svg") ],
],
};

View File

@ -1,8 +1,7 @@
const SCENES = {
Init:{
load() {
LOAD_STACK(SCENES.Offline);
CONTEXT.Scene = SCENES.Browse;
LOAD(SCENES.Offline);
RECONNECT();
return true;
},
@ -11,20 +10,20 @@ const SCENES = {
Offline:{
load() {
UI.nav([
UI.button("Reconnect", () => { RECONNECT(); })
UI.button("Reconnect", () => { RECONNECT(); }),
], []);
return true;
},
reconnect() {
LOAD(CONTEXT.Scene);
LOAD_URL();
}
},
Register:{
load() {
if(CONTEXT.Auth !== null) return false;
if(sessionStorage.getItem("auth") !== null) return false;
UI.mainmenu("register");
UI.mainnav([], []);
UI.mainnav([], [], { auth:true });
let container = document.createElement("section");
let form = document.createElement("div");
@ -81,8 +80,14 @@ const SCENES = {
let submit = document.getElementById("submit");
switch(data.status) {
case Status.Ok: {
CONTEXT.Auth = data;
LOAD(SCENES.Browse);
let b64_token = PACK.base64(data.token);
let b64_secret = PACK.base64(data.secret);
console.log(b64_token);
console.log(b64_secret);
sessionStorage.setItem("auth", b64_token);
sessionStorage.setItem("auth_secret", b64_secret);
LOAD_URL();
} break;
default: {
submit.removeAttribute("disabled");
@ -105,15 +110,15 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
Authenticate:{
load() {
if(CONTEXT.Auth !== null) return false;
if(sessionStorage.getItem("auth") !== null) return false;
UI.mainmenu("authenticate");
UI.mainnav([], []);
UI.mainnav([], [], { auth:true });
let container = document.createElement("section");
let form = document.createElement("div");
@ -163,8 +168,9 @@ const SCENES = {
let submit = document.getElementById("submit");
switch(data.status) {
case Status.Ok: {
CONTEXT.Auth = data;
LOAD(SCENES.Browse);
sessionStorage.setItem("auth", PACK.base64(data.token));
sessionStorage.setItem("auth_secret", PACK.base64(data.secret));
LOAD_URL();
} break;
case Status.Error: {
submit.removeAttribute("disabled");
@ -177,7 +183,7 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
@ -189,14 +195,15 @@ const SCENES = {
};
UI.mainmenu("browse");
UI.mainnav_std(
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
]
],
{ auth:true, session:true }
);
let table = document.createElement("table");
@ -205,6 +212,8 @@ const SCENES = {
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Omen", "/");
return true;
},
refresh() {
@ -229,13 +238,13 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
Continue:{
load() {
if(CONTEXT.Auth === null) return false;
if(sessionStorage.getItem("auth") === null) return false;
CONTEXT.Data = {
page:0,
@ -243,14 +252,15 @@ const SCENES = {
};
UI.mainmenu("continue");
UI.mainnav_std(
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
]
],
{ auth:true, session:true }
);
let table = document.createElement("table");
@ -259,6 +269,8 @@ const SCENES = {
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Omen - Continue", "/continue/");
return true;
},
refresh() {
@ -283,13 +295,13 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
Join:{
load() {
if(CONTEXT.Auth === null) return false;
if(sessionStorage.getItem("auth") === null) return false;
CONTEXT.Data = {
page:0,
@ -297,14 +309,15 @@ const SCENES = {
};
UI.mainmenu("join");
UI.mainnav_std(
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
]
],
{ auth:true, session:true }
);
let table = document.createElement("table");
@ -313,6 +326,8 @@ const SCENES = {
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Omen - Join", "/join/");
return true;
},
refresh() {
@ -337,7 +352,7 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
@ -349,14 +364,15 @@ const SCENES = {
};
UI.mainmenu("live");
UI.mainnav_std(
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
]
],
{ auth:true, session:true }
);
let table = document.createElement("table");
@ -365,6 +381,8 @@ const SCENES = {
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Omen - Live", "/live/");
return true;
},
refresh() {
@ -389,7 +407,7 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
@ -401,14 +419,15 @@ const SCENES = {
};
UI.mainmenu("history");
UI.mainnav_std(
UI.mainnav(
[ ],
[
UI.div([UI.text("0 - 0 of 0")]),
UI.button("◀", null),
UI.button("▶", null),
UI.button("Refresh", null),
]
],
{ auth:true }
);
let table = document.createElement("table");
@ -417,6 +436,8 @@ const SCENES = {
MAIN.appendChild(table);
SCENE.refresh();
history.pushState(null, "Omen - History", "/history/");
return true;
},
refresh() {
@ -440,7 +461,7 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
@ -458,6 +479,8 @@ const SCENES = {
MAIN.appendChild(body);
this.refresh("game.html");
history.pushState(null, "Omen - Guide", "/guide/");
return true;
},
refresh(page) {
@ -483,6 +506,8 @@ const SCENES = {
MAIN.appendChild(body);
this.refresh("main.html");
history.pushState(null, "Omen - About", "/about/");
return true;
},
refresh(page) {
@ -521,6 +546,7 @@ const SCENES = {
INTERFACE.init(data, INTERFACE.Mode.Online);
history.pushState(null, "Omen - Game", "/game/" + PACK.base64(data.token).slice(0, -1));
return true;
},
unload() {
@ -541,7 +567,7 @@ const SCENES = {
}
},
disconnect() {
LOAD_STACK(SCENES.Offline);
LOAD(SCENES.Offline);
},
},
@ -563,6 +589,7 @@ const SCENES = {
INTERFACE.init(data, INTERFACE.Mode.Local);
history.pushState(null, "Omen - Practice", "/practice/");
return true;
},
unload() {
@ -573,7 +600,7 @@ const SCENES = {
GameHistory:{
load(data) {
let buttons_bottom = [ ];
buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.Browse) }));
buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.History) }));
UI.nav([
UI.button("Rotate", () => { INTERFACE.rotate(); }),
@ -607,6 +634,7 @@ const SCENES = {
INTERFACE.init(data, INTERFACE.Mode.Replay);
history.pushState(null, "Omen - History", "/history/" + PACK.base64(data.token).slice(0, -1));
return true;
},
unload() {
@ -619,8 +647,6 @@ function LOAD(scene, data=null) {
UNLOAD();
UI.rebuild();
SCENE = scene;
CONTEXT.Scene = SCENE;
CONTEXT.Data = null;
if(!SCENE.load(data)) { LOAD(SCENES.Browse); }
}
@ -628,10 +654,47 @@ function UNLOAD() {
if(SCENE !== null && SCENE.unload !== undefined) { SCENE.unload(); }
}
function LOAD_STACK(scene, data=null) {
UNLOAD();
UI.rebuild();
SCENE = scene;
CONTEXT.Data = null;
if(!SCENE.load(data)) { LOAD(SCENES.Browse); }
function LOAD_URL() {
let parts = window.location.pathname.split("/");
if(parts.length > 1) {
switch(parts[1]) {
case "continue": LOAD(SCENES.Continue); break;
case "live": LOAD(SCENES.Live); break;
case "practice": LOAD(SCENES.GamePractice); break;
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]) {
let token = UNPACK.base64(parts[2] + "=");
MESSAGE_SESSION_JOIN(token, true);
}
} break;
case "game": {
LOAD(SCENES.Browse);
if(parts[2]) {
let token = UNPACK.base64(parts[2] + "=");
MESSAGE_SESSION_JOIN(token, false);
}
} break;
default: LOAD(SCENES.Browse);
}
} else {
LOAD(SCENES.Browse);
}
}

View File

@ -29,10 +29,16 @@ function RECONNECT() {
}
function RESUME() {
if(CONTEXT.Auth !== null) {
//sessionStorage.clear();
let b64_auth = sessionStorage.getItem("auth");
if(b64_auth !== null) {
let auth = UNPACK.base64(b64_auth);
let secret = UNPACK.base64(sessionStorage.getItem("auth_secret"));
MESSAGE_COMPOSE([
CONTEXT.Auth.token,
CONTEXT.Auth.secret,
PACK.u16(OpCode.Resume),
auth,
secret,
]);
} else {
if(SCENE.reconnect !== undefined) { SCENE.reconnect(); }
@ -40,265 +46,178 @@ function RESUME() {
}
function MESSAGE(event) {
if(SCENE.message !== undefined) {
let bytes = new Uint8Array(event.data);
let code = 0;
let index = 2;
let data = null;
let result = null;
let bytes = new Uint8Array(event.data);
let code = 0;
let index = 2;
let data = null;
let result = null;
if(bytes.length >= 2) {
code = (bytes[0] << 8) + bytes[1];
}
if(bytes.length >= 2) {
code = (bytes[0] << 8) + bytes[1];
}
switch(code) {
case OpCode.Register: {
console.log("RECV Register");
switch(code) {
case OpCode.Register: {
console.log("RECV Register");
if(bytes.length - index == 26) {
data = {
status:(bytes[2] << 8) + bytes[3],
token:new Uint8Array(8),
secret:new Uint8Array(16),
};
index += 2;
if(bytes.length - index == 26) {
data = {
status:(bytes[2] << 8) + bytes[3],
token:new Uint8Array(8),
secret:new Uint8Array(16),
};
index += 2;
for(let i = 0; i < 8; ++i) {
data.token[i] = bytes[index++];
}
for(let i = 0; i < 16; ++i) {
data.secret[i] = bytes[index++];
}
} else {
console.error("Register packet bad length:" + bytes.length);
return;
for(let i = 0; i < 8; ++i) {
data.token[i] = bytes[index++];
}
} break;
case OpCode.Authenticate: {
console.log("RECV Authenticate");
if(bytes.length - index == 26) {
data = {
status:(bytes[2] << 8) + bytes[3],
token:new Uint8Array(),
secret:new Uint8Array(),
};
index += 2;
for(let i = 0; i < 8; ++i) {
data.token += bytes[index++];
}
for(let i = 0; i < 16; ++i) {
data.secret += bytes[index++];
}
} else {
console.error("Authenticate packet bad length:" + bytes.length);
return;
for(let i = 0; i < 16; ++i) {
data.secret[i] = bytes[index++];
}
} break;
} else {
console.error("Register packet bad length:" + bytes.length);
return;
}
} break;
case OpCode.Resume: {
console.log("RECV Resume");
case OpCode.Authenticate: {
console.log("RECV Authenticate");
if(bytes.length - index == 26) {
data = {
status:(bytes[2] << 8) + bytes[3],
token:new Uint8Array(8),
secret:new Uint8Array(16),
};
index += 2;
for(let i = 0; i < 8; ++i) {
data.token[i] = bytes[index++];
}
for(let i = 0; i < 16; ++i) {
data.secret[i] = bytes[index++];
}
} else {
console.error("Authenticate packet bad length:" + bytes.length);
return;
}
} break;
case OpCode.Resume: {
console.log("RECV Resume");
result = UNPACK.u16(bytes, index);
index = result.index;
if(result.data != Status.Ok) {
sessionStorage.clear();
}
LOAD_URL();
} break;
case OpCode.Deauthenticate: {
console.log("RECV Deauthenticate");
} break;
case OpCode.SessionList: {
console.log("RECV Session list");
if(bytes.length - index >= 2) {
data = {
records: [],
};
result = UNPACK.u16(bytes, index);
index = result.index;
if(result.data != Status.Ok) {
CONTEXT.Auth = null;
}
let count = result.data;
LOAD(CONTEXT.Scene);
} break;
case OpCode.Deauthenticate: {
console.log("RECV Deauthenticate");
} break;
case OpCode.SessionList: {
console.log("RECV Session list");
if(bytes.length - index >= 2) {
data = {
records: [],
for(let i = 0; i < count; ++i) {
let record = {
token: new Uint8Array(8),
dawn: "",
dusk: "",
turn: 0,
move: "",
viewers: 0,
player: false,
};
result = UNPACK.u16(bytes, index);
index = result.index;
let count = result.data;
for(let i = 0; i < count; ++i) {
let record = {
token: new Uint8Array(8),
dawn: "",
dusk: "",
turn: 0,
move: "",
viewers: 0,
player: false,
};
if(index <= bytes.length + 8) {
for(let i = 0; i < 8; ++i) {
record.token[i] = bytes[index];
index += 1;
}
if(index <= bytes.length + 8) {
for(let i = 0; i < 8; ++i) {
record.token[i] = bytes[index];
index += 1;
}
result = UNPACK.string(bytes, index);
index = result.index;
record.dawn = result.data;
result = UNPACK.string(bytes, index);
index = result.index;
record.dusk = result.data;
result = UNPACK.u16(bytes, index);
index = result.index;
record.turn = result.data;
if(index <= bytes.length + 3) {
let move = new Uint8Array(3);
for(let i = 0; i < 3; ++i) {
move[i] = bytes[index];
index += 1;
}
record.move = UNPACK.move(move);
}
result = UNPACK.u32(bytes, index);
index = result.index;
record.viewers = result.data;
record.player = bytes[index++] != 0;
data.records.push(record);
}
}
} break;
case OpCode.SessionCreate:
case OpCode.SessionJoin: {
console.log("RECV SessionCreate/Join");
if(bytes.length - index == 11) {
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;
case OpCode.GameState: {
console.log("RECV GameState");
//if(bytes.length - index >= 22) {
data = {
status:0,
player:2,
turn:0,
play:new GAME.Play(),
dawn:"",
dusk:"",
pool_dawn:[ ],
pool_dusk:[ ],
pieces:[ ],
};
// Status
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
// Flags
result = UNPACK.u16(bytes, index);
index = result.index;
let flags = result.data;
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;
data.dawn = result.data;
result = UNPACK.string(bytes, index);
index = result.index;
data.dusk = result.data;
record.dawn = result.data;
result = UNPACK.string(bytes, index);
index = result.index;
record.dusk = result.data;
// Dawn pool
result = UNPACK.u16(bytes, index);
index = result.index;
let dawn_pool_bits = result.data;
record.turn = 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
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);
if(index <= bytes.length + 3) {
let move = new Uint8Array(3);
for(let i = 0; i < 3; ++i) {
move[i] = bytes[index];
index += 1;
}
record.move = UNPACK.move(move);
}
//}
} break;
case OpCode.GamePlay: {
console.log("RECV GamePlay");
result = UNPACK.u32(bytes, index);
index = result.index;
record.viewers = result.data;
record.player = bytes[index++] != 0;
data.records.push(record);
}
}
} break;
case OpCode.SessionCreate:
case OpCode.SessionJoin: {
console.log("RECV SessionCreate/Join");
if(bytes.length - index == 11) {
data = {
status:0,
play:new GAME.Play(0, 0, 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;
case OpCode.GameState: {
console.log("RECV GameState");
//if(bytes.length - index >= 22) {
data = {
status:0,
player:2,
turn:0,
play:new GAME.Play(),
dawn:"",
dusk:"",
pool_dawn:[ ],
pool_dusk:[ ],
pieces:[ ],
};
// Status
@ -306,33 +225,24 @@ function MESSAGE(event) {
index = result.index;
data.status = result.data;
// Turn
// Flags
result = UNPACK.u16(bytes, index);
index = result.index;
data.turn = result.data;
let flags = result.data;
// Play description
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;
} break;
case OpCode.GameHistory: {
console.log("RECV GameHistory");
data = {
status:0,
dawn:"",
dusk:"",
history:[ ],
};
// Status
// Turn
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
data.turn = result.data;
// Handles
result = UNPACK.string(bytes, index);
@ -343,30 +253,127 @@ function MESSAGE(event) {
index = result.index;
data.dusk = result.data;
// Pieces
// Dawn pool
result = UNPACK.u16(bytes, index);
index = result.index;
let history_length = result.data;
let dawn_pool_bits = result.data;
for(let i = 0; i < history_length; ++i) {
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
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;
data.history.push(new GAME.Play(
result.data & 0xF,
(result.data >> 4) & 0x3F,
(result.data >> 10) & 0x3F,
));
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);
}
}
} break;
//}
} break;
default:
console.log("RECV Undefined " + code);
return;
}
case OpCode.GamePlay: {
console.log("RECV GamePlay");
if(SCENE.message !== undefined) { SCENE.message(code, data) };
data = {
status:0,
play:new GAME.Play(0, 0, 0),
};
// Status
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
// Turn
result = UNPACK.u16(bytes, index);
index = result.index;
data.turn = result.data;
// Play description
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;
} 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;
}
if(SCENE.message !== undefined) { SCENE.message(code, data) };
}
function MESSAGE_COMPOSE(data) {

View File

@ -90,31 +90,20 @@ const UI = {
return table;
},
mainnav(left_children, right_children) {
let header = document.createElement("nav");
let left = document.createElement("section");
for(child of left_children) { left.appendChild(child); }
let right = document.createElement("section");
for(child of right_children) { right.appendChild(child); }
header.appendChild(left);
header.appendChild(right);
MAIN.appendChild(header);
},
mainnav_std(left_children, right_children) {
mainnav(left_children, right_children, features={}) {
let header = document.createElement("nav");
let left = document.createElement("section");
if(CONTEXT.Auth === null) {
left.appendChild(UI.button("Register", () => { LOAD_STACK(SCENES.Register); }));
left.appendChild(UI.button("Log In", () => { LOAD_STACK(SCENES.Authenticate); }));
if(sessionStorage.getItem("auth") === null) {
if(features.auth === true) {
left.appendChild(UI.button("Register", () => { LOAD(SCENES.Register); }));
left.appendChild(UI.button("Log In", () => { LOAD(SCENES.Authenticate); }));
}
} else {
left.appendChild(UI.button("Contest", () => { MESSAGE_SESSION_START(); }));
left.appendChild(UI.button("Challenge", () => { }));
if(features.session === true) {
left.appendChild(UI.button("Contest", () => { MESSAGE_SESSION_START(); }));
left.appendChild(UI.button("Challenge", () => { }));
}
}
for(child of left_children) { left.appendChild(child); }
@ -144,7 +133,7 @@ const UI = {
let bottom = [ ];
top.push(UI.button("Browse", () => { LOAD(SCENES.Browse); }, page == "browse"));
if(CONTEXT.Auth !== null) {
if(sessionStorage.getItem("auth") !== null) {
top.push(UI.button("Continue", () => { LOAD(SCENES.Continue); }, page == "continue"));
top.push(UI.button("Join", () => { LOAD(SCENES.Join); }, page == "join"));
}
@ -154,13 +143,13 @@ const UI = {
top.push(UI.button("Guide", () => { LOAD(SCENES.Guide); }, page == "guide"));
top.push(UI.button("About", () => { LOAD(SCENES.About); }, page == "about"));
if(CONTEXT.Auth !== null) {
if(sessionStorage.getItem("auth") !== null) {
bottom.push(UI.button("Logout", () => {
MESSAGE_COMPOSE([
PACK.u16(OpCode.Deauthenticate),
]);
CONTEXT.Auth = null;
LOAD(SCENE);
sessionStorage.clear();
LOAD_URL();
}));
}
@ -186,7 +175,7 @@ const UI = {
if(records[r].player) {
buttons.push(UI.button("Resume", join_callback));
} else {
if(CONTEXT.Auth !== null && (records[r].dawn == "" || records[r].dusk == "")) {
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));

View File

@ -13,6 +13,13 @@ const PACK = {
value & 0xFF
]);
},
base64(bytes) {
let str = "";
for(let i = 0; i < bytes.length; ++i) {
str += String.fromCharCode(bytes[i]);
}
return window.btoa(str);
},
};
const UNPACK = {
@ -107,7 +114,15 @@ const UNPACK = {
if(take != "") { str += " " + take; }
}
return str;
}
},
base64(data) {
let str = window.atob(data);
let bytes = new Uint8Array(str.length);
for(let i = 0; i < bytes.length; ++i) {
bytes[i] = str.charCodeAt(i);
}
return bytes;
},
};
const BITWISE = {