const toast = require('izitoast'); const cbor = require('borc'); const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://cryps.gg/ws' : 'ws://localhost:40000'; function errorToast(err) { console.error(err); return toast.error({ title: 'BEEP BOOP', message: err, position: 'topRight', }); } function createSocket(events) { let ws; let gameStateTimeout; function connect() { ws = new WebSocket(SOCKET_URL); ws.binaryType = 'arraybuffer'; // Connection opened ws.addEventListener('open', (event) => { toast.info({ message: 'connected', position: 'topRight', }); events.loginPrompt(); send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } }); }); // Listen for messages ws.addEventListener('message', onMessage); ws.addEventListener('error', (event) => { console.error('WebSocket error', event); account = null; // return setTimeout(connect, 5000); }); ws.addEventListener('close', (event) => { console.error('WebSocket closed', event); account = null; return setTimeout(connect, 5000); }); return ws; } // handle account auth within the socket itself // https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html let account = null; // ------------- // Incoming // ------------- function accountLogin(res) { const [struct, login] = res; account = login; events.setAccount(login); send({ method: 'account_cryps', params: {} }); send({ method: 'item_list', params: {} }); console.log(account); } function accountCryps(response) { const [structName, cryps] = response; events.setCryps(cryps); } function gameState(response) { const [structName, game] = response; events.setGame(game); } function gameJoinableList(response) { const [structName, gameList] = response; events.setGameList(gameList); } function crypSpawn(response) { const [structName, cryp] = response; console.log('got a new cryp', cryp); } function gamePve(response) { const [structName, game] = response; console.log('got a new game', game); } function itemList(response) { const [structName, items] = response; events.setItems(items); } // ------------- // Outgoing // ------------- function send(msg) { console.log('outgoing msg', msg); msg.token = account && account.token; ws.send(cbor.encode(msg)); } function sendAccountLogin(name, password) { send({ method: 'account_login', params: { name, password } }); } function sendAccountCreate(name, password) { send({ method: 'account_create', params: { name, password } }); } function sendCrypSpawn(name) { send({ method: 'cryp_spawn', params: { name } }); } function sendCrypLearn(id, skill) { send({ method: 'cryp_learn', params: { id, skill } }); } function sendCrypForget(id, skill) { send({ method: 'cryp_forget', params: { id, skill } }); } function sendGameState(id) { send({ method: 'game_state', params: { id } }); } function sendGamePve(crypIds) { send({ method: 'game_pve', params: { cryp_ids: crypIds } }); } function sendGamePvp(crypIds) { send({ method: 'game_pvp', params: { cryp_ids: crypIds } }); } function sendGameJoin(gameId, crypIds) { send({ method: 'game_join', params: { game_id: gameId, cryp_ids: crypIds } }); } function sendGameJoinableList() { send({ method: 'game_joinable_list', params: { } }); } function sendGameSkill(gameId, crypId, targetTeamId, skill) { send({ method: 'game_skill', params: { game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill, }, }); events.setActiveSkill(null); } function sendGameTarget(gameId, crypId, skillId) { send({ method: 'game_target', params: { game_id: gameId, cryp_id: crypId, skill_id: skillId } }); events.setActiveSkill(null); } function sendItemUse(item, target) { console.log(item, target); send({ method: 'item_use', params: { item, target } }); events.setActiveItem(null); } // ------------- // Events // ------------- function clearGameStateInterval() { clearInterval(gameStateTimeout); } // ------------- // Handling // ------------- // when the server sends a reply it will have one of these message types // this object wraps the reply types to a function const handlers = { cryp_spawn: crypSpawn, game_pve: gamePve, game_state: gameState, game_joinable_list: gameJoinableList, account_login: accountLogin, account_create: accountLogin, account_cryps: accountCryps, item_list: itemList, }; // decodes the cbor and // calls the handlers defined above based on message type function onMessage(event) { // decode binary msg from server const blob = new Uint8Array(event.data); const res = cbor.decode(blob); console.log(res); // check for error and split into response type and data if (res.err) return errorToast(res.err); const { method, params } = res; if (!handlers[method]) return errorToast(`${method} handler missing`); return handlers[method](params); } return { clearGameStateInterval, sendAccountLogin, sendAccountCreate, sendGameState, sendGamePve, sendGamePvp, sendGameJoin, sendGameJoinableList, sendGameSkill, sendGameTarget, sendCrypSpawn, sendCrypLearn, sendCrypForget, sendItemUse, connect, }; } module.exports = createSocket;