WC Tournament Queues

- Added new WC-specific queues to the front page.  Any scheduled tournament with 'wc' in the name will use this UI section instead of the main one.
- Added pop-up information on joining, leaving, and abandoning a tournament.
- Fixed some xml parsing bugs in the comm object for tournament queues
- Added new Championship Timer which grants both players 20 minutes and a 10 minute timeout
- Altered the scheduled tournament table creation to use the Championship Timer
This commit is contained in:
Christian 'ketura' McCarty
2023-08-12 00:40:13 -05:00
parent 548f36be87
commit 893ef932af
7 changed files with 233 additions and 37 deletions

View File

@@ -280,6 +280,14 @@ table.tables tr.privateForPlayer {
border-radius: 3px;
}
.wc-queues {
background-color: #FF5555;
}
.wc-events {
background-color: #FFFF55;
}
.tableFormatName {
text-align: center;
font-weight: bold;

View File

@@ -175,6 +175,34 @@
</table>
</div>
<div id="wcQueuesHeader" class='eventHeader wc-queues'>World Championship Queues<span class='count'>(0)</span></div>
<div id="wcQueuesContent" class='visibilityToggle'>
<table class='tables wc-queues'>
<tr>
<th width='10%'>Format</th>
<th width='20%'>Event</th>
<th width='16%'>Starts</th>
<th width='10%'>System</th>
<th width='6%'>Players</th>
<th width='10%'>Actions</th>
</tr>
</table>
</div>
<div id="wcEventsHeader" class='eventHeader wc-events'>World Championship Events<span class='count'>(0)</span></div>
<div id="wcEventsContent" class='visibilityToggle'>
<table class='tables wc-events'>
<tr>
<th width='10%'>Format</th>
<th width='20%'>Event</th>
<th width='16%'>Starts</th>
<th width='10%'>System</th>
<th width='6%'>Players</th>
<th width='10%'>Actions</th>
</tr>
</table>
</div>
<div id="tournamentQueuesHeader" class='eventHeader queues'>Tournament Queues<span class='count'>(0)</span></div>
<div id="tournamentQueuesContent" class='visibilityToggle'>
<table class='tables queues'>

View File

@@ -603,18 +603,19 @@ var GempLotrCommunication = Class.extend({
participantId:getUrlParam("participantId")},
success:this.deliveryCheck(callback),
error:this.errorCheck(errorMap),
dataType:"xml"
dataType:"text"
});
},
leaveQueue:function (queueId, errorMap) {
leaveQueue:function (queueId, callback, errorMap) {
$.ajax({
type:"POST",
url:this.url + "/hall/queue/" + queueId + "/leave",
cache:false,
data:{
participantId:getUrlParam("participantId")},
success:this.deliveryCheck(callback),
error:this.errorCheck(errorMap),
dataType:"xml"
dataType:"text"
});
},
dropFromTournament:function(tournamentId, callback, errorMap) {

View File

@@ -77,9 +77,9 @@ var GempLotrHallUI = Class.extend({
this.adminTab.hide();
this.comm.getPlayerInfo(function(json)
{
that.userInfo = json;
if(that.userInfo.type.includes("a") || that.userInfo.type.includes("l"))
{
that.userInfo = json;
if(that.userInfo.type.includes("a") || that.userInfo.type.includes("l"))
{
that.adminTab.show();
}
@@ -87,12 +87,12 @@ var GempLotrHallUI = Class.extend({
{
that.adminTab.hide();
}
});
});
var hallSettingsStr = $.cookie("hallSettings");
if (hallSettingsStr == null)
hallSettingsStr = "1|1|0|0|0";
hallSettingsStr = "1|1|0|0|0|0|0";
var hallSettings = hallSettingsStr.split("|");
this.initTable(hallSettings[0] == "1", "waitingTablesHeader", "waitingTablesContent");
@@ -100,6 +100,13 @@ var GempLotrHallUI = Class.extend({
this.initTable(hallSettings[2] == "1", "finishedTablesHeader", "finishedTablesContent");
this.initTable(hallSettings[3] == "1", "tournamentQueuesHeader", "tournamentQueuesContent");
this.initTable(hallSettings[4] == "1", "activeTournamentsHeader", "activeTournamentsContent");
this.initTable(hallSettings[5] == "1", "wcQueuesHeader", "wcQueuesContent");
this.initTable(hallSettings[6] == "1", "wcEventsHeader", "wcEventsContent");
$('#wcQueuesHeader').hide();
$('#wcQueuesContent').hide();
$('#wcEventsHeader').hide();
$('#wcEventsContent').hide();
$("#deckbuilder-button").button();
$("#bug-button").button();
@@ -230,6 +237,27 @@ var GempLotrHallUI = Class.extend({
closeText: ''
}).text(text);
},
showDialog:function(title, text, height) {
if(height == null)
height = 200
var dialog = $("<div></div>").dialog({
title: title,
resizable: true,
height: height,
modal: true,
closeOnEscape: true,
buttons: [
{
text: "OK",
click: function() {
$( this ).dialog( "close" );
}
}
],
closeText: ''
}).html(text);
},
updateDecks:function () {
var that = this;
@@ -250,14 +278,18 @@ var GempLotrHallUI = Class.extend({
},
processResponse:function (xml) {
if (xml != null) {
if (xml != null && xml != "OK") {
var root = xml.documentElement;
if (root.tagName == "error") {
var message = root.getAttribute("message");
this.chat.appendMessage(message, "warningMessage");
alert(message);
this.showDialog("Error", message);
return false;
}
}
return true;
},
processDecks:function (xml) {
@@ -335,6 +367,7 @@ var GempLotrHallUI = Class.extend({
for (var i = 0; i < queues.length; i++) {
var queue = queues[i];
var id = queue.getAttribute("id");
var isWC = id.toLowerCase().includes("wc");
var action = queue.getAttribute("action");
if (action == "add" || action == "update") {
var actionsField = $("<td></td>");
@@ -343,59 +376,123 @@ var GempLotrHallUI = Class.extend({
if (joined != "true" && queue.getAttribute("joinable") == "true") {
var but = $("<button>Join Queue</button>");
$(but).button().click((
function(queueId) {
function(queueInfo) {
return function () {
var deck = that.decksSelect.val();
if (deck != null)
that.comm.joinQueue(queueId, deck, function (xml) {
that.processResponse(xml);
});
if (deck == null)
return;
var queueId = queueInfo.getAttribute("id");
var queueName = queueInfo.getAttribute("queue");
var queueStart = queueInfo.getAttribute("start");
that.comm.joinQueue(queueId, deck, function (xml) {
var result = that.processResponse(xml);
if(result) {
that.showDialog("Joined Tournament", "You have signed up to participate in the <b>" + queueName
+ "</b> tournament.<br><br>You will use a snapshot of your '<b>" + deck +"</b>' deck as it is right now. " +
"If you need to change or update your deck, you will need to leave the queue and rejoin.<br><br>" +
"The first game begins at " + queueStart + ". Good luck!", 320);
}
}, that.hallErrorMap());
};
}
)(id));
)(queue));
actionsField.append(but);
} else if (joined == "true") {
var but = $("<button>Leave Queue</button>");
$(but).button().click((
function(queueId) {
function(queueInfo) {
return function() {
var queueId = queueInfo.getAttribute("id");
var queueName = queueInfo.getAttribute("queue");
var queueStart = queueInfo.getAttribute("start");
that.comm.leaveQueue(queueId, function (xml) {
that.processResponse(xml);
var result = that.processResponse(xml);
if(result) {
that.showDialog("Left Tournament", "You have been removed from the <b>" + queueName
+ "</b> tournament.<br><br>If you wish to rejoin, you will need to requeue before it starts at " + queueStart + ".", 230);
}
});
}
})(id));
})(queue));
actionsField.append(but);
}
var row = $("<tr class='queue" + id + "'><td>" + queue.getAttribute("format") + "</td>" +
"<td>" + queue.getAttribute("collection") + "</td>" +
var rowstr = "<tr class='queue" + id + "'><td>" + queue.getAttribute("format") + "</td>";
if(isWC) {
rowstr = "<tr class='wc-queue" + id + "'><td>" + queue.getAttribute("format") + "</td>" +
"<td>" + queue.getAttribute("queue") + "</td>" +
"<td>" + queue.getAttribute("start") + "</td>" +
"<td>" + queue.getAttribute("system") + "</td>" +
"<td>" + queue.getAttribute("playerCount") + "</td>" +
"</tr>";
}
else {
rowstr += "<td>" + queue.getAttribute("collection") + "</td>" +
"<td>" + queue.getAttribute("queue") + "</td>" +
"<td>" + queue.getAttribute("start") + "</td>" +
"<td>" + queue.getAttribute("system") + "</td>" +
"<td>" + queue.getAttribute("playerCount") + "</td>" +
"<td align='right'>" + formatPrice(queue.getAttribute("cost")) + "</td>" +
"<td>" + queue.getAttribute("prizes") + "</td>" +
"</tr>");
"</tr>";
}
var row = $(rowstr);
row.append(actionsField);
if (action == "add") {
$("table.queues", this.tablesDiv)
if(isWC) {
$("table.wc-queues", this.tablesDiv)
.append(row);
}
else {
$("table.queues", this.tablesDiv)
.append(row);
}
} else if (action == "update") {
$(".queue" + id, this.tablesDiv).replaceWith(row);
if(isWC) {
$(".wc-queue" + id, this.tablesDiv).replaceWith(row);
}
else {
$(".queue" + id, this.tablesDiv).replaceWith(row);
}
}
this.animateRowUpdate(".queue" + id);
if(isWC) {
this.animateRowUpdate(".wc-queue" + id);
}
else {
this.animateRowUpdate(".queue" + id);
}
} else if (action == "remove") {
$(".queue" + id, this.tablesDiv).remove();
if(isWC) {
$(".wc-queue" + id, this.tablesDiv).remove();
}
else {
$(".queue" + id, this.tablesDiv).remove();
}
}
}
if($('.wc-queues tr').length <= 1) {
$('#wcQueuesHeader').hide();
$('#wcQueuesContent').hide();
} else {
$('#wcQueuesHeader').show();
$('#wcQueuesContent').show();
}
var tournaments = root.getElementsByTagName("tournament");
for (var i = 0; i < tournaments.length; i++) {
var tournament = tournaments[i];
var id = tournament.getAttribute("id");
var isWC = id.includes("wc");
var action = tournament.getAttribute("action");
if (action == "add" || action == "update") {
var actionsField = $("<td></td>");
@@ -404,18 +501,36 @@ var GempLotrHallUI = Class.extend({
if (joined == "true") {
var but = $("<button>Drop from tournament</button>");
$(but).button().click((
function(tournamentId) {
function(tourneyInfo) {
var tourneyId = tournament.getAttribute("id");
var tourneyName = tournament.getAttribute("queue");
let isExecuted = confirm("Are you sure you want to resign from the " + tourneyName + " tournament? This cannot be undone.");
return function () {
that.comm.dropFromTournament(tournamentId, function (xml) {
if(isExecuted) {
that.comm.dropFromTournament(tournamentId, function (xml) {
that.processResponse(xml);
});
}
};
}
)(id));
)(tournament));
actionsField.append(but);
}
var row = $("<tr class='tournament" + id + "'><td>" + tournament.getAttribute("format") + "</td>" +
var rowstr = "";
if(isWC) {
rowstr = $("<tr class='wc-tournament" + id + "'><td>" + tournament.getAttribute("format") + "</td>" +
"<td>" + tournament.getAttribute("name") + "</td>" +
"<td>" + tournament.getAttribute("system") + "</td>" +
"<td>" + tournament.getAttribute("stage") + "</td>" +
"<td>" + tournament.getAttribute("round") + "</td>" +
"<td>" + tournament.getAttribute("playerCount") + "</td>" +
"</tr>");
}
else {
rowstr = $("<tr class='tournament" + id + "'><td>" + tournament.getAttribute("format") + "</td>" +
"<td>" + tournament.getAttribute("collection") + "</td>" +
"<td>" + tournament.getAttribute("name") + "</td>" +
"<td>" + tournament.getAttribute("system") + "</td>" +
@@ -423,21 +538,49 @@ var GempLotrHallUI = Class.extend({
"<td>" + tournament.getAttribute("round") + "</td>" +
"<td>" + tournament.getAttribute("playerCount") + "</td>" +
"</tr>");
}
var row = $(rowstr);
row.append(actionsField);
if (action == "add") {
$("table.tournaments", this.tablesDiv)
if(isWC) {
$("table.wc-tournaments", this.tablesDiv)
.append(row);
}
else {
$("table.tournaments", this.tablesDiv)
.append(row);
}
} else if (action == "update") {
$(".tournament" + id, this.tablesDiv).replaceWith(row);
if(isWC) {
$(".wc-tournament" + id, this.tablesDiv).replaceWith(row);
}
else {
$(".tournament" + id, this.tablesDiv).replaceWith(row);
}
}
this.animateRowUpdate(".tournament" + id);
} else if (action == "remove") {
$(".tournament" + id, this.tablesDiv).remove();
if(isWC) {
$(".wc-tournament" + id, this.tablesDiv).remove();
}
else {
$(".tournament" + id, this.tablesDiv).remove();
}
}
}
if($('.wc-events tr').length <= 1) {
$('#wcEventsHeader').hide();
$('#wcEventsContent').hide();
} else {
$('#wcEventsHeader').show();
$('#wcEventsContent').show();
}
var tables = root.getElementsByTagName("table");
for (var i = 0; i < tables.length; i++) {

View File

@@ -8,6 +8,7 @@ public record GameTimer(boolean longGame, String name, int maxSecondsPerPlayer,
public static final GameTimer GLACIAL_TIMER = new GameTimer(true, "Glacial", 60 * 60 * 24 * 3, 60 * 60 * 24);
// 5 minutes timeout, 40 minutes per game per player
public static final GameTimer COMPETITIVE_TIMER = new GameTimer(false, "Competitive", 60 * 40, 60 * 5);
public static final GameTimer CHAMPIONSHIP_TIMER = new GameTimer(false, "WC", 60 * 20, 60 * 10);
public static final GameTimer TOURNAMENT_TIMER = new GameTimer(false, "Tournament", 60 * 40, 60 * 5);
public static GameTimer ResolveTimer(String timer) {

View File

@@ -877,10 +877,15 @@ public class HallServer extends AbstractServer {
private final Tournament _tournament;
private final GameSettings tournamentGameSettings;
private final GameSettings wcGameSettings;
private HallTournamentCallback(Tournament tournament) {
_tournament = tournament;
tournamentGameSettings = new GameSettings(null, _formatLibrary.getFormat(_tournament.getFormat()),
null, null, true, false, false, false, GameTimer.TOURNAMENT_TIMER, null);
wcGameSettings = new GameSettings(null, _formatLibrary.getFormat(_tournament.getFormat()),
null, null, true, false, false, false, GameTimer.CHAMPIONSHIP_TIMER, null);
}
@Override
@@ -895,7 +900,11 @@ public class HallServer extends AbstractServer {
_hallDataAccessLock.writeLock().lock();
try {
if (!_shutdown) {
final GameTable gameTable = tableHolder.setupTournamentTable(tournamentGameSettings, participants);
var settings = tournamentGameSettings;
if(_tournament.getTournamentId().toLowerCase().contains("wc")) {
settings = wcGameSettings;
}
final GameTable gameTable = tableHolder.setupTournamentTable(settings, participants);
final LotroGameMediator mediator = createGameMediator(participants,
new GameResultListener() {
@Override
@@ -907,7 +916,7 @@ public class HallServer extends AbstractServer {
public void gameCancelled() {
createGameInternal(participants);
}
}, _tournament.getTournamentName(), tournamentGameSettings);
}, _tournament.getTournamentName(), settings);
gameTable.startGame(mediator);
}
} finally {

View File

@@ -10,6 +10,8 @@ import java.util.Date;
public class ScheduledTournamentQueue extends AbstractTournamentQueue implements TournamentQueue {
private static final long _signupTimeBeforeStart = 1000 * 60 * 60; // 60 minutes before start
private static final long _wcSignupTimeBeforeStart = 1000 * 60 * 60 * 24; // 24 hours before start
private final long _startTime;
private final int _minimumPlayers;
private final String _startCondition;
@@ -72,6 +74,10 @@ public class ScheduledTournamentQueue extends AbstractTournamentQueue implements
@Override
public boolean isJoinable() {
return System.currentTimeMillis() >= _startTime - _signupTimeBeforeStart;
long window = _signupTimeBeforeStart;
if(_scheduledTournamentId.toLowerCase().contains("wc")) {
window = _wcSignupTimeBeforeStart;
}
return System.currentTimeMillis() >= _startTime - window;
}
}