const { toast } = require('bulma-toast'); const cbor = require('borc'); const actions = require('./actions'); function errorToast(err) { console.error(err); return toast({ message: err, type: 'is-warning', duration: 5000, }); } // Create WebSocket connection. // requires the redux store in order to push updates // to components function createSocket(store) { let ws; function connect() { ws = new WebSocket('ws://localhost:40000'); ws.binaryType = 'arraybuffer'; // Connection opened ws.addEventListener('open', function wsOpen(event) { send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } }); }); // Listen for messages ws.addEventListener('message', onMessage); ws.addEventListener('error', function wsError(event) { console.error('WebSocket error', event); account = null; // return setTimeout(connect, 5000); }); ws.addEventListener('close', function wsClose(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; store.dispatch(actions.setAccount(login)); // send({ method: 'cryp_spawn', params: { name: 'muji' } }); send({ method: 'account_cryps', params: {} }); send({ method: 'item_list', params: {}}); console.log(account); } function accountCryps(response) { const [structName, cryps] = response; store.dispatch(actions.setCryps(cryps)); console.log('got my cryps', cryps); } function battleState(response) { const [structName, battle] = response; store.dispatch(actions.setBattle(battle)); } function crypSpawn(response) { const [structName, cryp] = response; console.log('got a new cryp', cryp); } function combatPve(response) { const [structName, battle] = response; console.log('got a new battle', battle); } function itemList(response) { const [structName, items] = response; store.dispatch(actions.setItems(items)); } // ------------- // Outgoing // ------------- function send(msg) { msg.token = account && account.token; ws.send(cbor.encode(msg)); } function sendAccountLogin(name, password) { send({ method: 'account_login', params: { name, password } }); } function sendCrypSpawn(name) { send({ method: 'cryp_spawn', params: { name } }); } function sendCombatPve(id) { send({ method: 'combat_pve', params: { id } }); } // ------------- // 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, combat_pve: combatPve, battle_state: battleState, 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 { sendAccountLogin, sendCombatPve, sendCrypSpawn, connect, }; } module.exports = createSocket;