function RECONNECT() { if(SOCKET === null) { console.log("Websocket connecting.."); SOCKET = new WebSocket("wss://" + location.hostname + ":38612"); SOCKET.binaryType = "arraybuffer"; SOCKET.addEventListener("error", () => { SOCKET = null; if(SCENE.disconnect !== undefined) { SCENE.disconnect(); } }); SOCKET.addEventListener("open", (event) => { if(SOCKET.readyState === WebSocket.OPEN) { console.log("Websocket connected."); SOCKET.addEventListener("message", MESSAGE); SOCKET.addEventListener("close", () => { console.log("Websocket closed."); SOCKET = null; if(SCENE.disconnect !== undefined) { SCENE.disconnect(); } RECONNECT(); }); RESUME(); } }); } else { RESUME(); } } function RESUME() { 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([ PACK.u16(OpCode.Resume), auth, secret, ]); } else { if(SCENE.reconnect !== undefined) { SCENE.reconnect(); } } } function MESSAGE(event) { 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]; } 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; 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; } } break; 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.Status: { console.log("RECV Status"); if(bytes.length - index == 4) { // Challenge count result = UNPACK.u16(bytes, index); index = result.index; STATUS.challenge = result.data; // Resume count result = UNPACK.u16(bytes, index); index = result.index; STATUS.resume = result.data; BADGE_UPDATE(STATUS.challenge > 0 || STATUS.resume > 0); UI.update_status(); } } break; case OpCode.SessionList: { console.log("RECV Session list"); if(bytes.length - index >= 2) { data = { records: [], }; 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, is_turn: false, is_complete: 0, }; // Token if(index <= bytes.length + 8) { for(let i = 0; i < 8; ++i) { record.token[i] = bytes[index]; index += 1; } } // Flags result = UNPACK.u32(bytes, index); index = result.index; let flags = result.data; record.player = flags & 3; record.is_turn = ((flags >> 2) & 1) != 0; record.is_complete = ((flags >> 3) & 3); // Dawn handle result = UNPACK.string(bytes, index); index = result.index; record.dawn = result.data; // Dusk handle result = UNPACK.string(bytes, index); index = result.index; record.dusk = result.data; // Turn number result = UNPACK.u16(bytes, index); index = result.index; record.turn = result.data; // Last move 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); } // Reviewer count result = UNPACK.u32(bytes, index); index = result.index; record.viewers = result.data; data.records.push(record); } } } break; case OpCode.SessionView: { console.log("RECV SessionCreate/Join"); if(bytes.length - index == 10) { data = { status:0, token:new Uint8Array(8), }; result = UNPACK.u16(bytes, index); index = result.index; data.status = result.data; for(let i = 0; i < 8; ++i) { data.token[i] = bytes[index++]; } } } break; case OpCode.GameState: { console.log("RECV GameState"); data = { status:0, token:new Uint8Array(8), player:2, 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++]; } // Flags result = UNPACK.u16(bytes, index); index = result.index; let flags = result.data; data.player = flags & 0x3; // 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; // History 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; case OpCode.GamePlay: { console.log("RECV GamePlay"); 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.ChallengeAnswer: { console.log("RECV ChallengeAnswer"); data = { status:0, token:new Uint8Array(8), }; // Status result = UNPACK.u16(bytes, index); index = result.index; data.status = result.data; for(let i = 0; i < 8; ++i) { data.token[i] = bytes[index++]; } } break; case OpCode.ChallengeList: { console.log("RECV ChallengeList"); data = { status:0, challenges:[ ], }; // Status result = UNPACK.u16(bytes, index); index = result.index; data.status = result.data; // Records result = UNPACK.u16(bytes, index); index = result.index; let length = result.data; for(let i = 0; i < length; ++i) { let record = { handle:"", }; // Handle result = UNPACK.string(bytes, index, UNPACK.u8); index = result.index; record.handle = result.data; data.challenges.push(record); } } break; case OpCode.UserList: { console.log("RECV UserList"); data = { status:0, users:[ ], }; // Status result = UNPACK.u16(bytes, index); index = result.index; data.status = result.data; // Records result = UNPACK.u16(bytes, index); index = result.index; let length = result.data; for(let i = 0; i < length; ++i) { let record = { handle:"", is_online:false, }; // Flags result = UNPACK.u8(bytes, index); index = result.index; let flags = result.data; record.is_online = (flags & 1) == 1; // Handle result = UNPACK.string(bytes, index, UNPACK.u8); index = result.index; record.handle = result.data; data.users.push(record); } } break; default: console.log("RECV Undefined " + code); return; } if(SCENE.message !== undefined) { SCENE.message(code, data) }; } function MESSAGE_COMPOSE(data) { if(SOCKET !== null) { let length = 0; for(let i = 0; i < data.length; ++i) { length += data[i].length; } let raw = new Uint8Array(length); length = 0; for(let i = 0; i < data.length; ++i) { raw.set(data[i], length); length += data[i].length; } SOCKET.send(raw); } } function MESSAGE_SESSION_LIST(page, game_state, is_player, is_live) { let flags = 0; flags |= game_state; flags |= (+is_player) << 2; flags |= (+is_live) << 3; MESSAGE_COMPOSE([ PACK.u16(OpCode.SessionList), PACK.u16(flags), PACK.u16(page), ]); } function MESSAGE_SESSION_VIEW(token, player) { MESSAGE_COMPOSE([ PACK.u16(OpCode.SessionView), token, PACK.u8(player), ]); } function MESSAGE_CHALLENGE(handle) { MESSAGE_COMPOSE([ PACK.u16(OpCode.Challenge), PACK.string(handle, PACK.u8), ]); } function MESSAGE_CHALLENGE_ANSWER(handle, answer) { MESSAGE_COMPOSE([ PACK.u16(OpCode.ChallengeAnswer), PACK.u8(+answer), PACK.string(handle, PACK.u8), ]); }