function RECONNECT() { if(SOCKET === null) { console.log("Websocket connecting.."); SOCKET = new WebSocket("wss://omen.kirisame.com:38612"); SOCKET.binaryType = "arraybuffer"; SOCKET.addEventListener("error", (event) => { SOCKET = null; LOAD_STACK(SCENES.Offline); }); SOCKET.addEventListener("open", (event) => { if(SOCKET.readyState === WebSocket.OPEN) { console.log("Websocket connected."); SOCKET.addEventListener("message", MESSAGE); SOCKET.addEventListener("close", (event) => { console.log("Websocket closed."); SOCKET = null; LOAD_STACK(SCENES.Offline); RECONNECT(); }); RESUME(); } }); } else { RESUME(); } } function RESUME() { if(CONTEXT.Auth !== null) { MESSAGE_COMPOSE([ CONTEXT.Auth.token, CONTEXT.Auth.secret, ]); } else { LOAD(CONTEXT.Scene); } } 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; 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(), 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; } } break; case OpCode.Resume: { console.log("RECV Resume"); result = UNPACK.u16(bytes, index); index = result.index; if(result.data != Status.Ok) { CONTEXT.Auth = null; } 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: [], }; 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; } } 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, 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; // 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; // Dawn pool result = UNPACK.u16(bytes, index); index = result.index; let dawn_pool_bits = 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); } } //} } break; case OpCode.GamePlay: { console.log("RECV GamePlay"); data = { status:0, move: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.move.source = result.data & 1; data.move.from = (result.data >> 1) & 0x3F; data.move.to = (result.data >> 7) & 0x3F; } 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_START() { MESSAGE_COMPOSE([ PACK.u16(OpCode.SessionCreate), ]); } function MESSAGE_SESSION_JOIN(token, player) { MESSAGE_COMPOSE([ PACK.u16(OpCode.SessionJoin), token, PACK.u8(player), ]); }