From 83bdc39d41ee05f2d4301ceb2c0d4fa7cfbe44f4 Mon Sep 17 00:00:00 2001 From: Josh Larson Date: Sun, 26 Mar 2017 17:49:19 -0500 Subject: [PATCH] Improved the speed of the server and frontend, primarily by making both more efficient --- data/lightspeed.sql | 4 +- .../common/data/HeavyweightBackendData.java | 2 +- .../common/utilities/TimeFormatUtilities.java | 53 +++++ .../lightspeed/CommunicationService.java | 37 +++- .../lightspeed_frontend/Frontend.java | 57 +++--- .../LightspeedFrontendGUI.java | 1 - .../communication/HttpClient.java | 3 +- .../gui/LightspeedPrimaryView.java | 15 +- .../gui/primary_tabs/BuildTab.java | 28 +-- .../gui/primary_tabs/DeploymentTab.java | 45 ++-- .../gui/primary_tabs/GeneralTab.java | 193 +++++++++--------- 11 files changed, 243 insertions(+), 195 deletions(-) create mode 100644 src/com/projectswg/common/utilities/TimeFormatUtilities.java diff --git a/data/lightspeed.sql b/data/lightspeed.sql index cc27af8..439f8ab 100644 --- a/data/lightspeed.sql +++ b/data/lightspeed.sql @@ -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 diff --git a/src/com/projectswg/common/data/HeavyweightBackendData.java b/src/com/projectswg/common/data/HeavyweightBackendData.java index 0ecd061..0ced3ce 100644 --- a/src/com/projectswg/common/data/HeavyweightBackendData.java +++ b/src/com/projectswg/common/data/HeavyweightBackendData.java @@ -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) { diff --git a/src/com/projectswg/common/utilities/TimeFormatUtilities.java b/src/com/projectswg/common/utilities/TimeFormatUtilities.java new file mode 100644 index 0000000..ce4cafc --- /dev/null +++ b/src/com/projectswg/common/utilities/TimeFormatUtilities.java @@ -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 . * +* * +***********************************************************************************/ +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 ""; + } + +} diff --git a/src/com/projectswg/lightspeed/CommunicationService.java b/src/com/projectswg/lightspeed/CommunicationService.java index fd8d10b..343ec65 100644 --- a/src/com/projectswg/lightspeed/CommunicationService.java +++ b/src/com/projectswg/lightspeed/CommunicationService.java @@ -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 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) { diff --git a/src/com/projectswg/lightspeed_frontend/Frontend.java b/src/com/projectswg/lightspeed_frontend/Frontend.java index 581766b..5340b3e 100644 --- a/src/com/projectswg/lightspeed_frontend/Frontend.java +++ b/src/com/projectswg/lightspeed_frontend/Frontend.java @@ -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) { diff --git a/src/com/projectswg/lightspeed_frontend/LightspeedFrontendGUI.java b/src/com/projectswg/lightspeed_frontend/LightspeedFrontendGUI.java index 8aff9b0..69a7c9f 100644 --- a/src/com/projectswg/lightspeed_frontend/LightspeedFrontendGUI.java +++ b/src/com/projectswg/lightspeed_frontend/LightspeedFrontendGUI.java @@ -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"); diff --git a/src/com/projectswg/lightspeed_frontend/communication/HttpClient.java b/src/com/projectswg/lightspeed_frontend/communication/HttpClient.java index 8c933ce..9a5f7fd 100644 --- a/src/com/projectswg/lightspeed_frontend/communication/HttpClient.java +++ b/src/com/projectswg/lightspeed_frontend/communication/HttpClient.java @@ -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_", "") + '&'); diff --git a/src/com/projectswg/lightspeed_frontend/gui/LightspeedPrimaryView.java b/src/com/projectswg/lightspeed_frontend/gui/LightspeedPrimaryView.java index 68d4472..8a3da49 100644 --- a/src/com/projectswg/lightspeed_frontend/gui/LightspeedPrimaryView.java +++ b/src/com/projectswg/lightspeed_frontend/gui/LightspeedPrimaryView.java @@ -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)); }); } diff --git a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/BuildTab.java b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/BuildTab.java index 1bd38e7..af62eeb 100644 --- a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/BuildTab.java +++ b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/BuildTab.java @@ -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])); diff --git a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/DeploymentTab.java b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/DeploymentTab.java index dadcab9..7dcfb09 100644 --- a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/DeploymentTab.java +++ b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/DeploymentTab.java @@ -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])); diff --git a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/GeneralTab.java b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/GeneralTab.java index a9ba434..5a6f7ab 100644 --- a/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/GeneralTab.java +++ b/src/com/projectswg/lightspeed_frontend/gui/primary_tabs/GeneralTab.java @@ -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 { public String toString(SharedServerData server) {