mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-07-18 00:55:40 +00:00
372 lines
8.9 KiB
JavaScript
372 lines
8.9 KiB
JavaScript
import { log } from "log";
|
|
|
|
const endpoints = {
|
|
LATENCY_CHECK: 3,
|
|
INIT: 4,
|
|
INIT_WEBRTC: 100,
|
|
OFFER: 101,
|
|
ANSWER: 102,
|
|
ICE_CANDIDATE: 103,
|
|
GAME_START: 104,
|
|
GAME_QUIT: 105,
|
|
GAME_SAVE: 106,
|
|
GAME_LOAD: 107,
|
|
GAME_SET_PLAYER_INDEX: 108,
|
|
GAME_RECORDING: 110,
|
|
GET_WORKER_LIST: 111,
|
|
GAME_ERROR_NO_FREE_SLOTS: 112,
|
|
GAME_RESET: 113,
|
|
|
|
APP_VIDEO_CHANGE: 150,
|
|
};
|
|
|
|
const endpointName = Object.fromEntries(
|
|
Object.entries(endpoints).map(([k, v]) => [v, k]),
|
|
);
|
|
|
|
let transport = {
|
|
send: (packet) => {
|
|
log.warn(
|
|
"Default transport is used! Change it with the api.transport variable.",
|
|
packet,
|
|
);
|
|
},
|
|
keyboard: (packet) => {
|
|
log.warn(
|
|
"Default transport is used! Change it with the api.transport variable.",
|
|
packet,
|
|
);
|
|
},
|
|
mouse: (packet) => {
|
|
log.warn(
|
|
"Default transport is used! Change it with the api.transport variable.",
|
|
packet,
|
|
);
|
|
},
|
|
};
|
|
|
|
const packet = (type, payload, id) => {
|
|
const packet = { t: type };
|
|
if (id !== undefined) packet.id = id;
|
|
if (payload !== undefined) packet.p = payload;
|
|
transport.send(packet);
|
|
};
|
|
|
|
const decodeBytes = (b) => String.fromCharCode.apply(null, new Uint8Array(b));
|
|
|
|
const keyboardPress = (() => {
|
|
// 0 1 2 3 4 5 6
|
|
// [CODE ] P MOD
|
|
const buffer = new ArrayBuffer(7);
|
|
const dv = new DataView(buffer);
|
|
|
|
return (pressed = false, e) => {
|
|
if (e.repeat) return; // skip pressed key events
|
|
|
|
const key = libretro.mod;
|
|
let code = libretro.map("", e.code);
|
|
let shift = e.shiftKey;
|
|
|
|
// a special Esc for &$&!& Firefox
|
|
if (shift && code === 96) {
|
|
code = 27;
|
|
shift = false;
|
|
}
|
|
|
|
const mod =
|
|
0 |
|
|
(e.altKey && key.ALT) |
|
|
(e.ctrlKey && key.CTRL) |
|
|
(e.metaKey && key.META) |
|
|
(shift && key.SHIFT) |
|
|
(e.getModifierState("NumLock") && key.NUMLOCK) |
|
|
(e.getModifierState("CapsLock") && key.CAPSLOCK) |
|
|
(e.getModifierState("ScrollLock") && key.SCROLLOCK);
|
|
dv.setUint32(0, code);
|
|
dv.setUint8(4, +pressed);
|
|
dv.setUint16(5, mod);
|
|
transport.keyboard(buffer);
|
|
};
|
|
})();
|
|
|
|
const mouse = {
|
|
MOVEMENT: 0,
|
|
BUTTONS: 1,
|
|
};
|
|
|
|
const mouseMove = (() => {
|
|
// 0 1 2 3 4
|
|
// T DX DY
|
|
const buffer = new ArrayBuffer(5);
|
|
const dv = new DataView(buffer);
|
|
|
|
return (dx = 0, dy = 0) => {
|
|
dv.setUint8(0, mouse.MOVEMENT);
|
|
dv.setInt16(1, dx);
|
|
dv.setInt16(3, dy);
|
|
transport.mouse(buffer);
|
|
};
|
|
})();
|
|
|
|
const mousePress = (() => {
|
|
// 0 1
|
|
// T B
|
|
const buffer = new ArrayBuffer(2);
|
|
const dv = new DataView(buffer);
|
|
|
|
// 0: Main button pressed, usually the left button or the un-initialized state
|
|
// 1: Auxiliary button pressed, usually the wheel button or the middle button (if present)
|
|
// 2: Secondary button pressed, usually the right button
|
|
// 3: Fourth button, typically the Browser Back button
|
|
// 4: Fifth button, typically the Browser Forward button
|
|
|
|
const b2r = [1, 4, 2, 0, 0]; // browser mouse button to retro button
|
|
// assumed that only one button pressed / released
|
|
|
|
return (button = 0, pressed = false) => {
|
|
dv.setUint8(0, mouse.BUTTONS);
|
|
dv.setUint8(1, pressed ? b2r[button] : 0);
|
|
transport.mouse(buffer);
|
|
};
|
|
})();
|
|
|
|
const libretro = (function () {
|
|
// RETRO_KEYBOARD
|
|
const retro = {
|
|
"": 0,
|
|
Unidentified: 0,
|
|
Unknown: 0, // ???
|
|
First: 0, // ???
|
|
Backspace: 8,
|
|
Tab: 9,
|
|
Clear: 12,
|
|
Enter: 13,
|
|
Return: 13,
|
|
Pause: 19,
|
|
Escape: 27,
|
|
Space: 32,
|
|
Exclaim: 33,
|
|
Quotedbl: 34,
|
|
Hash: 35,
|
|
Dollar: 36,
|
|
Ampersand: 38,
|
|
Quote: 39,
|
|
Leftparen: 40,
|
|
"(": 40,
|
|
Rightparen: 41,
|
|
")": 41,
|
|
Asterisk: 42,
|
|
Plus: 43,
|
|
Comma: 44,
|
|
Minus: 45,
|
|
Period: 46,
|
|
Slash: 47,
|
|
Digit0: 48,
|
|
Digit1: 49,
|
|
Digit2: 50,
|
|
Digit3: 51,
|
|
Digit4: 52,
|
|
Digit5: 53,
|
|
Digit6: 54,
|
|
Digit7: 55,
|
|
Digit8: 56,
|
|
Digit9: 57,
|
|
Colon: 58,
|
|
":": 58,
|
|
Semicolon: 59,
|
|
";": 59,
|
|
Less: 60,
|
|
"<": 60,
|
|
Equal: 61,
|
|
"=": 61,
|
|
Greater: 62,
|
|
">": 62,
|
|
Question: 63,
|
|
"?": 63,
|
|
// RETROK_AT = 64,
|
|
BracketLeft: 91,
|
|
"[": 91,
|
|
Backslash: 92,
|
|
"\\": 92,
|
|
BracketRight: 93,
|
|
"]": 93,
|
|
// RETROK_CARET = 94,
|
|
// RETROK_UNDERSCORE = 95,
|
|
Backquote: 96,
|
|
"`": 96,
|
|
KeyA: 97,
|
|
KeyB: 98,
|
|
KeyC: 99,
|
|
KeyD: 100,
|
|
KeyE: 101,
|
|
KeyF: 102,
|
|
KeyG: 103,
|
|
KeyH: 104,
|
|
KeyI: 105,
|
|
KeyJ: 106,
|
|
KeyK: 107,
|
|
KeyL: 108,
|
|
KeyM: 109,
|
|
KeyN: 110,
|
|
KeyO: 111,
|
|
KeyP: 112,
|
|
KeyQ: 113,
|
|
KeyR: 114,
|
|
KeyS: 115,
|
|
KeyT: 116,
|
|
KeyU: 117,
|
|
KeyV: 118,
|
|
KeyW: 119,
|
|
KeyX: 120,
|
|
KeyY: 121,
|
|
KeyZ: 122,
|
|
"{": 123,
|
|
"|": 124,
|
|
"}": 125,
|
|
Tilde: 126,
|
|
"~": 126,
|
|
Delete: 127,
|
|
|
|
Numpad0: 256,
|
|
Numpad1: 257,
|
|
Numpad2: 258,
|
|
Numpad3: 259,
|
|
Numpad4: 260,
|
|
Numpad5: 261,
|
|
Numpad6: 262,
|
|
Numpad7: 263,
|
|
Numpad8: 264,
|
|
Numpad9: 265,
|
|
NumpadDecimal: 266,
|
|
NumpadDivide: 267,
|
|
NumpadMultiply: 268,
|
|
NumpadSubtract: 269,
|
|
NumpadAdd: 270,
|
|
NumpadEnter: 271,
|
|
NumpadEqual: 272,
|
|
|
|
ArrowUp: 273,
|
|
ArrowDown: 274,
|
|
ArrowRight: 275,
|
|
ArrowLeft: 276,
|
|
Insert: 277,
|
|
Home: 278,
|
|
End: 279,
|
|
PageUp: 280,
|
|
PageDown: 281,
|
|
|
|
F1: 282,
|
|
F2: 283,
|
|
F3: 284,
|
|
F4: 285,
|
|
F5: 286,
|
|
F6: 287,
|
|
F7: 288,
|
|
F8: 289,
|
|
F9: 290,
|
|
F10: 291,
|
|
F11: 292,
|
|
F12: 293,
|
|
F13: 294,
|
|
F14: 295,
|
|
F15: 296,
|
|
|
|
NumLock: 300,
|
|
CapsLock: 301,
|
|
ScrollLock: 302,
|
|
ShiftRight: 303,
|
|
ShiftLeft: 304,
|
|
ControlRight: 305,
|
|
ControlLeft: 306,
|
|
AltRight: 307,
|
|
AltLeft: 308,
|
|
MetaRight: 309,
|
|
MetaLeft: 310,
|
|
// RETROK_LSUPER = 311,
|
|
// RETROK_RSUPER = 312,
|
|
// RETROK_MODE = 313,
|
|
// RETROK_COMPOSE = 314,
|
|
|
|
// RETROK_HELP = 315,
|
|
// RETROK_PRINT = 316,
|
|
// RETROK_SYSREQ = 317,
|
|
// RETROK_BREAK = 318,
|
|
// RETROK_MENU = 319,
|
|
Power: 320,
|
|
// RETROK_EURO = 321,
|
|
// RETROK_UNDO = 322,
|
|
// RETROK_OEM_102 = 323,
|
|
};
|
|
|
|
const retroMod = {
|
|
NONE: 0x0000,
|
|
SHIFT: 0x01,
|
|
CTRL: 0x02,
|
|
ALT: 0x04,
|
|
META: 0x08,
|
|
NUMLOCK: 0x10,
|
|
CAPSLOCK: 0x20,
|
|
SCROLLOCK: 0x40,
|
|
};
|
|
|
|
const _map = (key = "", code = "") => {
|
|
return retro[code] || retro[key] || 0;
|
|
};
|
|
|
|
return {
|
|
map: _map,
|
|
mod: retroMod,
|
|
};
|
|
})();
|
|
|
|
/**
|
|
* Server API.
|
|
*
|
|
* Requires the actual api.transport implementation.
|
|
*/
|
|
export const api = {
|
|
set transport(t) {
|
|
transport = t;
|
|
},
|
|
endpoint: endpoints,
|
|
endpointName: endpointName,
|
|
decode: (b) => JSON.parse(decodeBytes(b)),
|
|
decodeB64: (b64) => JSON.parse(atob(b64)),
|
|
server: {
|
|
initWebrtc: () => packet(endpoints.INIT_WEBRTC),
|
|
sendIceCandidate: (candidate) =>
|
|
packet(endpoints.ICE_CANDIDATE, btoa(JSON.stringify(candidate))),
|
|
sendSdp: (sdp) => packet(endpoints.ANSWER, btoa(JSON.stringify(sdp))),
|
|
latencyCheck: (id, list) => packet(endpoints.LATENCY_CHECK, list, id),
|
|
getWorkerList: () => packet(endpoints.GET_WORKER_LIST),
|
|
},
|
|
game: {
|
|
input: {
|
|
keyboard: {
|
|
press: keyboardPress,
|
|
},
|
|
mouse: {
|
|
move: mouseMove,
|
|
press: mousePress,
|
|
},
|
|
},
|
|
load: () => packet(endpoints.GAME_LOAD),
|
|
reset: (roomId) => packet(endpoints.GAME_RESET, { room_id: roomId }),
|
|
save: () => packet(endpoints.GAME_SAVE),
|
|
setPlayerIndex: (i) => packet(endpoints.GAME_SET_PLAYER_INDEX, i),
|
|
start: (game, roomId, record, recordUser, player) =>
|
|
packet(endpoints.GAME_START, {
|
|
game_name: game,
|
|
room_id: roomId,
|
|
player_index: player,
|
|
record: record,
|
|
record_user: recordUser,
|
|
}),
|
|
toggleRecording: (active = false, userName = "") =>
|
|
packet(endpoints.GAME_RECORDING, {
|
|
active: active,
|
|
user: userName,
|
|
}),
|
|
quit: (roomId) => packet(endpoints.GAME_QUIT, { room_id: roomId }),
|
|
},
|
|
};
|