package org.pixelgaffer.turnierserver.backend; import java.io.IOException; import java.util.Arrays; import org.json.JSONArray; import org.json.JSONObject; import org.pixelgaffer.turnierserver.Airbrake; import org.pixelgaffer.turnierserver.Parsers; import org.pixelgaffer.turnierserver.backend.Games.GameImpl; import org.pixelgaffer.turnierserver.backend.server.BackendFrontendConnectionHandler; import org.pixelgaffer.turnierserver.backend.server.message.BackendFrontendCommandProcessed; import org.pixelgaffer.turnierserver.backend.server.message.BackendFrontendResult; import org.pixelgaffer.turnierserver.networking.DatastoreFtpClient; import lombok.Getter; import lombok.ToString; @ToString public class Tournament implements Runnable { public static final int GAMES_PER_ENEMY = 2; @Getter private static Tournament currentTournament = null; private static Object lock = new Object(); @Getter private int id; @Getter private int gameId; @Getter private int requestId; @Getter private int aisPerGame; public Tournament (int id, int gameId, int requestId) { this.id = id; this.gameId = gameId; this.requestId = requestId; BackendMain.getLogger().todo("assuming 2 ais per game, this shouldn't be hardcoded"); this.aisPerGame = 2; synchronized (lock) { if (currentTournament != null) BackendMain.getLogger().warning("Überschreibe currentTournament (" + currentTournament + ") mit " + this); currentTournament = this; } new Thread(this, "Tournament-" + id).start(); } @Override public void run () { // die kis einlesen JSONArray ais = null; { Exception _e = null; for (int i = 0; (ais == null) && (i < 3); i++) { try { ais = new JSONArray(DatastoreFtpClient.retrieveTournamentAis(id)); } catch (Exception e) { Airbrake.log(e).printStackTrace(); _e = e; } } if (ais == null) { try { crashed(_e); } catch (IOException e) { Airbrake.log(e).printStackTrace(); } return; } } // ich benötige mindestens die anzahl an kis pro spiel kis für das // turnier if (ais.length() < aisPerGame) { try { crashed("At least " + aisPerGame + " AIs required to start the tournament"); } catch (IOException e) { Airbrake.log(e).printStackTrace(); } return; } try { processed(); } catch (IOException e) { Airbrake.log(e).printStackTrace(); } BackendMain.getLogger().info("Starte Turnier " + this + " mit " + ais.length() + " KIs"); int aiIds[] = new int[aisPerGame], c = 0, fails = 0; for (int i = 0; i < aiIds.length; i++) aiIds[i] = i; mainloop: do { int sandboxes = Workers.getStartableSandboxes(true); int games = sandboxes / aisPerGame; for (int i = 0; i < games; i++) { if (c >= GAMES_PER_ENEMY) { aiIds[aiIds.length - 1]++; c = 0; } for (int j = aiIds.length - 1; j >= 0; j--) { if (aiIds[j] < ais.length() - aiIds.length + j + 1) break; if (j == 0) break mainloop; aiIds[j - 1]++; aiIds[j] = aiIds[j - 1] + 1; } BackendMain.getLogger().debug("aiIds=" + Arrays.toString(aiIds) + "; c=" + c); String[] languages = new String[aisPerGame], aiNames = new String[aisPerGame]; for (int j = 0; j < aisPerGame; j++) { JSONObject ai = ais.getJSONObject(aiIds[j]); languages[j] = ai.getString("lang"); aiNames[j] = ai.getString("ai"); } BackendMain.getLogger().debug("startGame(gameId=" + gameId + ", requestId=" + requestId + ", tournament=true, languages=" + Arrays.toString(languages) + ", ais=" + Arrays.toString(aiNames)); try { GameImpl g = Games.startGame(gameId, requestId, true, languages, aiNames); fails = 0; // das frontend informieren JSONObject json = new JSONObject(); json.put("requestid", requestId); json.put("gameid", g.getUuid()); json.put("ais", aiNames); BackendFrontendConnectionHandler.getFrontend().sendMessage(json.toString().getBytes()); } catch (Exception e) { Airbrake.log(e).printStackTrace(); // try to restart that game if (++fails <= 3) { c--; i--; } } c++; } try { Workers.waitForAvailableWorker(); } catch (InterruptedException e) { Airbrake.log(e).printStackTrace(); } } while (true); BackendMain.getLogger().todo("wait until all games associated with this tournament have finished"); try { finished(); } catch (IOException e) { Airbrake.log(e).printStackTrace(); } } private void processed () throws IOException { BackendFrontendConnectionHandler.getFrontend().sendMessage( Parsers.getFrontend().parse(new BackendFrontendCommandProcessed(getRequestId()), false)); } private void crashed (Exception e) throws IOException { BackendMain.getLogger().critical("Das Turnier " + this + " ist gecrasht: " + e); synchronized (lock) { currentTournament = null; } BackendFrontendConnectionHandler.getFrontend().sendMessage( Parsers.getFrontend().parse(new BackendFrontendResult(getRequestId(), false, null, e), false)); } private void crashed (String msg) throws IOException { BackendMain.getLogger().critical("Das Turnier " + this + " ist gecrasht: " + msg); synchronized (lock) { currentTournament = null; } BackendFrontendConnectionHandler.getFrontend().sendMessage( Parsers.getFrontend().parse(new BackendFrontendResult(getRequestId(), false, msg, null), false)); } private void finished () throws IOException { BackendMain.getLogger().info("Das Turnier " + this + " ist fertig"); synchronized (lock) { currentTournament = null; } BackendFrontendConnectionHandler.getFrontend().sendMessage( Parsers.getFrontend().parse(new BackendFrontendResult(getRequestId(), true, null), false)); } }