Improved the speed of the server and frontend, primarily by making both more efficient

This commit is contained in:
Josh Larson
2017-03-26 17:49:19 -05:00
parent 8678a2b60a
commit 83bdc39d41
11 changed files with 243 additions and 195 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS builds (
id INTEGER PRIMARY KEY,
time INTEGER,
server_id TEXT REFERENCES servers(id),
server_id TEXT,
build_string TEXT,
test_string TEXT,
compile_time REAL,
@@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS builds (
CREATE TABLE IF NOT EXISTS deployments (
id INTEGER PRIMARY KEY,
server_id TEXT REFERENCES servers(id),
server_id TEXT,
build_id INTEGER REFERENCES builds(id),
start_time INTEGER,
end_time INTEGER
@@ -87,6 +87,7 @@ public class HeavyweightBackendData implements BackendData {
for (String serverId : serverTable.getServerStrings()) {
ServerServerData server = createServerFromInfo(serverTable.getServerById(serverId));
fetchBuildHistory(db, server);
fetchDeploymentHistory(db, server);
serverMap.put(serverId, server);
}
}
@@ -101,7 +102,6 @@ public class HeavyweightBackendData implements BackendData {
server.addBuild(build);
}
}
fetchDeploymentHistory(db, server);
}
private void fetchDeploymentHistory(RelationalDatabase db, ServerServerData server) {
@@ -0,0 +1,53 @@
/***********************************************************************************
* Copyright (c) 2015 /// Project SWG /// www.projectswg.com *
* *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
* *
* This file is part of Holocore. *
* *
* -------------------------------------------------------------------------------- *
* *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package com.projectswg.common.utilities;
public class TimeFormatUtilities {
public static String getTime(long time) {
int days = (int) (time / (24 * 3600E3));
int hours = (int) (time % (24 * 3600E3) / 3600E3);
int minutes = (int) ((time % 3600E3) / 60E3);
int seconds = (int) (time % 60E3) / 1000;
String text = "";
text += getPlurality("Day", days);
text += getPlurality("Hour", hours);
text += getPlurality("Minute", minutes);
text += getPlurality("Second", seconds);
return text;
}
private static String getPlurality(String base, int amount) {
if (amount > 1)
return amount + " " + base + "s ";
if (amount == 1)
return amount + " " + base + " ";
return "";
}
}
@@ -38,6 +38,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@@ -55,6 +58,7 @@ import org.nanohttpd.protocols.http.threading.IAsyncRunner;
import resources.common.BCrypt;
import me.joshlarson.json.JSONObject;
import com.projectswg.common.concurrency.PswgBasicScheduledThread;
import com.projectswg.common.concurrency.PswgThreadPool;
import com.projectswg.common.control.LightspeedService;
import com.projectswg.common.data.CsvTable;
@@ -70,8 +74,10 @@ public class CommunicationService extends LightspeedService {
private final NanoHTTPD httpServer;
private final Map<PacketType, HttpResponder> responders;
private final PswgBasicScheduledThread userTableUpdater;
private final BoundRunner runner;
private final CsvTable users;
private final ReadWriteLock userLock;
public CommunicationService() {
this.httpServer = new NanoHTTPD(44444) {
@@ -80,8 +86,10 @@ public class CommunicationService extends LightspeedService {
}
};
this.responders = new HashMap<>();
this.runner = new BoundRunner(20);
this.userTableUpdater = new PswgBasicScheduledThread("comm-user-table-updater", () -> updateUserTable());
this.runner = new BoundRunner(5);
this.users = new CsvTable(new File("data/users.csv"));
this.userLock = new ReentrantReadWriteLock(true);
httpServer.setAsyncRunner(runner);
registerForIntent(RegisterHttpListenerIntent.class, rhli -> handleRegisterListener(rhli));
@@ -97,6 +105,7 @@ public class CommunicationService extends LightspeedService {
File cert = new File("data", pubkey);
httpServer.makeSecure(createSSLSocketFactory(cert, pass), null);
}
userTableUpdater.startWithFixedDelay(0, 60000);
runner.start();
httpServer.start(config.getInt("HTTP-TIMEOUT", 2000), false);
} catch (IOException e) {
@@ -108,6 +117,7 @@ public class CommunicationService extends LightspeedService {
@Override
public boolean terminate() {
userTableUpdater.stop();
runner.stop();
httpServer.stop();
return super.terminate();
@@ -132,6 +142,18 @@ public class CommunicationService extends LightspeedService {
}
}
private void updateUserTable() {
Lock write = userLock.writeLock();
try {
write.lock();
users.read();
} catch (IOException e) {
Log.e(e);
} finally {
write.unlock();
}
}
private boolean authenticate(String combined, String host, String addr) {
if (combined == null)
return false;
@@ -143,12 +165,9 @@ public class CommunicationService extends LightspeedService {
split = combined.split(":", 2);
String user = split[0];
String pass = split[1];
synchronized (users) {
try {
users.read();
} catch (IOException e) {
Log.e(e);
}
Lock readLock = userLock.readLock();
try {
readLock.lock();
for (int i = 0; i < users.getRows(); i++) {
if (users.getRecord(i, "user").equals(user)) {
String recordedPass = users.getRecord(i, "pass");
@@ -159,8 +178,10 @@ public class CommunicationService extends LightspeedService {
return valid;
}
}
return false;
} finally {
readLock.unlock();
}
return false;
}
private Response serve(IHTTPSession session) {
@@ -55,7 +55,6 @@ import com.projectswg.common.network.packets.response.ResponseServerListPacket;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication;
import com.projectswg.lightspeed_frontend.communication.FrontendCommunication.ResponseCallback;
import com.projectswg.lightspeed_frontend.data.SharedBuildData;
import com.projectswg.lightspeed_frontend.data.SharedBuildData.BuildState;
import com.projectswg.lightspeed_frontend.data.SharedDeploymentData;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
import com.projectswg.lightspeed_frontend.intents.InboundPacketIntent;
@@ -70,7 +69,8 @@ public class Frontend {
private final Config config;
public Frontend() {
this.threadPool = new PswgThreadPool(10, "frontend-thread-pool");
this.threadPool = new PswgThreadPool(5, "frontend-thread-pool");
this.threadPool.setWaitForTermination(false);
this.scheduledUpdater = new PswgScheduledThreadPool(1, "frontend-updater");
this.data = new FrontendData();
File data = new File("cfg");
@@ -84,15 +84,21 @@ public class Frontend {
}
public void setAddress(InetSocketAddress address) {
communication.setAddress(address);
Log.i("Changing remote address to %s", address);
if (!address.equals(communication.getAddress())) {
communication.setAddress(address);
Log.i("Clearing all frontend data - requesting complete reload");
data.clearAll();
requestCompleteLoad();
}
}
public void start() {
Assert.test(communication.start());
threadPool.start();
scheduledUpdater.start();
scheduledUpdater.executeWithFixedDelay(5000, 5000, () -> requestCompleteLoad());
scheduledUpdater.executeWithFixedDelay(1000, 1000, () -> requestLatestUpdates());
scheduledUpdater.executeWithFixedDelay(0, 10000, () -> requestCompleteLoad());
scheduledUpdater.executeWithFixedDelay(3000, 3000, () -> requestLatestUpdates());
}
public void stop() {
@@ -132,7 +138,7 @@ public class Frontend {
send(p, null);
}
public void loadServerDetailed(String serverId, ResponseCallback callback) {
private void loadServerDetailed(String serverId, ResponseCallback callback) {
send(new RequestServerDetailedPacket(serverId), callback);
}
@@ -144,27 +150,19 @@ public class Frontend {
send(new RequestDeploymentDetailedPacket(deploymentId), callback);
}
public void loadBuildHistory(String serverId, ResponseCallback callback) {
private void loadBuildHistory(String serverId, ResponseCallback callback) {
send(new RequestBuildListPacket(serverId), callback);
}
public void loadDeploymentHistory(String serverId, ResponseCallback callback) {
private void loadDeploymentHistory(String serverId, ResponseCallback callback) {
send(new RequestDeploymentListPacket(serverId), callback);
}
public void requestCompleteLoad() {
private void requestCompleteLoad() {
send(new RequestServerListPacket(), (response) -> {
for (SharedServerData server : ((ResponseServerListPacket) response).getServerList()) {
loadBuildHistory(server.getName(), buildResponse -> {
SharedBuildData lastBuild = data.getServer(server.getName()).getLastBuild();
if (lastBuild != null)
loadBuildDetailed(lastBuild.getId(), null);
});
loadDeploymentHistory(server.getName(), deployResponse -> {
SharedDeploymentData lastDeployment = data.getServer(server.getName()).getLastDeployment();
if (lastDeployment != null)
loadDeploymentDetailed(lastDeployment.getId(), null);
});
loadBuildHistory(server.getName(), null);
loadDeploymentHistory(server.getName(), null);
}
});
}
@@ -173,15 +171,14 @@ public class Frontend {
SharedServerData server = data.getSelectedServer();
if (server == null)
return;
SharedBuildData lastBuild = data.getServer(server.getName()).getLastBuild();
if (lastBuild != null) {
BuildState state = lastBuild.getBuildState();
if (state.isInProgress())
loadBuildDetailed(lastBuild.getId(), null);
SharedBuildData lastBuild = server.getLastBuild();
if (lastBuild != null && lastBuild.getBuildState().isInProgress()) {
loadBuildDetailed(lastBuild.getId(), null);
}
SharedDeploymentData lastDeployment = data.getServer(server.getName()).getLastDeployment();
if (lastDeployment != null)
SharedDeploymentData lastDeployment = server.getLastDeployment();
if (lastDeployment != null) {
loadDeploymentDetailed(lastDeployment.getId(), null);
}
}
private void process(Packet packet) {
@@ -223,11 +220,15 @@ public class Frontend {
private void processBuildList(ResponseBuildListPacket list) {
SharedServerData server = data.getServer(list.getServerId());
SharedBuildData last = server.getLastBuild();
for (SharedBuildData build : list.getBuildList()) {
if (server.getBuildData(build.getId()) == null) {
server.addBuild(build);
}
}
SharedBuildData nLast = server.getLastBuild();
if (last != nLast && nLast != null)
loadBuildDetailed(nLast.getId(), null);
}
private void processBuildDetailed(ResponseBuildDetailedPacket detailed) {
@@ -247,11 +248,15 @@ public class Frontend {
private void processDeploymentList(ResponseDeploymentListPacket list) {
SharedServerData server = data.getServer(list.getServerId());
SharedDeploymentData last = server.getLastDeployment();
for (SharedDeploymentData deployment : list.getDeploymentList()) {
if (server.getDeploymentData(deployment.getId()) == null) {
server.addDeployment(deployment);
}
}
SharedDeploymentData nLast = server.getLastDeployment();
if (last != nLast && nLast != null)
loadDeploymentDetailed(nLast.getId(), null);
}
private void processDeploymentDetailed(ResponseDeploymentDetailedPacket detailed) {
@@ -66,7 +66,6 @@ public class LightspeedFrontendGUI extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
IntentManager.getInstance().initialize();
frontend.requestCompleteLoad();
primaryView = (LightspeedPrimaryView) FXMLUtilities.loadFxml("PrimaryView.fxml", Locale.ENGLISH);
Parent root = primaryView.getRoot();
primaryStage.setTitle("Lightspeed Frontend");
@@ -98,7 +98,9 @@ public class HttpClient {
return null;
}
try {
long start = System.nanoTime();
String str = requestString(address, packet);
Log.d("Took %.3fms to request and receive a packet", (System.nanoTime()-start)/1E6);
JSONObject obj = JSON.readObject(str, false);
if (obj == null) {
Log.d("Invalid JSON: URL=%s%n%s", address, str);
@@ -140,7 +142,6 @@ public class HttpClient {
private static String generateURL(InetSocketAddress address, Packet packet) throws UnsupportedEncodingException {
StringBuilder url = new StringBuilder("https://"+address.getAddress().getHostAddress()+":"+address.getPort()+"/?");
Log.d("URL: %s", url);
JSONObject obj = packet.getJSON();
if (packet instanceof PostPacket)
url.append("type=" + ((PostPacket) packet).getType().name().replace("POST_", "") + '&');
@@ -36,7 +36,6 @@ import java.util.ResourceBundle;
import com.projectswg.common.concurrency.PswgBasicScheduledThread;
import com.projectswg.common.concurrency.PswgThreadPool;
import com.projectswg.common.debug.Log;
import com.projectswg.common.javafx.FXMLController;
import com.projectswg.common.javafx.FXMLUtilities;
import com.projectswg.common.network.packets.post.PostBuildPacket;
@@ -127,18 +126,8 @@ public class LightspeedPrimaryView implements FXMLController {
private void updateAddress() {
ipUpdater.execute(() -> {
InetSocketAddress addr = createAddress();
InetSocketAddress prev = frontend.getCommunication().getAddress();
boolean changed = (prev == null || addr == null) ? prev != addr : !prev.equals(addr);
if (changed) {
Log.i("Changing remote address to %s", addr);
frontend.setAddress(addr);
Platform.runLater(() -> validIpRegion.setBackground(addr == null ? invalidIpBackground : validIpBackground));
if (addr != null) {
Log.i("Clearing all frontend data - requesting complete reload");
frontend.getData().clearAll();
frontend.requestCompleteLoad();
}
}
frontend.setAddress(addr);
Platform.runLater(() -> validIpRegion.setBackground(addr == null ? invalidIpBackground : validIpBackground));
});
}
@@ -32,7 +32,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import com.projectswg.common.concurrency.PswgBasicScheduledThread;
import com.projectswg.common.control.IntentManager;
import com.projectswg.common.data.TestDetails;
import com.projectswg.common.debug.Log;
import com.projectswg.common.javafx.FXMLController;
@@ -43,6 +43,7 @@ import com.projectswg.lightspeed_frontend.LightspeedFrontendGUI;
import com.projectswg.lightspeed_frontend.data.SharedBuildData;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
import com.projectswg.lightspeed_frontend.data.SharedBuildData.BuildState;
import com.projectswg.lightspeed_frontend.intents.InboundPacketIntent;
import javafx.application.Platform;
import javafx.fxml.FXML;
@@ -60,11 +61,9 @@ public class BuildTab implements FXMLController {
@FXML
private Label serverNameText, buildIdText, statusText, timeText, compileTimeText, testText;
private final PswgBasicScheduledThread scheduledUpdater;
private final Frontend frontend;
public BuildTab() {
this.scheduledUpdater = new PswgBasicScheduledThread("build-tab-updater", () -> requestUpdate());
this.frontend = LightspeedFrontendGUI.FRONTEND;
}
@@ -90,22 +89,9 @@ public class BuildTab implements FXMLController {
frontend.loadBuildDetailed(next.getId(), response -> Platform.runLater(() -> updateBuildDetails()));
updateBuildDetails();
});
IntentManager.getInstance().registerForIntent(InboundPacketIntent.class, ipi -> updateUi());
this.frontend.getData().getSelectedServerProperty().addListener((val, prev, next) -> updateUi());
updateUi();
scheduledUpdater.startWithFixedRate(10000, 10000);
}
@Override
public void terminate() {
scheduledUpdater.stop();
scheduledUpdater.awaitTermination(1000);
}
private void requestUpdate() {
SharedBuildData selected = frontend.getData().getSelectedBuild();
if (selected != null) {
frontend.loadBuildDetailed(selected.getId(), response -> updateUi());
}
}
private void updateUi() {
@@ -138,6 +124,10 @@ public class BuildTab implements FXMLController {
return 1;
return Long.compare(b.getId(), a.getId());
});
if (buildComboBox.getSelectionModel().getSelectedItem() == null) {
frontend.getData().setSelectedBuild(selectedServer.getLastBuild());
updateBuildDetails();
}
} else {
buildComboBox.getItems().clear();
buildComboBox.getItems().add(null);
@@ -262,12 +252,12 @@ public class BuildTab implements FXMLController {
public String toString(SharedBuildData build) {
if (build == null)
return "";
return "latest";
return "B " + build.getId();
}
public SharedBuildData fromString(String string) {
if (string.isEmpty())
if (string.isEmpty() || string.equals("latest"))
return null;
String [] parts = string.split(" ");
return frontend.getData().getBuild(Long.parseLong(parts[1]));
@@ -34,14 +34,17 @@ import java.util.Locale;
import java.util.ResourceBundle;
import com.projectswg.common.concurrency.PswgScheduledThreadPool;
import com.projectswg.common.control.IntentManager;
import com.projectswg.common.debug.Log;
import com.projectswg.common.javafx.FXMLController;
import com.projectswg.common.javafx.FXMLUtilities;
import com.projectswg.common.utilities.TimeFormatUtilities;
import com.projectswg.common.utilities.TimeUtilities;
import com.projectswg.lightspeed_frontend.Frontend;
import com.projectswg.lightspeed_frontend.LightspeedFrontendGUI;
import com.projectswg.lightspeed_frontend.data.SharedDeploymentData;
import com.projectswg.lightspeed_frontend.data.SharedServerData;
import com.projectswg.lightspeed_frontend.intents.InboundPacketIntent;
import javafx.application.Platform;
import javafx.fxml.FXML;
@@ -102,10 +105,10 @@ public class DeploymentTab implements FXMLController {
frontend.loadDeploymentDetailed(next.getId(), response -> Platform.runLater(() -> updateDeploymentDetails()));
updateDeploymentDetails();
});
IntentManager.getInstance().registerForIntent(InboundPacketIntent.class, ipi -> updateUi());
this.frontend.getData().getSelectedServerProperty().addListener((val, prev, next) -> updateUi());
updateUi();
scheduledUpdater.start();
scheduledUpdater.executeWithFixedRate(10000, 10000, () -> requestUpdate());
scheduledUpdater.executeWithFixedRate(1000, 1000, () -> Platform.runLater(() -> updateDeploymentDetails()));
}
@@ -115,13 +118,6 @@ public class DeploymentTab implements FXMLController {
scheduledUpdater.awaitTermination(1000);
}
private void requestUpdate() {
SharedDeploymentData selected = frontend.getData().getSelectedDeployment();
if (selected != null) {
frontend.loadDeploymentDetailed(selected.getId(), response -> updateUi());
}
}
private void updateUi() {
Platform.runLater(() -> {
updateDeploymentList();
@@ -152,6 +148,10 @@ public class DeploymentTab implements FXMLController {
return 1;
return Long.compare(b.getId(), a.getId());
});
if (deploymentComboBox.getSelectionModel().getSelectedItem() == null) {
frontend.getData().setSelectedDeployment(selectedServer.getLastDeployment());
updateDeploymentDetails();
}
} else {
deploymentComboBox.getItems().clear();
deploymentComboBox.getItems().add(null);
@@ -231,9 +231,9 @@ public class DeploymentTab implements FXMLController {
long start = selectedDeployment.getStartTime();
long end = selectedDeployment.getEndTime();
if (end == 0) {
uptimeText.setText(getTime(TimeUtilities.getTime()-start));
uptimeText.setText(TimeFormatUtilities.getTime(TimeUtilities.getTime()-start));
} else {
uptimeText.setText(getTime(end-start));
uptimeText.setText(TimeFormatUtilities.getTime(end-start));
}
}
}
@@ -256,27 +256,6 @@ public class DeploymentTab implements FXMLController {
}
}
private String getTime(long time) {
int days = (int) (time / (24*3600E3));
int hours = (int) (time % (24*3600E3) / 3600E3);
int minutes = (int) ((time % 3600E3) / 60E3);
int seconds = (int) (time % 60E3) / 1000;
String text = "";
text += getPlurality("Day", days);
text += getPlurality("Hour", hours);
text += getPlurality("Minute", minutes);
text += getPlurality("Second", seconds);
return text;
}
private String getPlurality(String base, int amount) {
if (amount > 1)
return amount + " " + base + "s ";
if (amount == 1)
return amount + " " + base + " ";
return "";
}
public interface SelectedDeploymentChangedCallback {
void onDeploymentChanged(SharedDeploymentData deployment);
}
@@ -285,12 +264,12 @@ public class DeploymentTab implements FXMLController {
public String toString(SharedDeploymentData deployment) {
if (deployment == null)
return "";
return "latest";
return "D " + deployment.getId() + " - B " + deployment.getBuild().getId();
}
public SharedDeploymentData fromString(String string) {
if (string.isEmpty())
if (string.isEmpty() || string.equals("latest"))
return null;
String [] parts = string.split(" ");
return frontend.getData().getDeployment(Long.parseLong(parts[1]));
@@ -29,12 +29,14 @@ package com.projectswg.lightspeed_frontend.gui.primary_tabs;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.prefs.Preferences;
import com.projectswg.common.concurrency.PswgBasicScheduledThread;
import com.projectswg.common.concurrency.PswgScheduledThreadPool;
import com.projectswg.common.control.IntentManager;
import com.projectswg.common.debug.Log;
import com.projectswg.common.javafx.FXMLController;
import com.projectswg.common.javafx.FXMLUtilities;
import com.projectswg.common.utilities.TimeFormatUtilities;
import com.projectswg.common.utilities.TimeUtilities;
import com.projectswg.lightspeed_frontend.Frontend;
import com.projectswg.lightspeed_frontend.LightspeedFrontendGUI;
@@ -68,11 +70,11 @@ public class GeneralTab implements FXMLController {
@FXML
private Label uptimeText;
private final PswgBasicScheduledThread scheduledUpdater;
private final PswgScheduledThreadPool scheduledUpdater;
private final Frontend frontend;
public GeneralTab() {
this.scheduledUpdater = new PswgBasicScheduledThread("general-tab-updater", () -> requestUpdate());
this.scheduledUpdater = new PswgScheduledThreadPool(1, "general-tab-updater");
this.frontend = LightspeedFrontendGUI.FRONTEND;
}
@@ -93,11 +95,14 @@ public class GeneralTab implements FXMLController {
serverComboBox.getSelectionModel().selectedItemProperty().addListener((val, prev, next) -> {
Log.d("selected server manually '%s'", next);
frontend.getData().setSelectedServer(next);
updateUiDetails();
if (next != null)
getPreferences().put("selected-server", next.getName());
updateGeneralDetails();
});
IntentManager.getInstance().registerForIntent(InboundPacketIntent.class, ipi -> Platform.runLater(() -> updateUi()));
IntentManager.getInstance().registerForIntent(InboundPacketIntent.class, ipi -> updateUi());
updateUi();
scheduledUpdater.startWithFixedRate(10000, 10000);
scheduledUpdater.start();
scheduledUpdater.executeWithFixedRate(1000, 1000, () -> Platform.runLater(() -> updateGeneralDetails()));
}
@Override
@@ -106,138 +111,144 @@ public class GeneralTab implements FXMLController {
scheduledUpdater.awaitTermination(1000);
}
private void requestUpdate() {
SharedServerData selected = frontend.getData().getSelectedServer();
if (selected != null) {
frontend.loadServerDetailed(selected.getName(), response -> updateUi());
}
}
private void updateUi() {
Platform.runLater(() -> {
updateServerList();
updateUiDetails();
updateGeneralDetails();
});
}
private void updateServerList() {
serverComboBox.setItems(frontend.getData().getServers());
}
private void updateUiDetails() {
SharedServerData selected = frontend.getData().getSelectedServer();
if (selected == null) {
setServerName("N/A");
setEmptyBuildStatus();
setEmptyDeploymentStatus();
setEmptyPopulationText();
setEmptyUptimeText();
} else {
SharedBuildData lastBuild = selected.getLastBuild();
SharedDeploymentData lastDeployment = selected.getLastDeployment();
setServerName(selected == null ? "N/A" : selected.getName());
if (lastBuild == null)
setEmptyBuildStatus();
else
setBuildStatus(lastBuild.getBuildState());
if (lastDeployment == null) {
setEmptyDeploymentStatus();
} else {
if (lastDeployment.getEndTime() != -1) {
setDeploymentStatus(false);
setEmptyPopulationText();
setUptimeText(lastDeployment.getEndTime()-lastDeployment.getStartTime());
} else {
setDeploymentStatus(true);
setPopulationText(0);
setUptimeText(TimeUtilities.getTime()-lastDeployment.getStartTime());
}
}
if (frontend.getData().getSelectedServer() == null) {
// If the user chose some other server last time around - use that, otherwise default to NGE
serverComboBox.getSelectionModel().select(frontend.getData().getServer(getPreferences().get("selected-server", "NGE")));
}
}
private void setServerName(String name) {
serverNameText.setText(name);
private void updateGeneralDetails() {
SharedServerData selected = frontend.getData().getSelectedServer();
setServerName(selected);
setBuildStatus(selected);
setDeploymentStatus(selected);
setPopulationText(selected);
setUptimeText(selected);
}
private void setEmptyBuildStatus() {
buildStatusText.getStyleClass().setAll("white-label");
buildStatusText.setText("N/A");
private void setServerName(SharedServerData server) {
String name = server == null ? "N/A" : server.getName();
setText(serverNameText, TextStyle.WHITE, name);
}
private void setBuildStatus(BuildState state) {
private void setBuildStatus(SharedServerData server) {
if (server == null) {
setText(buildStatusText, TextStyle.WHITE, "N/A");
return;
}
SharedBuildData lastBuild = server.getLastBuild();
if (lastBuild == null) {
setText(buildStatusText, TextStyle.WHITE, "Not Built");
return;
}
BuildState state = lastBuild.getBuildState();
TextStyle style = TextStyle.WHITE;
switch (state) {
case CREATED:
case UNKNOWN:
buildStatusText.getStyleClass().setAll("white-label");
style = TextStyle.WHITE;
break;
case CANCELLED:
case COMPILE_FAILED:
case TEST_FAILED:
case INSTALL_FAILED:
case FAILED:
buildStatusText.getStyleClass().setAll("red-label");
style = TextStyle.RED;
break;
case COMPILING:
case TESTING:
case INSTALLING:
case SUCCESS:
buildStatusText.getStyleClass().setAll("green-label");
style = TextStyle.GREEN;
break;
}
buildStatusText.setText(state.toString());
setText(buildStatusText, style, state.toString());
}
private void setEmptyDeploymentStatus() {
deploymentStatusText.getStyleClass().setAll("white-label");
deploymentStatusText.setText("N/A");
private void setDeploymentStatus(SharedServerData server) {
if (server == null) {
setText(deploymentStatusText, TextStyle.WHITE, "N/A");
return;
}
SharedDeploymentData deployment = server.getLastDeployment();
if (deployment == null) {
setText(deploymentStatusText, TextStyle.WHITE, "Not Deployed");
return;
}
if (deployment.getEndTime() == 0) {
setText(deploymentStatusText, TextStyle.GREEN, "Running");
} else {
setText(deploymentStatusText, TextStyle.WHITE, "Stopped");
}
}
private void setDeploymentStatus(boolean running) {
deploymentStatusText.getStyleClass().setAll(running ? "green-label" : "white-label");
deploymentStatusText.setText(running ? "Running" : "Idle");
private void setPopulationText(SharedServerData server) {
if (server == null) {
setText(populationText, TextStyle.WHITE, "N/A");
return;
}
SharedDeploymentData deployment = server.getLastDeployment();
if (deployment == null) {
setText(populationText, TextStyle.WHITE, "Not Deployed");
return;
}
populationText.setText(String.format("%d Characters Online", 0));
}
private void setEmptyPopulationText() {
populationText.setText("N/A");
private void setUptimeText(SharedServerData server) {
if (server == null) {
setText(uptimeText, TextStyle.WHITE, "N/A");
return;
}
SharedDeploymentData deployment = server.getLastDeployment();
if (deployment == null) {
setText(uptimeText, TextStyle.WHITE, "Not Deployed");
return;
}
long uptime;
TextStyle style;
if (deployment.getEndTime() == 0) {
uptime = TimeUtilities.getTime() - deployment.getStartTime();
style = TextStyle.GREEN;
} else {
uptime = deployment.getEndTime() - deployment.getStartTime();
style = TextStyle.WHITE;
}
setText(uptimeText, style, TimeFormatUtilities.getTime(uptime));
}
private void setPopulationText(int count) {
populationText.setText(String.format("%d Characters Online", count));
private void setText(Label label, TextStyle style, String text) {
switch (style) {
case WHITE: label.getStyleClass().setAll("white-label"); break;
case GREEN: label.getStyleClass().setAll("green-label"); break;
case RED: label.getStyleClass().setAll("red-label"); break;
}
label.setText(text);
}
private void setEmptyUptimeText() {
uptimeText.setText("N/A");
}
private void setUptimeText(long uptime) {
uptime /= 1000;
int seconds = (int) (uptime % 60);
uptime /= 60;
int minutes = (int) (uptime % 60);
uptime /= 60;
int hours = (int) (uptime % 24);
uptime /= 24;
String text = "";
text += getPlurality("Day", (int) uptime);
text += getPlurality("Hour", hours);
text += getPlurality("Minute", minutes);
text += getPlurality("Second", seconds);
uptimeText.setText(text);
}
private String getPlurality(String base, int amount) {
if (amount > 1)
return amount + " " + base + "s ";
if (amount == 1)
return amount + " " + base + " ";
return "";
private Preferences getPreferences() {
return frontend.getPreferences().node("general_tab");
}
public interface SelectedServerChangedCallback {
void onServerChanged(SharedServerData server);
}
private enum TextStyle {
WHITE,
GREEN,
RED
}
private class ServerStringConverter extends StringConverter<SharedServerData> {
public String toString(SharedServerData server) {