Adding ability for admins to manually create tournament tables
This commit is contained in:
@@ -103,7 +103,12 @@ public class GempukkuHttpRequestHandler extends SimpleChannelInboundHandler<Full
|
||||
_log.error("HTTP code " + code + " response for " + requestInformation.remoteIp + ": " + requestInformation.uri, exp);
|
||||
}
|
||||
|
||||
responseSender.writeError(exp.getStatus());
|
||||
if(exp.getMessage() != null) {
|
||||
responseSender.writeError(exp.getStatus(), Collections.singletonMap("message", exp.getMessage()));
|
||||
}
|
||||
else {
|
||||
responseSender.writeError(exp.getStatus());
|
||||
}
|
||||
} catch (Exception exp) {
|
||||
_log.error("Error response for " + uri, exp);
|
||||
responseSender.writeError(500);
|
||||
|
||||
@@ -2,12 +2,22 @@ package com.gempukku.lotro.async;
|
||||
|
||||
public class HttpProcessingException extends Exception {
|
||||
private final int _status;
|
||||
private final String _message;
|
||||
|
||||
public HttpProcessingException(int status) {
|
||||
this(status, "");
|
||||
}
|
||||
|
||||
public HttpProcessingException(int status, String message) {
|
||||
_status = status;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return _status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return _message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,10 @@ import com.gempukku.lotro.game.CardCollection;
|
||||
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
|
||||
import com.gempukku.lotro.game.Player;
|
||||
import com.gempukku.lotro.game.formats.LotroFormatLibrary;
|
||||
import com.gempukku.lotro.hall.GameTimer;
|
||||
import com.gempukku.lotro.hall.HallServer;
|
||||
import com.gempukku.lotro.league.*;
|
||||
import com.gempukku.lotro.logic.vo.LotroDeck;
|
||||
import com.gempukku.lotro.packs.ProductLibrary;
|
||||
import com.gempukku.lotro.service.AdminService;
|
||||
import com.gempukku.lotro.tournament.TournamentService;
|
||||
@@ -32,10 +34,8 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.sql.SQLException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class AdminRequestHandler extends LotroServerRequestHandler implements UriRequestHandler {
|
||||
private final LotroCardBlueprintLibrary _cardLibrary;
|
||||
@@ -91,6 +91,8 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
|
||||
previewConstructedLeague(request, responseWriter);
|
||||
} else if (uri.equals("/addConstructedLeague") && request.method() == HttpMethod.POST) {
|
||||
addConstructedLeague(request, responseWriter);
|
||||
} else if (uri.equals("/addTables") && request.method() == HttpMethod.POST) {
|
||||
addTables(request, responseWriter);
|
||||
} else if (uri.equals("/previewSoloDraftLeague") && request.method() == HttpMethod.POST) {
|
||||
previewSoloDraftLeague(request, responseWriter);
|
||||
} else if (uri.equals("/addSoloDraftLeague") && request.method() == HttpMethod.POST) {
|
||||
@@ -334,6 +336,65 @@ public class AdminRequestHandler extends LotroServerRequestHandler implements Ur
|
||||
return _leagueService.getCollectionTypeByCode(collectionType);
|
||||
}
|
||||
|
||||
private void addTables(HttpRequest request, ResponseWriter responseWriter) throws Exception {
|
||||
validateLeagueAdmin(request);
|
||||
|
||||
HttpPostRequestDecoder postDecoder = new HttpPostRequestDecoder(request);
|
||||
try {
|
||||
String name = getFormParameterSafely(postDecoder, "name");
|
||||
String tournamentID = getFormParameterSafely(postDecoder, "tournament");
|
||||
String formatCode = getFormParameterSafely(postDecoder, "format");
|
||||
String timerCode = getFormParameterSafely(postDecoder, "timer");
|
||||
List<String> playerones = getFormMultipleParametersSafely(postDecoder, "playerones[]");
|
||||
List<String> playertwos = getFormMultipleParametersSafely(postDecoder, "playertwos[]");
|
||||
|
||||
var tournament = _tournamentService.getTournamentById(tournamentID);
|
||||
|
||||
if(tournament == null) {
|
||||
throw new HttpProcessingException(400, "Tournament '" + tournamentID + "' not found.");
|
||||
}
|
||||
|
||||
var formats = _formatLibrary.getAllFormats();
|
||||
if(!formats.containsKey(formatCode)) {
|
||||
throw new HttpProcessingException(400, "Format code '" + formatCode + "' not found.");
|
||||
}
|
||||
|
||||
var format = formats.get(formatCode);
|
||||
|
||||
var timer = GameTimer.ResolveTimer(timerCode);
|
||||
|
||||
var submittedPlayers = Stream.concat(playerones.stream(), playertwos.stream()).toList();
|
||||
var decks = new HashMap<String, LotroDeck>();
|
||||
|
||||
for(String playerName : submittedPlayers) {
|
||||
var player = _playerDAO.getPlayer(playerName);
|
||||
if(player == null)
|
||||
throw new HttpProcessingException(400, "Player '" + playerName + "' does not exist.");
|
||||
|
||||
var deck = _tournamentService.getPlayerDeck(tournament.getTournamentId(), player.getName(), format.getName());
|
||||
if(deck == null)
|
||||
throw new HttpProcessingException(400, "Player '" + playerName + "' has no deck registered for '" + tournamentID + "'.");
|
||||
|
||||
if(decks.containsKey(player.getName())) {
|
||||
throw new HttpProcessingException(400, "Player '" + playerName + "' was listed twice.");
|
||||
}
|
||||
decks.put(player.getName(), deck);
|
||||
}
|
||||
|
||||
|
||||
var spawner = _hallServer.GetManualGameSpawner(tournament, format, timer, name);
|
||||
for(int i = 0; i < playerones.size(); i++) {
|
||||
String p1 = playerones.get(i);
|
||||
String p2 = playertwos.get(i);
|
||||
spawner.createGame(p1, decks.get(p1), p2, decks.get(p2));
|
||||
}
|
||||
|
||||
responseWriter.writeHtmlResponse("OK");
|
||||
} finally {
|
||||
postDecoder.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void addConstructedLeague(HttpRequest request, ResponseWriter responseWriter) throws Exception {
|
||||
validateLeagueAdmin(request);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ function gatherData(formElem) {
|
||||
|
||||
var inputs = $("input[type='text'], option:selected", formElem).each(
|
||||
function () {
|
||||
|
||||
var input = $(this);
|
||||
var name = null;
|
||||
var value = null;
|
||||
@@ -221,6 +222,32 @@ $(document).ready(
|
||||
$(".serieData").last().clone().appendTo(".series");
|
||||
});
|
||||
|
||||
$("#add-additional-table-button").button().click(
|
||||
function () {
|
||||
$(".tabledata").last().clone().appendTo(".tablesgroup");
|
||||
});
|
||||
|
||||
$("#add-tables-button").button().click(
|
||||
function () {
|
||||
let resultdiv = $("#tables-result");
|
||||
resultdiv.html("Processing...");
|
||||
|
||||
var data = gatherData($(".tablesgroup"))
|
||||
|
||||
console.log(data);
|
||||
|
||||
hall.comm.addTables(
|
||||
$("#table-name").val(),
|
||||
$("#table-tournament").val(),
|
||||
$("#table-format").val(),
|
||||
$("#table-timer").val(),
|
||||
data.playerone,
|
||||
data.playertwo,
|
||||
function (xml) {
|
||||
resultdiv.html("OK");
|
||||
}, leagueErrorMap(resultdiv));
|
||||
});
|
||||
|
||||
|
||||
hall.comm.getFormats(true,
|
||||
function (json)
|
||||
@@ -282,6 +309,30 @@ $(document).ready(
|
||||
}
|
||||
}
|
||||
sortOptionsByName("#sealed-format");
|
||||
|
||||
for (var prop in formats) {
|
||||
if (Object.prototype.hasOwnProperty.call(formats, prop)) {
|
||||
//console.log(prop);
|
||||
|
||||
if(formats[prop].name.includes("Limited"))
|
||||
continue;
|
||||
|
||||
let num = ("0000" + formats[prop].order).substr(-4);
|
||||
|
||||
var item = $("<option/>")
|
||||
.attr("value", prop)
|
||||
.text("" + num + " - " + formats[prop].name);
|
||||
$("#table-format").append(item);
|
||||
}
|
||||
}
|
||||
sortOptionsByName("#table-format");
|
||||
|
||||
$("#table-format option").each(function(index) {
|
||||
//console.log(this);
|
||||
let newText = $(this).text().replace(/\d+ - /, '');
|
||||
//console.log(newText);
|
||||
$(this).text(newText);
|
||||
});
|
||||
},
|
||||
{
|
||||
"400":function ()
|
||||
@@ -306,8 +357,14 @@ function leagueErrorMap(outputControl, callback=null) {
|
||||
if(callback!=null)
|
||||
callback();
|
||||
},
|
||||
"400":function() {
|
||||
outputControl.html("400: One of the provided parameters was malformed. Double-check your input and try again.");
|
||||
"400":function(xhr, status, request) {
|
||||
var message = xhr.getResponseHeader("message");
|
||||
if(message != null) {
|
||||
outputControl.html("400; malformed input: " + message);
|
||||
}
|
||||
else {
|
||||
outputControl.html("400: One of the provided parameters was malformed. Double-check your input and try again.");
|
||||
}
|
||||
if(callback!=null)
|
||||
callback();
|
||||
},
|
||||
@@ -412,46 +469,6 @@ function leagueErrorMap(outputControl, callback=null) {
|
||||
<b>Series definition:</b><br/>
|
||||
Format:
|
||||
<select id="constructed-format" name="format">
|
||||
<!-- <option value="fotr_block">Fellowship block</option>
|
||||
<option value="pc_fotr_block">Fellowship block (PC)</option>
|
||||
<option value="fotr1_block">Fellowship block - Set 1</option>
|
||||
<option value="fotr2_block">Fellowship block - Sets 1&2</option>
|
||||
<option value="fotr_poorman">Fellowship block - Poorman</option>
|
||||
<option value="fotr_highlander">Fellowship block - Highlander</option>
|
||||
<option value="ttt_block">Towers block</option>
|
||||
<option value="ttt1_block">Towers block - Set 4</option>
|
||||
<option value="ttt2_block">Towers block - Sets 4&5</option>
|
||||
<option value="towers_standard">Towers standard</option>
|
||||
<option value="ttt_standard">Towers standard - Sets 1-4</option>
|
||||
<option value="bohd_standard">Towers standard - Sets 1-5</option>
|
||||
<option value="ts_no_fotr">Towers standard - Sets 2-6</option>
|
||||
<option value="ts_reflections">Towers standard - Sets 1-6,9,14&16</option>
|
||||
<option value="king_block">King block</option>
|
||||
<option value="king1_block">King block - Set 7</option>
|
||||
<option value="king2_block">King block - Sets 7-8</option>
|
||||
<option value="movie">Movie block</option>
|
||||
<option value="pc_movie">Movie block (PC)</option>
|
||||
<option value="movie_exp">Movie block, no GLR (10R11)</option>
|
||||
<option value="movie7">Movie block - Sets 1-7</option>
|
||||
<option value="movie8">Movie block - Sets 1-8</option>
|
||||
<option value="movie9">Movie block - Sets 1-9</option>
|
||||
<option value="movie_sans9">Movie block - Sets 1-8,10</option>
|
||||
<option value="movie_highlander">Movie block - Highlander</option>
|
||||
<option value="war_block">War of the Ring block</option>
|
||||
<option value="war_block11">War of the Ring block - Set 11</option>
|
||||
<option value="war_block12">War of the Ring block - Sets 11-12</option>
|
||||
<option value="war_block14">War of the Ring block - Sets 10-14</option>
|
||||
<option value="hunter_block">Hunters block</option>
|
||||
<option value="war_standard">War of the Ring standard</option>
|
||||
<option value="standard">Standard</option>
|
||||
<option value="open">Open</option>
|
||||
<option value="open_legacy">Open - Sets 1-4</option>
|
||||
<option value="expanded">Expanded</option>
|
||||
<option value="pc_expanded">Expanded (PC)</option>
|
||||
<option value="test_pc_fotr_block">PLAYTEST FOTR (PC)</option>
|
||||
<option value="test_pc_movie">PLAYTEST Movie (PC)</option>
|
||||
<option value="test_pc_expanded">PLAYTEST Expanded (PC)</option>
|
||||
<option value="french">French</option> -->
|
||||
</select><br/>
|
||||
Serie duration in days: <input type="text" name="serieDuration" value="7"><br/>
|
||||
Maximum matches per serie: <input type="text" name="maxMatches" value="5"><br/>
|
||||
@@ -471,5 +488,40 @@ function leagueErrorMap(outputControl, callback=null) {
|
||||
Add Constructed League
|
||||
</button> <span id="constructed-league-result" style="display:inline-block;">Ready.</span>
|
||||
</div>
|
||||
|
||||
<h1>Manually Add Elimination Tables</h1>
|
||||
<div>
|
||||
Series Name:
|
||||
<input type="text" id="table-name"><br/>
|
||||
Tournament (for decks):
|
||||
<input type="text" id="table-tournament"><br/>
|
||||
Format:
|
||||
<select id="table-format" name="format">
|
||||
</select><br/>
|
||||
Timer (player clock / timeout):
|
||||
<select id="table-timer">
|
||||
<option value="Competitive">Competitive (40/5)</option>
|
||||
<option value="WC">Championship (20/10)</option>
|
||||
<option value="WC_Expanded">Championship Expanded (25/10)</option>
|
||||
<option value="Blitz!">Blitz (30/5)</option>
|
||||
<option value="Default">Default (80/5)</option>
|
||||
<option value="Slow">Slow (120/10)</option>
|
||||
</select><br/>
|
||||
<div class="tablesgroup">
|
||||
<div class="tabledata">
|
||||
<br/><b>Table definition:</b><br/>
|
||||
Player 1: <input type="text" name="playerone" ><br/>
|
||||
Player 2: <input type="text" name="playertwo" ><br/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="add-additional-table-button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false" style="padding:4px;">
|
||||
Add Another Table
|
||||
</button>
|
||||
|
||||
<button id="add-tables-button" class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" role="button" aria-disabled="false" style="padding:4px;">
|
||||
Add Tables
|
||||
</button> <span id="tables-result" style="display:inline-block;">Ready.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1045,6 +1045,25 @@ var GempLotrCommunication = Class.extend({
|
||||
});
|
||||
},
|
||||
|
||||
addTables:function (name, tournament, format, timer, playerones, playertwos, callback, errorMap) {
|
||||
$.ajax({
|
||||
type:"POST",
|
||||
url:this.url + "/admin/addTables",
|
||||
cache:false,
|
||||
data:{
|
||||
name:name,
|
||||
tournament:tournament,
|
||||
format:format,
|
||||
timer:timer,
|
||||
playerones:playerones,
|
||||
playertwos:playertwos
|
||||
},
|
||||
success:this.deliveryCheck(callback),
|
||||
error:this.errorCheck(errorMap),
|
||||
dataType:"html"
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
|
||||
//NEVER EVER EVER use this for actual authentication
|
||||
|
||||
@@ -653,7 +653,7 @@ var GempLotrHallUI = Class.extend({
|
||||
|
||||
//TODO: Replace this with an actual fix on the server side
|
||||
if(name.includes("Casual - WC")) {
|
||||
name = "<td><b>Group Stage - 2023 World Championship</b></td>"
|
||||
name = "<td><b>2023 World Championship - " + userDesc + "</b></td>"
|
||||
}
|
||||
row.append(name);
|
||||
row.append("<td>" + statusDescription + "</td>");
|
||||
|
||||
@@ -106,7 +106,7 @@ public class LotroServer extends AbstractServer {
|
||||
if(gameSettings.getLeague() != null) {
|
||||
spectate = true;
|
||||
}
|
||||
else if(gameSettings.isCompetitive() || gameSettings.isPrivateGame() || gameSettings.isHiddenGame()) {
|
||||
else if(gameSettings.isPrivateGame() || gameSettings.isHiddenGame()) {
|
||||
spectate = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,14 @@ public record GameTimer(boolean longGame, String name, int maxSecondsPerPlayer,
|
||||
return GameTimer.SLOW_TIMER;
|
||||
case "glacial":
|
||||
return GameTimer.GLACIAL_TIMER;
|
||||
case "competitive":
|
||||
return GameTimer.COMPETITIVE_TIMER;
|
||||
case "wc":
|
||||
return GameTimer.CHAMPIONSHIP_TIMER;
|
||||
case "wc_expanded":
|
||||
return GameTimer.EXPANDED_CHAMPIONSHIP_TIMER;
|
||||
case "tournament":
|
||||
return GameTimer.TOURNAMENT_TIMER;
|
||||
}
|
||||
}
|
||||
return GameTimer.DEFAULT_TIMER;
|
||||
|
||||
@@ -802,10 +802,10 @@ public class HallServer extends AbstractServer {
|
||||
private HallTournamentCallback(Tournament tournament) {
|
||||
_tournament = tournament;
|
||||
tournamentGameSettings = new GameSettings(null, _formatLibrary.getFormat(_tournament.getFormat()),
|
||||
null, null, true, false, false, false, GameTimer.TOURNAMENT_TIMER, null);
|
||||
null, null, true, true, false, false, GameTimer.TOURNAMENT_TIMER, null);
|
||||
|
||||
wcGameSettings = new GameSettings(null, _formatLibrary.getFormat(_tournament.getFormat()),
|
||||
null, null, true, false, false, false, GameTimer.CHAMPIONSHIP_TIMER, null);
|
||||
null, null, true, true, false, false, GameTimer.CHAMPIONSHIP_TIMER, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -856,4 +856,53 @@ public class HallServer extends AbstractServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ManualGameSpawner GetManualGameSpawner(Tournament tournament, LotroFormat format, GameTimer timer, String description) {
|
||||
return new ManualGameSpawner(tournament, format, timer, description);
|
||||
}
|
||||
public class ManualGameSpawner implements TournamentCallback {
|
||||
private final Tournament _tournament;
|
||||
private final GameSettings _settings;
|
||||
|
||||
private ManualGameSpawner(Tournament tournament, LotroFormat format, GameTimer timer, String description) {
|
||||
_tournament = tournament;
|
||||
_settings = new GameSettings(null, format,
|
||||
null, null, true, false, false, false, timer, description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGame(String playerOne, LotroDeck deckOne, String playerTwo, LotroDeck deckTwo) {
|
||||
final LotroGameParticipant[] participants = new LotroGameParticipant[2];
|
||||
participants[0] = new LotroGameParticipant(playerOne, deckOne);
|
||||
participants[1] = new LotroGameParticipant(playerTwo, deckTwo);
|
||||
createGameInternal(participants);
|
||||
}
|
||||
|
||||
private void createGameInternal(final LotroGameParticipant[] participants) {
|
||||
_hallDataAccessLock.writeLock().lock();
|
||||
try {
|
||||
if (!_shutdown) {
|
||||
|
||||
final GameTable gameTable = tableHolder.setupTournamentTable(_settings, participants);
|
||||
final LotroGameMediator mediator = createGameMediator(participants,
|
||||
_notifyHallListeners, _tournament.getTournamentName(), _settings);
|
||||
gameTable.startGame(mediator);
|
||||
}
|
||||
} finally {
|
||||
_hallDataAccessLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastMessage(String message) {
|
||||
try {
|
||||
//check-in callback
|
||||
_hallChat.sendMessage("TournamentSystem", message, true);
|
||||
} catch (PrivateInformationException exp) {
|
||||
// Ignore, sent as admin
|
||||
} catch (ChatCommandErrorException e) {
|
||||
// Ignore, no command
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user