package org.pixelgaffer.turnierserver.codr;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.io.FileUtils;
import org.pixelgaffer.turnierserver.codr.utilities.Dialog;
import org.pixelgaffer.turnierserver.codr.utilities.ErrorLog;
import org.pixelgaffer.turnierserver.codr.utilities.Exceptions.NewException;
import org.pixelgaffer.turnierserver.codr.utilities.Exceptions.NothingDoneException;
import org.pixelgaffer.turnierserver.codr.utilities.Exceptions.UpdateException;
import org.pixelgaffer.turnierserver.codr.utilities.Resources;
import org.pixelgaffer.turnierserver.codr.utilities.Settings;
import org.pixelgaffer.turnierserver.codr.utilities.WebConnector;
import org.pixelgaffer.turnierserver.codr.view.ControllerAiManagement;
import org.pixelgaffer.turnierserver.codr.view.ControllerGameManagement;
import org.pixelgaffer.turnierserver.codr.view.ControllerRanking;
import org.pixelgaffer.turnierserver.codr.view.ControllerRoot;
import org.pixelgaffer.turnierserver.codr.view.ControllerStartPage;
import org.pixelgaffer.turnierserver.codr.view.ControllerSubmission;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
/**
* Managet das Komplette Programm
*
* @author Philip
*/
public class MainApp extends Application {
public static Stage stage;
public static ControllerRoot cRoot;
public static ControllerStartPage cStart;
public static ControllerAiManagement cAi;
public static ControllerGameManagement cGame;
public static ControllerRanking cRanking;
public static ControllerSubmission cSubmission;
public static Settings settings;
public static String version = "1.02"; //VERSION//VERSION//VERSION//VERSION//VERSION//VERSION//VERSION//
public static WebConnector webConnector;
public static GameManager gameManager = new GameManager();
public static AiManager aiManager = new AiManager();
public static StringProperty actualGameType = new SimpleStringProperty(null);
public static ObservableList<String> gametypes = FXCollections.observableArrayList();
public static ObservableList<String> languages = FXCollections.observableArrayList();
public static ObservableList<GameOnline> onlineGames = FXCollections.observableArrayList();
public static ObservableList<AiOnline> onlineAis = FXCollections.observableArrayList();
public static ObservableList<AiOnline> ownOnlineAis = FXCollections.observableArrayList();
/**
* Main-Methode
*
* @param args Argumente
*/
public static void main(String[] args) {
launch(args);
}
/**
* start-Methode (wegen: extends Application)
*/
public void start(Stage _stage) throws Exception {
ErrorLog.clear();
ErrorLog.write("Programm startet... (Version " + version + ")", true);
checkNewVersion(false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> exit()));
stage = new Stage(StageStyle.DECORATED);
settings = new Settings();
settings.loadUrl();
webConnector = new WebConnector("http://" + Settings.webUrl + "/api/");
gametypes = webConnector.loadGametypesFromFile();
languages = webConnector.loadLangsFromFile();
CodeEditor.writeSyntax();
if (gametypes == null || languages == null) {
showSplashStage(_stage);
} else {
loadOnlineResources();
showMainStage();
}
ErrorLog.write("Programm gestartet", true);
}
/**
* wird aufgerufen, wenn Codr geschlossen wird
*/
public void exit() {
if (settings != null)
settings.store(cStart);
if (cAi != null && cAi.version != null)
cAi.version.saveCode();
if (cGame != null && cGame.runningGame != null) {
try {
cGame.runningGame.game.finishGame();
} catch (IOException e) {
e.printStackTrace();
}
}
checkNewVersion(true);
ErrorLog.write("Programm beendet", true);
}
/**
* Überprüft in einem Thread, ob sich die Verbindung zum Server geändert hat.
* Wenn er fertig ist, wird die Benutzeroberfläche entsprechend angepasst.
*/
public static void updateConnected() {
Task<Boolean> updateC = new Task<Boolean>() {
public Boolean call() {
if (MainApp.webConnector.ping()) {
return true;
} else {
return false;
}
}
};
cStart.prOnlineResources.setVisible(true);
updateC.valueProperty().addListener((observableValue, oldValue, newValue) -> {
if (cSubmission == null)
try {
Thread.sleep(20);
} catch (Exception e) {
}
cStart.prOnlineResources.setVisible(false);
if (newValue) {
cStart.lbIsOnline.setText("Es besteht eine Internetverbindung");
cStart.btTryOnline.setText("nach Aktualisierungen suchen");
cStart.vbLogin.setDisable(false);
cGame.tabOnline.setDisable(false);
cRoot.tabRanking.setDisable(false);
} else {
cStart.lbIsOnline.setText("Momentan besteht keine Internetverbindung");
cStart.btTryOnline.setText("Erneut versuchen");
cStart.vbLogin.setDisable(true);
cGame.tabOnline.setDisable(true);
cRoot.tabRanking.setDisable(true);
}
});
Thread thread = new Thread(updateC, "updateConnected");
thread.setDaemon(true);
thread.start();
}
/**
* Überprüft in einem Thread, ob der Benutzer eingeloggt ist.
* Wenn er fertig ist, wird die Benutzeroberfläche entsprechend angepasst.
*/
public static void updateLoggedIn() {
Task<Boolean> updateL = new Task<Boolean>() {
public Boolean call() {
try {
if (webConnector.isLoggedIn()) {
return true;
} else {
return false;
}
} catch (IOException e) {
MainApp.updateConnected();
return false;
}
}
};
cStart.prLogin.setVisible(true);
cStart.prLogin1.setVisible(true);
updateL.valueProperty().addListener((observableValue, oldValue, newValue) -> {
if (cSubmission == null)
try {
Thread.sleep(20);
} catch (Exception e) {
}
if (newValue) {
cStart.vbLogin.getChildren().clear();
cStart.vbLogin.getChildren().add(cStart.hbLogout);
cGame.tpNewGameOnline.setDisable(false);
cGame.tpNewGameOnline.setExpanded(true);
cAi.btUpload.setVisible(true);
cRanking.btChallenge.setVisible(true);
} else {
cStart.vbLogin.getChildren().clear();
cStart.vbLogin.getChildren().add(cStart.gpLogin);
cGame.tpNewGameOnline.setDisable(true);
cGame.tpNewGameOnline.setExpanded(false);
cAi.btUpload.setVisible(false);
cRanking.btChallenge.setVisible(false);
}
cStart.prLogin.setVisible(false);
cStart.prLogin1.setVisible(false);
});
Thread thread = new Thread(updateL, "updateLoggedIn");
thread.setDaemon(true);
thread.start();
}
/**
* Überprüft, ob eine neue Version von Codr im gleichen Verzeichnis existiert und updatet sich selbst.
* Dies funktioniert nur, wenn Codr als .jar kompiliert ist.
*
* @param newStartWarning Gibt an, ob gewarnt werden soll, bevor Codr neu gestartet wird.
*/
public static void checkNewVersion(boolean newStartWarning) {
File myself = new File((System.getProperty("java.class.path").split(System.getProperty("path.separator"))[0]));
if (myself.isDirectory()) {
ErrorLog.write("Du hast nicht die Jar-Version von Codr");
return;
}
// ist neu
if (myself.getName().equals("CodrNewVersion.jar")) {
File oldCodr = new File("Codr.jar");
for (int i = 0; i < 5; i++) { // 5 Versuche mit Pausen, um zu warten, bis oldCodr freigegeben ist.
try {
FileUtils.copyFile(myself, oldCodr);
Runtime.getRuntime().exec(new String[] { "java", "-jar", oldCodr.getName() });
System.exit(0);
} catch (IOException e) {
ErrorLog.write("Fehler beim Updaten: " + e);
}
try {
Thread.sleep(100); // vor nächstem Versuch warten
} catch (InterruptedException e) {
}
}
} else { // ist normal
File toDelete = new File("CodrNewVersion.jar");
if (toDelete.exists()) {
for (int i = 0; i < 5; i++) { // 5 Versuche mit Pausen, um zu warten, bis toDelete freigegeben ist.
try {
if (Resources.compareFiles(myself, toDelete)) {
try {
org.apache.commons.io.FileUtils.forceDelete(toDelete);
ErrorLog.write("CodrNewVersion.jar wurde gelöscht, da sie identisch mit der aktuellen Version ist.");
return;
} catch (Exception e) {
ErrorLog.write(e.toString());
}
} else {
ErrorLog.write("Eine neue Version ist verfügbar, sie wird ausgeführt...");
if (newStartWarning) {
Dialog.info("Codr wird jetzt neu gestartet.");
}
Runtime.getRuntime().exec(new String[] { "java", "-jar", toDelete.getName() });
ErrorLog.write("Ausführen fertig.");
System.exit(0);
}
} catch (IOException e) {
ErrorLog.write("Fehler beim Updaten: " + e);
return;
}
try {
Thread.sleep(100); // vor nächstem Versuch warten
} catch (InterruptedException e) {
}
}
}
}
}
/**
* Sucht nach neunen Spieltypen, neuen Sprachen und Aktualisierungen von Codr.
*/
public static void loadOnlineResources() {
final Task<Object> updateTask = new Task<Object>() {
@Override
protected Object call() throws InterruptedException {
try {
webConnector.updateGametypes();
} catch (NewException e) {
gametypes = e.newValues;
updateMessage("neue Spieltypen");
} catch (UpdateException e) {
} catch (NothingDoneException e) {
} catch (IOException e) {
}
try {
webConnector.updateLanguages();
} catch (NewException e) {
languages = e.newValues;
updateMessage("neue Sprachen");
} catch (NothingDoneException e) {
} catch (IOException e) {
}
try {
File myself = new File((System.getProperty("java.class.path").split(System.getProperty("path.separator"))[0]));
if (!myself.isDirectory()) {
byte[] onlineHash = webConnector.getCodrHash();
byte[] myHash = Resources.getHash(myself);
if (!Arrays.equals(onlineHash, myHash))
if (webConnector.updateCodr())
updateMessage("neuer Codr");
}
} catch (Exception e) {
e.printStackTrace();
}
updateMessage("laden fertig");
return null;
}
};
updateTask.messageProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
onlineResourcesFinished(newValue);
}
});
if (cStart != null)
cStart.prOnlineResources.setVisible(true);
Thread thread = new Thread(updateTask, "updateOnlineResources");
thread.setDaemon(true);
thread.start();
}
/**
* Nimmt die Ergebnisse der Suche von loadOnlineResources entgegen.
* @param text
*/
private static void onlineResourcesFinished(String text) {
switch (text) {
case "neue Spieltypen":
if (Dialog.okAbort("Neue Spieltypen sind verfügbar. Wollen Sie zum aktuellen wechseln?")) {
cStart.cbGameTypes.getSelectionModel().selectLast();
}
break;
case "neue Sprachen":
// languages = ;
Dialog.info("Neue Sprachen sind verfügbar");
break;
case "neuer Codr":
if (Dialog.okAbort("Eine neue Version von Codr ist verfügbar.\nJetzt neustarten?")) {
checkNewVersion(false);
}
break;
case "laden fertig":
if (cStart != null) {
cStart.prOnlineResources.setVisible(false);
}
break;
}
}
/**
* Lädt die online-KIs und -Spiele
*/
public static void loadOnlineRanking() {
Task<ObservableList<GameOnline>> loadOnlineGames = new Task<ObservableList<GameOnline>>() {
public ObservableList<GameOnline> call() {
ObservableList<GameOnline> newOnlineGames = MainApp.webConnector.getGames(MainApp.actualGameType.get());
return newOnlineGames;
}
};
Task<ObservableList<AiOnline>> loadOnline = new Task<ObservableList<AiOnline>>() {
public ObservableList<AiOnline> call() {
ObservableList<AiOnline> newOnline = null;
try {
newOnline = MainApp.webConnector.getAis(MainApp.actualGameType.get());
} catch (Exception e) {
e.printStackTrace();
}
return newOnline;
}
};
Task<ObservableList<AiOnline>> loadOwn = new Task<ObservableList<AiOnline>>() {
public ObservableList<AiOnline> call() {
ObservableList<AiOnline> newOwnOnline = null;
try {
if (MainApp.webConnector.isLoggedIn())
newOwnOnline = MainApp.webConnector.getOwnAis(MainApp.actualGameType.get());
} catch (IOException e) {
return null;
}
return newOwnOnline;
}
};
loadOnlineGames.valueProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue != null) {
onlineGames.clear();
onlineGames.addAll(newValue);
} else {
MainApp.updateConnected();
}
cGame.btActualize.setVisible(true);
cGame.prActualize.setVisible(false);
});
loadOnline.valueProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue != null) {
onlineAis.clear();
onlineAis.addAll(newValue);
} else {
MainApp.updateConnected();
}
cRanking.btActualize.setVisible(true);
cRanking.prActualize.setVisible(false);
});
loadOwn.valueProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue != null) {
ownOnlineAis.clear();
ownOnlineAis.addAll(newValue);
} else {
MainApp.updateLoggedIn();
}
});
//Visuelles
cRanking.btActualize.setVisible(false);
cRanking.prActualize.setVisible(true);
cGame.btActualize.setVisible(false);
cGame.prActualize.setVisible(true);
Thread thread1 = new Thread(loadOnlineGames, "loadOnlineGames");
thread1.setDaemon(true);
thread1.start();
Thread thread2 = new Thread(loadOnline, "loadOnlineAis");
thread2.setDaemon(true);
thread2.start();
Thread thread3 = new Thread(loadOwn, "loadOwnOnlineAis");
thread3.setDaemon(true);
thread3.start();
}
// /**
// * Verbindet die Spiele und die KIs miteinander, nachdem sie Runtergeladen wurden.
// */
// public void connectGamesPlayers() {
// Map<Integer, GameOnline> games = new HashMap<Integer, GameOnline>();
// Map<Integer, AiOnline> ais = new HashMap<Integer, AiOnline>();
//
// for (GameOnline game : onlineGames) {
// games.put(game.ID, game);
// }
// for (AiOnline ai : onlineAis) {
// ais.put(ai.id, ai);
// }
//
//
// for (GameOnline game : onlineGames) {
// for (ParticipantResult part : game.participants) {
// part.ai = ais.get(part.aiID);
// }
// }
// for (AiOnline ai : onlineAis) {
// for (int id : ai.onlineGameIDs) {
// ai.onlineGames.add(games.get(id));
// }
// }
// return;
// }
/**
* Zeigt den Startbildschirm beim ersten Start von Codr an.
*/
public void showSplashStage(Stage splashStage) {
final Task<Object> downloadTask = new Task<Object>() {
@Override
protected Object call() throws InterruptedException {
updateMessage("Gametypen werden geladen");
try {
webConnector.updateGametypes();
} catch (NewException e) {
gametypes = e.newValues;
} catch (NothingDoneException | UpdateException e) {
} catch (IOException e) {
ErrorLog.write("Bitte stellen Sie beim ersten Start eine Verbindung zum Internet her");
for (int i = 100; i >= 0; i--) {
updateMessage("Keine Internetverbindung (" + i + ")");
Thread.sleep(1000);
}
System.exit(1);
}
updateMessage("Sprachen werden geladen");
try {
webConnector.updateLanguages();
} catch (NewException e) {
languages = e.newValues;
} catch (NothingDoneException e) {
} catch (IOException e) {
ErrorLog.write("Bitte stellen Sie beim ersten Start eine Verbindung zum Internet her");
for (int i = 100; i >= 0; i--) {
updateMessage("Keine Internetverbindung (" + i + ")");
Thread.sleep(1000);
}
System.exit(1);
}
return null;
}
};
// Screen erstellen
ImageView img = new ImageView(Resources.codr());
ProgressBar loadProgress = new ProgressBar();
loadProgress.setPrefWidth(400 - 20);
Label progressText = new Label("Die Spiellogiken werden heruntergeladen . . .");
Pane splashLayout = new VBox();
((VBox) splashLayout).setSpacing(5);
((VBox) splashLayout).setAlignment(Pos.CENTER);
splashLayout.getChildren().addAll(img, loadProgress, progressText);
progressText.setAlignment(Pos.CENTER);
splashLayout.setStyle("-fx-padding: 10; " + "-fx-border-color: derive(black, 90%); " + "-fx-border-width:1; " + "-fx-background-color: white;");
progressText.textProperty().bind(downloadTask.messageProperty());
splashLayout.setEffect(new DropShadow());
Scene splashScene = new Scene(splashLayout);
splashStage.initStyle(StageStyle.UNDECORATED);
final Rectangle2D bounds = Screen.getPrimary().getBounds();
splashStage.setScene(splashScene);
splashStage.setX(bounds.getMinX() + bounds.getWidth() / 2 - 400 / 2);
splashStage.setY(bounds.getMinY() + bounds.getHeight() / 2 - 200);
splashStage.setTitle("Codr");
splashStage.getIcons().add(Resources.codrIcon());
splashStage.show();
downloadTask.messageProperty().addListener((observableValue, oldValue, newValue) -> {
if (newValue.equals("Keine Internetverbindung (100)")) {
Dialog.info("Codr braucht beim ersten Start eine Internetverbindung.", "keine Internetverbindung");
System.exit(1);
}
});
downloadTask.stateProperty().addListener((observableValue, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
FadeTransition fadeSplash = new FadeTransition(Duration.seconds(1), splashLayout);
fadeSplash.setFromValue(1.0);
fadeSplash.setToValue(0);
fadeSplash.setOnFinished(actionEvent -> splashStage.hide());
fadeSplash.play();
showMainStage();
}
});
Thread thread = new Thread(downloadTask, "splashUpdate");
thread.setDaemon(true);
thread.start();
}
/**
* Kümmert sich um die Darstellung des Fensters.
*/
public void showMainStage() {
AnchorPane root = new AnchorPane();
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/RootLayout.fxml"));
root = (AnchorPane) loader.load();
((ControllerRoot) loader.getController()).setMainApp(this);
} catch (IOException e) {
ErrorLog.write("RootLayout konnte nicht geladen werden (FXML-Fehler): " + e);
e.printStackTrace();
}
cStart.loadAis();
settings.load(cStart);
stage.setTitle("Codr");
stage.getIcons().add(Resources.codrIcon());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}