Got it working to a point...

This commit is contained in:
Chris Blanchard 2016-03-19 13:31:30 +00:00
parent 0daf5a64b4
commit 1363826137
11 changed files with 343 additions and 203 deletions

View file

@ -15,6 +15,7 @@ const SelectPlayerButton = React.createClass({
selectPlayer(e) {
e.preventDefault();
this.props.socket.emit("gather:select", {
type: this.props.gather.type,
player: parseInt(e.target.value, 10)
});
},
@ -257,12 +258,16 @@ const JoinGatherButton = React.createClass({
joinGather(e) {
e.preventDefault();
this.props.socket.emit("gather:join");
this.props.socket.emit("gather:join", {
type: this.props.gather.type
});
},
leaveGather(e) {
e.preventDefault();
this.props.socket.emit("gather:leave");
this.props.socket.emit("gather:leave", {
type: this.props.gather.type
});
},
cooldownTime() {
@ -324,6 +329,7 @@ const GatherActions = React.createClass({
voteRegather(e) {
e.preventDefault(e);
this.props.socket.emit("gather:vote", {
type: this.props.gather.type,
regather: (e.target.value === "true")
});
},
@ -380,11 +386,13 @@ const VoteButton = React.createClass({
propTypes: {
socket: React.PropTypes.object.isRequired,
candidate: React.PropTypes.object.isRequired,
thisGatherer: React.PropTypes.object
thisGatherer: React.PropTypes.object,
gather: React.PropTypes.object.isRequired
},
cancelVote(e) {
this.props.socket.emit("gather:vote", {
type: this.props.gather.type,
leader: {
candidate: null
}
@ -394,6 +402,7 @@ const VoteButton = React.createClass({
vote(e) {
e.preventDefault();
this.props.socket.emit("gather:vote", {
type: this.props.gather.type,
leader: {
candidate: parseInt(e.target.value, 10)
}
@ -441,6 +450,7 @@ const ServerVoting = React.createClass({
return e => {
e.preventDefault();
this.props.socket.emit("gather:vote", {
type: this.props.gather.type,
server: {
id: serverId
}
@ -465,7 +475,7 @@ const ServerVoting = React.createClass({
}).map(server => {
let votes = self.votesForServer(server);
let style = thisGatherer.serverVote.some(voteId => voteId === server.id) ?
"list-group-item list-group-item-default" : "list-group-item";
"list-group-item list-group-item-success" : "list-group-item";
return (
<a href="#"
className={style}
@ -505,6 +515,7 @@ const MapVoting = React.createClass({
return e => {
e.preventDefault();
this.props.socket.emit("gather:vote", {
type: this.props.gather.type,
map: {
id: mapId
}
@ -674,6 +685,61 @@ const LifeformIcons = exports.LifeformIcons = React.createClass({
}
});
const GatherMenu = exports.GatherMenu = React.createClass({
propTypes: {
gatherPool: React.PropTypes.object.isRequired,
currentGather: React.PropTypes.string.isRequired,
gatherSelectedCallback: React.PropTypes.func.isRequired
},
onClick(gather) {
return () => {
this.props.gatherSelectedCallback(gather.type);
}
},
itemClass(gather) {
let className = ["list-group-item", "pointer"];
if (gather.type === this.props.currentGather) {
className.push("list-group-item-success");
}
return className.join(" ");
},
gatherPoolArray() {
const gatherArray = [];
const gatherPool = this.props.gatherPool;
for (let attr in gatherPool) {
if (gatherPool.hasOwnProperty(attr)) {
gatherArray.push(gatherPool[attr]);
}
}
return gatherArray.sort((a, b) => a.name - b.name);
},
render() {
return (
<div className="panel panel-primary add-bottom">
<div className="panel-heading">Gather Menu</div>
<ul className="list-group">
{
this.gatherPoolArray().map(gather => {
return (
<li onClick={this.onClick(gather)} className={this.itemClass(gather)}
key={gather.type}>
<strong>{gather.name}</strong>
<br />
{gather.description}
</li>
);
})
}
</ul>
</div>
);
}
});
const GathererListItem = React.createClass({
propTypes: {
user: React.PropTypes.object.isRequired,
@ -687,6 +753,7 @@ const GathererListItem = React.createClass({
bootGatherer(e) {
e.preventDefault();
this.props.socket.emit("gather:leave", {
type: this.props.gather.type,
gatherer: parseInt(e.target.value, 10) || null
});
},
@ -755,6 +822,7 @@ const GathererListItem = React.createClass({
<span className="badge add-right">{votes + " votes"}</span>
<VoteButton
socket={socket}
gather={gather}
thisGatherer={thisGatherer}
soundController={soundController}
candidate={gatherer} />
@ -860,7 +928,9 @@ const Gatherers = React.createClass({
joinGather(e) {
e.preventDefault();
this.props.socket.emit("gather:join");
this.props.socket.emit("gather:join", {
type: this.props.gather.type
});
},
render() {

View file

@ -23,7 +23,7 @@ const ArchivedGathers = exports.ArchivedGathers = React.createClass({
});
return (
<div className="panel panel-primary">
<div className="panel panel-primary archived-gather-panel">
<div className="panel-heading">Archived Gathers</div>
<div className="panel-body">
{archive}

View file

@ -1,47 +0,0 @@
const React = require("react");
const GatherMenu = exports.GatherMenu = React.createClass({
getInitialState() {
return {
open: false
};
},
toggleOpen(e) {
e.preventDefault();
this.setState({ open: !this.state.open });
},
chevron() {
if (this.state.open) {
return <i className="fa fa-angle-down pull-right"></i>;
} else {
return <i className="fa fa-angle-right pull-right"></i>;
}
},
render() {
const open = this.state.open;
let componentClass = ["treeview"];
let dropdown;
if (open) {
componentClass.push("active");
dropdown = (
<ul className="treeview-menu menu-open" style={{display: "block"}}>
<li><a href="#" onClick={this.changeGather}>Public Gather</a></li>
</ul>
);
}
return (
<li className={componentClass.join(" ")}>
<a href="#" onClick={this.toggleOpen}>
<i className="fa fa-play-circle"></i><span>Gathers</span>
{this.chevron()}
</a>
{dropdown}
</li>
);
}
});

View file

@ -1,11 +1,10 @@
import {News} from "javascripts/components/news";
import {Events} from "javascripts/components/event";
import {Gather} from "javascripts/components/gather";
import {Gather, GatherMenu} from "javascripts/components/gather";
import {InfoButton} from "javascripts/components/info";
import {AdminPanel} from "javascripts/components/admin";
import {Chatroom} from "javascripts/components/message";
import {SoundPanel} from "javascripts/components/sound";
import {GatherMenu} from "javascripts/components/gatherMenu";
import {SettingsPanel} from "javascripts/components/settings";
import {ArchivedGathers} from "javascripts/components/gatherArchive";
import {CurrentUser, ProfileModal, UserMenu} from "javascripts/components/user";
@ -155,9 +154,15 @@ const GatherPage = React.createClass({
return {
modal: null,
gather: {
gatherers: []
gatherPool: {
public: {
gatherers: []
},
skilled: {
gatherers: []
}
},
currentGather: "public",
users: [],
messages: [],
maps: [],
@ -175,6 +180,10 @@ const GatherPage = React.createClass({
};
},
currentGather() {
return this.state.gatherPool[this.state.currentGather];
},
componentDidMount() {
let self = this;
let socket = this.props.socket;
@ -187,7 +196,7 @@ const GatherPage = React.createClass({
if (state.from === 'gathering'
&& state.to === 'election'
&& this.thisGatherer()) {
&& this.thisGatherer(data.type)) {
soundController.playGatherMusic();
}
@ -228,11 +237,13 @@ const GatherPage = React.createClass({
});
socket.on("gather:refresh", (data) => {
const gatherPool = this.state.gatherPool;
const type = data.type;
gatherPool[type] = data.gather;
self.setState({
gather: data.gather,
maps: data.maps,
servers: data.servers,
previousGather: data.previousGather
gatherPool: gatherPool
});
this.updateTitle();
});
@ -267,7 +278,7 @@ const GatherPage = React.createClass({
},
updateTitle() {
let gather = this.state.gather;
let gather = this.currentGather();
if (gather && this.state.updateTitle) {
document.title = `NSL Gathers (${gather.gatherers.length}/12)`;
return;
@ -292,8 +303,9 @@ const GatherPage = React.createClass({
this.updateTitle();
},
thisGatherer() {
let gather = this.state.gather;
thisGatherer(gatherType) {
if (gatherType === undefined) gatherType = this.state.currentGather;
let gather = this.state.gatherPool[gatherType];
let user = this.state.user;
if (gather && user && gather.gatherers.length) {
return gather.gatherers
@ -347,6 +359,14 @@ const GatherPage = React.createClass({
});
},
onGatherSelected(gatherName) {
let gather = this.state.gatherPool[gatherName];
if (gather === undefined) return;
this.setState({
currentGather: gather.type
});
},
render() {
const socket = this.props.socket;
@ -442,10 +462,6 @@ const GatherPage = React.createClass({
</ul>
<UserMenu users={this.state.users} user={this.state.user}
socket={socket} mountModal={this.mountModal}/>
<ul className="sidebar-menu">
<li className="header">Gathers</li>
<GatherMenu />
</ul>
<ul className="sidebar-menu">
<li className="header">Information</li>
<TeamSpeakButton />
@ -461,19 +477,22 @@ const GatherPage = React.createClass({
socket={socket}
maps={this.state.maps}
user={this.state.user}
gather={this.state.gather}
gather={this.currentGather()}
servers={this.state.servers}
thisGatherer={this.thisGatherer()}
previousGather={this.state.previousGather}
soundController={this.state.soundController} />
</div>
<div className="col-lg-4 col-md-12 col-sm-12">
<GatherMenu
gatherPool={this.state.gatherPool}
currentGather={this.state.currentGather}
gatherSelectedCallback={this.onGatherSelected} />
{eventsPanel}
</div>
</div>
<hr />
<div className="row">
<div className="col-lg-8 col-md-12 col-sm-12">
<div className="col-lg-12 col-md-12 col-sm-12">
<ArchivedGathers archive={this.state.archive}
maps={this.state.maps}
servers={this.state.servers} />

View file

@ -71,6 +71,11 @@ html, body {
/*Gather Styles*/
.archived-gather-panel {
max-width: 1140px;
margin: 0 auto;
}
.vote-button {
min-width: 60px;
}

View file

@ -3,7 +3,7 @@
const path = require("path");
const winston = require("winston");
const config = require("./config.js");
const Gather = require("../lib/gather/gather_singleton");
const Gather = require("../lib/gather/gather_pool").get("public");
const mongoose = require("mongoose");
const Message = mongoose.model("Message");
const cors = require("cors");

View file

@ -17,28 +17,26 @@
*
*/
var Map = require("./map");
var Server = require("./server");
var mongoose = require("mongoose");
var Gather = require("./gather_singleton");
var ArchivedGather = mongoose.model("ArchivedGather");
var Event = mongoose.model("Event");
var _ = require("lodash");
var winston = require("winston");
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 emitGather = (socket, gather) => {
socket.emit("gather:refresh", {
gather: gather ? gather.toJson() : null,
type: gather ? gather.type : null,
maps: Map.list,
servers: Server.list
});
}
module.exports = function (namespace) {
var refreshGather = _.debounce(function () {
namespace.emit("gather:refresh", {
gather: Gather.current ? Gather.current.toJson() : null,
maps: Map.list,
servers: Server.list
});
}, 200, {
leading: true,
trailing: true
});
var refreshArchive = () => {
const refreshArchive = () => {
ArchivedGather.recent((error, recentGathers) => {
if (error) return winston.error(error);
namespace.emit("gather:archive:refresh", {
@ -49,26 +47,62 @@ module.exports = function (namespace) {
});
};
Gather.registerCallback('onDone', refreshGather);
Gather.registerCallback('onEvent', refreshGather);
Gather.registerCallback('onEvent', (event, from, to) => {
if (from !== to) {
namespace.emit("stateChange", {
event: event,
state: {
from: from,
to: to
}
});
const refreshGather = type => {
if (type === undefined) {
for (let attr in gatherRefreshers) {
gatherRefreshers[attr].call();
}
} else {
const refresh = gatherRefreshers[type];
if (refresh) refresh();
}
}
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,
servers: Server.list
});
}, 200, {
leading: true,
trailing: true
});
gatherManager.restart();
});
Gather.onArchiveUpdate(refreshArchive);
Gather.restart();
// ***** Generate Test Users *****
if (process.env.POPULATE_GATHER) {
let helper = require("./helper");
helper.createTestUsers({ gather: Gather.current }, refreshGather);
GatherPool.forEach(gatherManager => {
helper.createTestUsers({
gather: gatherManager.current
}, refreshGather());
});
}
namespace.on("connection", function (socket) {
@ -82,61 +116,76 @@ module.exports = function (namespace) {
});
socket.on("gather:join", function (data) {
let gather = Gather.current;
if (!data) data = {};
const gatherManager = GatherPool.get(data.type);
if (!gatherManager) return;
const gather = gatherManager.current;
if (gather.can("addGatherer")) gather.addGatherer(socket._user);
Event.joiner(socket._user);
refreshGather();
refreshGather(data.type);
});
socket.on("gather:refresh", function () {
socket.emit("gather:refresh", {
gather: Gather.current ? Gather.current.toJson() : null,
maps: Map.list,
servers: Server.list
});
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)
});
let removeGatherer = user => {
let gather = Gather.current;
const removeGatherer = (gather, user) => {
let gatherLeaver = gather.getGatherer(user);
if (gather.can("removeGatherer")) {
gather.removeGatherer(user);
}
if (user.cooldown) gather.applyCooldown(user);
Event.leaver(gatherLeaver.user);
refreshGather();
refreshGather(gather.type);
}
socket.on("gather:leave", function (data) {
if (data && data.gatherer) {
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({ id: data.gatherer, cooldown: true });
removeGatherer(gather, { id: data.gatherer, cooldown: true });
} else {
// Remove gatherer attached to socket
removeGatherer(socket._user);
removeGatherer(gather, socket._user);
}
});
socket.on("gather:select", function (data) {
let gather = Gather.current;
if (!data) data = {};
const gatherManager = GatherPool.get(data.type);
if (!gatherManager) return;
const gather = gatherManager.current;
let playerId = data.player;
// Check team & leader
let gatherLeader = gather.getGatherer(socket._user);
let gatherer = gatherPool.getGatherer(socket._user);
// Cancel if not gatherer or leader
if (gatherLeader === null || gatherLeader.leader === false) {
if (gatherer === null || gatherer.leader === false) {
return null;
}
// Cancel if id belongs to a leader
let selectedPlayer = gather.getGatherer({id: playerId});
let selectedPlayer = gatherPool.getGatherer({id: playerId});
if (selectedPlayer === null || selectedPlayer.leader) {
return null;
}
let team = gatherLeader.team;
let team = gatherer.team;
let method = (team === 'alien') ? gather.moveToAlien : gather.moveToMarine;
method.call(gather, selectedPlayer.user, socket._user);
@ -154,7 +203,7 @@ module.exports = function (namespace) {
Event.playerSelected(socket._user, data, gather);
refreshGather();
refreshGather(data.type);
});
socket.on("disconnect", function () {
@ -162,7 +211,11 @@ module.exports = function (namespace) {
});
socket.on("gather:vote", function (data) {
let gather = Gather.current;
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);
@ -182,17 +235,23 @@ module.exports = function (namespace) {
gather.regather(socket._user, data.regather);
}
refreshGather();
refreshGather(data.type);
});
socket.on("gather:reset", function () {
socket.on("gather:reset", function (data) {
if (!data) data = {};
const gatherManager = GatherPool.get(data.type);
if (!gatherManager) return;
if (socket._user.isGatherAdmin()) {
Gather.reset();
refreshGather();
GatherManager.reset();
refreshGather(data.type);
Event.adminRegather(socket._user);
}
});
refreshGather();
// Refresh gather
for (let attr in gatherRefreshers) {
gatherRefreshers[attr].call();
}
});
};

View file

@ -317,6 +317,7 @@ Gather.prototype.toJson = function () {
return {
name: this.name,
description: this.description,
type: this.type,
gatherers: this.gatherers,
state: this.current,
pickingTurn: this.pickingTurn(),

104
lib/gather/gather_pool.js Normal file
View file

@ -0,0 +1,104 @@
"use strict"
/*
* Implements a pool of concurrent gathers
* (no longer a singleton class, should rename)
*
*/
const _ = require("lodash");
const Gather = require("./gather");
const winston = require("winston");
const mongoose = require("mongoose");
const ArchivedGather = mongoose.model("ArchivedGather");
let gatherCallbacks = {};
let archiveUpdatedCallback = () => {};
const GatherPool = new Map();
const GATHER_CONFIGS = [
{
type: "public",
name: "Public Gather",
description: "No requirements, 6v6"
},
{
type: "skilled",
name: "Competitive Gather",
description: "Hive Requirements, 6v6"
}
];
GATHER_CONFIGS.forEach(config => {
const gatherManager = {
type: config.type,
name: config.name,
registerCallback: function (type, method) {
if (this.gatherCallbacks[type]) {
this.gatherCallbacks[type].push(method);
} else {
this.gatherCallbacks[type] = [method];
}
},
onArchiveUpdate: function (callback) {
archiveUpdatedCallback = callback;
},
restart: function () {
this.previousGather = undefined;
this.current = undefined;
return newGather();
},
reset: function () {
return newGather();
},
current: Gather(),
previous: undefined,
gatherCallbacks: {}
};
gatherManager.gatherCallbacks['onDone'] = [function () {
rotateGather();
}];
const newGather = () => {
const newGatherConfig = _.clone(config);
newGatherConfig.onEvent = function () {
gatherManager.gatherCallbacks['onEvent'].forEach(cb => {
cb.apply(this, [].slice.call(arguments))
});
};
newGatherConfig.onDone = function () {
gatherManager.gatherCallbacks['onDone'].forEach(cb => {
cb.apply(this, [].slice.call(arguments))
});
};
return gatherManager.current = Gather(newGatherConfig);
};
const archiveGather = gather => {
ArchivedGather.archive(gather, (error, result) => {
if (error) return winston.error(error);
if (archiveUpdatedCallback
&& typeof archiveUpdatedCallback === 'function') {
archiveUpdatedCallback();
}
});
};
const rotateGather = () => {
if (gatherManager.current) {
gatherManager.previous = gatherManager.current;
archiveGather(gatherManager.previous);
}
return newGather();
};
GatherPool.set(config.type, gatherManager)
});
// Register initial callback to reset gather when state is `done`
module.exports = GatherPool;

View file

@ -1,71 +0,0 @@
"use strict"
let Gather = require("./gather");
let gatherCallbacks = {};
let archiveUpdatedCallback = () => {};
let winston = require("winston");
let mongoose = require("mongoose");
let ArchivedGather = mongoose.model("ArchivedGather");
// Register initial callback to reset gather when state is `done`
gatherCallbacks['onDone'] = [function () {
rotateGather();
}];
let newGather = () => {
return SingletonClass.current = Gather({
onEvent: function () {
gatherCallbacks['onEvent'].forEach(cb => {
cb.apply(this, [].slice.call(arguments))
});
},
onDone: function () {
gatherCallbacks['onDone'].forEach(cb => {
cb.apply(this, [].slice.call(arguments))
});
}
});
};
let archiveGather = gather => {
ArchivedGather.archive(gather, (error, result) => {
if (error) return winston.error(error);
if (archiveUpdatedCallback
&& typeof archiveUpdatedCallback === 'function') {
archiveUpdatedCallback();
}
});
};
let rotateGather = () => {
if (SingletonClass.current) {
SingletonClass.previous = SingletonClass.current;
archiveGather(SingletonClass.previous);
}
return newGather();
}
let SingletonClass = {
registerCallback: function (type, method) {
if (gatherCallbacks[type]) {
gatherCallbacks[type].push(method);
} else {
gatherCallbacks[type] = [method];
}
},
onArchiveUpdate: function (callback) {
archiveUpdatedCallback = callback;
},
restart: function () {
this.previousGather = undefined;
this.current = undefined;
return newGather();
},
reset: function () {
return newGather();
},
current: Gather(),
previous: undefined
};
module.exports = SingletonClass;

View file

@ -5,7 +5,7 @@ var steam = require("steam");
var winston = require("winston");
var password = process.env.GATHER_STEAM_PASSWORD;
var account_name = process.env.GATHER_STEAM_ACCOUNT;
var Gather = require("../gather/gather_singleton");
var Gather = require("../gather/gather_pool").get("public");
function SteamBot(config) {
let self = this;