diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index 3c4a4ff..e778d23 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -299,6 +299,12 @@ logback-classic ${logback.version} + + com.dustinredmond.fxtrayicon + FXTrayIcon + 4.0.1 + + diff --git a/G-Earth/src/main/java/gearth/GEarth.java b/G-Earth/src/main/java/gearth/GEarth.java index cc5fb52..2b6a696 100644 --- a/G-Earth/src/main/java/gearth/GEarth.java +++ b/G-Earth/src/main/java/gearth/GEarth.java @@ -1,41 +1,33 @@ package gearth; import gearth.misc.AdminValidator; -import gearth.misc.Cacher; import gearth.misc.UpdateChecker; -import gearth.misc.listenerpattern.ObservableObject; import gearth.ui.GEarthController; +import gearth.ui.GEarthProperties; import gearth.ui.themes.Theme; -import gearth.ui.themes.ThemeFactory; import gearth.ui.titlebar.TitleBarConfig; import gearth.ui.titlebar.TitleBarController; import javafx.application.Application; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Alert; -import javafx.scene.image.Image; import javafx.stage.Stage; import javafx.stage.StageStyle; +import java.io.IOException; + public class GEarth extends Application { public static GEarth main; public static String version = "1.5.3"; public static String gitApi = "https://api.github.com/repos/sirjonasxx/G-Earth/releases/latest"; - public static ObservableObject observableTheme; private Stage stage; private GEarthController controller; - static { - observableTheme = new ObservableObject<>( - Cacher.getCacheContents().has("theme") ? - ThemeFactory.themeForTitle(Cacher.getCacheContents().getString("theme")) : - ThemeFactory.getDefaultTheme() - ); - } @Override public void start(Stage primaryStage) throws Exception{ @@ -52,9 +44,28 @@ public class GEarth extends Application { } controller = loader.getController(); controller.setStage(primaryStage); - stage.initStyle(StageStyle.TRANSPARENT); + primaryStage.initStyle(StageStyle.TRANSPARENT); primaryStage.setScene(new Scene(root)); + primaryStage.setAlwaysOnTop(GEarthProperties.isAlwaysOnTop()); + GEarthProperties.alwaysOnTopProperty + .addListener((observable, oldValue, newValue) -> primaryStage.setAlwaysOnTop(newValue)); + + initTitleBar(primaryStage); + initTheme(); + + primaryStage.setResizable(false); + primaryStage.sizeToScene(); + + primaryStage.show(); + primaryStage.setOnCloseRequest(event -> closeGEarth()); + + AdminValidator.validate(); + UpdateChecker.checkForUpdates(); + + } + + private void initTitleBar(Stage primaryStage) throws IOException { TitleBarController.create(primaryStage, new TitleBarConfig() { @Override public boolean displayThemePicker() { @@ -66,11 +77,6 @@ public class GEarth extends Application { return true; } -// @Override -// public boolean allowResizing() { -// return false; -// } - @Override public void onCloseClicked() { closeGEarth(); @@ -83,25 +89,20 @@ public class GEarth extends Application { @Override public void setTheme(Theme theme) { - setGearthTheme(theme); + GEarthProperties.themeProperty.set(theme); } @Override public Theme getCurrentTheme() { - return observableTheme.getObject(); + return GEarthProperties.getTheme(); } }); - primaryStage.setResizable(false); - primaryStage.sizeToScene(); - - setGearthTheme(observableTheme.getObject()); - - primaryStage.show(); - primaryStage.setOnCloseRequest(event -> closeGEarth()); - - AdminValidator.validate(); - UpdateChecker.checkForUpdates(); + } + private void initTheme() { + stage.titleProperty().bind(GEarthProperties.themeTitleBinding); + Bindings.bindContent(stage.getScene().getStylesheets(), GEarthProperties.styleSheets); + Bindings.bindContent(stage.getIcons(), GEarthProperties.icons); } private void closeGEarth() { @@ -110,30 +111,6 @@ public class GEarth extends Application { System.exit(0); } - private void setGearthTheme(Theme theme) { - Cacher.put("theme", theme.title()); - observableTheme.setObject(theme); - Theme defaultTheme = ThemeFactory.getDefaultTheme(); - -// Platform.runLater(() -> { - stage.getScene().getStylesheets().clear(); - stage.getScene().getStylesheets().add(GEarth.class.getResource(String.format("/gearth/ui/themes/%s/styling.css", theme.internalName())).toExternalForm()); - - stage.getIcons().clear(); - stage.getIcons().add(new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName())))); - stage.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version); - - controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream( - String.format( - "/gearth/ui/themes/%s/logo.png", - theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName() - ) - ))); - controller.infoController.version.setText(stage.getTitle()); -// }); - - } - public static String[] args; public static void main(String[] args) { @@ -161,14 +138,6 @@ public class GEarth extends Application { return null; } - public static ObservableObject getThemeObservable() { - return observableTheme; - } - - public static Theme getTheme() { - return observableTheme.getObject(); - } - public static void setAlertOwner(Alert alert) { alert.initOwner(main.stage); } diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java b/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java index d801aeb..b3feef8 100644 --- a/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java @@ -2,19 +2,14 @@ package gearth.extensions; import gearth.misc.HostInfo; import gearth.misc.listenerpattern.Observable; -import gearth.misc.listenerpattern.ObservableObject; import gearth.protocol.HMessage; import gearth.protocol.HPacket; import gearth.services.packet_info.PacketInfo; import gearth.services.packet_info.PacketInfoManager; import javafx.beans.property.ObjectProperty; -import org.reactfx.util.Lists; +import javafx.beans.property.SimpleObjectProperty; import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Stream; public abstract class ExtensionBase extends IExtension { @@ -33,10 +28,11 @@ public abstract class ExtensionBase extends IExtension { volatile PacketInfoManager packetInfoManager = PacketInfoManager.EMPTY; - ObservableObject observableHostInfo = new ObservableObject<>(null); + + ObjectProperty hostInfoProperty = new SimpleObjectProperty<>(); void updateHostInfo(HostInfo hostInfo) { - observableHostInfo.setObject(hostInfo); + hostInfoProperty.set(hostInfo); } /** @@ -67,7 +63,7 @@ public abstract class ExtensionBase extends IExtension { * @param messageListener the callback */ public void intercept(HMessage.Direction direction, String hashOrName, MessageListener messageListener) { - Map> listeners = + final Map> listeners = direction == HMessage.Direction.TOCLIENT ? hashOrNameIncomingListeners : hashOrNameOutgoingListeners; @@ -197,6 +193,6 @@ public abstract class ExtensionBase extends IExtension { } public HostInfo getHostInfo() { - return observableHostInfo.getObject(); + return hostInfoProperty.get(); } } diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java b/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java index 6922c93..8ebe983 100644 --- a/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java @@ -1,17 +1,17 @@ package gearth.extensions; +import com.sun.org.apache.xpath.internal.operations.Bool; import gearth.misc.HostInfo; import gearth.misc.listenerpattern.Observable; import gearth.services.packet_info.PacketInfoManager; import javafx.application.HostServices; import javafx.application.Platform; -import javafx.beans.InvalidationListener; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.stage.Stage; import gearth.protocol.HMessage; import gearth.protocol.HPacket; -import java.util.function.Consumer; - /** * Created by Jonas on 22/09/18. */ @@ -99,8 +99,17 @@ public abstract class ExtensionForm extends ExtensionBase { } public HostInfo getHostInfo() { - return extension.observableHostInfo.getObject(); + return extension.hostInfoProperty.get(); + } + + private final BooleanProperty fieldsInitializedProperty = new SimpleBooleanProperty(false); + + public void setFieldsInitialised(boolean value) { + fieldsInitializedProperty.set(value); + } + + public BooleanProperty fieldsInitialisedProperty() { + return fieldsInitializedProperty; } - Observable fieldsInitialized = new Observable<>(Runnable::run); } diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java b/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java index 8f67369..1cde28a 100644 --- a/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java @@ -60,7 +60,8 @@ public class ExtensionFormLauncher extends Application { extensionForm.extension = extension; extensionForm.primaryStage = primaryStage; - extensionForm.fieldsInitialized.fireEvent(); + extensionForm.setFieldsInitialised(true); + Thread t = new Thread(() -> { extension.run(); //when the extension has ended, close this process diff --git a/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormLauncher.java b/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormLauncher.java index ecb8ff2..4fab268 100644 --- a/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormLauncher.java +++ b/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormLauncher.java @@ -55,7 +55,7 @@ public class InternalExtensionFormLauncher { - primaryStage.getScene().getRoot().getStyleClass().add(defaultTheme.title().replace(" ", "-").toLowerCase()); - primaryStage.getScene().getRoot().getStyleClass().add(defaultTheme.isDark() ? "g-dark" : "g-light"); - }); + Platform.runLater(() -> primaryStage.getScene().getRoot().getStyleClass().addAll( + defaultTheme.title().replace(" ", "-").toLowerCase(), + defaultTheme.isDark() ? "g-dark" : "g-light" + )); - ExtensionForm extensionForm = loader.getController(); - extensionForm.fieldsInitialized.addListener(() -> extensionForm.extension.observableHostInfo.addListener(hostInfo -> { - if (hostInfo.getAttributes().containsKey("theme")) { - String themeTitle = hostInfo.getAttributes().get("theme"); - Theme theme = ThemeFactory.themeForTitle(themeTitle); + final ExtensionForm extensionForm = loader.getController(); + extensionForm + .fieldsInitialisedProperty() + .addListener(observable -> listenForThemeChange(primaryStage, config, extensionForm)); + return extensionForm; + } + + private static void listenForThemeChange(Stage primaryStage, DefaultTitleBarConfig config, ExtensionForm extensionForm) { + extensionForm.extension.hostInfoProperty.addListener((observable, oldValue, newValue) -> { + final String themeTitle = newValue.getAttributes().get("theme"); + if (themeTitle != null) { + final Theme theme = ThemeFactory.themeForTitle(themeTitle); if (config.getCurrentTheme() != theme) { - String styleClassOld = config.getCurrentTheme().title().replace(" ", "-").toLowerCase(); - String lightClassOld = config.getCurrentTheme().isDark() ? "g-dark" : "g-light"; - String styleClassNew = theme.title().replace(" ", "-").toLowerCase(); - String lightClassNew = theme.isDark() ? "g-dark" : "g-light"; + final String styleClassOld = config.getCurrentTheme().title().replace(" ", "-").toLowerCase(); + final String lightClassOld = config.getCurrentTheme().isDark() ? "g-dark" : "g-light"; + final String styleClassNew = theme.title().replace(" ", "-").toLowerCase(); + final String lightClassNew = theme.isDark() ? "g-dark" : "g-light"; config.setTheme(theme); - Parent currentRoot = primaryStage.getScene().getRoot(); + final Parent currentRoot = primaryStage.getScene().getRoot(); Platform.runLater(() -> { currentRoot.getStyleClass().remove(styleClassOld); currentRoot.getStyleClass().add(styleClassNew); @@ -62,13 +69,11 @@ public abstract class ThemedExtensionFormCreator extends ExtensionFormCreator { }); } } - })); - - - return extensionForm; + }); } protected abstract String getTitle(); + protected abstract URL getFormResource(); // can be overridden for more settings diff --git a/G-Earth/src/main/java/gearth/misc/BindingsUtil.java b/G-Earth/src/main/java/gearth/misc/BindingsUtil.java new file mode 100644 index 0000000..e87f893 --- /dev/null +++ b/G-Earth/src/main/java/gearth/misc/BindingsUtil.java @@ -0,0 +1,62 @@ +package gearth.misc; + +import javafx.beans.binding.ObjectBinding; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.Property; +import javafx.beans.property.StringProperty; +import javafx.collections.ObservableList; +import javafx.util.StringConverter; + +/** + * Provides utility methods for bindings. + * + * @author Dorving + */ +public final class BindingsUtil { + + /** + * Ensures the list always contains the value of the binding. + */ + public static void addAndBindContent(ObservableList list, ObjectBinding binding) { + binding.addListener((observable, oldValue, newValue) -> { + list.remove(oldValue); + list.add(newValue); + }); + list.add(binding.get()); + } + + /** + * Sets the value of a property and binds it bidirectionally to another property. + */ + public static void setAndBindBiDirectional(Property a, Property b) { + a.setValue(b.getValue()); + a.bindBidirectional(b); + } + + /** + * Sets the value of a string property and binds it bidirectionally to another property using a converter. + */ + public static void setAndBindBiDirectional(StringProperty a, Property b, StringConverter converter) { + a.setValue(converter.toString(b.getValue())); + a.bindBidirectional(b, converter); + } + + /** + * Sets the value of a string property and binds it bidirectionally to an integer property. + */ + public static void setAndBindBiDirectional(StringProperty a, IntegerProperty b) { + setAndBindBiDirectional(a, b, STRING_INT_CONVERTER); + } + + private static final StringConverter STRING_INT_CONVERTER = new StringConverter() { + @Override + public String toString(Number object) { + return Integer.toString(object.intValue()); + } + + @Override + public Number fromString(String string) { + return Integer.parseInt(string); + } + }; +} diff --git a/G-Earth/src/main/java/gearth/misc/Cacher.java b/G-Earth/src/main/java/gearth/misc/Cacher.java index 882c4ab..0b1f699 100644 --- a/G-Earth/src/main/java/gearth/misc/Cacher.java +++ b/G-Earth/src/main/java/gearth/misc/Cacher.java @@ -1,6 +1,10 @@ package gearth.misc; import gearth.GEarth; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.Property; +import javafx.beans.property.StringProperty; import org.json.JSONArray; import org.json.JSONObject; @@ -10,6 +14,8 @@ import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.util.List; +import java.util.function.Consumer; +import java.util.function.Function; /** * Created by Jonas on 28/09/18. @@ -130,4 +136,36 @@ public class Cacher { public static void clear() { clear(DEFAULT_CACHE_FILENAME); } + + public static > void ifEnumPresent(String key, Class enumClass, Consumer consumer) { + if (getCacheContents().has(key)) { + final E value = getCacheContents().getEnum(enumClass, key); + consumer.accept(value); + } + } + public static > void bindEnum(String key, Class enumClass, ObjectProperty valueProperty) { + if (getCacheContents().has(key)) { + final E value = getCacheContents().getEnum(enumClass, key); + valueProperty.set(value); + } + valueProperty.addListener((observable, oldValue, newValue) -> put(key, newValue)); + } + + public static void bindString(String key, StringProperty valueProperty) { + bind(key, valueProperty, getCacheContents()::getString); + } + public static void bindNumber(String key, Property valueProperty) { + bind(key, valueProperty, getCacheContents()::getNumber); + } + public static void bindBoolean(String key, BooleanProperty valueProperty) { + bind(key, valueProperty, getCacheContents()::getBoolean); + } + public static void bindJSONObject(String key, ObjectProperty valueProperty) { + bind(key, valueProperty, getCacheContents()::getJSONObject); + } + private static void bind(String key, Property valueProperty, Function reader) { + if (getCacheContents().has(key)) + valueProperty.setValue(reader.apply(key)); + valueProperty.addListener((observable, oldValue, newValue) -> put(key, newValue)); + } } diff --git a/G-Earth/src/main/java/gearth/misc/listenerpattern/ObservableObject.java b/G-Earth/src/main/java/gearth/misc/listenerpattern/ObservableObject.java deleted file mode 100644 index 4fd62de..0000000 --- a/G-Earth/src/main/java/gearth/misc/listenerpattern/ObservableObject.java +++ /dev/null @@ -1,22 +0,0 @@ -package gearth.misc.listenerpattern; - -import java.util.function.Consumer; - -public class ObservableObject extends Observable> { - - private T object; - - public ObservableObject(T object) { - super(); - this.object = object; - } - - public void setObject(T object) { - this.object = object; - fireEvent(c -> c.accept(object)); - } - - public T getObject() { - return object; - } -} diff --git a/G-Earth/src/main/java/gearth/protocol/HConnection.java b/G-Earth/src/main/java/gearth/protocol/HConnection.java index 495481f..854800e 100644 --- a/G-Earth/src/main/java/gearth/protocol/HConnection.java +++ b/G-Earth/src/main/java/gearth/protocol/HConnection.java @@ -13,29 +13,29 @@ import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.flash.unix.LinuxRawIpFlashProxyProvider; import gearth.protocol.connection.proxy.unity.UnityProxyProvider; import gearth.services.extension_handler.ExtensionHandler; +import gearth.ui.GEarthProperties; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import java.io.IOException; import java.util.function.Consumer; public class HConnection { - public static volatile boolean DECRYPTPACKETS = true; - public static volatile boolean DEBUG = false; - private volatile ExtensionHandler extensionHandler = null; private volatile Object[] trafficObservables = {new Observable(), new Observable(), new Observable()}; - private volatile Observable stateObservable = new Observable<>(); - private volatile Observable> developerModeChangeObservable = new Observable<>(); - private volatile HState state = HState.NOT_CONNECTED; + @Deprecated + private volatile Observable stateObservable = new Observable<>(); + + private final ObjectProperty stateProperty = new SimpleObjectProperty<>(HState.NOT_CONNECTED); + private volatile HProxy proxy = null; private ProxyProviderFactory proxyProviderFactory; private ProxyProvider proxyProvider = null; - private volatile boolean developerMode = false; - public HConnection() { HConnection selff = this; proxyProviderFactory = new ProxyProviderFactory( @@ -47,16 +47,16 @@ public class HConnection { PacketSafetyManager.PACKET_SAFETY_MANAGER.initialize(this); } + public ObjectProperty stateProperty() { + return stateProperty; + } + public HState getState() { - return state; + return stateProperty.get(); } private void setState(HState state) { - if (state != this.state) { - HState buffer = this.state; - this.state = state; - stateObservable.fireEvent(l -> l.stateChanged(buffer, state)); - } + stateProperty.set(state); } // autodetect mode @@ -107,6 +107,7 @@ public class HConnection { } } + @Deprecated public Observable getStateObservable() { return stateObservable; } @@ -165,11 +166,12 @@ public class HConnection { } public boolean canSendPacket(HMessage.Direction direction, HPacket packet) { - return isPacketSendingAllowed(direction, packet) && (developerMode || isPacketSendingSafe(direction, packet)); + return isPacketSendingAllowed(direction, packet) + && (GEarthProperties.isDeveloperModeEnabled() || isPacketSendingSafe(direction, packet)); } public boolean isPacketSendingAllowed(HMessage.Direction direction, HPacket packet) { - if (state != HState.CONNECTED) return false; + if (getState() != HState.CONNECTED) return false; HProxy proxy = this.proxy; if (proxy == null) return false; @@ -196,14 +198,6 @@ public class HConnection { return packetsContainer.isPacketSafe(packet.headerId(), direction); } - public void setDeveloperMode(boolean developerMode) { - this.developerMode = developerMode; - developerModeChangeObservable.fireEvent(listener -> listener.accept(developerMode)); - } - - public void onDeveloperModeChange(Consumer onChange) { - developerModeChangeObservable.addListener(onChange); - } public String getClientHost() { if (proxy == null) { diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/FlashProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/FlashProxyProvider.java index 6df5f7a..03e3af9 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/FlashProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/FlashProxyProvider.java @@ -10,15 +10,14 @@ import gearth.protocol.memory.Rc4Obtainer; import gearth.protocol.packethandler.flash.IncomingFlashPacketHandler; import gearth.protocol.packethandler.flash.OutgoingFlashPacketHandler; import gearth.protocol.packethandler.flash.FlashPacketHandler; +import gearth.ui.GEarthProperties; import gearth.ui.titlebar.TitleBarController; import gearth.ui.translations.LanguageBundle; import javafx.application.Platform; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.Label; -import javafx.scene.image.Image; import javafx.scene.layout.Region; -import javafx.stage.Stage; import java.io.IOException; import java.net.Socket; @@ -47,7 +46,7 @@ public abstract class FlashProxyProvider implements ProxyProvider { client.setSoTimeout(0); server.setSoTimeout(0); - if (HConnection.DEBUG) System.out.println(server.getLocalAddress().getHostAddress() + ": " + server.getLocalPort()); + if (GEarthProperties.isDebugModeEnabled()) System.out.println(server.getLocalAddress().getHostAddress() + ": " + server.getLocalPort()); Rc4Obtainer rc4Obtainer = new Rc4Obtainer(hConnection); OutgoingFlashPacketHandler outgoingHandler = new OutgoingFlashPacketHandler(server.getOutputStream(), hConnection.getTrafficObservables(), hConnection.getExtensionHandler()); @@ -73,7 +72,7 @@ public abstract class FlashProxyProvider implements ProxyProvider { try { if (!server.isClosed()) server.close(); if (!client.isClosed()) client.close(); - if (HConnection.DEBUG) System.out.println("STOP"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("STOP"); if (datastream[0]) { onConnectEnd(); }; diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/NormalFlashProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/NormalFlashProxyProvider.java index 49b4c6e..b04b9f3 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/NormalFlashProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/NormalFlashProxyProvider.java @@ -1,6 +1,5 @@ package gearth.protocol.connection.proxy.flash; -import gearth.GEarth; import gearth.misc.Cacher; import gearth.protocol.HConnection; import gearth.protocol.connection.*; @@ -10,12 +9,11 @@ import gearth.protocol.hostreplacer.hostsfile.HostReplacer; import gearth.protocol.hostreplacer.hostsfile.HostReplacerFactory; import gearth.protocol.portchecker.PortChecker; import gearth.protocol.portchecker.PortCheckerFactory; +import gearth.ui.GEarthProperties; import gearth.ui.titlebar.TitleBarController; import javafx.application.Platform; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; -import javafx.scene.image.Image; -import javafx.stage.Stage; import java.io.IOException; import java.net.*; @@ -128,7 +126,7 @@ public class NormalFlashProxyProvider extends FlashProxyProvider { Socket client = proxy_server.accept(); proxy = potentialProxy; closeAllProxies(proxy); - if (HConnection.DEBUG) System.out.println("accepted a proxy"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("accepted a proxy"); new Thread(() -> { try { @@ -173,7 +171,7 @@ public class NormalFlashProxyProvider extends FlashProxyProvider { } - if (HConnection.DEBUG) System.out.println("done waiting for clients with: " + hConnection.getState() ); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("done waiting for clients with: " + hConnection.getState() ); } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/unix/LinuxRawIpFlashProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/unix/LinuxRawIpFlashProxyProvider.java index 3b31f78..4d1df09 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/unix/LinuxRawIpFlashProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/flash/unix/LinuxRawIpFlashProxyProvider.java @@ -7,6 +7,7 @@ import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.SocksConfiguration; import gearth.protocol.hostreplacer.ipmapping.IpMapper; import gearth.protocol.hostreplacer.ipmapping.IpMapperFactory; +import gearth.ui.GEarthProperties; import java.io.IOException; import java.net.*; @@ -54,7 +55,7 @@ public class LinuxRawIpFlashProxyProvider extends FlashProxyProvider { maybeAddMapping(); - if (HConnection.DEBUG) System.out.println("Added mapping for raw IP"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("Added mapping for raw IP"); ServerSocket proxy_server = new ServerSocket(proxy.getIntercept_port(), 10, InetAddress.getByName(proxy.getIntercept_host())); proxy.initProxy(proxy_server); @@ -62,10 +63,10 @@ public class LinuxRawIpFlashProxyProvider extends FlashProxyProvider { stateSetter.setState(HState.WAITING_FOR_CLIENT); while ((hConnection.getState() == HState.WAITING_FOR_CLIENT) && !proxy_server.isClosed()) { try { - if (HConnection.DEBUG) System.out.println("try accept proxy"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("try accept proxy"); Socket client = proxy_server.accept(); - if (HConnection.DEBUG) System.out.println("accepted a proxy"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("accepted a proxy"); new Thread(() -> { try { @@ -151,7 +152,7 @@ public class LinuxRawIpFlashProxyProvider extends FlashProxyProvider { createSocksProxyThread(client); } else if (preConnectedServerConnections.isEmpty()) { - if (HConnection.DEBUG) System.out.println("pre-made server connections ran out of stock"); + if (GEarthProperties.isDebugModeEnabled()) System.out.println("pre-made server connections ran out of stock"); } else { startProxyThread(client, preConnectedServerConnections.poll(), proxy); diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java index b86a4fc..50b7d8d 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/nitro/NitroProxyProvider.java @@ -9,6 +9,8 @@ import gearth.protocol.connection.proxy.ProxyProvider; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy; import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback; import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +18,7 @@ import java.io.IOException; import java.net.ServerSocket; import java.util.concurrent.atomic.AtomicBoolean; -public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener { +public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, ChangeListener { private static final Logger logger = LoggerFactory.getLogger(NitroProxyProvider.class); @@ -53,7 +55,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa originalWebsocketUrl = null; originalCookies = null; - connection.getStateObservable().addListener(this); + connection.stateProperty().addListener(this); logger.info("Starting http proxy"); @@ -112,7 +114,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa stateSetter.setState(HState.NOT_CONNECTED); - connection.getStateObservable().removeListener(this); + connection.stateProperty().removeListener(this); logger.info("Nitro proxy stopped"); }).start(); @@ -131,16 +133,15 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa } @Override - public void stateChanged(HState oldState, HState newState) { - if (oldState == HState.WAITING_FOR_CLIENT && newState == HState.CONNECTED) { + public void changed(ObservableValue observable, HState oldValue, HState newValue) { + if (oldValue == HState.WAITING_FOR_CLIENT && newValue == HState.CONNECTED) { // Unregister but do not stop http proxy. // We are not stopping the http proxy because some requests might still require it to be running. nitroHttpProxy.pause(); } // Catch setState ABORTING inside NitroWebsocketClient. - if (newState == HState.ABORTING) { + if (newValue == HState.ABORTING) abort(); - } } } diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityProxyProvider.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityProxyProvider.java index 58308db..29ecd40 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityProxyProvider.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityProxyProvider.java @@ -1,18 +1,21 @@ package gearth.protocol.connection.proxy.unity; import gearth.protocol.HConnection; -import gearth.protocol.StateChangeListener; import gearth.protocol.connection.HProxySetter; import gearth.protocol.connection.HState; import gearth.protocol.connection.HStateSetter; import gearth.protocol.connection.proxy.ProxyProvider; import gearth.services.unity_tools.GUnityFileServer; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; @@ -22,6 +25,9 @@ import static gearth.services.unity_tools.GUnityFileServer.FILESERVER_PORT; public class UnityProxyProvider implements ProxyProvider { + private final static Logger LOGGER = LoggerFactory.getLogger(UnityProxyProvider.class); + + public static final int PORT_REQUESTER_SERVER_PORT = 9039; private final HProxySetter proxySetter; private final HStateSetter stateSetter; private final HConnection hConnection; @@ -34,7 +40,6 @@ public class UnityProxyProvider implements ProxyProvider { this.hConnection = hConnection; } - @Override public void start() throws IOException { // https://happyhyppo.ro/2016/03/21/minimal-websockets-communication-with-javajetty-and-angularjs/ @@ -45,13 +50,13 @@ public class UnityProxyProvider implements ProxyProvider { while (fail && port < 9100) { try { packetHandlerServer = new Server(port); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/ws"); - HandlerList handlers = new HandlerList(); + final HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { context }); - ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); + final ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); wscontainer.addEndpoint(ServerEndpointConfig.Builder .create(UnityCommunicator.class, "/packethandler") // the endpoint url .configurator(new UnityCommunicatorConfig(proxySetter, stateSetter, hConnection, this)) @@ -65,10 +70,8 @@ public class UnityProxyProvider implements ProxyProvider { } } - if (fail) { + if (fail) throw new Exception(); - } - startPortRequestServer(port); startUnityFileServer(); @@ -79,17 +82,16 @@ public class UnityProxyProvider implements ProxyProvider { try { packetHandlerServer.stop(); } catch (Exception ex) { - ex.printStackTrace(); + LOGGER.error("Failed to close packet handler server", ex); } - e.printStackTrace(); + LOGGER.error("Failed to connect to unity proxy", e); } } @Override public synchronized void abort() { - if (packetHandlerServer == null) { + if (packetHandlerServer == null) return; - } final Server abortThis = packetHandlerServer; stateSetter.setState(HState.ABORTING); @@ -97,7 +99,7 @@ public class UnityProxyProvider implements ProxyProvider { try { abortThis.stop(); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed to abort", e); } finally { stateSetter.setState(HState.NOT_CONNECTED); } @@ -106,43 +108,30 @@ public class UnityProxyProvider implements ProxyProvider { } private void startUnityFileServer() throws Exception { - Server server = new Server(FILESERVER_PORT); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + final Server server = new Server(FILESERVER_PORT); + final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.addServlet(new ServletHolder(new GUnityFileServer()), "/*"); - HandlerList handlers = new HandlerList(); + final HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { context }); server.setHandler(handlers); server.start(); - StateChangeListener fileServerCloser = new StateChangeListener() { - @Override - public void stateChanged(HState oldState, HState newState) { - if (oldState == HState.WAITING_FOR_CLIENT || newState == HState.NOT_CONNECTED) { - hConnection.getStateObservable().removeListener(this); - try { - server.stop(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - }; - hConnection.getStateObservable().addListener(fileServerCloser); + + stopServerOnDisconnect(server); } private void startPortRequestServer(int packetHandlerPort) throws Exception { - Server portRequestServer = new Server(9039); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + final Server portRequestServer = new Server(PORT_REQUESTER_SERVER_PORT); + final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/ws"); - HandlerList handlers = new HandlerList(); + final HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { context }); portRequestServer.setHandler(handlers); - ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); + final ServerContainer wscontainer = WebSocketServerContainerInitializer.configureContext(context); wscontainer.addEndpoint(ServerEndpointConfig.Builder .create(PortRequester.class, "/portrequest") // the endpoint url .configurator(new PortRequesterConfig(packetHandlerPort)) @@ -150,19 +139,22 @@ public class UnityProxyProvider implements ProxyProvider { portRequestServer.start(); - StateChangeListener portRequesterCloser = new StateChangeListener() { + stopServerOnDisconnect(portRequestServer); + } + + private void stopServerOnDisconnect(Server server) { + hConnection.stateProperty().addListener(new ChangeListener() { @Override - public void stateChanged(HState oldState, HState newState) { - if (oldState == HState.WAITING_FOR_CLIENT || newState == HState.NOT_CONNECTED) { - hConnection.getStateObservable().removeListener(this); + public void changed(ObservableValue observable, HState oldValue, HState newValue) { + if (oldValue == HState.WAITING_FOR_CLIENT || newValue == HState.NOT_CONNECTED) { + hConnection.stateProperty().removeListener(this); try { - portRequestServer.stop(); + server.stop(); } catch (Exception e) { - e.printStackTrace(); + LOGGER.error("Failed to stop server {}", server, e); } } } - }; - hConnection.getStateObservable().addListener(portRequesterCloser); + }); } } diff --git a/G-Earth/src/main/java/gearth/protocol/memory/Rc4Obtainer.java b/G-Earth/src/main/java/gearth/protocol/memory/Rc4Obtainer.java index 616354c..86689de 100644 --- a/G-Earth/src/main/java/gearth/protocol/memory/Rc4Obtainer.java +++ b/G-Earth/src/main/java/gearth/protocol/memory/Rc4Obtainer.java @@ -7,11 +7,13 @@ import gearth.protocol.crypto.RC4; import gearth.protocol.memory.habboclient.HabboClient; import gearth.protocol.memory.habboclient.HabboClientFactory; import gearth.protocol.packethandler.PayloadBuffer; -import gearth.protocol.packethandler.flash.BufferChangeListener; import gearth.protocol.packethandler.flash.FlashPacketHandler; +import gearth.ui.GEarthProperties; import gearth.ui.titlebar.TitleBarController; import gearth.ui.translations.LanguageBundle; import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import javafx.scene.control.Hyperlink; @@ -37,21 +39,22 @@ public class Rc4Obtainer { public void setFlashPacketHandlers(FlashPacketHandler... flashPacketHandlers) { this.flashPacketHandlers = Arrays.asList(flashPacketHandlers); for (FlashPacketHandler handler : flashPacketHandlers) { - BufferChangeListener bufferChangeListener = new BufferChangeListener() { + final InvalidationListener bufferChangeListener = new InvalidationListener() { @Override - public void act() { + public void invalidated(Observable observable) { if (handler.isEncryptedStream()) { - onSendFirstEncryptedMessage(handler); - handler.getBufferChangeObservable().removeListener(this); + Rc4Obtainer.this.onSendFirstEncryptedMessage(handler); + handler.incomingBufferProperty().removeListener(this); } } }; - handler.getBufferChangeObservable().addListener(bufferChangeListener); + handler.incomingBufferProperty().addListener(bufferChangeListener); } } private void onSendFirstEncryptedMessage(FlashPacketHandler flashPacketHandler) { - if (!HConnection.DECRYPTPACKETS) return; + if (GEarthProperties.isPacketDecryptionDisabled()) + return; flashPacketHandlers.forEach(FlashPacketHandler::block); diff --git a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/BufferChangeListener.java b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/BufferChangeListener.java deleted file mode 100644 index e39d6e7..0000000 --- a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/BufferChangeListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package gearth.protocol.packethandler.flash; - - -public interface BufferChangeListener { - - void act(); - -} diff --git a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/FlashPacketHandler.java b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/FlashPacketHandler.java index 98b7ffe..921d54c 100644 --- a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/FlashPacketHandler.java +++ b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/FlashPacketHandler.java @@ -1,13 +1,14 @@ package gearth.protocol.packethandler.flash; -import gearth.misc.listenerpattern.Observable; -import gearth.protocol.HConnection; import gearth.protocol.HMessage; import gearth.protocol.HPacket; import gearth.protocol.crypto.RC4; import gearth.protocol.packethandler.PacketHandler; import gearth.protocol.packethandler.PayloadBuffer; import gearth.services.extension_handler.ExtensionHandler; +import gearth.ui.GEarthProperties; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; import java.io.IOException; import java.io.OutputStream; @@ -18,7 +19,7 @@ public abstract class FlashPacketHandler extends PacketHandler { protected static final boolean DEBUG = false; - private volatile OutputStream out; + private final OutputStream out; private volatile boolean isTempBlocked = false; volatile boolean isDataStream = false; @@ -36,7 +37,7 @@ public abstract class FlashPacketHandler extends PacketHandler { FlashPacketHandler(OutputStream outputStream, Object[] trafficObservables, ExtensionHandler extensionHandler) { super(extensionHandler, trafficObservables); out = outputStream; - this.payloadBuffer = new PayloadBuffer(); + payloadBuffer = new PayloadBuffer(); } public boolean isDataStream() {return isDataStream;} @@ -49,6 +50,7 @@ public abstract class FlashPacketHandler extends PacketHandler { } public void act(byte[] buffer) throws IOException { + if (!isDataStream) { synchronized (sendLock) { out.write(buffer); @@ -56,20 +58,19 @@ public abstract class FlashPacketHandler extends PacketHandler { return; } - bufferChangeObservable.fireEvent(); + incomingBufferProperty.set(buffer); if (!isEncryptedStream) { payloadBuffer.push(buffer); } - else if (!HConnection.DECRYPTPACKETS) { + else if (GEarthProperties.isPacketDecryptionDisabled()) { synchronized (sendLock) { out.write(buffer); } } else if (decryptcipher == null) { - for (int i = 0; i < buffer.length; i++) { - tempEncryptedBuffer.add(buffer[i]); - } + for (byte b : buffer) + tempEncryptedBuffer.add(b); } else { byte[] tm = decryptcipher.rc4(buffer); @@ -162,9 +163,10 @@ public abstract class FlashPacketHandler extends PacketHandler { protected abstract void printForDebugging(byte[] bytes); - private Observable bufferChangeObservable = new Observable<>(BufferChangeListener::act); - public Observable getBufferChangeObservable() { - return bufferChangeObservable; + private final ObjectProperty incomingBufferProperty = new SimpleObjectProperty<>(); + + public ObjectProperty incomingBufferProperty() { + return incomingBufferProperty; } public int getCurrentIndex() { diff --git a/G-Earth/src/main/java/gearth/services/extension_handler/ExtensionHandler.java b/G-Earth/src/main/java/gearth/services/extension_handler/ExtensionHandler.java index 87a57d8..1df5eda 100644 --- a/G-Earth/src/main/java/gearth/services/extension_handler/ExtensionHandler.java +++ b/G-Earth/src/main/java/gearth/services/extension_handler/ExtensionHandler.java @@ -1,7 +1,6 @@ package gearth.services.extension_handler; import gearth.GEarth; -import gearth.misc.HostInfo; import gearth.misc.listenerpattern.Observable; import gearth.protocol.HConnection; import gearth.protocol.HMessage; @@ -12,14 +11,15 @@ import gearth.services.extension_handler.extensions.GEarthExtension; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducer; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory; import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerObserver; -import gearth.ui.themes.Theme; +import gearth.ui.GEarthProperties; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.util.Pair; import java.io.IOException; import java.util.*; -import java.util.function.Consumer; -public class ExtensionHandler { +public class ExtensionHandler implements ChangeListener { private final List gEarthExtensions = new ArrayList<>(); private final HConnection hConnection; @@ -47,38 +47,13 @@ public class ExtensionHandler { } private void initialize() { - GEarth.getThemeObservable().addListener(theme -> { + GEarthProperties.hostInfoBinding.addListener(((observable1, oldValue, newValue) -> { synchronized (gEarthExtensions) { - for (GEarthExtension extension : gEarthExtensions) { - extension.updateHostInfo(getHostInfo()); - } + gEarthExtensions.forEach(extension -> extension.updateHostInfo(newValue)); } - }); - - hConnection.getStateObservable().addListener((oldState, newState) -> { - if (newState == HState.CONNECTED) { - synchronized (gEarthExtensions) { - for (GEarthExtension extension : gEarthExtensions) { - extension.connectionStart( - hConnection.getDomain(), - hConnection.getServerPort(), - hConnection.getHotelVersion(), - hConnection.getClientIdentifier(), - hConnection.getClientType(), - hConnection.getPacketInfoManager() - ); - } - } - } - if (oldState == HState.CONNECTED) { - synchronized (gEarthExtensions) { - for (GEarthExtension extension : gEarthExtensions) { - extension.connectionEnd(); - } - } - } - }); + })); + hConnection.stateProperty().addListener(this); extensionProducers = ExtensionProducerFactory.getAll(); extensionProducers.forEach(extensionProducer -> extensionProducer.startProducing(createExtensionProducerObserver())); } @@ -259,7 +234,7 @@ public class ExtensionHandler { extension.getClickedObservable().addListener(extension::doubleclick); observable.fireEvent(l -> l.onExtensionConnect(extension)); - extension.init(hConnection.getState() == HState.CONNECTED, getHostInfo()); + extension.init(hConnection.getState() == HState.CONNECTED, GEarthProperties.getHostInfo()); if (hConnection.getState() == HState.CONNECTED) { extension.connectionStart( hConnection.getDomain(), @@ -274,16 +249,6 @@ public class ExtensionHandler { }; } - private HostInfo getHostInfo() { - HashMap attributes = new HashMap<>(); - attributes.put("theme", GEarth.getTheme().title()); - return new HostInfo( - "G-Earth", - GEarth.version, - attributes - ); - } - public List getExtensionProducers() { return extensionProducers; } @@ -296,5 +261,26 @@ public class ExtensionHandler { extensionProducers.add(producer); } - + @Override + public void changed(ObservableValue observable, HState oldValue, HState newValue) { + if (newValue == HState.CONNECTED) { + synchronized (gEarthExtensions) { + for (GEarthExtension extension : gEarthExtensions) { + extension.connectionStart( + hConnection.getDomain(), + hConnection.getServerPort(), + hConnection.getHotelVersion(), + hConnection.getClientIdentifier(), + hConnection.getClientType(), + hConnection.getPacketInfoManager() + ); + } + } + } + if (oldValue == HState.CONNECTED) { + synchronized (gEarthExtensions) { + gEarthExtensions.forEach(GEarthExtension::connectionEnd); + } + } + } } diff --git a/G-Earth/src/main/java/gearth/ui/GEarthProperties.java b/G-Earth/src/main/java/gearth/ui/GEarthProperties.java new file mode 100644 index 0000000..4a5637d --- /dev/null +++ b/G-Earth/src/main/java/gearth/ui/GEarthProperties.java @@ -0,0 +1,228 @@ +package gearth.ui; + +import gearth.GEarth; +import gearth.misc.BindingsUtil; +import gearth.misc.Cacher; +import gearth.misc.HostInfo; +import gearth.protocol.connection.HClient; +import gearth.ui.themes.Theme; +import gearth.ui.themes.ThemeFactory; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.ObjectBinding; +import javafx.beans.binding.StringBinding; +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.image.Image; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Objects; + +/** + * Handles G-Earth's observable properties. + * + * @author Dorving + */ +public final class GEarthProperties { + + private static final Logger LOGGER = LoggerFactory.getLogger(GEarthProperties.class); + + public static final ObjectProperty themeProperty = new SimpleObjectProperty<>(); + + public static final StringBinding themeTitleBinding; + + public static final ObjectBinding hostInfoBinding; + + public static final ObjectBinding logoImageBinding; + public static final ObjectBinding logoSmallImageBinding; + public static final ObservableList icons = FXCollections.observableArrayList(); + + public static final ObjectBinding styleSheetBinding; + public static final ObservableList styleSheets = FXCollections.observableArrayList(); + + public static final ObjectProperty clientTypeProperty = new SimpleObjectProperty<>(); + + public static final BooleanProperty autoDetectProperty = new SimpleBooleanProperty(false); + public static final StringProperty hostProperty = new SimpleStringProperty(); + public static final IntegerProperty portProperty = new SimpleIntegerProperty(); + + public static final BooleanProperty alwaysOnTopProperty = new SimpleBooleanProperty(false); + public static final BooleanProperty enableDeveloperModeProperty = new SimpleBooleanProperty(false); + public static final BooleanProperty enableDebugProperty = new SimpleBooleanProperty(false); + public static final BooleanProperty disablePacketDecryptionProperty = new SimpleBooleanProperty(false); + public static final BooleanProperty enableSocksProperty = new SimpleBooleanProperty(false); + public static final StringProperty socksHostProperty = new SimpleStringProperty(); + public static final IntegerProperty socksPortProperty = new SimpleIntegerProperty(); + public static final BooleanProperty enableGPythonProperty = new SimpleBooleanProperty(false); + public static final BooleanProperty alwaysAdminProperty = new SimpleBooleanProperty(false); + public static final StringProperty notesProperty = new SimpleStringProperty(); + + static { + themeProperty.addListener((observable, oldValue, newValue) -> Cacher.put("theme", newValue.title())); + final Theme value = Cacher.getCacheContents().has("theme") ? + ThemeFactory.themeForTitle(Cacher.getCacheContents().getString("theme")) : + ThemeFactory.getDefaultTheme(); + + LOGGER.debug("Loading theme {}", value); + + themeProperty.set(Objects.requireNonNull(value, "Unable to load theme")); + + styleSheetBinding = createThemeStylesheetBinding(); + logoSmallImageBinding = createThemeImageBinding("logoSmall.png"); + logoImageBinding = createThemeImageBinding("logo.png"); + hostInfoBinding = createThemeHostInfoBinding(); + themeTitleBinding = createThemeTitleBinding(); + + BindingsUtil.addAndBindContent(icons, logoSmallImageBinding); + BindingsUtil.addAndBindContent(styleSheets, styleSheetBinding); + + Cacher.bindEnum("last_client_mode", HClient.class, clientTypeProperty); + Cacher.bindBoolean("auto_detect", autoDetectProperty); + Cacher.bindString("host", hostProperty); + Cacher.bindNumber("port", portProperty); + convertOldConnectionSettingsIfPresent(); + + Cacher.bindBoolean("always_on_top", alwaysOnTopProperty); + Cacher.bindBoolean("always_admin", alwaysAdminProperty); + Cacher.bindBoolean("develop_mode", enableDeveloperModeProperty); + Cacher.bindBoolean("debug_mode_enabled", enableDebugProperty); + Cacher.bindBoolean("packet_decryption_disabled", disablePacketDecryptionProperty); + Cacher.bindBoolean("socks_enabled", enableSocksProperty); + Cacher.bindString("socks_host", socksHostProperty); + Cacher.bindNumber("socks_port", socksPortProperty); + Cacher.bindBoolean("use_gpython", enableGPythonProperty); + Cacher.bindString("notepad_text", notesProperty); + } + + public static Theme getTheme() { + return themeProperty.get(); + } + + public static String getThemeTitle() { + return themeTitleBinding.get(); + } + + public static HostInfo getHostInfo() { + return hostInfoBinding.get(); + } + + public static Image getLogoImage() { + return logoImageBinding.get(); + } + + public static Image getLogoSmallImage() { + return logoSmallImageBinding.get(); + } + + public static String getStyleSheet() { + return styleSheetBinding.get(); + } + + public static HClient getClientType() { + return clientTypeProperty.get(); + } + + public static boolean isPacketDecryptionDisabled() { + return disablePacketDecryptionProperty.get(); + } + + public static boolean isDebugModeEnabled() { + return enableDebugProperty.get(); + } + + public static boolean isDeveloperModeEnabled() { + return enableDeveloperModeProperty.get(); + } + + public static boolean isAlwaysOnTop() { + return alwaysOnTopProperty.get(); + } + + public static String getSocksHost() { + return socksHostProperty.get(); + } + + public static int getSocksPort() { + return socksPortProperty.get(); + } + + private static StringBinding createThemeTitleBinding() { + return Bindings.createStringBinding(() -> { + final Theme theme = getTheme(); + return theme.overridesTitle() ? theme.title() : ThemeFactory.getDefaultTheme().title(); + }, themeProperty); + } + + private static ObjectBinding createThemeHostInfoBinding() { + return Bindings.createObjectBinding(() -> new HostInfo( + "G-Earth", + GEarth.version, + new HashMap<>(Collections.singletonMap("theme", getTheme().title())) + ), themeProperty); + } + + private static ObjectBinding createThemeStylesheetBinding() { + return Bindings.createObjectBinding(() -> { + final String pathToStyleSheet = getThemeRelativePath("styling.css"); + final URL styleSheetInput = GEarth.class.getResource(pathToStyleSheet); + if (styleSheetInput == null) { + LOGGER.error("Could not load style sheet `{}`, input-stream is null for {}", "styling.css", pathToStyleSheet); + return null; + } + return styleSheetInput.toExternalForm(); + }, themeProperty); + } + + private static ObjectBinding createThemeImageBinding(String imageName) { + return Bindings.createObjectBinding(() -> { + final Theme theme = getTheme(); + final String pathToLogo = getThemeRelativePath(theme.overridesLogo() + ? theme + : ThemeFactory.getDefaultTheme(), imageName); + try (final InputStream imageInput = GEarth.class.getResourceAsStream(pathToLogo)) { + if (imageInput == null) { + LOGGER.error("Could not load image `{}`, input-stream is null for {}", imageName, pathToLogo); + return null; + } + return new Image(imageInput); + } catch (Exception e) { + LOGGER.error("Failed to load image `{}", imageName, e); + return null; + } + }, themeProperty); + } + + private static String getThemeRelativePath(String fileName) { + return getThemeRelativePath(getTheme(), fileName); + } + + private static String getThemeRelativePath(Theme theme, String fileName) { + return String.format("/gearth/ui/themes/%s/" + fileName, theme.internalName()); + } + + private static final String KEY_LAST_CONNECTION_SETTINGS = "last_connection_settings"; + private static final String KEY_AUTODETECT = "auto_detect"; + private static final String KEY_HOST = "host"; + private static final String KEY_PORT = "port"; + + private static void convertOldConnectionSettingsIfPresent() { + /* + BACKWARDS COMPATABILITY + */ + if (Cacher.getCacheContents().has(KEY_LAST_CONNECTION_SETTINGS)) { + final JSONObject jsonObject = Cacher.getCacheContents().getJSONObject(KEY_LAST_CONNECTION_SETTINGS); + if (jsonObject != null) { + autoDetectProperty.set(jsonObject.getBoolean(KEY_AUTODETECT)); + hostProperty.set(jsonObject.getString(KEY_HOST)); + portProperty.set(jsonObject.getInt(KEY_PORT)); + Cacher.getCacheContents().remove(KEY_LAST_CONNECTION_SETTINGS); + } + } + } +} diff --git a/G-Earth/src/main/java/gearth/ui/subforms/connection/ConnectionController.java b/G-Earth/src/main/java/gearth/ui/subforms/connection/ConnectionController.java index 1a38967..fe6e093 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/connection/ConnectionController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/connection/ConnectionController.java @@ -1,19 +1,19 @@ package gearth.ui.subforms.connection; import gearth.GEarth; -import gearth.misc.Cacher; +import gearth.misc.BindingsUtil; +import gearth.protocol.HConnection; import gearth.protocol.connection.HClient; import gearth.protocol.connection.HState; import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.services.Constants; +import gearth.ui.GEarthProperties; import gearth.ui.translations.TranslatableString; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.scene.control.*; -import gearth.protocol.HConnection; import gearth.ui.SubForm; import javafx.scene.layout.GridPane; -import org.json.JSONObject; import java.util.ArrayList; import java.util.HashSet; @@ -22,11 +22,6 @@ import java.util.Set; public class ConnectionController extends SubForm { - private final String CONNECTION_INFO_CACHE_KEY = "last_connection_settings"; - private final String AUTODETECT_CACHE = "auto_detect"; - private final String HOST_CACHE = "host"; - private final String PORT_CACHE = "port"; - public ComboBox inpPort; public ComboBox inpHost; public Button btnConnect; @@ -40,7 +35,6 @@ public class ConnectionController extends SubForm { private volatile int fullyInitialized = 0; - public static final String CLIENT_CACHE_KEY = "last_client_mode"; public ToggleGroup tgl_clientMode; public RadioButton rd_unity; public RadioButton rd_flash; @@ -59,38 +53,16 @@ public class ConnectionController extends SubForm { Constants.UNITY_PACKETS = rd_unity.isSelected(); }); - if (Cacher.getCacheContents().has(CLIENT_CACHE_KEY)) { - switch (Cacher.getCacheContents().getEnum(HClient.class, CLIENT_CACHE_KEY)) { - case FLASH: - rd_flash.setSelected(true); - break; - case UNITY: - rd_unity.setSelected(true); - break; - case NITRO: - rd_nitro.setSelected(true); - break; - } - } + GEarthProperties.clientTypeProperty + .addListener((observable, oldValue, newValue) -> selectClientType(newValue)); + selectClientType(GEarthProperties.clientTypeProperty.getValue()); + cbx_autodetect.selectedProperty().addListener(observable -> updateInputUI()); + inpPort.getEditor().textProperty().addListener(observable -> updateInputUI()); - Object object; - String hostRemember = null; - String portRemember = null; - if ((object = Cacher.get(CONNECTION_INFO_CACHE_KEY)) != null) { - JSONObject connectionSettings = (JSONObject) object; - boolean autoDetect = connectionSettings.getBoolean(AUTODETECT_CACHE); - hostRemember = connectionSettings.getString(HOST_CACHE); - portRemember = connectionSettings.getInt(PORT_CACHE) + ""; - cbx_autodetect.setSelected(autoDetect); - } - - inpPort.getEditor().textProperty().addListener(observable -> { - updateInputUI(); - }); - cbx_autodetect.selectedProperty().addListener(observable -> { - updateInputUI(); - }); + BindingsUtil.setAndBindBiDirectional(cbx_autodetect.selectedProperty(), GEarthProperties.autoDetectProperty); + BindingsUtil.setAndBindBiDirectional(outHost.textProperty(), GEarthProperties.hostProperty); + BindingsUtil.setAndBindBiDirectional(outPort.textProperty(), GEarthProperties.portProperty); List knownHosts = ProxyProviderFactory.autoDetectHosts; Set hosts = new HashSet<>(); @@ -110,6 +82,8 @@ public class ConnectionController extends SubForm { int hostSelectIndex = 0; int portSelectIndex = 0; + final String hostRemember = GEarthProperties.hostProperty.get(); + final String portRemember = Integer.toString(GEarthProperties.portProperty.get()); if (hostRemember != null) { hostSelectIndex = hostsSorted.indexOf(hostRemember); portSelectIndex = portsSorted.indexOf(portRemember); @@ -138,15 +112,31 @@ public class ConnectionController extends SubForm { initLanguageBinding(); } + private void selectClientType(HClient newValue) { + switch (newValue) { + case FLASH: + rd_flash.setSelected(true); + break; + case UNITY: + rd_unity.setSelected(true); + break; + case NITRO: + rd_nitro.setSelected(true); + break; + } + } private void updateInputUI() { if (parentController == null) return; - grd_clientSelection.setDisable(getHConnection().getState() != HState.NOT_CONNECTED); - txtfield_hotelversion.setText(getHConnection().getHotelVersion()); + final HConnection hConnection = getHConnection(); + final HState hConnectionState = hConnection.getState(); - btnConnect.setDisable(getHConnection().getState() == HState.PREPARING || getHConnection().getState() == HState.ABORTING); + grd_clientSelection.setDisable(hConnectionState != HState.NOT_CONNECTED); + txtfield_hotelversion.setText(hConnection.getHotelVersion()); + + btnConnect.setDisable(hConnectionState == HState.PREPARING || hConnectionState == HState.ABORTING); if (!cbx_autodetect.isSelected() && !btnConnect.isDisable() && useFlash()) { @@ -159,15 +149,15 @@ public class ConnectionController extends SubForm { } } - inpHost.setDisable(getHConnection().getState() != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); - inpPort.setDisable(getHConnection().getState() != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); + inpHost.setDisable(hConnectionState != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); + inpPort.setDisable(hConnectionState != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); cbx_autodetect.setDisable(!useFlash()); outHost.setDisable(!useFlash()); outPort.setDisable(!useFlash()); - inpHost.setDisable(!useFlash() || getHConnection().getState() != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); - inpPort.setDisable(!useFlash() || getHConnection().getState() != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); + inpHost.setDisable(!useFlash() || hConnectionState != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); + inpPort.setDisable(!useFlash() || hConnectionState != HState.NOT_CONNECTED || cbx_autodetect.isSelected()); } public void onParentSet(){ @@ -178,37 +168,28 @@ public class ConnectionController extends SubForm { } } - getHConnection().getStateObservable().addListener((oldState, newState) -> Platform.runLater(() -> { + getHConnection().stateProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(() -> { updateInputUI(); - if (newState == HState.NOT_CONNECTED) { + if (newValue == HState.NOT_CONNECTED) { state.setKey(0, "tab.connection.state.notconnected"); connect.setKey(0, "tab.connection.button.connect"); outHost.setText(""); outPort.setText(""); } - else if (oldState == HState.NOT_CONNECTED) { + else if (oldValue == HState.NOT_CONNECTED) connect.setKey(0, "tab.connection.button.abort"); - } - - if (newState == HState.CONNECTED) { + if (newValue == HState.CONNECTED) state.setKey(0, "tab.connection.state.connected"); - } - if (newState == HState.WAITING_FOR_CLIENT) { + if (newValue == HState.WAITING_FOR_CLIENT) state.setKey(0, "tab.connection.state.waiting"); + if (newValue == HState.CONNECTED && useFlash()) { + final String host = getHConnection().getDomain(); + final int port = getHConnection().getServerPort(); + outHost.setText(host); + outPort.setText(Integer.toString(port)); + GEarthProperties.hostProperty.set(host); + GEarthProperties.portProperty.set(port); } - - if (newState == HState.CONNECTED && useFlash()) { - outHost.setText(getHConnection().getDomain()); - outPort.setText(getHConnection().getServerPort()+""); - - JSONObject connectionSettings = new JSONObject(); - connectionSettings.put(AUTODETECT_CACHE, cbx_autodetect.isSelected()); - connectionSettings.put(HOST_CACHE, inpHost.getEditor().getText()); - connectionSettings.put(PORT_CACHE, Integer.parseInt(inpPort.getEditor().getText())); - - Cacher.put(CONNECTION_INFO_CACHE_KEY, connectionSettings); - } - })); Platform.runLater(this::updateInputUI); @@ -234,9 +215,11 @@ public class ConnectionController extends SubForm { String port = GEarth.getArgument("--port"); if (host != null && port != null) { Platform.runLater(() -> { - if (!inpHost.getItems().contains(host)) inpHost.getItems().add(host); + if (!inpHost.getItems().contains(host)) + inpHost.getItems().add(host); inpHost.getSelectionModel().select(host); - if (!inpPort.getItems().contains(port)) inpPort.getItems().add(port); + if (!inpPort.getItems().contains(port)) + inpPort.getItems().add(port); inpPort.getSelectionModel().select(port); cbx_autodetect.setSelected(false); }); @@ -275,12 +258,9 @@ public class ConnectionController extends SubForm { } else if (isClientMode(HClient.NITRO)) { getHConnection().startNitro(); } - - - if (HConnection.DEBUG) System.out.println("connecting"); + if (GEarthProperties.isDebugModeEnabled()) + System.out.println("connecting"); }).start(); - - } else { getHConnection().abort(); @@ -289,13 +269,10 @@ public class ConnectionController extends SubForm { @Override protected void onExit() { - if (rd_flash.isSelected()) { - Cacher.put(CLIENT_CACHE_KEY, HClient.FLASH); - } else if (rd_unity.isSelected()) { - Cacher.put(CLIENT_CACHE_KEY, HClient.UNITY); - } else if (rd_nitro.isSelected()) { - Cacher.put(CLIENT_CACHE_KEY, HClient.NITRO); - } + GEarthProperties.clientTypeProperty.set( + rd_flash.isSelected() ? HClient.FLASH + : rd_unity.isSelected() ? HClient.UNITY + : HClient.NITRO); getHConnection().abort(); } diff --git a/G-Earth/src/main/java/gearth/ui/subforms/extensions/ExtensionsController.java b/G-Earth/src/main/java/gearth/ui/subforms/extensions/ExtensionsController.java index 69b6546..a3774fc 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/extensions/ExtensionsController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/extensions/ExtensionsController.java @@ -8,6 +8,7 @@ import gearth.services.extension_handler.extensions.implementations.network.exec import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunner; import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunnerFactory; import gearth.services.g_python.GPythonShell; +import gearth.ui.GEarthProperties; import gearth.ui.SubForm; import gearth.ui.subforms.extensions.logger.ExtensionLogger; import gearth.ui.translations.LanguageBundle; @@ -48,6 +49,8 @@ public class ExtensionsController extends SubForm { scroller.widthProperty().addListener(observable -> header_ext.setPrefWidth(scroller.getWidth())); extensionLogger = new ExtensionLogger(); + btn_install.disableProperty().bind(GEarthProperties.enableDeveloperModeProperty.not()); + initLanguageBinding(); } @@ -79,8 +82,6 @@ public class ExtensionsController extends SubForm { extensionLogger.log(text); } })); - - getHConnection().onDeveloperModeChange(this::setLocalInstallingEnabled); } diff --git a/G-Earth/src/main/java/gearth/ui/subforms/extra/ExtraController.java b/G-Earth/src/main/java/gearth/ui/subforms/extra/ExtraController.java index 409a36a..1c3d170 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/extra/ExtraController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/extra/ExtraController.java @@ -1,25 +1,25 @@ package gearth.ui.subforms.extra; import gearth.GEarth; -import gearth.misc.Cacher; -import gearth.protocol.HConnection; +import gearth.misc.BindingsUtil; import gearth.protocol.connection.HState; import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.SocksConfiguration; import gearth.services.always_admin.AdminService; import gearth.services.g_python.GPythonVersionUtils; +import gearth.ui.GEarthProperties; import gearth.ui.SubForm; import gearth.ui.subforms.info.InfoController; import gearth.ui.titlebar.TitleBarController; import gearth.ui.translations.LanguageBundle; import gearth.ui.translations.TranslatableString; import javafx.application.Platform; +import javafx.beans.binding.Bindings; import javafx.event.ActionEvent; import javafx.scene.control.*; import javafx.scene.layout.FlowPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Region; -import org.json.JSONObject; import java.io.IOException; import java.util.Optional; @@ -31,16 +31,6 @@ public class ExtraController extends SubForm implements SocksConfiguration { public static final String INFO_URL_GPYTHON = "https://github.com/sirjonasxx/G-Earth/wiki/G-Python-qtConsole"; - public static final String NOTEPAD_CACHE_KEY = "notepad_text"; - public static final String DEVELOP_CACHE_KEY = "develop_mode"; - public static final String ALWAYS_ADMIN_KEY = "always_admin"; - public static final String SOCKS_CACHE_KEY = "socks_config"; - public static final String GPYTHON_CACHE_KEY = "use_gpython"; - - public static final String SOCKS_IP = "ip"; - public static final String SOCKS_PORT = "port"; -// public static final String IGNORE_ONCE = "ignore_once"; - public TextArea txtarea_notepad; @@ -69,32 +59,23 @@ public class ExtraController extends SubForm implements SocksConfiguration { url_troubleshooting.setTooltip(new Tooltip("https://github.com/sirjonasxx/G-Earth/wiki/Troubleshooting")); InfoController.activateHyperlink(url_troubleshooting); - String notepadInitValue = (String)Cacher.get(NOTEPAD_CACHE_KEY); - if (notepadInitValue != null) { - txtarea_notepad.setText(notepadInitValue); - } + BindingsUtil.setAndBindBiDirectional(txtarea_notepad.textProperty(), GEarthProperties.notesProperty); - if (Cacher.getCacheContents().has(SOCKS_CACHE_KEY)) { - JSONObject socksInitValue = Cacher.getCacheContents().getJSONObject(SOCKS_CACHE_KEY); - txt_socksIp.setText(socksInitValue.getString(SOCKS_IP) + ":" + socksInitValue.getInt(SOCKS_PORT)); -// cbx_socksUseIfNeeded.setSelected(socksInitValue.getBoolean(IGNORE_ONCE)); - } - - if (Cacher.getCacheContents().has(GPYTHON_CACHE_KEY)) { - cbx_gpython.setSelected(Cacher.getCacheContents().getBoolean(GPYTHON_CACHE_KEY)); - } - - if (Cacher.getCacheContents().has(ALWAYS_ADMIN_KEY)) { - cbx_admin.setSelected(Cacher.getCacheContents().getBoolean(ALWAYS_ADMIN_KEY)); - } - - cbx_debug.selectedProperty().addListener(observable -> HConnection.DEBUG = cbx_debug.isSelected()); - cbx_disableDecryption.selectedProperty().addListener(observable -> HConnection.DECRYPTPACKETS = !cbx_disableDecryption.isSelected()); - - cbx_useSocks.selectedProperty().addListener(observable -> grd_socksInfo.setDisable(!cbx_useSocks.isSelected())); + txt_socksIp.textProperty().set(GEarthProperties.getSocksHost()+":"+GEarthProperties.getSocksPort()); + GEarthProperties.socksHostProperty.bind(Bindings.createStringBinding(this::getSocksHost, txt_socksIp.textProperty())); + GEarthProperties.socksPortProperty.bind(Bindings.createIntegerBinding(this::getSocksPort, txt_socksIp.textProperty())); + grd_socksInfo.disableProperty().bind(GEarthProperties.enableSocksProperty.not()); + BindingsUtil.setAndBindBiDirectional(cbx_useSocks.selectedProperty(), GEarthProperties.enableSocksProperty); ProxyProviderFactory.setSocksConfig(this); + BindingsUtil.setAndBindBiDirectional(cbx_debug.selectedProperty(), GEarthProperties.enableDebugProperty); + BindingsUtil.setAndBindBiDirectional(cbx_disableDecryption.selectedProperty(), GEarthProperties.disablePacketDecryptionProperty); + BindingsUtil.setAndBindBiDirectional(cbx_alwaysOnTop.selectedProperty(), GEarthProperties.alwaysOnTopProperty); + BindingsUtil.setAndBindBiDirectional(cbx_develop.selectedProperty(), GEarthProperties.enableDeveloperModeProperty); + BindingsUtil.setAndBindBiDirectional(cbx_admin.selectedProperty(), GEarthProperties.alwaysAdminProperty); + BindingsUtil.setAndBindBiDirectional(cbx_gpython.selectedProperty(), GEarthProperties.enableGPythonProperty); + initLanguageBinding(); } @@ -102,47 +83,16 @@ public class ExtraController extends SubForm implements SocksConfiguration { protected void onParentSet() { adminService = new AdminService(cbx_admin.isSelected(), getHConnection()); getHConnection().addTrafficListener(1, message -> adminService.onMessage(message)); - getHConnection().getStateObservable().addListener((oldState, newState) -> {if (newState == HState.CONNECTED) adminService.onConnect();}); - - parentController.getStage().setAlwaysOnTop(cbx_alwaysOnTop.isSelected()); - cbx_alwaysOnTop.selectedProperty().addListener(observable -> parentController.getStage().setAlwaysOnTop(cbx_alwaysOnTop.isSelected())); - - cbx_advanced.selectedProperty().addListener(observable -> updateAdvancedUI()); - getHConnection().getStateObservable().addListener((oldState, newState) -> { - if (oldState == HState.NOT_CONNECTED || newState == HState.NOT_CONNECTED) { + getHConnection().stateProperty().addListener((observable, oldValue, newValue) -> { + if (newValue == HState.CONNECTED) + adminService.onConnect(); + if (oldValue == HState.NOT_CONNECTED || newValue == HState.NOT_CONNECTED) updateAdvancedUI(); - } }); - - if (Cacher.getCacheContents().has(DEVELOP_CACHE_KEY)) { - boolean inDevelopMode = Cacher.getCacheContents().getBoolean(DEVELOP_CACHE_KEY); - setDevelopMode(inDevelopMode); - } - + cbx_advanced.selectedProperty().addListener(observable -> updateAdvancedUI()); updateAdvancedUI(); } - @Override - protected void onExit() { - Cacher.put(NOTEPAD_CACHE_KEY, txtarea_notepad.getText()); - Cacher.put(GPYTHON_CACHE_KEY, cbx_gpython.isSelected()); - Cacher.put(ALWAYS_ADMIN_KEY, cbx_admin.isSelected()); - Cacher.put(DEVELOP_CACHE_KEY, cbx_develop.isSelected()); - saveSocksConfig(); - } - - private void saveSocksConfig() { - if (txt_socksIp.getText().contains(":")) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put(SOCKS_IP, getSocksHost()); - jsonObject.put(SOCKS_PORT, getSocksPort()); - Cacher.put(SOCKS_CACHE_KEY, jsonObject); - } - else { - Cacher.remove(SOCKS_CACHE_KEY); - } - } - private void updateAdvancedUI() { if (!cbx_advanced.isSelected()) { cbx_debug.setSelected(false); @@ -158,7 +108,6 @@ public class ExtraController extends SubForm implements SocksConfiguration { @Override public boolean useSocks() { - saveSocksConfig(); return cbx_useSocks.isSelected(); } @@ -265,7 +214,6 @@ public class ExtraController extends SubForm implements SocksConfiguration { private void setDevelopMode(boolean enabled) { cbx_develop.setSelected(enabled); - getHConnection().setDeveloperMode(enabled); } public void adminCbxClick(ActionEvent actionEvent) { diff --git a/G-Earth/src/main/java/gearth/ui/subforms/info/InfoController.java b/G-Earth/src/main/java/gearth/ui/subforms/info/InfoController.java index 8187826..6c478db 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/info/InfoController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/info/InfoController.java @@ -1,23 +1,26 @@ package gearth.ui.subforms.info; import gearth.GEarth; +import gearth.ui.GEarthProperties; import gearth.ui.titlebar.TitleBarController; import gearth.ui.translations.LanguageBundle; import gearth.ui.translations.TranslatableString; import javafx.event.ActionEvent; +import javafx.fxml.Initializable; import javafx.scene.control.*; import gearth.ui.SubForm; import javafx.scene.image.ImageView; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import javafx.scene.web.WebView; import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; /** * Created by Jonas on 06/04/18. */ -public class InfoController extends SubForm { +public class InfoController extends SubForm implements Initializable { public ImageView img_logo; public Hyperlink link_darkbox; public Hyperlink link_g_gearth; @@ -38,7 +41,12 @@ public class InfoController extends SubForm { }); } - public void initialize() { + @Override + public void initialize(URL location, ResourceBundle resources) { + + img_logo.imageProperty().bind(GEarthProperties.logoImageBinding); + version.textProperty().bind(GEarthProperties.themeTitleBinding); + link_darkbox.setTooltip(new Tooltip("https://darkbox.nl")); link_d_gearth.setTooltip(new Tooltip("https://discord.gg/AVkcF8y")); link_g_gearth.setTooltip(new Tooltip("https://github.com/sirjonasxx/G-Earth")); @@ -87,4 +95,5 @@ public class InfoController extends SubForm { btn_donate.textProperty().bind(new TranslatableString("%s", "tab.info.donate")); } + } diff --git a/G-Earth/src/main/java/gearth/ui/subforms/injection/InjectionController.java b/G-Earth/src/main/java/gearth/ui/subforms/injection/InjectionController.java index aca0358..fcc377b 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/injection/InjectionController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/injection/InjectionController.java @@ -4,10 +4,14 @@ import gearth.misc.Cacher; import gearth.protocol.HConnection; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.ui.GEarthProperties; import gearth.ui.SubForm; import gearth.ui.translations.LanguageBundle; import gearth.ui.translations.TranslatableString; import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.binding.Bindings; import javafx.event.ActionEvent; import javafx.scene.control.*; import javafx.scene.input.MouseButton; @@ -38,10 +42,10 @@ public class InjectionController extends SubForm { private TranslatableString corruption, pcktInfo; protected void onParentSet() { - getHConnection().onDeveloperModeChange(developMode -> updateUI()); - getHConnection().getStateObservable().addListener((oldState, newState) -> Platform.runLater(this::updateUI)); - - inputPacket.textProperty().addListener(event -> Platform.runLater(this::updateUI)); + final InvalidationListener updateUI = observable -> Platform.runLater(this::updateUI); + GEarthProperties.enableDeveloperModeProperty.addListener(updateUI); + getHConnection().stateProperty().addListener(updateUI); + inputPacket.textProperty().addListener(updateUI); } public void initialize() { diff --git a/G-Earth/src/main/java/gearth/ui/subforms/scheduler/InteractableScheduleItem.java b/G-Earth/src/main/java/gearth/ui/subforms/scheduler/InteractableScheduleItem.java index 7651cd6..d131eda 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/scheduler/InteractableScheduleItem.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/scheduler/InteractableScheduleItem.java @@ -9,6 +9,15 @@ import gearth.services.scheduler.listeners.OnBeingUpdatedListener; import gearth.services.scheduler.listeners.OnDeleteListener; import gearth.services.scheduler.listeners.OnEditListener; import gearth.services.scheduler.listeners.OnUpdatedListener; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ObjectPropertyBase; +import javafx.event.ActionEvent; +import javafx.event.Event; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.control.ButtonBase; +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; public class InteractableScheduleItem extends ScheduleItem implements StringifyAble { diff --git a/G-Earth/src/main/java/gearth/ui/subforms/scheduler/SchedulerController.java b/G-Earth/src/main/java/gearth/ui/subforms/scheduler/SchedulerController.java index b2a95c5..4c9d341 100644 --- a/G-Earth/src/main/java/gearth/ui/subforms/scheduler/SchedulerController.java +++ b/G-Earth/src/main/java/gearth/ui/subforms/scheduler/SchedulerController.java @@ -7,9 +7,12 @@ import gearth.protocol.StateChangeListener; import gearth.protocol.connection.HState; import gearth.services.scheduler.Interval; import gearth.services.scheduler.Scheduler; +import gearth.ui.GEarthProperties; import gearth.ui.translations.LanguageBundle; import gearth.ui.translations.TranslatableString; import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.binding.Bindings; import javafx.event.ActionEvent; import javafx.scene.control.*; import javafx.scene.layout.GridPane; @@ -88,8 +91,9 @@ public class SchedulerController extends SubForm { @Override protected void onParentSet() { scheduler = new Scheduler<>(getHConnection()); - getHConnection().onDeveloperModeChange(developMode -> updateUI()); - getHConnection().getStateObservable().addListener((oldState, newState) -> updateUI()); + final InvalidationListener updateUI = observable -> Platform.runLater(this::updateUI); + GEarthProperties.enableDeveloperModeProperty.addListener(updateUI); + getHConnection().stateProperty().addListener(updateUI); updateUI(); } diff --git a/G-Earth/src/main/java/gearth/ui/titlebar/GEarthThemedTitleBarConfig.java b/G-Earth/src/main/java/gearth/ui/titlebar/GEarthThemedTitleBarConfig.java index 83495a0..4166e68 100644 --- a/G-Earth/src/main/java/gearth/ui/titlebar/GEarthThemedTitleBarConfig.java +++ b/G-Earth/src/main/java/gearth/ui/titlebar/GEarthThemedTitleBarConfig.java @@ -1,16 +1,13 @@ package gearth.ui.titlebar; -import gearth.GEarth; -import gearth.ui.themes.Theme; +import gearth.ui.GEarthProperties; import javafx.stage.Stage; -import java.util.function.Consumer; - public class GEarthThemedTitleBarConfig extends DefaultTitleBarConfig { public GEarthThemedTitleBarConfig(Stage stage) { - super(stage, GEarth.getTheme()); - GEarth.getThemeObservable().addListener(this::setTheme); + super(stage, GEarthProperties.getTheme()); + GEarthProperties.themeProperty.addListener((observable, oldValue, newValue) -> setTheme(newValue)); } @Override