Merge pull request #101 from uecasm/options
Refactor game options into subobject.
This commit is contained in:
commit
dae20ee332
|
@ -29,7 +29,7 @@ created for the user now.
|
|||
--%>
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
|
||||
<%@ page import="javax.servlet.http.HttpSession" %>
|
||||
<%@ page import="net.socialgamer.cah.data.Game" %>
|
||||
<%@ page import="net.socialgamer.cah.data.GameOptions" %>
|
||||
<%
|
||||
// Ensure a session exists for the user.
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -43,6 +43,7 @@ HttpSession hSession = request.getSession(true);
|
|||
<title>Pretend You're Xyzzy</title>
|
||||
<script type="text/javascript" src="js/jquery-1.8.2.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.cookie.js"></script>
|
||||
<script type="text/javascript" src="js/jquery.json.js"></script>
|
||||
<script type="text/javascript" src="js/QTransform.js"></script>
|
||||
<script type="text/javascript" src="js/jquery-ui.js"></script>
|
||||
<script type="text/javascript" src="js/cah.js"></script>
|
||||
|
@ -387,9 +388,9 @@ HttpSession hSession = request.getSession(true);
|
|||
<label id="score_limit_template_label" for="score_limit_template">Score limit:</label>
|
||||
<select id="score_limit_template" class="score_limit">
|
||||
<%
|
||||
for (int i = Game.MIN_SCORE_LIMIT; i <= Game.MAX_SCORE_LIMIT; i++) {
|
||||
for (int i = GameOptions.MIN_SCORE_LIMIT; i <= GameOptions.MAX_SCORE_LIMIT; i++) {
|
||||
%>
|
||||
<option <%= i == Game.DEFAULT_SCORE_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<option <%= i == GameOptions.DEFAULT_SCORE_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
<br/>
|
||||
|
@ -397,9 +398,9 @@ HttpSession hSession = request.getSession(true);
|
|||
<select id="player_limit_template" class="player_limit"
|
||||
aria-label="Player limit. Having more than 10 players may cause issues both for screen readers and traditional browsers.">
|
||||
<%
|
||||
for (int i = Game.MIN_PLAYER_LIMIT; i <= Game.MAX_PLAYER_LIMIT; i++) {
|
||||
for (int i = GameOptions.MIN_PLAYER_LIMIT; i <= GameOptions.MAX_PLAYER_LIMIT; i++) {
|
||||
%>
|
||||
<option <%= i == Game.DEFAULT_PLAYER_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<option <%= i == GameOptions.DEFAULT_PLAYER_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
Having more than 10 players may get cramped!
|
||||
|
@ -408,9 +409,9 @@ HttpSession hSession = request.getSession(true);
|
|||
<select id="spectator_limit_template" class="spectator_limit"
|
||||
aria-label="Spectator limit.">
|
||||
<%
|
||||
for (int i = Game.MIN_SPECTATOR_LIMIT; i <= Game.MAX_SPECTATOR_LIMIT; i++) {
|
||||
for (int i = GameOptions.MIN_SPECTATOR_LIMIT; i <= GameOptions.MAX_SPECTATOR_LIMIT; i++) {
|
||||
%>
|
||||
<option <%= i == Game.DEFAULT_SPECTATOR_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<option <%= i == GameOptions.DEFAULT_SPECTATOR_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<% } %>
|
||||
</select>
|
||||
Spectators can watch and chat, but not actually play. Not even as Czar.
|
||||
|
@ -433,9 +434,9 @@ HttpSession hSession = request.getSession(true);
|
|||
<label id="blanks_limit_label" title="Blank cards allow a player to type in their own answer.">
|
||||
Also include <select id="blanks_limit_template" class="blanks_limit">
|
||||
<%
|
||||
for (int i = Game.MIN_BLANK_CARD_LIMIT; i <= Game.MAX_BLANK_CARD_LIMIT; i++) {
|
||||
for (int i = GameOptions.MIN_BLANK_CARD_LIMIT; i <= GameOptions.MAX_BLANK_CARD_LIMIT; i++) {
|
||||
%>
|
||||
<option <%= i == Game.DEFAULT_BLANK_CARD_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<option <%= i == GameOptions.DEFAULT_BLANK_CARD_LIMIT ? "selected='selected' " : "" %>value="<%= i %>"><%= i %></option>
|
||||
<% } %>
|
||||
</select> blank white cards.
|
||||
</label>
|
||||
|
|
|
@ -134,46 +134,13 @@ cah.ajax.Builder.prototype.withCardId = function(cardId) {
|
|||
};
|
||||
|
||||
/**
|
||||
* @param {Array}
|
||||
* cardSets List of card set ids to use in the request.
|
||||
* @param {Object}
|
||||
* options Game options to use in the request.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withCardSets = function(cardSets) {
|
||||
cah.ajax.Builder.prototype.withGameOptions = function(options) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.CARD_SETS] = cardSets.join(',');
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number}
|
||||
* playerLimit Player limit field to use in the request.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withPlayerLimit = function(playerLimit) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.PLAYER_LIMIT] = playerLimit;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number}
|
||||
* spectatorLimit Spectator limit field to use in the request.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withSpectatorLimit = function(spectatorLimit) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.SPECTATOR_LIMIT] = spectatorLimit;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number}
|
||||
* scoreLimit Score limit field to use in the request.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withScoreLimit = function(scoreLimit) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.SCORE_LIMIT] = scoreLimit;
|
||||
this.data[cah.$.AjaxRequest.GAME_OPTIONS] = $.toJSON(options);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -188,28 +155,6 @@ cah.ajax.Builder.prototype.withPassword = function(password) {
|
|||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number}
|
||||
* blanksLimit Blanks limit field to use in the request.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withBlanksLimit = function(blanksLimit) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.BLANKS_LIMIT] = blanksLimit;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean}
|
||||
* useTimer Whether or not the game should use the idle timer.
|
||||
* @returns {cah.ajax.Builder} This object.
|
||||
*/
|
||||
cah.ajax.Builder.prototype.withUseTimer = function(useTimer) {
|
||||
this.assertNotExecuted_();
|
||||
this.data[cah.$.AjaxRequest.USE_TIMER] = useTimer;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {boolean}
|
||||
* wall Whether or not this is a warn-all ("wall").
|
||||
|
|
|
@ -34,20 +34,15 @@ cah.$.AjaxRequest = function() {
|
|||
};
|
||||
cah.$.AjaxRequest.prototype.dummyForAutocomplete = undefined;
|
||||
cah.$.AjaxRequest.WALL = "wall";
|
||||
cah.$.AjaxRequest.USE_TIMER = "ut";
|
||||
cah.$.AjaxRequest.CARD_SETS = "css";
|
||||
cah.$.AjaxRequest.MESSAGE = "m";
|
||||
cah.$.AjaxRequest.CARD_ID = "cid";
|
||||
cah.$.AjaxRequest.GAME_ID = "gid";
|
||||
cah.$.AjaxRequest.EMOTE = "me";
|
||||
cah.$.AjaxRequest.OP = "o";
|
||||
cah.$.AjaxRequest.PLAYER_LIMIT = "pL";
|
||||
cah.$.AjaxRequest.NICKNAME = "n";
|
||||
cah.$.AjaxRequest.SCORE_LIMIT = "sl";
|
||||
cah.$.AjaxRequest.CARD_ID = "cid";
|
||||
cah.$.AjaxRequest.MESSAGE = "m";
|
||||
cah.$.AjaxRequest.BLANKS_LIMIT = "bl";
|
||||
cah.$.AjaxRequest.SPECTATOR_LIMIT = "vL";
|
||||
cah.$.AjaxRequest.GAME_OPTIONS = "go";
|
||||
cah.$.AjaxRequest.SERIAL = "s";
|
||||
cah.$.AjaxRequest.PASSWORD = "pw";
|
||||
cah.$.AjaxRequest.OP = "o";
|
||||
cah.$.AjaxRequest.NICKNAME = "n";
|
||||
|
||||
cah.$.AjaxResponse = function() {
|
||||
// Dummy constructor to make Eclipse auto-complete.
|
||||
|
@ -59,6 +54,7 @@ cah.$.AjaxResponse.GAME_ID = "gid";
|
|||
cah.$.AjaxResponse.HAND = "h";
|
||||
cah.$.AjaxResponse.PLAYER_INFO = "pi";
|
||||
cah.$.AjaxResponse.BLACK_CARD = "bc";
|
||||
cah.$.AjaxResponse.GAME_OPTIONS = "go";
|
||||
cah.$.AjaxResponse.IN_PROGRESS = "ip";
|
||||
cah.$.AjaxResponse.GAMES = "gl";
|
||||
cah.$.AjaxResponse.NICKNAME = "n";
|
||||
|
@ -188,16 +184,22 @@ cah.$.GameInfo.prototype.dummyForAutocomplete = undefined;
|
|||
cah.$.GameInfo.HOST = "H";
|
||||
cah.$.GameInfo.STATE = "S";
|
||||
cah.$.GameInfo.PLAYERS = "P";
|
||||
cah.$.GameInfo.USE_TIMER = "ut";
|
||||
cah.$.GameInfo.BLANKS_LIMIT = "bl";
|
||||
cah.$.GameInfo.CARD_SETS = "css";
|
||||
cah.$.GameInfo.SPECTATORS = "V";
|
||||
cah.$.GameInfo.SPECTATOR_LIMIT = "vL";
|
||||
cah.$.GameInfo.ID = "gid";
|
||||
cah.$.GameInfo.PLAYER_LIMIT = "pL";
|
||||
cah.$.GameInfo.PASSWORD = "pw";
|
||||
cah.$.GameInfo.GAME_OPTIONS = "go";
|
||||
cah.$.GameInfo.HAS_PASSWORD = "hp";
|
||||
cah.$.GameInfo.SCORE_LIMIT = "sl";
|
||||
|
||||
cah.$.GameOptionData = function() {
|
||||
// Dummy constructor to make Eclipse auto-complete.
|
||||
};
|
||||
cah.$.GameOptionData.prototype.dummyForAutocomplete = undefined;
|
||||
cah.$.GameOptionData.USE_TIMER = "ut";
|
||||
cah.$.GameOptionData.CARD_SETS = "css";
|
||||
cah.$.GameOptionData.BLANKS_LIMIT = "bl";
|
||||
cah.$.GameOptionData.SPECTATOR_LIMIT = "vL";
|
||||
cah.$.GameOptionData.PLAYER_LIMIT = "pL";
|
||||
cah.$.GameOptionData.PASSWORD = "pw";
|
||||
cah.$.GameOptionData.SCORE_LIMIT = "sl";
|
||||
|
||||
cah.$.GamePlayerInfo = function() {
|
||||
// Dummy constructor to make Eclipse auto-complete.
|
||||
|
|
|
@ -771,6 +771,7 @@ cah.Game.prototype.insertIntoDocument = function() {
|
|||
*/
|
||||
cah.Game.prototype.updateGameStatus = function(data) {
|
||||
var gameInfo = data[cah.$.AjaxResponse.GAME_INFO];
|
||||
var options = gameInfo[cah.$.AjaxResponse.GAME_OPTIONS];
|
||||
this.host_ = gameInfo[cah.$.GameInfo.HOST];
|
||||
|
||||
if (this.host_ == cah.nickname && gameInfo[cah.$.GameInfo.STATE] == cah.$.GameState.LOBBY) {
|
||||
|
@ -791,22 +792,22 @@ cah.Game.prototype.updateGameStatus = function(data) {
|
|||
this.hideOptions_();
|
||||
}
|
||||
|
||||
$(".score_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.SCORE_LIMIT]);
|
||||
$(".player_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PLAYER_LIMIT]);
|
||||
$(".spectator_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.SPECTATOR_LIMIT]);
|
||||
$(".game_password", this.optionsElement_).val(gameInfo[cah.$.GameInfo.PASSWORD]);
|
||||
if (gameInfo[cah.$.GameInfo.USE_TIMER]) {
|
||||
$(".score_limit", this.optionsElement_).val(options[cah.$.GameOptionData.SCORE_LIMIT]);
|
||||
$(".player_limit", this.optionsElement_).val(options[cah.$.GameOptionData.PLAYER_LIMIT]);
|
||||
$(".spectator_limit", this.optionsElement_).val(options[cah.$.GameOptionData.SPECTATOR_LIMIT]);
|
||||
$(".game_password", this.optionsElement_).val(options[cah.$.GameOptionData.PASSWORD]);
|
||||
if (options[cah.$.GameOptionData.USE_TIMER]) {
|
||||
$(".use_timer", this.optionsElement_).attr("checked", "checked");
|
||||
} else {
|
||||
$(".use_timer", this.optionsElement_).removeAttr("checked");
|
||||
}
|
||||
var cardSetIds = gameInfo[cah.$.GameInfo.CARD_SETS];// .split(',');
|
||||
var cardSetIds = options[cah.$.GameOptionData.CARD_SETS];// .split(',');
|
||||
$(".card_set", this.optionsElement_).removeAttr("checked");
|
||||
for ( var key in cardSetIds) {
|
||||
var cardSetId = cardSetIds[key];
|
||||
$("#card_set_" + this.id_ + "_" + cardSetId, this.optionsElement_).attr("checked", "checked");
|
||||
}
|
||||
$(".blanks_limit", this.optionsElement_).val(gameInfo[cah.$.GameInfo.BLANKS_LIMIT]);
|
||||
$(".blanks_limit", this.optionsElement_).val(options[cah.$.GameOptionData.BLANKS_LIMIT]);
|
||||
|
||||
var playerInfos = data[cah.$.AjaxResponse.PLAYER_INFO];
|
||||
for ( var index in playerInfos) {
|
||||
|
@ -1417,13 +1418,16 @@ cah.Game.prototype.optionChanged_ = function(e) {
|
|||
for ( var i = 0; i < selectedCardSets.length; i++) {
|
||||
cardSetIds.push(selectedCardSets[i].value);
|
||||
}
|
||||
cah.Ajax.build(cah.$.AjaxOperation.CHANGE_GAME_OPTIONS).withGameId(this.id_).withScoreLimit(
|
||||
$(".score_limit", this.optionsElement_).val()).withPlayerLimit(
|
||||
$(".player_limit", this.optionsElement_).val()).withSpectatorLimit(
|
||||
$(".spectator_limit", this.optionsElement_).val()).withCardSets(cardSetIds).withPassword(
|
||||
$(".game_password", this.optionsElement_).val()).withBlanksLimit(
|
||||
$(".blanks_limit", this.optionsElement_).val()).withUseTimer(
|
||||
!!$('.use_timer', this.optionsElement_).attr('checked')).run();
|
||||
var options = {};
|
||||
options[cah.$.GameOptionData.CARD_SETS] = cardSetIds.join(',');
|
||||
options[cah.$.GameOptionData.SCORE_LIMIT] = $(".score_limit", this.optionsElement_).val();
|
||||
options[cah.$.GameOptionData.PLAYER_LIMIT] = $(".player_limit", this.optionsElement_).val();
|
||||
options[cah.$.GameOptionData.SPECTATOR_LIMIT] = $(".spectator_limit", this.optionsElement_).val();
|
||||
options[cah.$.GameOptionData.PASSWORD] = $(".game_password", this.optionsElement_).val();
|
||||
options[cah.$.GameOptionData.BLANKS_LIMIT] = $(".blanks_limit", this.optionsElement_).val();
|
||||
options[cah.$.GameOptionData.USE_TIMER] = !!$('.use_timer', this.optionsElement_).attr('checked');
|
||||
|
||||
cah.Ajax.build(cah.$.AjaxOperation.CHANGE_GAME_OPTIONS).withGameId(this.id_).withGameOptions(options).run();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -228,6 +228,8 @@ cah.GameListLobby = function(parentElem, data) {
|
|||
* @private
|
||||
*/
|
||||
this.data_ = data;
|
||||
|
||||
var options = data[cah.$.GameInfo.GAME_OPTIONS];
|
||||
|
||||
this.element_.id = "gamelist_lobby_" + this.id_;
|
||||
$(parentElem).append(this.element_);
|
||||
|
@ -241,14 +243,15 @@ cah.GameListLobby = function(parentElem, data) {
|
|||
$(".gamelist_lobby_join", this.element_).click(cah.bind(this, this.joinClick));
|
||||
$(".gamelist_lobby_spectate", this.element_).click(cah.bind(this, this.spectateClick));
|
||||
$(".gamelist_lobby_player_count", this.element_).text(data[cah.$.GameInfo.PLAYERS].length);
|
||||
$(".gamelist_lobby_max_players", this.element_).text(data[cah.$.GameInfo.PLAYER_LIMIT]);
|
||||
$(".gamelist_lobby_max_players", this.element_).text(options[cah.$.GameOptionData.PLAYER_LIMIT]);
|
||||
$(".gamelist_lobby_spectator_count", this.element_).text(data[cah.$.GameInfo.SPECTATORS].length);
|
||||
$(".gamelist_lobby_max_spectators", this.element_).text(data[cah.$.GameInfo.SPECTATOR_LIMIT]);
|
||||
$(".gamelist_lobby_goal", this.element_).text(data[cah.$.GameInfo.SCORE_LIMIT]);
|
||||
$(".gamelist_lobby_max_spectators", this.element_).text(options[cah.$.GameOptionData.SPECTATOR_LIMIT]);
|
||||
$(".gamelist_lobby_goal", this.element_).text(options[cah.$.GameOptionData.SCORE_LIMIT]);
|
||||
var cardSets = options[cah.$.GameOptionData.CARD_SETS];
|
||||
var cardSetNames = [];
|
||||
data[cah.$.GameInfo.CARD_SETS].sort();
|
||||
for ( var key in data[cah.$.GameInfo.CARD_SETS]) {
|
||||
var cardSetId = data[cah.$.GameInfo.CARD_SETS][key];
|
||||
cardSets.sort();
|
||||
for (var key in cardSets) {
|
||||
var cardSetId = cardSets[key];
|
||||
cardSetNames.push(cah.CardSet.list[cardSetId].getName());
|
||||
}
|
||||
$(".gamelist_lobby_cardset", this.element_).html(cardSetNames.join(', '));
|
||||
|
@ -260,11 +263,10 @@ cah.GameListLobby = function(parentElem, data) {
|
|||
$(this.element_).attr(
|
||||
"aria-label",
|
||||
data[cah.$.GameInfo.HOST] + "'s game, with " + data[cah.$.GameInfo.PLAYERS].length + " of "
|
||||
+ data[cah.$.GameInfo.PLAYER_LIMIT] + " players, and "
|
||||
+ data[cah.$.GameInfo.SPECTATORS].length + " of " + data[cah.$.GameInfo.SPECTATOR_LIMIT]
|
||||
+ "spectators. " + statusMessage + ". Goal is " + data[cah.$.GameInfo.SCORE_LIMIT]
|
||||
+ " Awesome Points. Using " + cardSetNames.length + " card set"
|
||||
+ (cardSetNames.length == 1 ? "" : "s") + ". "
|
||||
+ options[cah.$.GameOptionData.PLAYER_LIMIT] + " players, and " + data[cah.$.GameInfo.SPECTATORS].length
|
||||
+ " of " + options[cah.$.GameOptionData.SPECTATOR_LIMIT] + "spectators. " + statusMessage + ". Goal is "
|
||||
+ options[cah.$.GameOptionData.SCORE_LIMIT] + " Awesome Points. Using " + cardSetNames.length
|
||||
+ " card set" + (cardSetNames.length == 1 ? "" : "s") + ". "
|
||||
+ (data[cah.$.GameInfo.HAS_PASSWORD] ? "Has" : "Does not have") + " a password.");
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/**
|
||||
* jQuery JSON plugin 2.4.0
|
||||
*
|
||||
* @author Brantley Harris, 2009-2011
|
||||
* @author Timo Tijhof, 2011-2012
|
||||
* @source This plugin is heavily influenced by MochiKit's serializeJSON, which is
|
||||
* copyrighted 2005 by Bob Ippolito.
|
||||
* @source Brantley Harris wrote this plugin. It is based somewhat on the JSON.org
|
||||
* website's http://www.json.org/json2.js, which proclaims:
|
||||
* "NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.", a sentiment that
|
||||
* I uphold.
|
||||
* @license MIT License <http://www.opensource.org/licenses/mit-license.php>
|
||||
*/
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
|
||||
meta = {
|
||||
'\b': '\\b',
|
||||
'\t': '\\t',
|
||||
'\n': '\\n',
|
||||
'\f': '\\f',
|
||||
'\r': '\\r',
|
||||
'"' : '\\"',
|
||||
'\\': '\\\\'
|
||||
},
|
||||
hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* jQuery.toJSON
|
||||
* Converts the given argument into a JSON representation.
|
||||
*
|
||||
* @param o {Mixed} The json-serializable *thing* to be converted
|
||||
*
|
||||
* If an object has a toJSON prototype, that will be used to get the representation.
|
||||
* Non-integer/string keys are skipped in the object, as are keys that point to a
|
||||
* function.
|
||||
*
|
||||
*/
|
||||
$.toJSON = typeof JSON === 'object' && JSON.stringify ? JSON.stringify : function (o) {
|
||||
if (o === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
var pairs, k, name, val,
|
||||
type = $.type(o);
|
||||
|
||||
if (type === 'undefined') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Also covers instantiated Number and Boolean objects,
|
||||
// which are typeof 'object' but thanks to $.type, we
|
||||
// catch them here. I don't know whether it is right
|
||||
// or wrong that instantiated primitives are not
|
||||
// exported to JSON as an {"object":..}.
|
||||
// We choose this path because that's what the browsers did.
|
||||
if (type === 'number' || type === 'boolean') {
|
||||
return String(o);
|
||||
}
|
||||
if (type === 'string') {
|
||||
return $.quoteString(o);
|
||||
}
|
||||
if (typeof o.toJSON === 'function') {
|
||||
return $.toJSON(o.toJSON());
|
||||
}
|
||||
if (type === 'date') {
|
||||
var month = o.getUTCMonth() + 1,
|
||||
day = o.getUTCDate(),
|
||||
year = o.getUTCFullYear(),
|
||||
hours = o.getUTCHours(),
|
||||
minutes = o.getUTCMinutes(),
|
||||
seconds = o.getUTCSeconds(),
|
||||
milli = o.getUTCMilliseconds();
|
||||
|
||||
if (month < 10) {
|
||||
month = '0' + month;
|
||||
}
|
||||
if (day < 10) {
|
||||
day = '0' + day;
|
||||
}
|
||||
if (hours < 10) {
|
||||
hours = '0' + hours;
|
||||
}
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
if (milli < 100) {
|
||||
milli = '0' + milli;
|
||||
}
|
||||
if (milli < 10) {
|
||||
milli = '0' + milli;
|
||||
}
|
||||
return '"' + year + '-' + month + '-' + day + 'T' +
|
||||
hours + ':' + minutes + ':' + seconds +
|
||||
'.' + milli + 'Z"';
|
||||
}
|
||||
|
||||
pairs = [];
|
||||
|
||||
if ($.isArray(o)) {
|
||||
for (k = 0; k < o.length; k++) {
|
||||
pairs.push($.toJSON(o[k]) || 'null');
|
||||
}
|
||||
return '[' + pairs.join(',') + ']';
|
||||
}
|
||||
|
||||
// Any other object (plain object, RegExp, ..)
|
||||
// Need to do typeof instead of $.type, because we also
|
||||
// want to catch non-plain objects.
|
||||
if (typeof o === 'object') {
|
||||
for (k in o) {
|
||||
// Only include own properties,
|
||||
// Filter out inherited prototypes
|
||||
if (hasOwn.call(o, k)) {
|
||||
// Keys must be numerical or string. Skip others
|
||||
type = typeof k;
|
||||
if (type === 'number') {
|
||||
name = '"' + k + '"';
|
||||
} else if (type === 'string') {
|
||||
name = $.quoteString(k);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
type = typeof o[k];
|
||||
|
||||
// Invalid values like these return undefined
|
||||
// from toJSON, however those object members
|
||||
// shouldn't be included in the JSON string at all.
|
||||
if (type !== 'function' && type !== 'undefined') {
|
||||
val = $.toJSON(o[k]);
|
||||
pairs.push(name + ':' + val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return '{' + pairs.join(',') + '}';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* jQuery.evalJSON
|
||||
* Evaluates a given json string.
|
||||
*
|
||||
* @param str {String}
|
||||
*/
|
||||
$.evalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
|
||||
/*jshint evil: true */
|
||||
return eval('(' + str + ')');
|
||||
};
|
||||
|
||||
/**
|
||||
* jQuery.secureEvalJSON
|
||||
* Evals JSON in a way that is *more* secure.
|
||||
*
|
||||
* @param str {String}
|
||||
*/
|
||||
$.secureEvalJSON = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
|
||||
var filtered =
|
||||
str
|
||||
.replace(/\\["\\\/bfnrtu]/g, '@')
|
||||
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
|
||||
.replace(/(?:^|:|,)(?:\s*\[)+/g, '');
|
||||
|
||||
if (/^[\],:{}\s]*$/.test(filtered)) {
|
||||
/*jshint evil: true */
|
||||
return eval('(' + str + ')');
|
||||
}
|
||||
throw new SyntaxError('Error parsing JSON, source is not valid.');
|
||||
};
|
||||
|
||||
/**
|
||||
* jQuery.quoteString
|
||||
* Returns a string-repr of a string, escaping quotes intelligently.
|
||||
* Mostly a support function for toJSON.
|
||||
* Examples:
|
||||
* >>> jQuery.quoteString('apple')
|
||||
* "apple"
|
||||
*
|
||||
* >>> jQuery.quoteString('"Where are we going?", she asked.')
|
||||
* "\"Where are we going?\", she asked."
|
||||
*/
|
||||
$.quoteString = function (str) {
|
||||
if (str.match(escape)) {
|
||||
return '"' + str.replace(escape, function (a) {
|
||||
var c = meta[a];
|
||||
if (typeof c === 'string') {
|
||||
return c;
|
||||
}
|
||||
c = a.charCodeAt();
|
||||
return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
|
||||
}) + '"';
|
||||
}
|
||||
return '"' + str + '"';
|
||||
};
|
||||
|
||||
}(jQuery));
|
|
@ -208,19 +208,14 @@ public class Constants {
|
|||
*/
|
||||
public enum AjaxRequest {
|
||||
CARD_ID("cid"),
|
||||
CARD_SETS("css"),
|
||||
EMOTE("me"),
|
||||
GAME_ID("gid"),
|
||||
GAME_OPTIONS("go"),
|
||||
MESSAGE("m"),
|
||||
NICKNAME("n"),
|
||||
OP("o"),
|
||||
PASSWORD("pw"),
|
||||
PLAYER_LIMIT("pL"),
|
||||
SPECTATOR_LIMIT("vL"),
|
||||
SCORE_LIMIT("sl"),
|
||||
BLANKS_LIMIT("bl"),
|
||||
SERIAL("s"),
|
||||
USE_TIMER("ut"),
|
||||
WALL("wall");
|
||||
|
||||
private final String field;
|
||||
|
@ -242,13 +237,14 @@ public class Constants {
|
|||
BLACK_CARD("bc"),
|
||||
@DuplicationAllowed
|
||||
CARD_ID(AjaxRequest.CARD_ID),
|
||||
@DuplicationAllowed
|
||||
CARD_SETS(AjaxRequest.CARD_SETS),
|
||||
CARD_SETS("css"),
|
||||
ERROR("e"),
|
||||
ERROR_CODE("ec"),
|
||||
@DuplicationAllowed
|
||||
GAME_ID(AjaxRequest.GAME_ID),
|
||||
GAME_INFO("gi"),
|
||||
@DuplicationAllowed
|
||||
GAME_OPTIONS(AjaxRequest.GAME_OPTIONS),
|
||||
GAMES("gl"),
|
||||
HAND("h"),
|
||||
/**
|
||||
|
@ -601,27 +597,15 @@ public class Constants {
|
|||
* Fields for information about a game.
|
||||
*/
|
||||
public enum GameInfo {
|
||||
@DuplicationAllowed
|
||||
CARD_SETS(AjaxRequest.CARD_SETS),
|
||||
HAS_PASSWORD("hp"),
|
||||
HOST("H"),
|
||||
@DuplicationAllowed
|
||||
ID(AjaxRequest.GAME_ID),
|
||||
@DuplicationAllowed
|
||||
PASSWORD(AjaxRequest.PASSWORD),
|
||||
@DuplicationAllowed
|
||||
PLAYER_LIMIT(AjaxRequest.PLAYER_LIMIT),
|
||||
GAME_OPTIONS(AjaxRequest.GAME_OPTIONS),
|
||||
HAS_PASSWORD("hp"),
|
||||
PLAYERS("P"),
|
||||
@DuplicationAllowed
|
||||
SPECTATOR_LIMIT(AjaxRequest.SPECTATOR_LIMIT),
|
||||
SPECTATORS("V"),
|
||||
@DuplicationAllowed
|
||||
SCORE_LIMIT(AjaxRequest.SCORE_LIMIT),
|
||||
@DuplicationAllowed
|
||||
BLANKS_LIMIT(AjaxRequest.BLANKS_LIMIT),
|
||||
STATE("S"),
|
||||
@DuplicationAllowed
|
||||
USE_TIMER(AjaxRequest.USE_TIMER);
|
||||
STATE("S");
|
||||
|
||||
private final String key;
|
||||
|
||||
|
@ -639,6 +623,36 @@ public class Constants {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fields for options about a game.
|
||||
*/
|
||||
public enum GameOptionData {
|
||||
BLANKS_LIMIT("bl"),
|
||||
@DuplicationAllowed
|
||||
CARD_SETS(AjaxResponse.CARD_SETS),
|
||||
@DuplicationAllowed
|
||||
PASSWORD(AjaxRequest.PASSWORD),
|
||||
PLAYER_LIMIT("pL"),
|
||||
SPECTATOR_LIMIT("vL"),
|
||||
SCORE_LIMIT("sl"),
|
||||
USE_TIMER("ut");
|
||||
|
||||
private final String key;
|
||||
|
||||
GameOptionData(final String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
GameOptionData(final Enum<?> key) {
|
||||
this.key = key.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keys for the information about players in a game.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/**
|
||||
* Copyright (c) 2012, Andy Janata
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||
* and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
||||
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.socialgamer.cah;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
|
||||
/**
|
||||
* Wrap around an {@code JSONObject}, to allow parameters to be retrieved by enum value.
|
||||
*
|
||||
* @author Gavin Lambert (uecasm)
|
||||
*/
|
||||
public class JsonWrapper {
|
||||
private final JSONObject json;
|
||||
|
||||
/**
|
||||
* Create a new JsonWrapper.
|
||||
*
|
||||
* @param json
|
||||
* The JSON text to parse.
|
||||
*/
|
||||
public JsonWrapper(final String json) {
|
||||
this.json = (JSONObject) JSONValue.parse(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a property as an Object, or null if the property does not exist.
|
||||
*
|
||||
* @param key
|
||||
* Property to get (should be one of the defined Constants).
|
||||
* @return Value of the property, or null if property does not exist.
|
||||
*/
|
||||
public Object getValue(final Object key) {
|
||||
return json.get(key.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a property as a String, or the default if the parameter does not exist.
|
||||
*
|
||||
* @param parameter
|
||||
* Parameter to get.
|
||||
* @param defaultValue
|
||||
* The value to return if the parameter does not exist.
|
||||
* @return Value of parameter, or the default if parameter does not exist.
|
||||
*/
|
||||
public String getString(final Object key, final String defaultValue) {
|
||||
final Object value = getValue(key);
|
||||
return (value == null) ? defaultValue : value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a property as an integer, or the default if the parameter does not exist.
|
||||
*
|
||||
* @param parameter
|
||||
* Parameter to get.
|
||||
* @param defaultValue
|
||||
* The value to return if the parameter does not exist.
|
||||
* @return Value of parameter, or the default if parameter does not exist.
|
||||
*/
|
||||
public int getInteger(final Object key, final int defaultValue) {
|
||||
final Object value = getValue(key);
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
return (value == null) ? defaultValue : Integer.parseInt(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a property as a boolean, or the default if the parameter does not exist.
|
||||
*
|
||||
* @param parameter
|
||||
* Parameter to get.
|
||||
* @param defaultValue
|
||||
* The value to return if the parameter does not exist.
|
||||
* @return Value of parameter, or the default if parameter does not exist.
|
||||
*/
|
||||
public boolean getBoolean(final Object key, final boolean defaultValue) {
|
||||
final Object value = getValue(key);
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
return (value == null) ? defaultValue : Boolean.parseBoolean(value.toString());
|
||||
}
|
||||
}
|
|
@ -25,15 +25,12 @@ package net.socialgamer.cah.data;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -87,20 +84,6 @@ import com.google.inject.Provider;
|
|||
public class Game {
|
||||
private static final Logger logger = Logger.getLogger(Game.class);
|
||||
|
||||
// TODO move these out to pyx.properties
|
||||
public static final int MIN_SCORE_LIMIT = 4;
|
||||
public static final int DEFAULT_SCORE_LIMIT = 8;
|
||||
public static final int MAX_SCORE_LIMIT = 69;
|
||||
public static final int MIN_PLAYER_LIMIT = 3;
|
||||
public static final int DEFAULT_PLAYER_LIMIT = 10;
|
||||
public static final int MAX_PLAYER_LIMIT = 20;
|
||||
public static final int MIN_SPECTATOR_LIMIT = 0;
|
||||
public static final int DEFAULT_SPECTATOR_LIMIT = 10;
|
||||
public static final int MAX_SPECTATOR_LIMIT = 20;
|
||||
public static final int MIN_BLANK_CARD_LIMIT = 0;
|
||||
public static final int DEFAULT_BLANK_CARD_LIMIT = 0;
|
||||
public static final int MAX_BLANK_CARD_LIMIT = 30;
|
||||
|
||||
private final int id;
|
||||
/**
|
||||
* All players present in the game.
|
||||
|
@ -121,11 +104,7 @@ public class Game {
|
|||
private final Object blackCardLock = new Object();
|
||||
private WhiteDeck whiteDeck;
|
||||
private GameState state;
|
||||
|
||||
// These are the default values new games get.
|
||||
private int blanksInDeck = DEFAULT_BLANK_CARD_LIMIT;
|
||||
private int playerLimit = DEFAULT_PLAYER_LIMIT;
|
||||
private int spectatorLimit = DEFAULT_SPECTATOR_LIMIT;
|
||||
private final GameOptions options = new GameOptions();
|
||||
|
||||
private int judgeIndex = 0;
|
||||
|
||||
|
@ -169,11 +148,6 @@ public class Game {
|
|||
private volatile ScheduledFuture<?> lastScheduledFuture;
|
||||
private final ScheduledThreadPoolExecutor globalTimer;
|
||||
|
||||
private int scoreGoal = DEFAULT_SCORE_LIMIT;
|
||||
private final Set<Integer> cardSetIds = new HashSet<Integer>();
|
||||
private String password = "";
|
||||
private boolean useIdleTimer = true;
|
||||
|
||||
/**
|
||||
* Create a new game.
|
||||
*
|
||||
|
@ -215,7 +189,7 @@ public class Game {
|
|||
public void addPlayer(final User user) throws TooManyPlayersException, IllegalStateException {
|
||||
logger.info(String.format("%s joined game %d.", user.toString(), id));
|
||||
synchronized (players) {
|
||||
if (playerLimit >= 3 && players.size() >= playerLimit) {
|
||||
if (options.playerLimit >= 3 && players.size() >= options.playerLimit) {
|
||||
throw new TooManyPlayersException();
|
||||
}
|
||||
// this will throw IllegalStateException if the user is already in a game, including this one.
|
||||
|
@ -351,7 +325,7 @@ public class Game {
|
|||
IllegalStateException {
|
||||
logger.info(String.format("%s joined game %d as a spectator.", user.toString(), id));
|
||||
synchronized (spectators) {
|
||||
if (spectators.size() >= spectatorLimit) {
|
||||
if (spectators.size() >= options.spectatorLimit) {
|
||||
throw new TooManySpectatorsException();
|
||||
}
|
||||
// this will throw IllegalStateException if the user is already in a game, including this one.
|
||||
|
@ -424,6 +398,29 @@ public class Game {
|
|||
connectedUsers.broadcastToList(playersToUsers(), type, masterData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends updated player information about a specific player to all players in the game.
|
||||
*
|
||||
* @param player
|
||||
* The player whose information has been changed.
|
||||
*/
|
||||
public void notifyPlayerInfoChange(final Player player) {
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(player));
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends updated game information to all players in the game.
|
||||
*/
|
||||
private void notifyGameOptionsChanged() {
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_OPTIONS_CHANGED.toString());
|
||||
data.put(LongPollResponse.GAME_INFO, getInfo(true));
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The game's current state.
|
||||
*/
|
||||
|
@ -456,27 +453,12 @@ public class Game {
|
|||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
return options.password;
|
||||
}
|
||||
|
||||
public void updateGameSettings(final int newScoreGoal, final int newMaxPlayers,
|
||||
final int newMaxSpectators, final Collection<Integer> newCardSetIds, final int newMaxBlanks,
|
||||
final String newPassword, final boolean newUseTimer) {
|
||||
this.scoreGoal = newScoreGoal;
|
||||
this.playerLimit = newMaxPlayers;
|
||||
this.spectatorLimit = newMaxSpectators;
|
||||
synchronized (this.cardSetIds) {
|
||||
this.cardSetIds.clear();
|
||||
this.cardSetIds.addAll(newCardSetIds);
|
||||
}
|
||||
this.blanksInDeck = newMaxBlanks;
|
||||
this.password = newPassword;
|
||||
this.useIdleTimer = newUseTimer;
|
||||
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_OPTIONS_CHANGED.toString());
|
||||
data.put(LongPollResponse.GAME_INFO, getInfo(true));
|
||||
broadcastToPlayers(MessageType.GAME_EVENT, data);
|
||||
public void updateGameSettings(final GameOptions newOptions) {
|
||||
this.options.update(newOptions);
|
||||
notifyGameOptionsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -510,20 +492,8 @@ public class Game {
|
|||
}
|
||||
info.put(GameInfo.HOST, host.getUser().getNickname());
|
||||
info.put(GameInfo.STATE, state.toString());
|
||||
final List<Integer> cardSetIdsCopy;
|
||||
synchronized (this.cardSetIds) {
|
||||
cardSetIdsCopy = new ArrayList<Integer>(this.cardSetIds);
|
||||
}
|
||||
info.put(GameInfo.CARD_SETS, cardSetIdsCopy);
|
||||
info.put(GameInfo.BLANKS_LIMIT, blanksInDeck);
|
||||
info.put(GameInfo.PLAYER_LIMIT, playerLimit);
|
||||
info.put(GameInfo.SPECTATOR_LIMIT, spectatorLimit);
|
||||
info.put(GameInfo.SCORE_LIMIT, scoreGoal);
|
||||
info.put(GameInfo.USE_TIMER, useIdleTimer);
|
||||
if (includePassword) {
|
||||
info.put(GameInfo.PASSWORD, password);
|
||||
}
|
||||
info.put(GameInfo.HAS_PASSWORD, password != null && !password.equals(""));
|
||||
info.put(GameInfo.GAME_OPTIONS, options.serialize(includePassword));
|
||||
info.put(GameInfo.HAS_PASSWORD, options.password != null && !options.password.equals(""));
|
||||
|
||||
final Player[] playersCopy = players.toArray(new Player[players.size()]);
|
||||
final List<String> playerNames = new ArrayList<String>(playersCopy.length);
|
||||
|
@ -632,7 +602,7 @@ public class Game {
|
|||
playerStatus = GamePlayerStatus.JUDGE;
|
||||
}
|
||||
// TODO win-by-x
|
||||
else if (player.getScore() >= scoreGoal) {
|
||||
else if (player.getScore() >= options.scoreGoal) {
|
||||
playerStatus = GamePlayerStatus.WINNER;
|
||||
} else {
|
||||
playerStatus = GamePlayerStatus.IDLE;
|
||||
|
@ -669,16 +639,17 @@ public class Game {
|
|||
if (started) {
|
||||
logger.info(String.format("Starting game %d with card sets %s, %d blanks, %d max players, " +
|
||||
"%d max spectators, %d score limit, players %s.",
|
||||
id, cardSetIds, blanksInDeck, playerLimit, spectatorLimit, scoreGoal, players));
|
||||
id, options.cardSetIds, options.blanksInDeck, options.playerLimit,
|
||||
options.spectatorLimit, options.scoreGoal, players));
|
||||
// do this stuff outside the players lock; they will lock players again later for much less
|
||||
// time, and not at the same time as trying to lock users, which has caused deadlocks
|
||||
synchronized (cardSetIds) {
|
||||
synchronized (options.cardSetIds) {
|
||||
Session session = null;
|
||||
try {
|
||||
session = sessionProvider.get();
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<CardSet> cardSets = session.createQuery("from CardSet where id in (:ids)")
|
||||
.setParameterList("ids", cardSetIds).list();
|
||||
.setParameterList("ids", options.cardSetIds).list();
|
||||
|
||||
blackDeck = new BlackDeck(cardSets);
|
||||
whiteDeck = new WhiteDeck(cardSets, blanksInDeck);
|
||||
|
@ -698,8 +669,8 @@ public class Game {
|
|||
}
|
||||
|
||||
public boolean hasBaseDeck() {
|
||||
synchronized (cardSetIds) {
|
||||
if (cardSetIds.isEmpty()) {
|
||||
synchronized (options.cardSetIds) {
|
||||
if (options.cardSetIds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -708,7 +679,7 @@ public class Game {
|
|||
session = sessionProvider.get();
|
||||
final Number baseDeckCount = (Number) session
|
||||
.createQuery("select count(*) from CardSet where id in (:ids) and base_deck = true")
|
||||
.setParameterList("ids", cardSetIds).uniqueResult();
|
||||
.setParameterList("ids", options.cardSetIds).uniqueResult();
|
||||
|
||||
return baseDeckCount.intValue() > 0;
|
||||
} catch (final Exception e) {
|
||||
|
@ -779,7 +750,7 @@ public class Game {
|
|||
}
|
||||
|
||||
// Perhaps figure out a better way to do this...
|
||||
final int playTimer = useIdleTimer ? PLAY_TIMEOUT_BASE
|
||||
final int playTimer = options.useIdleTimer ? PLAY_TIMEOUT_BASE
|
||||
+ (PLAY_TIMEOUT_PER_CARD * blackCard.getPick()) : Integer.MAX_VALUE;
|
||||
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
|
@ -971,7 +942,7 @@ public class Game {
|
|||
state = GameState.JUDGING;
|
||||
|
||||
// Perhaps figure out a better way to do this...
|
||||
final int judgeTimer = useIdleTimer ? JUDGE_TIMEOUT_BASE
|
||||
final int judgeTimer = options.useIdleTimer ? JUDGE_TIMEOUT_BASE
|
||||
+ (JUDGE_TIMEOUT_PER_CARD * playedCards.size() * blackCard.getPick()) : Integer.MAX_VALUE;
|
||||
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
|
@ -1364,19 +1335,6 @@ public class Game {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends updated player information about a specific player to all players in the game.
|
||||
*
|
||||
* @param player
|
||||
* The player whose information has been changed.
|
||||
*/
|
||||
public void notifyPlayerInfoChange(final Player player) {
|
||||
final HashMap<ReturnableData, Object> data = getEventMap();
|
||||
data.put(LongPollResponse.EVENT, LongPollEvent.GAME_PLAYER_INFO_CHANGE.toString());
|
||||
data.put(LongPollResponse.PLAYER_INFO, getPlayerInfo(player));
|
||||
broadcastToPlayers(MessageType.GAME_PLAYER_EVENT, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The judge has selected a card. The {@code cardId} passed in may be any white cards's ID for
|
||||
* black cards that have multiple selection, however only the first card in the set's ID will be
|
||||
|
@ -1426,7 +1384,7 @@ public class Game {
|
|||
synchronized (roundTimerLock) {
|
||||
final SafeTimerTask task;
|
||||
// TODO win-by-x option
|
||||
if (cardPlayer.getScore() >= scoreGoal) {
|
||||
if (cardPlayer.getScore() >= options.scoreGoal) {
|
||||
task = new SafeTimerTask() {
|
||||
@Override
|
||||
public void process() {
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/**
|
||||
* Copyright (c) 2012, Andy Janata
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
* provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of conditions
|
||||
* and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other materials provided
|
||||
* with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
||||
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package net.socialgamer.cah.data;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.socialgamer.cah.Constants.GameOptionData;
|
||||
import net.socialgamer.cah.JsonWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* Options for an individual game.
|
||||
*
|
||||
* @author Gavin Lambert (uecasm)
|
||||
*/
|
||||
public class GameOptions {
|
||||
// TODO move these out to pyx.properties
|
||||
public static final int MIN_SCORE_LIMIT = 4;
|
||||
public static final int DEFAULT_SCORE_LIMIT = 8;
|
||||
public static final int MAX_SCORE_LIMIT = 69;
|
||||
public static final int MIN_PLAYER_LIMIT = 3;
|
||||
public static final int DEFAULT_PLAYER_LIMIT = 10;
|
||||
public static final int MAX_PLAYER_LIMIT = 20;
|
||||
public static final int MIN_SPECTATOR_LIMIT = 0;
|
||||
public static final int DEFAULT_SPECTATOR_LIMIT = 10;
|
||||
public static final int MAX_SPECTATOR_LIMIT = 20;
|
||||
public static final int MIN_BLANK_CARD_LIMIT = 0;
|
||||
public static final int DEFAULT_BLANK_CARD_LIMIT = 0;
|
||||
public static final int MAX_BLANK_CARD_LIMIT = 30;
|
||||
|
||||
// These are the default values new games get.
|
||||
public int blanksInDeck = DEFAULT_BLANK_CARD_LIMIT;
|
||||
public int playerLimit = DEFAULT_PLAYER_LIMIT;
|
||||
public int spectatorLimit = DEFAULT_SPECTATOR_LIMIT;
|
||||
public int scoreGoal = DEFAULT_SCORE_LIMIT;
|
||||
public final Set<Integer> cardSetIds = new HashSet<Integer>();
|
||||
public String password = "";
|
||||
public boolean useIdleTimer = true;
|
||||
|
||||
/**
|
||||
* Update the options in-place (so that the Game doesn't need more locks).
|
||||
*
|
||||
* @param newOptions
|
||||
* The new options to use.
|
||||
*/
|
||||
public void update(final GameOptions newOptions) {
|
||||
this.scoreGoal = newOptions.scoreGoal;
|
||||
this.playerLimit = newOptions.playerLimit;
|
||||
this.spectatorLimit = newOptions.spectatorLimit;
|
||||
synchronized (this.cardSetIds) {
|
||||
this.cardSetIds.clear();
|
||||
this.cardSetIds.addAll(newOptions.cardSetIds);
|
||||
}
|
||||
this.blanksInDeck = newOptions.blanksInDeck;
|
||||
this.password = newOptions.password;
|
||||
this.useIdleTimer = newOptions.useIdleTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the options in a form that can be sent to clients.
|
||||
*
|
||||
* @param includePassword
|
||||
* Include the actual password with the information. This should only be
|
||||
* sent to people in the game.
|
||||
* @return This game's general information: ID, host, state, player list, etc.
|
||||
*/
|
||||
public Map<GameOptionData, Object> serialize(final boolean includePassword) {
|
||||
final Map<GameOptionData, Object> info = new HashMap<GameOptionData, Object>();
|
||||
|
||||
info.put(GameOptionData.CARD_SETS, cardSetIds);
|
||||
info.put(GameOptionData.BLANKS_LIMIT, blanksInDeck);
|
||||
info.put(GameOptionData.PLAYER_LIMIT, playerLimit);
|
||||
info.put(GameOptionData.SPECTATOR_LIMIT, spectatorLimit);
|
||||
info.put(GameOptionData.SCORE_LIMIT, scoreGoal);
|
||||
info.put(GameOptionData.USE_TIMER, useIdleTimer);
|
||||
if (includePassword) {
|
||||
info.put(GameOptionData.PASSWORD, password);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static GameOptions deserialize(final String text) {
|
||||
final GameOptions options = new GameOptions();
|
||||
|
||||
if (text == null || text.isEmpty()) {
|
||||
return options;
|
||||
}
|
||||
|
||||
final JsonWrapper json = new JsonWrapper(text);
|
||||
|
||||
final String[] cardSetsParsed = json.getString(GameOptionData.CARD_SETS, "").split(",");
|
||||
for (final String cardSetId : cardSetsParsed) {
|
||||
if (!cardSetId.isEmpty()) {
|
||||
options.cardSetIds.add(Integer.parseInt(cardSetId));
|
||||
}
|
||||
}
|
||||
|
||||
options.blanksInDeck = Math.max(MIN_BLANK_CARD_LIMIT, Math.min(MAX_BLANK_CARD_LIMIT,
|
||||
json.getInteger(GameOptionData.BLANKS_LIMIT, options.blanksInDeck)));
|
||||
options.playerLimit = Math.max(MIN_PLAYER_LIMIT, Math.min(MAX_PLAYER_LIMIT,
|
||||
json.getInteger(GameOptionData.PLAYER_LIMIT, options.playerLimit)));
|
||||
options.spectatorLimit = Math.max(MIN_SPECTATOR_LIMIT, Math.min(MAX_SPECTATOR_LIMIT,
|
||||
json.getInteger(GameOptionData.SPECTATOR_LIMIT, options.spectatorLimit)));
|
||||
options.scoreGoal = Math.max(MIN_SCORE_LIMIT, Math.min(MAX_SCORE_LIMIT,
|
||||
json.getInteger(GameOptionData.SCORE_LIMIT, options.scoreGoal)));
|
||||
options.useIdleTimer = json.getBoolean(GameOptionData.USE_TIMER, options.useIdleTimer);
|
||||
options.password = json.getString(GameOptionData.PASSWORD, options.password);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
package net.socialgamer.cah.handlers;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
|
@ -15,6 +13,7 @@ import net.socialgamer.cah.Constants.ReturnableData;
|
|||
import net.socialgamer.cah.RequestWrapper;
|
||||
import net.socialgamer.cah.data.Game;
|
||||
import net.socialgamer.cah.data.GameManager;
|
||||
import net.socialgamer.cah.data.GameOptions;
|
||||
import net.socialgamer.cah.data.User;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
@ -40,49 +39,17 @@ public class ChangeGameOptionHandler extends GameWithPlayerHandler {
|
|||
return error(ErrorCode.ALREADY_STARTED);
|
||||
} else {
|
||||
try {
|
||||
final int scoreLimit = Integer.parseInt(request.getParameter(AjaxRequest.SCORE_LIMIT));
|
||||
final int playerLimit = Integer.parseInt(request.getParameter(AjaxRequest.PLAYER_LIMIT));
|
||||
final int spectatorLimit = Integer.parseInt(request
|
||||
.getParameter(AjaxRequest.SPECTATOR_LIMIT));
|
||||
|
||||
final String[] cardSetsParsed = request.getParameter(AjaxRequest.CARD_SETS).split(",");
|
||||
final Set<Integer> cardSetIds = new HashSet<Integer>();
|
||||
for (final String cardSetId : cardSetsParsed) {
|
||||
if (!cardSetId.isEmpty()) {
|
||||
cardSetIds.add(Integer.parseInt(cardSetId));
|
||||
}
|
||||
}
|
||||
|
||||
final int blanksLimit = Integer.parseInt(request.getParameter(AjaxRequest.BLANKS_LIMIT));
|
||||
final String value = request.getParameter(AjaxRequest.GAME_OPTIONS);
|
||||
final GameOptions options = GameOptions.deserialize(value);
|
||||
final String oldPassword = game.getPassword();
|
||||
String password = request.getParameter(AjaxRequest.PASSWORD);
|
||||
if (password == null) {
|
||||
password = "";
|
||||
}
|
||||
// We're not directly assigning this with Boolean.valueOf() because we want to default to
|
||||
// true if it isn't specified, though that should never happen.
|
||||
boolean useTimer = true;
|
||||
final String useTimerString = request.getParameter(AjaxRequest.USE_TIMER);
|
||||
if (null != useTimerString && !"".equals(useTimerString)) {
|
||||
useTimer = Boolean.valueOf(useTimerString);
|
||||
}
|
||||
// make sure the new settings are in the valid range
|
||||
if (scoreLimit < Game.MIN_SCORE_LIMIT || scoreLimit > Game.MAX_SCORE_LIMIT
|
||||
|| playerLimit < Game.MIN_PLAYER_LIMIT || playerLimit > Game.MAX_PLAYER_LIMIT
|
||||
|| spectatorLimit < Game.MIN_SPECTATOR_LIMIT
|
||||
|| spectatorLimit > Game.MAX_SPECTATOR_LIMIT
|
||||
|| blanksLimit < Game.MIN_BLANK_CARD_LIMIT || blanksLimit > Game.MAX_BLANK_CARD_LIMIT) {
|
||||
return error(ErrorCode.BAD_REQUEST);
|
||||
}
|
||||
game.updateGameSettings(scoreLimit, playerLimit, spectatorLimit, cardSetIds, blanksLimit,
|
||||
password, useTimer);
|
||||
game.updateGameSettings(options);
|
||||
|
||||
// only broadcast an update if the password state has changed, because it needs to change
|
||||
// the text on the join button and the sort order
|
||||
if (!password.equals(oldPassword)) {
|
||||
if (!game.getPassword().equals(oldPassword)) {
|
||||
gameManager.broadcastGameListRefresh();
|
||||
}
|
||||
} catch (final NumberFormatException nfe) {
|
||||
} catch (final Exception e) {
|
||||
return error(ErrorCode.BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue