623 lines
18 KiB
JavaScript
623 lines
18 KiB
JavaScript
function RECONNECT() {
|
|
if(SOCKET === null) {
|
|
console.log("Websocket connecting..");
|
|
SOCKET = new WebSocket("wss://" + location.hostname + ":38612");
|
|
SOCKET.binaryType = "arraybuffer";
|
|
SOCKET.addEventListener("error", () => {
|
|
console.log("Websocket 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();
|
|
});
|
|
|
|
MESSAGE_COMPOSE([
|
|
PACK.u16(OpCode.Hello),
|
|
]);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
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 {
|
|
LOAD_URL();
|
|
}
|
|
}
|
|
|
|
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.Hello: {
|
|
console.log("RECV Hello");
|
|
|
|
data = {
|
|
version:"",
|
|
};
|
|
|
|
result = UNPACK.string(bytes, index, UNPACK.u8);
|
|
index = result.index;
|
|
data.version = result.data;
|
|
} break;
|
|
|
|
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),
|
|
handle:"",
|
|
};
|
|
index += 2;
|
|
|
|
// Token
|
|
for(let i = 0; i < 8; ++i) {
|
|
data.token[i] = bytes[index++];
|
|
}
|
|
|
|
// Secret
|
|
for(let i = 0; i < 16; ++i) {
|
|
data.secret[i] = bytes[index++];
|
|
}
|
|
|
|
// User Handle
|
|
result = UNPACK.string(bytes, index, UNPACK.u8);
|
|
index = result.index;
|
|
data.handle = result.data;
|
|
} else {
|
|
console.error("Authenticate packet bad length:" + bytes.length);
|
|
return;
|
|
}
|
|
} break;
|
|
|
|
case OpCode.Resume: {
|
|
console.log("RECV Resume");
|
|
|
|
// Status
|
|
result = UNPACK.u16(bytes, index);
|
|
index = result.index;
|
|
if(result.data == Status.Ok) {
|
|
CONTEXT.Auth = {
|
|
handle: "",
|
|
};
|
|
|
|
// User Handle
|
|
result = UNPACK.string(bytes, index, UNPACK.u8);
|
|
index = result.index;
|
|
CONTEXT.Auth.handle = result.data;
|
|
} else {
|
|
sessionStorage.clear();
|
|
}
|
|
|
|
LOAD_URL();
|
|
} break;
|
|
|
|
case OpCode.Deauthenticate: {
|
|
console.log("RECV Deauthenticate");
|
|
} break;
|
|
|
|
case OpCode.Summary: {
|
|
console.log("RECV Summary");
|
|
|
|
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 SessionList");
|
|
|
|
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,
|
|
moves: [ ],
|
|
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 moves
|
|
result = UNPACK.u32(bytes, index);
|
|
index = result.index;
|
|
let moves = result.data;
|
|
for(let m = 0; m < 4; ++m) {
|
|
if((moves & 0x80) != 0) {
|
|
record.moves.push(moves & 0xFF);
|
|
moves >>= 8;
|
|
}
|
|
}
|
|
|
|
// 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 SessionView");
|
|
|
|
if(bytes.length - index == 11) {
|
|
data = {
|
|
status:0,
|
|
player:0,
|
|
is_complete:false,
|
|
token:new Uint8Array(8),
|
|
};
|
|
|
|
// Status
|
|
result = UNPACK.u16(bytes, index);
|
|
index = result.index;
|
|
data.status = result.data;
|
|
|
|
// Flags
|
|
result = UNPACK.u8(bytes, index);
|
|
index = result.index;
|
|
let flags = result.data;
|
|
data.is_complete = (flags & 1) == 1;
|
|
data.player = (flags >> 1) & 3;
|
|
|
|
// Token
|
|
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,
|
|
|
|
undo:0,
|
|
|
|
dawn:"",
|
|
dusk:"",
|
|
dawn_online:false,
|
|
dusk_online:false,
|
|
spectators:0,
|
|
|
|
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 & 3;
|
|
data.dawn_online = (flags >> 2) & 1;
|
|
data.dusk_online = (flags >> 3) & 1;
|
|
|
|
data.undo = (flags >> 8) & 3;
|
|
|
|
result = UNPACK.u32(bytes, index);
|
|
index = result.index;
|
|
data.spectators = 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;
|
|
|
|
// 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.GameMessage: {
|
|
console.log("RECV GameMessage");
|
|
|
|
result = UNPACK.u32(bytes, index);
|
|
index = result.index;
|
|
let high = result.data;
|
|
|
|
result = UNPACK.u32(bytes, index);
|
|
index = result.index;
|
|
let low = result.data;
|
|
|
|
let opcode = low & 0xFF;
|
|
let dat = ((low >> 8) & 0xFFFFFF) | ((high & 0xFF) << 24);
|
|
let ext = (high >> 8) & 0xFFFFFF;
|
|
|
|
data = {
|
|
code:opcode,
|
|
};
|
|
|
|
switch(opcode) {
|
|
case GameMessage.Error: { } break;
|
|
|
|
case GameMessage.Move: {
|
|
data.turn = dat & 0xFFFF;
|
|
data.from = (dat >> 16) & 0x3F;
|
|
data.to = (dat >> 22) & 0x3F;
|
|
} break;
|
|
|
|
case GameMessage.Drop: {
|
|
data.turn = dat & 0xFFFF;
|
|
data.piece = (dat >> 16) & 0x3F;
|
|
data.to = (dat >> 22) & 0x3F;
|
|
} break;
|
|
|
|
case GameMessage.Alt: {
|
|
data.turn = dat & 0xFFFF;
|
|
data.from = (dat >> 16) & 0x3F;
|
|
data.to = (dat >> 22) & 0x3F;
|
|
} break;
|
|
|
|
case GameMessage.Online: {
|
|
data.dawn = (dat & 0x1) != 0;
|
|
data.dusk = (dat & 0x2) != 0;
|
|
data.spectators = (dat >> 2) & 0xFF_FFFF;
|
|
} break;
|
|
|
|
case GameMessage.Undo: {
|
|
data.state = dat & 0x1;
|
|
data.turn = (dat >> 1) & 0xFFFF;
|
|
} break;
|
|
|
|
case GameMessage.Resign: { } break;
|
|
|
|
case GameMessage.Reaction: {
|
|
data.index = dat & 0xFFFF;
|
|
} break;
|
|
}
|
|
} 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;
|
|
|
|
case OpCode.InviteList: {
|
|
console.log("RECV InviteList");
|
|
|
|
data = {
|
|
status:0,
|
|
records:[ ],
|
|
};
|
|
|
|
// 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 token = "";
|
|
|
|
for(let c = 0; c < 4; ++c) {
|
|
token += String.fromCharCode(bytes[index++]);
|
|
} token += "-";
|
|
|
|
for(let c = 0; c < 4; ++c) {
|
|
token += String.fromCharCode(bytes[index++]);
|
|
} token += "-";
|
|
|
|
for(let c = 0; c < 4; ++c) {
|
|
token += String.fromCharCode(bytes[index++]);
|
|
}
|
|
|
|
data.records.push(token);
|
|
}
|
|
} break;
|
|
|
|
case OpCode.InviteAcquire: {
|
|
console.log("RECV InviteAcquire");
|
|
|
|
data = {
|
|
status:0,
|
|
token:"",
|
|
};
|
|
|
|
// Status
|
|
result = UNPACK.u16(bytes, index);
|
|
index = result.index;
|
|
data.status = result.data;
|
|
|
|
// Token
|
|
for(let c = 0; c < 12; ++c) {
|
|
data.token += String.fromCharCode(bytes[index++]);
|
|
}
|
|
} break;
|
|
|
|
case OpCode.TestResult: {
|
|
result = UNPACK.u8(bytes, index);
|
|
index = result.index;
|
|
let test_result = result.data;
|
|
|
|
result = UNPACK.string(bytes, index, UNPACK.u16);
|
|
index = result.index;
|
|
let text = result.data;
|
|
|
|
if(test_result == 0) {
|
|
console.log("BD " + text);
|
|
}
|
|
} 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),
|
|
]);
|
|
}
|