"use strict"; /* * Gather Controller * * Server API * gather:refresh - Refreshes active gather * gather:archive:refresh - Refreshes gather archive * * Client API * gather:join - Assigns user to gather * gather:vote - Assigns vote to map, leader or server * gather:leave - Leave gather * gather:select - Select player for team * gather:refresh - Refresh gather for client * gather:reset - Resets gather (ADMIN) * */ const Map = require("./map"); const Server = require("./server"); const mongoose = require("mongoose"); const GatherPool = require("./gather_pool"); const ArchivedGather = mongoose.model("ArchivedGather"); const Event = mongoose.model("Event"); const _ = require("lodash"); const winston = require("winston"); const kickTimeout = 300 // sec module.exports = function (namespace) { const emitGather = (socket, gather) => { socket.emit("gather:refresh", { gather: gather ? gather.toJson() : null, type: gather ? gather.type : null, maps: Map.list, }); }; const removeGatherer = (gather, user) => { let gatherLeaver = gather.getGatherer(user); if (!gatherLeaver) { winston.info(`${user.username} ${user.id} attempted to leave ${gather.type} gather, but was not in gather.`); return; } if (gather.can("removeGatherer")) { gather.removeGatherer(user); } if (user.cooldown) gather.applyCooldown(user); Event.leaver(gatherLeaver.user); refreshGather(gather.type); } const removeAfkGathererTimer = (gather, user) => { const interval = setInterval(() => { if (gather.getGatherer(user).user.online) { clearInterval(interval); } const now = Date.now(); const awayDuration = (now / 1000) - (user.lastSeen / 1000); if (awayDuration >= kickTimeout) { console.log("kicked from being afk : "); removeGatherer(gather, user); clearInterval(interval); } }, 1000) } const refreshArchive = () => { ArchivedGather.recent((error, recentGathers) => { if (error) return winston.error(error); namespace.emit("gather:archive:refresh", { archive: recentGathers, maps: Map.list }); }); }; const refreshGather = type => { if (type === undefined) { for (let attr in gatherRefreshers) { gatherRefreshers[attr].call(); } } else { const refresh = gatherRefreshers[type]; if (refresh) refresh(); } } const updateUserAsOnlineInGather = (userId) => { return GatherPool.forEach((gatherManager, type) => { for (var gatherer of gatherManager.current.gatherers) { if (gatherer.user.id == userId && !gatherer.user.online) { gatherer.user.online = true; } } }); }; const refreshGathersWhereUserWentOffline = (userId) => { return GatherPool.forEach((gatherManager, type) => { for (var gatherer of gatherManager.current.gatherers) { if (gatherer.user.id == userId && gatherer.user.online) { gatherer.user.online = false; gatherer.user.lastSeen = Date.now(); removeAfkGathererTimer(gatherManager.current, { ...gatherer.user, cooldown: false }); refreshGather(type); } } }); }; const gatherRefreshers = {}; // Stores debounced procedures to refresh gathers GatherPool.forEach((gatherManager, type) => { let config = gatherManager.config; gatherManager.registerCallback('onDone', refreshGather.bind(null, type)); gatherManager.registerCallback('onEvent', refreshGather.bind(null, type)); gatherManager.registerCallback('onEvent', (event, from, to) => { if (from !== to) { namespace.emit("stateChange", { type: type, event: event, state: { from: from, to: to } }); } }); gatherManager.onArchiveUpdate(refreshArchive); gatherRefreshers[type] = _.debounce(function () { namespace.emit("gather:refresh", { gather: gatherManager.current ? gatherManager.current.toJson() : null, type: gatherManager.current ? gatherManager.current.type : null, maps: Map.list, }); }, 200, { leading: true, trailing: true }); gatherManager.restart(); }); // ***** Generate Test Users ***** if (process.env.POPULATE_GATHER) { let helper = require("./helper"); GatherPool.forEach(gatherManager => { helper.createTestUsers({ gather: gatherManager.current }, refreshGather()); }); } namespace.on("connection", function (socket) { const connectedUserId = socket._user && socket._user.id; updateUserAsOnlineInGather(connectedUserId); ArchivedGather.recent((error, recentGathers) => { if (error) return winston.error(error); socket.emit("gather:archive:refresh", { archive: recentGathers, maps: Map.list }); }); socket.on("disconnect", () => { const onlineIds = Object.values(namespace.sockets).map(s => s._user.id); // need to check if this disconnect actually meant user went offline // they could have another tab open... if (onlineIds.indexOf(connectedUserId) === -1) { refreshGathersWhereUserWentOffline(connectedUserId); } }); socket.on("gather:join", function (data) { if (!data) data = {}; const gatherManager = GatherPool.get(data.type); if (!gatherManager) return; const gather = gatherManager.current; if (gather.can("addGatherer")) { gather.addGatherer(socket._user); if (gather.containsUser(socket._user)) { Event.joiner(socket._user); } else { socket.emit("notify", { type: "error", message: "Unable to add you to the gather" }); } refreshGather(data.type); } }); socket.on("gather:refresh", function (data) { // Refresh all gathers if (!data) data = {}; if (data.type === undefined) { GatherPool.forEach(manager => emitGather(socket, manager.current)); return; } // Otherwise refresh specified gather const gatherManager = GatherPool.get(data.type); if (gatherManager == undefined) return; emitGather(socket, gatherManager.current) }); socket.on("gather:leave", function (data) { if (!data) data = {}; const gatherManager = GatherPool.get(data.type); if (!gatherManager) return; const gather = gatherManager.current; if (data.gatherer) { // Remove gatherer defined by ID (admins only) if (!socket._user.isGatherAdmin()) return; removeGatherer(gather, { id: data.gatherer, cooldown: true }); let adminName = socket._user.username; let playerId = data.gatherer; winston.info(`Admin removal: ${adminName} removed ${playerId} from the gather.`); } else { // Remove gatherer attached to socket removeGatherer(gather, socket._user); } }); socket.on("gather:select", function (data) { if (!data) data = {}; const gatherManager = GatherPool.get(data.type); if (!gatherManager) return; const gather = gatherManager.current; let playerId = data.player; // Check team & leader let gatherer = gather.getGatherer(socket._user); // Cancel if not gatherer or leader if (gatherer === null || gatherer.leader === false) { return null; } // Cancel if id belongs to a leader let selectedPlayer = gather.getGatherer({id: playerId}); if (selectedPlayer === null || selectedPlayer.leader) { return null; } let team = gatherer.team; let method = (team === 'alien') ? gather.moveToAlien : gather.moveToMarine; method.call(gather, selectedPlayer.user, socket._user); // Check if last player and add to last team if (gather.lobby().length === 1) { let assignLast = (gather.marines().length === 6) ? gather.moveToAlien : gather.moveToMarine; assignLast.call(gather, gather.lobby().pop()); } if (gather.can("confirmSelection")) { gather.confirmSelection(socket._user); } Event.playerSelected(socket._user, data, gather); refreshGather(data.type); }); socket.on("gather:vote", function (data) { if (!data) data = {}; const gatherManager = GatherPool.get(data.type); if (!gatherManager) return; const gather = gatherManager.current; if (data.leader) { gather.selectLeader(socket._user, data.leader.candidate); Event.leaderVote(socket._user, data, gather); } if (data.map) { gather.toggleMapVote(socket._user, data.map.id); Event.mapVote(socket._user, data, gather, Map.list); } if (data.server) { gather.toggleServerVote(socket._user, data.server.id); Event.serverVote(socket._user, data, gather, Server.list); } if (typeof data.regather === 'boolean' && gather.can("regather")) { gather.regather(socket._user, data.regather); } refreshGather(data.type); }); socket.on("gather:reset", function (data) { if (!data) data = {}; const gatherManager = GatherPool.get(data.type); if (!gatherManager) return; if (socket._user.isGatherAdmin()) { gatherManager.reset(); refreshGather(data.type); Event.adminRegather(socket._user); } }); // Refresh gather for (let attr in gatherRefreshers) { gatherRefreshers[attr].call(); } }); };