mnml/client/src/socket.jsx
2019-04-08 15:59:56 +10:00

325 lines
9.1 KiB
JavaScript

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;
// handle account auth within the socket itself
// https://www.christian-schneider.net/CrossSiteWebSocketHijacking.html
let account = null;
// -------------
// 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 sendAccountDemo() {
send({ method: 'account_demo', params: {} });
}
function sendAccountCryps() {
send({ method: 'account_cryps', params: {} });
}
function sendAccountPlayers() {
send({ method: 'account_players', params: {} });
}
function sendAccountZone() {
send({ method: 'account_zone', params: {} });
}
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 sendGameJoin(gameId, crypIds) {
send({ method: 'game_join', params: { game_id: gameId, cryp_ids: crypIds } });
}
function sendSpecForget(id, spec) {
send({ method: 'cryp_unspec', params: { id, spec } });
}
function sendPlayerMmCrypsSet(crypIds) {
send({ method: 'player_mm_cryps_set', params: { cryp_ids: crypIds } });
}
function sendPlayerState(instanceId) {
send({ method: 'player_state', params: { instance_id: instanceId } });
}
function sendVboxAccept(instanceId, group, index) {
send({ method: 'player_vbox_accept', params: { instance_id: instanceId, group, index } });
}
function sendVboxApply(instanceId, crypId, index) {
send({ method: 'player_vbox_apply', params: { instance_id: instanceId, cryp_id: crypId, index } });
events.setActiveVar(null);
}
function sendVboxUnequip(instanceId, crypId, target) {
send({ method: 'player_vbox_unequip', params: { instance_id: instanceId, cryp_id: crypId, target } });
events.clearInfo();
}
function sendVboxDiscard(instanceId) {
send({ method: 'player_vbox_discard', params: { instance_id: instanceId } });
}
function sendVboxCombine(instanceId, indices) {
send({ method: 'player_vbox_combine', params: { instance_id: instanceId, indices } });
events.clearCombiner();
}
function sendVboxReclaim(instanceId, index) {
send({ method: 'player_vbox_reclaim', params: { instance_id: instanceId, index } });
}
function sendGameSkill(gameId, crypId, targetCrypId, skill) {
send({
method: 'game_skill',
params: {
game_id: gameId, cryp_id: crypId, target_cryp_id: targetCrypId, 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 sendZoneCreate() {
send({ method: 'zone_create', params: {} });
}
function sendZoneJoin(zoneId, nodeId, crypIds) {
send({ method: 'zone_join', params: { zone_id: zoneId, node_id: nodeId, cryp_ids: crypIds } });
}
function sendZoneClose(zoneId) {
send({ method: 'zone_close', params: { zone_id: zoneId } });
}
function sendInstanceJoin(cryps) {
send({ method: 'instance_join', params: { cryp_ids: cryps, pve: true } });
}
function sendInstanceReady(instanceId) {
send({ method: 'instance_ready', params: { instance_id: instanceId } });
}
function sendInstanceScores(instanceId) {
send({ method: 'instance_scores', params: { instance_id: instanceId } });
}
// -------------
// Incoming
// -------------
function accountLogin(res) {
const [struct, login] = res;
account = login;
events.setAccount(login);
sendAccountCryps();
sendAccountPlayers();
}
function accountPlayerList(res) {
const [struct, playerList] = res;
events.setInstanceList(playerList);
}
function accountCryps(response) {
const [structName, cryps] = response;
events.setCrypList(cryps);
}
function gameState(response) {
const [structName, game] = response;
events.setGame(game);
}
function crypSpawn(response) {
const [structName, cryp] = response;
}
function zoneState(response) {
const [structName, zone] = response;
events.setZone(zone);
}
function playerState(response) {
const [structName, player] = response;
events.setPlayer(player);
}
function instanceScores(response) {
const [structName, scores] = response;
events.setScores(scores);
}
// -------------
// Setup
// -------------
// 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,
cryp_forget: () => true,
cryp_learn: () => true,
game_state: gameState,
account_login: accountLogin,
account_create: accountLogin,
account_cryps: accountCryps,
account_players: accountPlayerList,
instance_scores: instanceScores,
zone_create: res => console.log(res),
zone_state: zoneState,
zone_close: res => console.log(res),
player_state: playerState,
};
function errHandler(error) {
switch (error) {
case 'no active zone': return sendZoneCreate();
case 'no cryps selected': return events.errorPrompt('select_cryps');
case 'node requirements not met': return events.errorPrompt('complete_nodes');
case 'cryp at max skills (4)': return events.errorPrompt('max_skills');
default: return errorToast(error);
}
}
// 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);
const { method, params } = res;
console.log(res);
// check for error and split into response type and data
if (res.err) return errHandler(res.err);
if (!handlers[method]) return errorToast(`${method} handler missing`);
return handlers[method](params);
}
function connect() {
ws = new WebSocket(SOCKET_URL);
ws.binaryType = 'arraybuffer';
// Connection opened
ws.addEventListener('open', () => {
toast.info({
message: 'connected',
position: 'topCenter',
});
// if (!account) events.loginPrompt();
if (process.env.NODE_ENV !== 'production') {
// send({ method: 'account_login', params: { name: 'ntr', password: 'grepgrepgrep' } });
}
return true;
});
// 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);
toast.warning({
message: 'disconnected',
position: 'topCenter',
});
return setTimeout(connect, 5000);
});
return ws;
}
return {
sendAccountLogin,
sendAccountCreate,
sendAccountDemo,
sendAccountCryps,
sendAccountPlayers,
sendAccountZone,
sendGameState,
sendGameJoin,
sendGameSkill,
sendGameTarget,
sendCrypSpawn,
sendCrypLearn,
sendCrypForget,
sendSpecForget,
sendZoneCreate,
sendZoneJoin,
sendZoneClose,
sendInstanceJoin,
sendInstanceReady,
sendInstanceScores,
sendPlayerMmCrypsSet,
sendPlayerState,
sendVboxAccept,
sendVboxApply,
sendVboxReclaim,
sendVboxCombine,
sendVboxDiscard,
sendVboxUnequip,
connect,
};
}
module.exports = createSocket;