Communication with HallServer is now less bandwith intensive, as only updates are sent on the line.

This commit is contained in:
marcins78@gmail.com
2013-01-01 04:01:53 +00:00
parent 7199ad6381
commit 11b5933d70
12 changed files with 629 additions and 99 deletions

View File

@@ -40,6 +40,11 @@
<artifactId>commons-collections</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>

View File

@@ -25,7 +25,7 @@ public class CachedPlayerDAO implements PlayerDAO, Cached {
@Override
public int getItemCount() {
return _playerById.size()+_playerByName.size();
return _playerById.size() + _playerByName.size();
}
@Override
@@ -33,8 +33,10 @@ public class CachedPlayerDAO implements PlayerDAO, Cached {
Player player = (Player) _playerById.get(id);
if (player == null) {
player = _delegate.getPlayer(id);
_playerById.put(id, player);
_playerByName.put(player.getName(), player);
if (player != null) {
_playerById.put(id, player);
_playerByName.put(player.getName(), player);
}
}
return player;
}
@@ -44,8 +46,10 @@ public class CachedPlayerDAO implements PlayerDAO, Cached {
Player player = (Player) _playerByName.get(playerName);
if (player == null) {
player = _delegate.getPlayer(playerName);
_playerById.put(player.getId(), player);
_playerByName.put(player.getName(), player);
if (player != null) {
_playerById.put(player.getId(), player);
_playerByName.put(player.getName(), player);
}
}
return player;
}

View File

@@ -33,7 +33,6 @@ public class LotroGameMediator {
private boolean _allowSpectators;
private boolean _cancellable;
// private final int _maxSecondsForGamePerPlayer = 60 * 40; // 40 minutes
private final int _channelInactivityTimeoutPeriod = 1000 * 60 * 5; // 5 minutes
private final int _playerDecisionTimeoutPeriod = 1000 * 60 * 10; // 10 minutes
private ReentrantReadWriteLock _lock = new ReentrantReadWriteLock(true);
@@ -97,8 +96,8 @@ public class LotroGameMediator {
return _lotroGame.getWinnerPlayerId();
}
public Set<String> getPlayersPlaying() {
return Collections.unmodifiableSet(_playersPlaying);
public List<String> getPlayersPlaying() {
return new LinkedList<String>(_playersPlaying);
}
public String getGameStatus() {
@@ -236,7 +235,7 @@ public class LotroGameMediator {
// Channel is stale (user no longer connected to game, to save memory, we remove the channel
// User can always reconnect and establish a new channel
GatheringParticipantCommunicationChannel channel = playerChannels.getValue();
if (currentTime > channel.getLastConsumed().getTime() + _channelInactivityTimeoutPeriod) {
if (currentTime > channel.getLastConsumed().getTime() + _playerDecisionTimeoutPeriod) {
_lotroGame.removeGameStateListener(channel);
_communicationChannels.remove(playerId);
}

View File

@@ -38,8 +38,8 @@ public class AwaitingTable {
return _players.containsKey(playerId);
}
public Set<String> getPlayerNames() {
return Collections.unmodifiableSet(_players.keySet());
public List<String> getPlayerNames() {
return new LinkedList<String>(_players.keySet());
}
public Set<LotroGameParticipant> getPlayers() {

View File

@@ -0,0 +1,19 @@
package com.gempukku.lotro.hall;
import java.util.Map;
public interface HallChannelVisitor {
public void channelNumber(int channelNumber);
public void serverTime(String serverTime);
public void runningPlayerGame(String gameId);
public void playerBusy(boolean busy);
public void addTournamentQueue(String queueId, Map<String, String> props);
public void updateTournamentQueue(String queueId, Map<String, String> props);
public void removeTournamentQueue(String queueId);
public void addTable(String tableId, Map<String, String> props);
public void updateTable(String tableId, Map<String, String> props);
public void removeTable(String tableId);
}

View File

@@ -0,0 +1,119 @@
package com.gempukku.lotro.hall;
import com.gempukku.lotro.game.Player;
import org.apache.commons.lang.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HallCommunicationChannel {
private int _channelNumber;
private long _lastConsumed;
private Map<String, Map<String, String>> _tournamentQueuePropsOnClient = new HashMap<String, Map<String, String>>();
private Map<String, Map<String, String>> _tablePropsOnClient = new HashMap<String, Map<String, String>>();
public HallCommunicationChannel(int channelNumber) {
_channelNumber = channelNumber;
}
public int getChannelNumber() {
return _channelNumber;
}
public long getLastConsumed() {
return _lastConsumed;
}
public synchronized void processCommunicationChannel(HallServer hallServer, Player player, final HallChannelVisitor hallChannelVisitor) {
hallChannelVisitor.channelNumber(_channelNumber);
final Map<String, Map<String, String>> tournamentsOnServer = new HashMap<String, Map<String, String>>();
final Map<String, Map<String, String>> tablesOnServer = new HashMap<String, Map<String, String>>();
hallServer.processHall(player,
new HallInfoVisitor() {
@Override
public void serverTime(String time) {
hallChannelVisitor.serverTime(time);
}
@Override
public void playerBusy(boolean busy) {
hallChannelVisitor.playerBusy(busy);
}
@Override
public void visitTable(String tableId, String gameId, boolean watchable, TableStatus status, String statusDescription, String formatName, String tournamentName, List<String> playerIds, String winner) {
Map<String, String> props = new HashMap<String, String>();
props.put("gameId", gameId);
props.put("watchable", String.valueOf(watchable));
props.put("status", String.valueOf(status));
props.put("statusDescription", statusDescription);
props.put("format", formatName);
props.put("tournament", tournamentName);
props.put("players", StringUtils.join(playerIds, ","));
if (winner != null)
props.put("winner", winner);
tablesOnServer.put(tableId, props);
}
@Override
public void visitTournamentQueue(String tournamentQueueKey, int cost, String collectionName, String formatName, String tournamentQueueName, int playerCount, boolean playerSignedUp) {
Map<String, String> props = new HashMap<String, String>();
props.put("cost", String.valueOf(cost));
props.put("collection", collectionName);
props.put("format", formatName);
props.put("queue", tournamentQueueName);
props.put("playerCount", String.valueOf(playerCount));
props.put("signedUp", String.valueOf(playerSignedUp));
tournamentsOnServer.put(tournamentQueueKey, props);
}
@Override
public void runningPlayerGame(String gameId) {
hallChannelVisitor.runningPlayerGame(gameId);
}
});
for (Map.Entry<String, Map<String, String>> tournamentQueueOnClient : _tournamentQueuePropsOnClient.entrySet()) {
String tournamentQueueId = tournamentQueueOnClient.getKey();
Map<String, String> tournamentProps = tournamentQueueOnClient.getValue();
Map<String, String> tournamentLatestProps = tournamentsOnServer.get(tournamentQueueId);
if (tournamentLatestProps != null) {
if (!tournamentProps.equals(tournamentLatestProps))
hallChannelVisitor.updateTournamentQueue(tournamentQueueId, tournamentLatestProps);
} else {
hallChannelVisitor.removeTournamentQueue(tournamentQueueId);
}
}
for (Map.Entry<String, Map<String, String>> tournamentQueueOnServer : tournamentsOnServer.entrySet())
if (!_tournamentQueuePropsOnClient.containsKey(tournamentQueueOnServer.getKey()))
hallChannelVisitor.addTournamentQueue(tournamentQueueOnServer.getKey(), tournamentQueueOnServer.getValue());
_tournamentQueuePropsOnClient = tournamentsOnServer;
for (Map.Entry<String, Map<String, String>> tableOnClient : _tablePropsOnClient.entrySet()) {
String tableId = tableOnClient.getKey();
Map<String, String> tableProps = tableOnClient.getValue();
Map<String, String> tableLatestProps = tablesOnServer.get(tableId);
if (tableLatestProps != null) {
if (!tableProps.equals(tableLatestProps))
hallChannelVisitor.updateTable(tableId, tableLatestProps);
} else {
hallChannelVisitor.removeTable(tableId);
}
}
for (Map.Entry<String, Map<String, String>> tableOnServer : tablesOnServer.entrySet())
if (!_tablePropsOnClient.containsKey(tableOnServer.getKey()))
hallChannelVisitor.addTable(tableOnServer.getKey(), tableOnServer.getValue());
_tablePropsOnClient = tablesOnServer;
_lastConsumed = System.currentTimeMillis();
}
}

View File

@@ -1,17 +1,19 @@
package com.gempukku.lotro.hall;
import java.util.Set;
import java.util.List;
public interface HallInfoVisitor {
public enum TableStatus {
WAITING, PLAYING, FINISHED
}
public void serverTime(String time);
public void playerIsWaiting(boolean waiting);
public void playerBusy(boolean busy);
public void visitTable(String tableId, String gameId, boolean watchable, String tableStatus, String formatName, String tournamentName, Set<String> playerIds, String winner);
public void runningPlayerDraft(String draftId);
public void runningPlayerGame(String gameId);
public void visitTable(String tableId, String gameId, boolean watchable, TableStatus status, String statusDescription, String formatName, String tournamentName, List<String> playerIds, String winner);
public void visitTournamentQueue(String tournamentQueueKey, int cost, String collectionName, String formatName, String tournamentQueueName, int playerCount, boolean playerSignedUp);
public void runningPlayerGame(String gameId);
}

View File

@@ -16,6 +16,8 @@ import com.gempukku.lotro.logic.timing.GameResultListener;
import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.tournament.*;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -44,7 +46,8 @@ public class HallServer extends AbstractServer {
private Map<String, AwaitingTable> _awaitingTables = new LinkedHashMap<String, AwaitingTable>();
private Map<String, RunningTable> _runningTables = new LinkedHashMap<String, RunningTable>();
private Map<Player, Long> _lastVisitedPlayers = new HashMap<Player, Long>();
private Map<Player, HallCommunicationChannel> _playerChannelCommunication = new HashMap<Player, HallCommunicationChannel>();
private int _nextChannelNumber = 0;
private Map<String, Tournament> _runningTournaments = new LinkedHashMap<String, Tournament>();
private Map<String, TournamentQueue> _tournamentQueues = new HashMap<String, TournamentQueue>();
@@ -259,23 +262,51 @@ public class HallServer extends AbstractServer {
}
}
public void processHall(Player player, HallInfoVisitor visitor) {
public void signupUserForHall(Player player, HallChannelVisitor hallChannelVisitor) {
_hallDataAccessLock.readLock().lock();
try {
HallCommunicationChannel channel = new HallCommunicationChannel(_nextChannelNumber++);
channel.processCommunicationChannel(this, player, hallChannelVisitor);
_playerChannelCommunication.put(player, channel);
} finally {
_hallDataAccessLock.readLock().unlock();
}
}
public void processHall(Player player, int channelNumber, HallChannelVisitor hallChannelVisitor) {
_hallDataAccessLock.readLock().lock();
try {
HallCommunicationChannel communicationChannel = _playerChannelCommunication.get(player);
if (communicationChannel != null) {
if (communicationChannel.getChannelNumber() == channelNumber) {
communicationChannel.processCommunicationChannel(this, player, hallChannelVisitor);
} else {
throw new WebApplicationException(Response.Status.CONFLICT);
}
} else {
throw new WebApplicationException(Response.Status.GONE);
}
} finally {
_hallDataAccessLock.readLock().unlock();
}
}
protected void processHall(Player player, HallInfoVisitor visitor) {
_hallDataAccessLock.readLock().lock();
try {
_lastVisitedPlayers.put(player, System.currentTimeMillis());
visitor.playerIsWaiting(isPlayerBusy(player.getName()));
visitor.serverTime(DateUtils.getStringDateWithHour());
visitor.playerBusy(isPlayerBusy(player.getName()));
// First waiting
for (Map.Entry<String, AwaitingTable> tableInformation : _awaitingTables.entrySet()) {
final AwaitingTable table = tableInformation.getValue();
Set<String> players;
List<String> players;
if (table.getLeague() != null)
players = Collections.<String>emptySet();
players = Collections.<String>emptyList();
else
players = table.getPlayerNames();
visitor.visitTable(tableInformation.getKey(), null, false, "Waiting", table.getLotroFormat().getName(), getTournamentName(table), players, null);
visitor.visitTable(tableInformation.getKey(), null, false, HallInfoVisitor.TableStatus.WAITING, "Waiting", table.getLotroFormat().getName(), getTournamentName(table), players, null);
}
// Then non-finished
@@ -286,7 +317,7 @@ public class HallServer extends AbstractServer {
LotroGameMediator lotroGameMediator = _lotroServer.getGameById(runningTable.getGameId());
if (lotroGameMediator != null) {
if (!lotroGameMediator.isFinished())
visitor.visitTable(runningGame.getKey(), runningTable.getGameId(), player.getType().contains("a") || lotroGameMediator.isAllowSpectators(), lotroGameMediator.getGameStatus(), runningTable.getFormatName(), runningTable.getTournamentName(), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner());
visitor.visitTable(runningGame.getKey(), runningTable.getGameId(), player.getType().contains("a") || lotroGameMediator.isAllowSpectators(), HallInfoVisitor.TableStatus.PLAYING, lotroGameMediator.getGameStatus(), runningTable.getFormatName(), runningTable.getTournamentName(), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner());
else
finishedTables.put(runningGame.getKey(), runningTable);
}
@@ -297,7 +328,7 @@ public class HallServer extends AbstractServer {
final RunningTable runningTable = nonPlayingGame.getValue();
LotroGameMediator lotroGameMediator = _lotroServer.getGameById(runningTable.getGameId());
if (lotroGameMediator != null)
visitor.visitTable(nonPlayingGame.getKey(), runningTable.getGameId(), false, lotroGameMediator.getGameStatus(), runningTable.getFormatName(), runningTable.getTournamentName(), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner());
visitor.visitTable(nonPlayingGame.getKey(), runningTable.getGameId(), false, HallInfoVisitor.TableStatus.FINISHED, lotroGameMediator.getGameStatus(), runningTable.getFormatName(), runningTable.getTournamentName(), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner());
}
for (Map.Entry<String, TournamentQueue> tournamentQueueEntry : _tournamentQueues.entrySet()) {
@@ -486,11 +517,11 @@ public class HallServer extends AbstractServer {
}
long currentTime = System.currentTimeMillis();
Map<Player, Long> visitCopy = new LinkedHashMap<Player, Long>(_lastVisitedPlayers);
for (Map.Entry<Player, Long> lastVisitedPlayer : visitCopy.entrySet()) {
if (currentTime > lastVisitedPlayer.getValue() + _playerInactivityPeriod) {
Map<Player, HallCommunicationChannel> visitCopy = new LinkedHashMap<Player, HallCommunicationChannel>(_playerChannelCommunication);
for (Map.Entry<Player, HallCommunicationChannel> lastVisitedPlayer : visitCopy.entrySet()) {
if (currentTime > lastVisitedPlayer.getValue().getLastConsumed() + _playerInactivityPeriod) {
Player player = lastVisitedPlayer.getKey();
_lastVisitedPlayers.remove(player);
_playerChannelCommunication.remove(player);
leaveAwaitingTables(player);
leaveQueues(player);
}

View File

@@ -5,8 +5,8 @@ import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.LotroFormat;
import com.gempukku.lotro.game.Player;
import com.gempukku.lotro.game.formats.LotroFormatLibrary;
import com.gempukku.lotro.hall.HallChannelVisitor;
import com.gempukku.lotro.hall.HallException;
import com.gempukku.lotro.hall.HallInfoVisitor;
import com.gempukku.lotro.hall.HallServer;
import com.gempukku.lotro.league.LeagueSerieData;
import com.gempukku.lotro.league.LeagueService;
@@ -122,6 +122,36 @@ public class HallResource extends AbstractResource {
return result.toString();
}
@POST
@Produces(MediaType.APPLICATION_XML)
@Path("/update")
public Document updateHall(
@FormParam("participantId") String participantId,
@FormParam("channelNumber") int channelNumber,
@Context HttpServletRequest request,
@Context HttpServletResponse response) throws ParserConfigurationException, Exception {
Player resourceOwner = getResourceOwnerSafely(request, participantId);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element hall = doc.createElement("hall");
hall.setAttribute("currency", String.valueOf(_collectionManager.getPlayerCollection(resourceOwner, "permanent").getCurrency()));
String motd = _hallServer.getMOTD();
if (motd != null)
hall.setAttribute("motd", motd);
_hallServer.processHall(resourceOwner, channelNumber, new SerializeHallInfoVisitor(doc, hall));
doc.appendChild(hall);
processDeliveryServiceNotification(request, response);
return doc;
}
@GET
@Produces(MediaType.APPLICATION_XML)
public Document getHall(
@@ -141,7 +171,7 @@ public class HallResource extends AbstractResource {
if (motd != null)
hall.setAttribute("motd", motd);
_hallServer.processHall(resourceOwner, new SerializeHallInfoVisitor(doc, hall));
_hallServer.signupUserForHall(resourceOwner, new SerializeHallInfoVisitor(doc, hall));
for (Map.Entry<String, LotroFormat> format : _formatLibrary.getHallFormats().entrySet()) {
Element formatElem = doc.createElement("format");
formatElem.setAttribute("type", format.getKey());
@@ -248,7 +278,7 @@ public class HallResource extends AbstractResource {
return doc;
}
private class SerializeHallInfoVisitor implements HallInfoVisitor {
private class SerializeHallInfoVisitor implements HallChannelVisitor {
private Document _doc;
private Element _hall;
@@ -258,47 +288,69 @@ public class HallResource extends AbstractResource {
}
@Override
public void serverTime(String time) {
_hall.setAttribute("serverTime", time);
public void channelNumber(int channelNumber) {
_hall.setAttribute("channelNumber", String.valueOf(channelNumber));
}
@Override
public void playerIsWaiting(boolean waiting) {
_hall.setAttribute("waiting", String.valueOf(waiting));
public void serverTime(String serverTime) {
_hall.setAttribute("serverTime", serverTime);
}
@Override
public void visitTable(String tableId, String gameId, boolean watchable, String tableStatus, String formatName, String tournamentName, Set<String> playerIds, String winner) {
public void addTournamentQueue(String queueId, Map<String, String> props) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "add");
queue.setAttribute("id", queueId);
for (Map.Entry<String, String> attribute : props.entrySet())
queue.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(queue);
}
@Override
public void updateTournamentQueue(String queueId, Map<String, String> props) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "update");
queue.setAttribute("id", queueId);
for (Map.Entry<String, String> attribute : props.entrySet())
queue.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(queue);
}
@Override
public void removeTournamentQueue(String queueId) {
Element queue = _doc.createElement("queue");
queue.setAttribute("action", "remove");
queue.setAttribute("id", queueId);
_hall.appendChild(queue);
}
@Override
public void addTable(String tableId, Map<String, String> props) {
Element table = _doc.createElement("table");
table.setAttribute("action", "add");
table.setAttribute("id", tableId);
if (gameId != null)
table.setAttribute("gameId", gameId);
table.setAttribute("status", tableStatus);
table.setAttribute("watchable", String.valueOf(watchable));
table.setAttribute("format", formatName);
table.setAttribute("tournament", tournamentName);
table.setAttribute("players", mergeStrings(playerIds));
if (winner != null)
table.setAttribute("winner", winner);
for (Map.Entry<String, String> attribute : props.entrySet())
table.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(table);
}
@Override
public void runningPlayerDraft(String draftId) {
//To change body of implemented methods use File | Settings | File Templates.
public void updateTable(String tableId, Map<String, String> props) {
Element table = _doc.createElement("table");
table.setAttribute("action", "update");
table.setAttribute("id", tableId);
for (Map.Entry<String, String> attribute : props.entrySet())
table.setAttribute(attribute.getKey(), attribute.getValue());
_hall.appendChild(table);
}
@Override
public void visitTournamentQueue(String tournamentQueueKey, int cost, String collectionName, String formatName,
String tournamentQueueName, int playerCount, boolean playerSignedUp) {
Element tournamentQueue = _doc.createElement("tournamentQueue");
tournamentQueue.setAttribute("id", tournamentQueueKey);
tournamentQueue.setAttribute("cost", String.valueOf(cost));
tournamentQueue.setAttribute("tournament", tournamentQueueName);
tournamentQueue.setAttribute("players", String.valueOf(playerCount));
tournamentQueue.setAttribute("format", formatName);
tournamentQueue.setAttribute("joined", String.valueOf(playerSignedUp));
_hall.appendChild(tournamentQueue);
public void removeTable(String tableId) {
Element table = _doc.createElement("table");
table.setAttribute("action", "remove");
table.setAttribute("id", tableId);
_hall.appendChild(table);
}
@Override
@@ -307,6 +359,11 @@ public class HallResource extends AbstractResource {
runningGame.setAttribute("id", gameId);
_hall.appendChild(runningGame);
}
@Override
public void playerBusy(boolean busy) {
_hall.setAttribute("busy", String.valueOf(busy));
}
}
private String mergeStrings(Set<String> strings) {

View File

@@ -14,6 +14,12 @@
background-color: #000000;
}
.eventHeader {
font-size: 120%;
font-weight: bolder;
background-color: #666666;
}
.leagueName, #stats .period, .tournamentName, .playerStatHeader {
font-size: 200%;
font-weight: bolder;

View File

@@ -499,6 +499,19 @@ var GempLotrCommunication = Class.extend({
dataType:"xml"
});
},
updateHall:function (callback, channelNumber, errorMap) {
$.ajax({
type:"POST",
url:this.url + "/hall/update",
cache:false,
data:{
channelNumber:channelNumber,
participantId:getUrlParam("participantId") },
success:this.deliveryCheck(callback),
error:this.errorCheck(errorMap),
dataType:"xml"
});
},
joinQueue:function (queueId, deckName, callback, errorMap) {
$.ajax({
type:"POST",

View File

@@ -11,9 +11,8 @@ var GempLotrHallUI = Class.extend({
tablesDiv:null,
buttonsDiv:null,
showEmptyTableQueues:null,
pocketDiv:null,
hallChannelId: null,
init:function (div, url, chat) {
this.div = div;
@@ -40,6 +39,12 @@ var GempLotrHallUI = Class.extend({
this.tablesDiv = $("<div></div>");
this.tablesDiv.css({overflow:"auto", left:"0px", top:"0px", width:width + "px", height:(height - 30) + "px"});
this.addQueuesTable();
this.addWaitingTablesTable();
this.addPlayingTablesTable();
this.addFinishedTablesTable();
this.div.append(this.tablesDiv);
this.buttonsDiv = $("<div></div>");
@@ -90,8 +95,6 @@ var GempLotrHallUI = Class.extend({
this.decksSelect = $("<select style='width: 220px'></select>");
this.decksSelect.hide();
var showQueuesCheck = $("<input type='checkbox' id='showEmptyTableQueuesCheck' value='showQueues'/><label for='showEmptyTableQueuesCheck'>Show empty queues</label>");
this.buttonsDiv.append(this.supportedFormatsSelect);
this.buttonsDiv.append(this.decksSelect);
this.buttonsDiv.append(this.createTableButton);
@@ -106,25 +109,163 @@ var GempLotrHallUI = Class.extend({
this.buttonsDiv.append(this.leaveTableButton);
this.buttonsDiv.append(" | ");
this.buttonsDiv.append(showQueuesCheck);
this.div.append(this.buttonsDiv);
this.updateHall();
this.getHall();
this.updateDecks();
},
addQueuesTable: function() {
var header = $("<div class='eventHeader'></div>");
var content = $("<div></div>");
var toggleContent = $("<div>Toggle tournament queues</div>").button({
icons: {
primary: "ui-icon-circlesmall-minus"
},
text: false
});
toggleContent.css({width: "13px", height: "15px"});
toggleContent.click(
function() {
if (toggleContent.button("option", "icons")["primary"] == "ui-icon-circlesmall-minus")
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-plus"});
else
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-minus"});
content.toggle("blind", {}, 200);
});
header.append(toggleContent);
header.append(" Tournament queues");
var table = $("<table class='tables queues'></table>");
table.append("<tr><th width='20%'>Format</th><th width='20%'>Collection</th><th width='20%'>Queue name</th><th width='10%'>Players</th><th width='10%'>Cost</th><th width='20%'>Actions</th></tr>");
content.append(table);
this.tablesDiv.append(header);
this.tablesDiv.append(content);
},
addWaitingTablesTable: function() {
var header = $("<div class='eventHeader'></div>");
var content = $("<div></div>");
var toggleContent = $("<div>Toggle waiting tables</div>").button({
icons: {
primary: "ui-icon-circlesmall-minus"
},
text: false
});
toggleContent.css({width: "13px", height: "15px"});
toggleContent.click(
function() {
if (toggleContent.button("option", "icons")["primary"] == "ui-icon-circlesmall-minus")
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-plus"});
else
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-minus"});
content.toggle("blind", {}, 200);
});
header.append(toggleContent);
header.append(" Waiting tables");
var table = $("<table class='tables waitingTables'></table>");
table.append("<tr><th width='20%'>Format</th><th width='30%'>Tournament</th><th width='10%'>Status</th><th width='20%'>Players</th><th width='20%'>Actions</th></tr>");
content.append(table);
this.tablesDiv.append(header);
this.tablesDiv.append(content);
},
addPlayingTablesTable: function() {
var header = $("<div class='eventHeader'></div>");
var content = $("<div></div>");
var toggleContent = $("<div>Toggle playing tables</div>").button({
icons: {
primary: "ui-icon-circlesmall-minus"
},
text: false
});
toggleContent.css({width: "13px", height: "15px"});
toggleContent.click(
function() {
if (toggleContent.button("option", "icons")["primary"] == "ui-icon-circlesmall-minus")
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-plus"});
else
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-minus"});
content.toggle("blind", {}, 200);
});
header.append(toggleContent);
header.append(" Playing tables");
var table = $("<table class='tables playingTables'></table>");
table.append("<tr><th width='20%'>Format</th><th width='30%'>Tournament</th><th width='10%'>Status</th><th width='20%'>Players</th><th width='20%'>Actions</th></tr>");
content.append(table);
this.tablesDiv.append(header);
this.tablesDiv.append(content);
},
addFinishedTablesTable: function() {
var header = $("<div class='eventHeader'></div>");
var content = $("<div></div>");
var toggleContent = $("<div>Toggle finished tables</div>").button({
icons: {
primary: "ui-icon-circlesmall-minus"
},
text: false
});
toggleContent.css({width: "13px", height: "15px"});
toggleContent.click(
function() {
if (toggleContent.button("option", "icons")["primary"] == "ui-icon-circlesmall-minus")
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-plus"});
else
toggleContent.button("option", "icons", {primary: "ui-icon-circlesmall-minus"});
content.toggle("blind", {}, 200);
});
header.append(toggleContent);
header.append(" Finished tables");
var table = $("<table class='tables finishedTables'></table>");
table.append("<tr><th width='20%'>Format</th><th width='30%'>Tournament</th><th width='10%'>Status</th><th width='20%'>Players</th><th width='20%'>Winner</th></tr>");
content.append(table);
this.tablesDiv.append(header);
this.tablesDiv.append(content);
},
hallResized:function (width, height) {
this.tablesDiv.css({overflow:"auto", left:"0px", top:"0px", width:width + "px", height:(height - 30) + "px"});
this.buttonsDiv.css({left:"0px", top:(height - 30) + "px", width:width + "px", height:29 + "px", align:"right", backgroundColor:"#000000", "border-top-width":"1px", "border-top-color":"#ffffff", "border-top-style":"solid"});
},
getHall: function() {
var that = this;
this.comm.getHall(function(xml) {
that.processHall(xml);
});
},
updateHall:function () {
var that = this;
this.comm.getHall(function (xml) {
that.processHall(xml);
this.comm.updateHall(
function (xml) {
that.processHall(xml);
}, this.hallChannelId,
{
"409":function() {
that.chat.appendMessage("You have accessed Game Hall in another browser, press F5 (refresh) to regain access in this window");
},
"410":function() {
that.chat.appendMessage("You have been inactive for too long, press F5 (refresh) to enter Game Hall again");
}
});
},
@@ -163,9 +304,11 @@ var GempLotrHallUI = Class.extend({
},
processHall:function (xml) {
var that = this;
var root = xml.documentElement;
if (root.tagName == "hall") {
this.tablesDiv.html("");
this.hallChannelId = root.getAttribute("channelNumber");
var currency = root.getAttribute("currency");
this.pocketDiv.html(formatPrice(currency));
@@ -174,41 +317,171 @@ var GempLotrHallUI = Class.extend({
if (motd != null)
$("#motd").html("<b>MOTD:</b> " + motd);
var waiting = root.getAttribute("waiting") == "true";
var busy = root.getAttribute("busy") == "true";
var tablesTable = $("<table class='tables'></table>");
tablesTable.append("<tr><th width='20%'>Format</th><th width='30%'>Tournament</th><th width='10%'>Status</th><th width='20%'>Players</th><th width='20%'>Actions</th></tr>");
var queues = root.getElementsByTagName("queue");
for (var i = 0; i < queues.length; i++) {
var queue = queues[i];
var id = queue.getAttribute("id");
var action = queue.getAttribute("action");
if (action == "add" || action == "update") {
var actionsField = $("<td></td>");
var tournamentQueues = root.getElementsByTagName("tournamentQueue");
for (var i = 0; i < tournamentQueues.length; i++) {
var tournamentQueue = tournamentQueues[i];
var id = tournamentQueue.getAttribute("id");
var tournamentName = tournamentQueue.getAttribute("tournament");
var players = parseInt(tournamentQueue.getAttribute("players"));
var formatName = tournamentQueue.getAttribute("format");
var joined = tournamentQueue.getAttribute("joined");
if (players > 0 || $("#showEmptyTableQueuesCheck").prop("checked"))
this.appendTournamentQueue(tablesTable, id, formatName, tournamentName, players, joined);
var joined = queue.getAttribute("signedUp");
if (joined != "true") {
var but = $("<button>Join queue</button>");
$(but).button().click(
function (event) {
var deck = that.decksSelect.val();
if (deck != null)
that.comm.joinQueue(id, deck, function (xml) {
that.processResponse(xml);
});
});
actionsField.append(but);
} else {
var but = $("<button>Leave queue</button>");
$(but).button().click(
function (event) {
var deck = that.decksSelect.val();
if (deck != null)
that.comm.leaveQueue(id, deck, function (xml) {
that.processResponse(xml);
});
});
actionsField.append(but);
}
var row = $("<tr class='queue" + id + "'><td>" + queue.getAttribute("format") + "</td>" +
"<td>" + queue.getAttribute("collection") + "</td>" +
"<td>" + queue.getAttribute("queue") + "</td>" +
"<td>" + queue.getAttribute("playerCount") + "</td>" +
"<td>" + formatPrice(queue.getAttribute("cost")) + "</td>" +
"</tr>");
row.append(actionsField);
if (action == "add") {
$("table.queues", this.tablesDiv)
.append(row);
} else if (action == "update") {
$(".queue" + id, this.tablesDiv).replaceWith(row);
}
} else if (action == "remove") {
$(".queue" + id, this.tablesDiv).remove();
}
}
var tables = root.getElementsByTagName("table");
for (var i = 0; i < tables.length; i++) {
var table = tables[i];
var id = table.getAttribute("id");
var gameId = table.getAttribute("gameId");
var status = table.getAttribute("status");
var watchable = table.getAttribute("watchable");
var playersAttr = table.getAttribute("players");
var formatName = table.getAttribute("format");
var tournamentName = table.getAttribute("tournament");
var players = new Array();
if (playersAttr.length > 0)
players = playersAttr.split(",");
var winner = table.getAttribute("winner");
var action = table.getAttribute("action");
if (action == "add" || action == "update") {
var status = table.getAttribute("status");
var tableDiv = this.appendTable(tablesTable, id, gameId, watchable, status, formatName, tournamentName, players, waiting, winner);
var gameId = table.getAttribute("gameId");
var statusDescription = table.getAttribute("statusDescription");
var watchable = table.getAttribute("watchable");
var playersAttr = table.getAttribute("players");
var formatName = table.getAttribute("format");
var tournamentName = table.getAttribute("tournament");
var players = new Array();
if (playersAttr.length > 0)
players = playersAttr.split(",");
var winner = table.getAttribute("winner");
var row = $("<tr class='table" + id + "'></tr>");
row.append("<td>" + formatName + "</td>");
row.append("<td>" + tournamentName + "</td>");
row.append("<td>" + statusDescription + "</td>");
var playersStr = "";
for (var i = 0; i < players.length; i++) {
if (i > 0)
playersStr += ", ";
playersStr += players[i];
}
row.append("<td>" + playersStr + "</td>");
var lastField = $("<td></td>");
if (status == "WAITING" && !busy) {
var that = this;
var but = $("<button>Join table</button>");
$(but).button().click(
function (event) {
var deck = that.decksSelect.val();
if (deck != null)
that.comm.joinTable(id, deck, function (xml) {
that.processResponse(xml);
});
});
lastField.append(but);
}
if (status == "PLAYING" && watchable == "true") {
var but = $("<button>Watch game</button>");
$(but).button().click(
function (event) {
var participantId = getUrlParam("participantId");
var participantIdAppend = "";
if (participantId != null)
participantIdAppend = "&participantId=" + participantId;
location.href = "/gemp-lotr/game.html?gameId=" + gameId + participantIdAppend;
});
lastField.append(but);
}
if (status == "FINISHED" && winner != null) {
lastField.append(winner);
}
row.append(lastField);
if (action == "add") {
if (status == "WAITING") {
$("table.waitingTables", this.tablesDiv)
.append(row);
} else if (status == "PLAYING") {
$("table.playingTables", this.tablesDiv)
.append(row);
} else if (status == "FINISHED") {
$("table.finishedTables", this.tablesDiv)
.append(row);
}
} else if (action == "update") {
if (status == "WAITING") {
if ($(".table"+id, $("table.waitingTables")).length > 0) {
$(".table" + id, this.tablesDiv).replaceWith(row);
} else {
$(".table" + id, this.tablesDiv).remove();
$("table.waitingTables", this.tablesDiv)
.append(row);
}
} else if (status == "PLAYING") {
if ($(".table"+id, $("table.playingTables")).length > 0) {
$(".table" + id, this.tablesDiv).replaceWith(row);
} else {
$(".table" + id, this.tablesDiv).remove();
$("table.playingTables", this.tablesDiv)
.append(row);
}
} else if (status == "FINISHED") {
if ($(".table"+id, $("table.finishedTables")).length > 0) {
$(".table" + id, this.tablesDiv).replaceWith(row);
} else {
$(".table" + id, this.tablesDiv).remove();
$("table.finishedTables", this.tablesDiv)
.append(row);
}
}
}
} else if (action == "remove") {
$(".table" + id, this.tablesDiv).remove();
}
}
this.tablesDiv.append(tablesTable);
var skipReload = false;
var games = root.getElementsByTagName("game");
@@ -233,7 +506,7 @@ var GempLotrHallUI = Class.extend({
this.supportedFormatsInitialized = true;
}
if (waiting) {
if (busy) {
this.supportedFormatsSelect.hide();
this.decksSelect.hide();
this.createTableButton.hide();
@@ -257,7 +530,8 @@ var GempLotrHallUI = Class.extend({
}, 1000);
}
}
},
}
,
appendTournamentQueue:function (container, id, formatName, tournamentName, players, joined) {
var row = $("<tr></tr>");
@@ -298,7 +572,8 @@ var GempLotrHallUI = Class.extend({
container.append(row);
},
}
,
appendTable:function (container, id, gameId, watchable, status, formatName, tournamentName, players, waiting, winner) {
var row = $("<tr></tr>");