mnml/client/src/socket.jsx
2018-11-20 14:33:40 +11:00

222 lines
6.1 KiB
JavaScript
Executable File

const { toast } = require('bulma-toast');
const cbor = require('borc');
const actions = require('./actions');
const SOCKET_URL = process.env.NODE_ENV === 'production' ? 'wss://cryps.gg/ws' : 'ws://localhost:40000';
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;
let gameStateTimeout;
function connect() {
ws = new WebSocket(SOCKET_URL);
ws.binaryType = 'arraybuffer';
// Connection opened
ws.addEventListener('open', (event) => {
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;
store.dispatch(actions.setAccount(login));
// send({ method: 'cryp_spawn', params: { name: 'bees' } });
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 gameState(response) {
const [structName, game] = response;
clearInterval(gameStateTimeout);
gameStateTimeout = setTimeout(() => sendGameState(game.id), 1000);
store.dispatch(actions.setGame(game));
}
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;
store.dispatch(actions.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 sendAccountRegister(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(id) {
send({ method: 'game_pve', params: { id } });
}
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 sendGameSkill(gameId, crypId, targetTeamId, skill) {
send({
method: 'game_skill',
params: {
game_id: gameId, cryp_id: crypId, target_team_id: targetTeamId, skill,
},
});
store.dispatch(actions.setActiveSkill(null));
}
function sendGameTarget(gameId, crypId, skillId) {
send({ method: 'game_target', params: { game_id: gameId, cryp_id: crypId, skill_id: skillId } });
store.dispatch(actions.setActiveIncoming(null));
}
function sendItemUse(item, target) {
console.log(item, target);
send({ method: 'item_use', params: { item, target } });
store.dispatch(actions.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,
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,
sendAccountRegister,
sendGameState,
sendGamePve,
sendGamePvp,
sendGameJoin,
sendGameSkill,
sendGameTarget,
sendCrypSpawn,
sendCrypLearn,
sendCrypForget,
sendItemUse,
connect,
};
}
module.exports = createSocket;