From 15628b5eec35a9a2ad99f4c5ae5eec6177c8faa7 Mon Sep 17 00:00:00 2001 From: sirjonasxx <36828922+sirjonasxx@users.noreply.github.com> Date: Sun, 26 Apr 2020 02:35:54 +0200 Subject: [PATCH] complete rework of extensions internally --- .../java/gearth/extensions/Extension.java | 27 +- .../extensions/parsers/HEntityType.java | 3 - .../extensionserver/ExtensionServer.java | 220 ++++++++++++ .../extensions/ExtensionListener.java | 15 + .../extensions/GEarthExtension.java | 143 ++++++++ .../extensionproducers/ExtensionProducer.java | 7 + .../ExtensionProducerFactory.java | 20 ++ .../ExtensionProducerObserver.java | 8 + .../extensions/network/NetworkExtension.java | 219 ++++++++++++ .../network/NetworkExtensionInfo.java | 101 ++++++ .../network/NetworkExtensionsProducer.java | 95 ++++++ .../authentication/Authenticator.java | 11 +- .../network}/executer/ExecutionInfo.java | 2 +- .../network}/executer/ExtensionRunner.java | 2 +- .../executer/ExtensionRunnerFactory.java | 2 +- .../executer/NormalExtensionRunner.java | 4 +- .../extensions/simple/ExampleExtension.java | 111 ++++++ .../extensions/simple/README.md | 6 + .../simple/SimpleExtensionProducer.java | 15 + .../ui/extensions/ExtensionItemContainer.java | 14 +- .../ExtensionItemContainerProducer.java | 4 +- .../java/gearth/ui/extensions/Extensions.java | 320 ++---------------- .../gearth/ui/extensions/GEarthExtension.java | 263 -------------- .../extensions/GEarthExtensionsRegistrer.java | 52 --- 24 files changed, 1016 insertions(+), 648 deletions(-) create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/ExtensionServer.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/ExtensionListener.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/GEarthExtension.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducer.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerFactory.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerObserver.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtension.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionInfo.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionsProducer.java rename G-Earth/src/main/java/gearth/{ui/extensions => services/extensionserver/extensions/network}/authentication/Authenticator.java (88%) rename G-Earth/src/main/java/gearth/{ui/extensions => services/extensionserver/extensions/network}/executer/ExecutionInfo.java (96%) rename G-Earth/src/main/java/gearth/{ui/extensions => services/extensionserver/extensions/network}/executer/ExtensionRunner.java (82%) rename G-Earth/src/main/java/gearth/{ui/extensions => services/extensionserver/extensions/network}/executer/ExtensionRunnerFactory.java (81%) rename G-Earth/src/main/java/gearth/{ui/extensions => services/extensionserver/extensions/network}/executer/NormalExtensionRunner.java (97%) create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/ExampleExtension.java create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/README.md create mode 100644 G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/SimpleExtensionProducer.java delete mode 100644 G-Earth/src/main/java/gearth/ui/extensions/GEarthExtension.java delete mode 100644 G-Earth/src/main/java/gearth/ui/extensions/GEarthExtensionsRegistrer.java diff --git a/G-Earth/src/main/java/gearth/extensions/Extension.java b/G-Earth/src/main/java/gearth/extensions/Extension.java index bdde07e..5376f5a 100644 --- a/G-Earth/src/main/java/gearth/extensions/Extension.java +++ b/G-Earth/src/main/java/gearth/extensions/Extension.java @@ -2,6 +2,8 @@ package gearth.extensions; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.services.extensionserver.ExtensionServer; +import gearth.services.extensionserver.extensions.network.NetworkExtensionInfo; import gearth.ui.extensions.Extensions; import java.io.*; @@ -118,10 +120,10 @@ public abstract class Extension implements IExtension{ packet.fixLength(); - if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INFOREQUEST) { + if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST) { ExtensionInfo info = getInfoAnnotations(); - HPacket response = new HPacket(Extensions.INCOMING_MESSAGES_IDS.EXTENSIONINFO); + HPacket response = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONINFO); response.appendString(info.Title()) .appendString(info.Author()) .appendString(info.Version()) @@ -134,7 +136,7 @@ public abstract class Extension implements IExtension{ .appendBoolean(canDelete); writeToStream(response.toBytes()); } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) { String host = packet.readString(); int connectionPort = packet.readInteger(); String hotelVersion = packet.readString(); @@ -142,10 +144,10 @@ public abstract class Extension implements IExtension{ notifyConnectionListeners(host, connectionPort, hotelVersion, harbleMessagesPath); onStartConnection(); } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.CONNECTIONEND) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONEND) { onEndConnection(); } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.FLAGSCHECK) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.FLAGSCHECK) { // body = an array of G-Earths gearth flags if (flagRequestCallback != null) { int arraysize = packet.readInteger(); @@ -157,16 +159,13 @@ public abstract class Extension implements IExtension{ } flagRequestCallback = null; } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INIT) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INIT) { initExtension(); } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.FREEFLOW) { - // nothing to be done yet - } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK) { onClick(); } - else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) { + else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) { String stringifiedMessage = packet.readLongString(); HMessage habboMessage = new HMessage(stringifiedMessage); HPacket habboPacket = habboMessage.getPacket(); @@ -200,7 +199,7 @@ public abstract class Extension implements IExtension{ } habboMessage.getPacket().resetReadIndex(); - HPacket response = new HPacket(Extensions.INCOMING_MESSAGES_IDS.MANIPULATEDPACKET); + HPacket response = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.MANIPULATEDPACKET); response.appendLongString(habboMessage.stringify()); writeToStream(response.toBytes()); @@ -247,7 +246,7 @@ public abstract class Extension implements IExtension{ return send(packet, HMessage.Direction.TOSERVER); } private boolean send(HPacket packet, HMessage.Direction direction) { - HPacket packet1 = new HPacket(Extensions.INCOMING_MESSAGES_IDS.SENDMESSAGE); + HPacket packet1 = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.SENDMESSAGE); packet1.appendByte(direction == HMessage.Direction.TOCLIENT ? (byte)0 : (byte)1); packet1.appendInt(packet.getBytesLength()); packet1.appendBytes(packet.toBytes()); @@ -307,7 +306,7 @@ public abstract class Extension implements IExtension{ * @param s the text to be written */ public void writeToConsole(String s) { - HPacket packet = new HPacket(Extensions.INCOMING_MESSAGES_IDS.EXTENSIONCONSOLELOG); + HPacket packet = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONCONSOLELOG); packet.appendString(s); try { writeToStream(packet.toBytes()); diff --git a/G-Earth/src/main/java/gearth/extensions/parsers/HEntityType.java b/G-Earth/src/main/java/gearth/extensions/parsers/HEntityType.java index c740d4e..2158034 100644 --- a/G-Earth/src/main/java/gearth/extensions/parsers/HEntityType.java +++ b/G-Earth/src/main/java/gearth/extensions/parsers/HEntityType.java @@ -3,9 +3,6 @@ package gearth.extensions.parsers; import java.util.HashMap; import java.util.Map; -/** - * Created by Jeunez on 8/01/2019. - */ public enum HEntityType { HABBO(1), PET(2), diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/ExtensionServer.java b/G-Earth/src/main/java/gearth/services/extensionserver/ExtensionServer.java new file mode 100644 index 0000000..1e3d2ab --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/ExtensionServer.java @@ -0,0 +1,220 @@ +package gearth.services.extensionserver; + +import gearth.Main; +import gearth.misc.harble_api.HarbleAPIFetcher; +import gearth.protocol.HConnection; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.services.extensionserver.extensions.ExtensionListener; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducerFactory; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducerObserver; +import gearth.services.extensionserver.extensions.GEarthExtension; + +import java.util.*; + +public class ExtensionServer { + + private final List gEarthExtensions = new ArrayList<>(); + private final HConnection hConnection; + private List extensionProducers; + + + public ExtensionServer(HConnection hConnection) { + this.hConnection = hConnection; + initialize(); + } + + private void initialize() { + + hConnection.addStateChangeListener((oldState, newState) -> { + if (newState == HConnection.State.CONNECTED) { + HarbleAPIFetcher.fetch(hConnection.getHotelVersion()); + synchronized (gEarthExtensions) { + for (GEarthExtension extension : gEarthExtensions) { + extension.connectionStart( + hConnection.getDomain(), + hConnection.getServerPort(), + hConnection.getHotelVersion(), + HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath() + ); + } + } + } + if (oldState == HConnection.State.CONNECTED) { + synchronized (hConnection) { + for (GEarthExtension extension : gEarthExtensions) { + extension.connectionEnd(); + } + } + } + }); + + + hConnection.addTrafficListener(1, message -> { + Set collection; + synchronized (gEarthExtensions) { + collection = new HashSet<>(gEarthExtensions); + } + HMessage result = new HMessage(message); + + boolean[] isblock = new boolean[1]; + synchronized (collection) { + for (GEarthExtension extension : collection) { + ExtensionListener respondCallback = new ExtensionListener() { + @Override + public void manipulatedPacket(HMessage responseMessage) { + if (responseMessage.getDestination() == message.getDestination() && responseMessage.getIndex() == message.getIndex()) { + synchronized (result) { + if (!message.equals(responseMessage)) { + result.constructFromHMessage(responseMessage); + } + if (responseMessage.isBlocked()) { + isblock[0] = true; + } + synchronized (collection) { + collection.remove(extension); + } + + extension.removeExtensionListener(this); + } + } + } + }; + extension.registerExtensionListener(respondCallback); + } + } + + Set collection2; + synchronized (collection) { + collection2 = new HashSet<>(collection); + } + + synchronized (collection2) { + for (GEarthExtension extension : collection2) { + extension.packetIntercept(new HMessage(message)); + } + } + + //block untill all extensions have responded + List willdelete = new ArrayList<>(); + while (true) { + synchronized (collection) { + if (collection.isEmpty()) { + break; + } + + synchronized (gEarthExtensions) { + for (GEarthExtension extension : collection) { + if (!gEarthExtensions.contains(extension)) willdelete.add(extension); + } + } + + for (int i = willdelete.size() - 1; i >= 0; i--) { + collection.remove(willdelete.get(i)); + willdelete.remove(i); + } + } + + + try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} + } + + message.constructFromHMessage(result); + + if (isblock[0]) { + message.setBlocked(true); + } + }); + + extensionProducers = ExtensionProducerFactory.getAll(); + extensionProducers.forEach(this::initializeExtensionProducer); + } + + private void initializeExtensionProducer(ExtensionProducer producer) { + producer.startProducing(new ExtensionProducerObserver() { + @Override + public void onExtensionConnect(GEarthExtension extension) { + synchronized (gEarthExtensions) { + gEarthExtensions.add(extension); + } + + + ExtensionListener listener = new ExtensionListener() { + @Override + public void flagsRequest() { + extension.provideFlags(Main.args); + } + + @Override + public void sendMessage(HMessage.Direction direction, HPacket packet) { + if (direction == HMessage.Direction.TOCLIENT) { + hConnection.sendToClientAsync(packet); + } + else { + hConnection.sendToServerAsync(packet); + } + } + + @Override + public void log(String text) { + // todo make simple logger for Extensions, the implementation now is temporary + System.out.println(text); + } + + @Override + public void hasClosed() { + synchronized (gEarthExtensions) { + gEarthExtensions.remove(extension); + } + extension.removeExtensionListener(this); + extension.delete(); + } + }; + + extension.registerExtensionListener(listener); + extension.init(); + + if (hConnection.getState() == HConnection.State.CONNECTED) { + extension.connectionStart( + hConnection.getDomain(), + hConnection.getServerPort(), + hConnection.getHotelVersion(), + HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath() + ); + } + + extension.onRemoveClick(observable -> extension.close()); + extension.onClick(observable -> extension.doubleclick()); + + notifyExtensionConnectListeners(extension); + } + }); + } + + + public List getExtensionProducers() { + return extensionProducers; + } + + public interface ExtensionConnectListener { + void extensionConnected(GEarthExtension e); + } + private List listeners = new ArrayList<>(); + public void onExtensionConnected(ExtensionConnectListener extensionConnectListener) { + synchronized (gEarthExtensions) { + for (GEarthExtension gEarthExtension : gEarthExtensions) { + extensionConnectListener.extensionConnected(gEarthExtension); + } + } + listeners.add(extensionConnectListener); + } + public void removeExtensionConnectListener(ExtensionConnectListener extensionConnectListener) { + listeners.remove(extensionConnectListener); + } + private void notifyExtensionConnectListeners(GEarthExtension extension) { + for (int i = listeners.size() - 1; i >= 0; i--) { + listeners.get(i).extensionConnected(extension); + } + } +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/ExtensionListener.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/ExtensionListener.java new file mode 100644 index 0000000..60bf0c5 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/ExtensionListener.java @@ -0,0 +1,15 @@ +package gearth.services.extensionserver.extensions; + +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; + +public abstract class ExtensionListener { + + // override whatever you need + protected void manipulatedPacket(HMessage hMessage) {} + protected void flagsRequest() {} + protected void sendMessage(HMessage.Direction direction, HPacket packet) {} + protected void log(String text) {} + protected void hasClosed() {} + +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/GEarthExtension.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/GEarthExtension.java new file mode 100644 index 0000000..3f7ecdf --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/GEarthExtension.java @@ -0,0 +1,143 @@ +package gearth.services.extensionserver.extensions; + +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import javafx.beans.InvalidationListener; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public abstract class GEarthExtension { + + + // ------ static extension information -------- + public abstract String getAuthor(); + public abstract String getDescription(); + public abstract String getTitle(); + public abstract String getVersion(); + + public String getFileName() { + return ""; // override in extensions over network if executed from file + } + + public abstract boolean isFireButtonUsed(); + public abstract boolean isDeleteButtonVisible(); + public abstract boolean isLeaveButtonVisible(); + public abstract boolean isInstalledExtension(); + // -------------------------------------------- + + + + + + + // ------- actions you can perform towards the extension --------- + public abstract void doubleclick(); + public abstract void packetIntercept(HMessage hMessage); + public abstract void provideFlags(String[] flags); + public abstract void connectionStart(String host, int port, String hotelVersion, String harbleMessagesPath); + public abstract void connectionEnd(); + public abstract void init(); + public abstract void close(); + // --------------------------------------------------------------- + + + + + + // ----------------- listen to the extension --------------------- + protected final List extensionListeners = new ArrayList<>(); + public void registerExtensionListener(ExtensionListener listener) { + this.extensionListeners.add(listener); + } + public void removeExtensionListener(ExtensionListener listener) { + this.extensionListeners.remove(listener); + } + private void notifyListeners(Consumer consumer) { + for (int i = extensionListeners.size() - 1; i >= 0; i--) { + consumer.accept(extensionListeners.get(i)); + } + + extensionListeners.forEach(consumer); + } + + protected void sendManipulatedPacket(HMessage hMessage) { + int orgIndex = hMessage.getPacket().getReadIndex(); + notifyListeners(listener -> { + hMessage.getPacket().setReadIndex(6); + listener.manipulatedPacket(hMessage); + }); + hMessage.getPacket().setReadIndex(orgIndex); + } + protected void requestFlags() { + notifyListeners(ExtensionListener::flagsRequest); + } + protected void sendMessage(HMessage.Direction direction, HPacket packet) { + int orgIndex = packet.getReadIndex(); + notifyListeners(listener -> { + packet.setReadIndex(6); + listener.sendMessage(direction, packet); + }); + packet.setReadIndex(orgIndex); + } + protected void log(String text) { + notifyListeners(listener -> listener.log(text)); + } + protected void hasClosed() { + notifyListeners(ExtensionListener::hasClosed); + } + // -------------------------------------------------------------------- + + + + + + + + // ----------- methods for interaction with G-Earth UI, don't use/change them ---------------- + + private final List onRemoveClickListener = new ArrayList<>(); + public void onRemoveClick(InvalidationListener listener) { + synchronized (onRemoveClickListener) { + onRemoveClickListener.add(listener); + } + } + public void isRemoveClickTrigger() { + synchronized (onRemoveClickListener) { + for (int i = onRemoveClickListener.size() - 1; i >= 0; i--) { + onRemoveClickListener.get(i).invalidated(null); + } + } + } + + private final List onClickListener = new ArrayList<>(); + public void onClick(InvalidationListener listener) { + synchronized (onClickListener) { + onClickListener.add(listener); + } + } + public void isClickTrigger() { + synchronized (onClickListener) { + for (int i = onClickListener.size() - 1; i >= 0; i--) { + onClickListener.get(i).invalidated(null); + } + } + } + + private final List onDeleteListeners = new ArrayList<>(); + public void onDelete(InvalidationListener listener) { + synchronized (onDeleteListeners) { + onDeleteListeners.add(listener); + } + } + public void delete() { + synchronized (onDeleteListeners) { + for (int i = onDeleteListeners.size() - 1; i >= 0; i--) { + onDeleteListeners.get(i).invalidated(null); + } + } + } + // ---------------------------------------------------------------------------------------- + +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducer.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducer.java new file mode 100644 index 0000000..92702ba --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducer.java @@ -0,0 +1,7 @@ +package gearth.services.extensionserver.extensions.extensionproducers; + +public interface ExtensionProducer { + + void startProducing(ExtensionProducerObserver observer); + +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerFactory.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerFactory.java new file mode 100644 index 0000000..df222d8 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerFactory.java @@ -0,0 +1,20 @@ +package gearth.services.extensionserver.extensions.extensionproducers; + +import gearth.services.extensionserver.extensions.network.NetworkExtensionsProducer; +import gearth.services.extensionserver.extensions.simple.SimpleExtensionProducer; + +import java.util.Arrays; +import java.util.List; + +public class ExtensionProducerFactory { + // returns one of every ExtensionProducer class we have created, to support all types of extensions + + public static List getAll() { + return Arrays.asList( + new NetworkExtensionsProducer(), + new SimpleExtensionProducer() + ); + } + + +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerObserver.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerObserver.java new file mode 100644 index 0000000..1e0e243 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/extensionproducers/ExtensionProducerObserver.java @@ -0,0 +1,8 @@ +package gearth.services.extensionserver.extensions.extensionproducers; + +import gearth.services.extensionserver.extensions.GEarthExtension; +import gearth.services.extensionserver.extensions.network.NetworkExtension; + +public interface ExtensionProducerObserver { + void onExtensionConnect(GEarthExtension extension); +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtension.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtension.java new file mode 100644 index 0000000..b5ba05f --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtension.java @@ -0,0 +1,219 @@ +package gearth.services.extensionserver.extensions.network; + +import gearth.Main; +import gearth.protocol.HMessage; +import gearth.services.extensionserver.extensions.GEarthExtension; +import javafx.beans.InvalidationListener; +import gearth.protocol.HPacket; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import gearth.services.extensionserver.extensions.network.authentication.Authenticator; +import java.net.Socket; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Jonas on 21/06/18. + */ +public class NetworkExtension extends GEarthExtension { + + private String title; + private String author; + private String version; + private String description; + + private boolean fireEventButtonVisible; + private boolean leaveButtonVisible; + private boolean deleteButtonVisible; + + private boolean isInstalledExtension; // <- extension is in the extensions directory + private String fileName; + private String cookie; + + private Socket connection; + + NetworkExtension(HPacket extensionInfo, Socket connection) { + this.title = extensionInfo.readString(); + this.author = extensionInfo.readString(); + this.version = extensionInfo.readString(); + this.description = extensionInfo.readString(); + this.fireEventButtonVisible = extensionInfo.readBoolean(); + + this.isInstalledExtension = extensionInfo.readBoolean(); + this.fileName = extensionInfo.readString(); + this.cookie = extensionInfo.readString(); + + this.leaveButtonVisible = extensionInfo.readBoolean(); + this.deleteButtonVisible = extensionInfo.readBoolean(); + + this.connection = connection; + + NetworkExtension selff = this; + new Thread(() -> { + try { + InputStream inputStream = connection.getInputStream(); + DataInputStream dIn = new DataInputStream(inputStream); + + while (!connection.isClosed()) { + int length = dIn.readInt(); + byte[] headerandbody = new byte[length + 4]; + + int amountRead = 0; + while (amountRead < length) { + amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); + } + + HPacket message = new HPacket(headerandbody); + message.fixLength(); + + synchronized (selff.extensionListeners) { + if (message.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.REQUESTFLAGS) { + requestFlags(); + } + else if (message.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.SENDMESSAGE) { + byte side = message.readByte(); + int byteLength = message.readInteger(); + byte[] packetAsByteArray = message.readBytes(byteLength); + + HPacket packet = new HPacket(packetAsByteArray); + if (!packet.isCorrupted()) { + sendMessage( + side == 0 ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER, + packet + ); + } + } + else if (message.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.MANIPULATEDPACKET) { + String stringifiedresponse = message.readLongString(6); + HMessage responseMessage = new HMessage(stringifiedresponse); + sendManipulatedPacket(responseMessage); + } + else if (message.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONCONSOLELOG) { + log(message.readString()); + } + + } + + } + + } catch (IOException e) { + // An extension disconnected, which is OK + } finally { + synchronized (selff.extensionListeners) { + hasClosed(); + } + if (!connection.isClosed()) { + try { + connection.close(); + } catch (IOException e) { +// e.printStackTrace(); + } + } + } + }).start(); + + + } + + + public String getAuthor() { + return author; + } + public String getDescription() { + return description; + } + public String getTitle() { + return title; + } + public String getVersion() { + return version; + } + public boolean isFireButtonUsed() { + return fireEventButtonVisible; + } + public String getFileName() { + return fileName; + } + public String getCookie() { + return cookie; + } + public boolean isDeleteButtonVisible() { + return deleteButtonVisible; + } + public boolean isLeaveButtonVisible() { + return leaveButtonVisible; + } + + public boolean isInstalledExtension() { + return isInstalledExtension; + } + + + private boolean sendMessage(HPacket message) { + try { + synchronized (this) { + connection.getOutputStream().write(message.toBytes()); + } + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public void doubleclick() { + sendMessage(new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK)); + } + + @Override + public void packetIntercept(HMessage hMessage) { + String stringified = hMessage.stringify(); + HPacket manipulatePacketRequest = new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT); + manipulatePacketRequest.appendLongString(stringified); + sendMessage(manipulatePacketRequest); + } + + @Override + public void provideFlags(String[] flags) { + HPacket packet = new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.FLAGSCHECK); + packet.appendInt(flags.length); + for (String flag : flags) { + packet.appendString(flag); + } + sendMessage(packet); + } + + @Override + public void connectionStart(String host, int port, String hotelVersion, String harbleMessagesPath) { + sendMessage( + new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) + .appendString(host) + .appendInt(port) + .appendString(hotelVersion) + .appendString(harbleMessagesPath) + ); + } + + @Override + public void connectionEnd() { + sendMessage( + new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONEND) + ); + } + + @Override + public void init() { + sendMessage( + new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INIT) + ); + } + + @Override + public void close() { + try { + connection.close(); + } catch (IOException ignored) { } + } +} \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionInfo.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionInfo.java new file mode 100644 index 0000000..9c99cb8 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionInfo.java @@ -0,0 +1,101 @@ +package gearth.services.extensionserver.extensions.network; + +public class NetworkExtensionInfo { + + /** + * THE EXTENSION COMMUNCATION PRINCIPLES & PROTOCOL: + * + * You will be able to write extensions in ANY language you want, but we will only provide an interface + * for Java so if you write your own in for example Python, make SURE you do it correctly or it could fuck G-Earth. + * + * Also, don't let the method where you manipulate the packets block. Similiar as how you must not block things in an UI thread. + * Why? Because Habbo relies on the TCP protocol, which ENSURES that packets get received in the right order, so we will not be fucking that up. + * That means that all packets following the packet you're manipulating in your extension will be blocked from being sent untill you're done. + * TIP: If you're trying to replace a packet in your extension but you know it will take time, just block the packet, end the method, and let something asynchronous send + * the editted packet when you're done. + * + * + * You may ignore everything beneath this line if you're extending the abstract Extension class we provide in Java. + * ----------------------------------------------------------------------------------------------------------------- + * + * (0. We recommend to use a cross-platform language for your extension) + * + * 1. An extension will run as a seperate process on your device and has to be called with the flag "-p ", + * where is a random port where the G-Earth local extension server will run on. Your extension has to connect with this server. + * + * 2. G-Earth will open your program only ONCE, that is on the boot of G-Earth or when you install the exension. + * Same story goes for closing the connection between the program and G-Earth, only once (on uninstall or close of G-Earth). + * + * You may also run your extension completely seperate from G-Earth for debugging purpose for example, then it won't be installed in G-Earth + * (but you have to configure the port yourself, which will be displayed in the extension page) + * + * 3. Once a connection is made, your extension will have to deal with the following incoming & outgoing messages as described (follows the same protocol structure as Habbo communication does): + * (if an object is sent; the object will be sent with its String representation from the StringifyAble interface, so the object's class must implement that) + * + * INCOMING MESSAGES: (marked with * if you're required to correctly respond or take action, ** if it's a response on something you requested) + * ----------------------------------------------------------------------------------------------------- + * | ID | TITLE | BODY & DESCRIPTION | + * ----------------------------------------------------------------------------------------------------- + * | 1 | ON-DOUBLECLICK | No body, the extension has been double clicked from within G-Earth | ( <- typically for tanji-module-like extensions you will open the UI here) + * ----------------------------------------------------------------------------------------------------- + * | 2 | INFO-REQUEST* | Needs response with extension info (name, desc, author, version, ..), | + * | | | exact implementation is found in the Java abstract Extension class | + * ----------------------------------------------------------------------------------------------------- + * | 3 | PACKET-INTERCEPT* | Includes the whole HMessage as body, needs response with the | + * | | | manipulated HMessage (OUTGOING id: 2) | + * ----------------------------------------------------------------------------------------------------- + * | 4 | FLAGS-CHECK** | Body: String with G-Earth's boot flags (args from static gearth method) | + * ----------------------------------------------------------------------------------------------------- + * | 5 | CONNECTION START | just a note that a new connection has been made, | + * | | | you could check this yourself as well (listen to out:4000 packet) | + * | | | host/port, hotel version | + * ----------------------------------------------------------------------------------------------------- + * | 6 | CONNECTION END | Empty body, just a note that a connection has ended | + * ----------------------------------------------------------------------------------------------------- + * | 7 | INIT | Empty body, a connection with G-Earth has been set up | + * ----------------------------------------------------------------------------------------------------- + * | 99 | FREE FLOW | extension-specific body | + * ----------------------------------------------------------------------------------------------------- + * + * OUTGOING MESSAGES: (marked with * if that is a response to one of the msgs above) + * ----------------------------------------------------------------------------------------------------- + * | ID | TITLE | BODY & DESCRIPTION | + * ----------------------------------------------------------------------------------------------------- + * | 1 | EXTENSION-INFO* | Response for INFO-REQUEST | + * ----------------------------------------------------------------------------------------------------- + * | 2 | MANIPULATED-PACKET*| Response for PACKET-INTERCEPT | + * ----------------------------------------------------------------------------------------------------- + * | 3 | REQUEST-FLAGS | Request G-Earth's flags, results in incoming FLAGS-CHECK response | + * ----------------------------------------------------------------------------------------------------- + * | 4 | SEND-MESSAGE | Body: HMessage object. Sends the HPacket wrapped in the HMessage | + * | | | to the client/server | + * ----------------------------------------------------------------------------------------------------- + * | 99 | FREE FLOW | extension-specific body | + * ----------------------------------------------------------------------------------------------------- + * + * 4. Your extension will only appear in the extension list once the EXTENSION-INFO has been received by G-Earth + * + * + */ + + + public static class OUTGOING_MESSAGES_IDS { + public static final int ONDOUBLECLICK = 1; + public static final int INFOREQUEST = 2; // backend: implemented + public static final int PACKETINTERCEPT = 3; // backend: implemented + public static final int FLAGSCHECK = 4; // backend: implemented + public static final int CONNECTIONSTART = 5; // backend: implemented + public static final int CONNECTIONEND = 6; // backend: implemented + public static final int INIT = 7; // backend: implemented + } + + + public static class INCOMING_MESSAGES_IDS { + public static final int EXTENSIONINFO = 1; // backend: implemented + public static final int MANIPULATEDPACKET = 2; // backend: implemented + public static final int REQUESTFLAGS = 3; // backend: implemented + public static final int SENDMESSAGE = 4; // backend: implemented + public static final int EXTENSIONCONSOLELOG = 98; + } + +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionsProducer.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionsProducer.java new file mode 100644 index 0000000..23c594d --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/NetworkExtensionsProducer.java @@ -0,0 +1,95 @@ +package gearth.services.extensionserver.extensions.network; + +import gearth.protocol.HPacket; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducerObserver; +import gearth.services.extensionserver.extensions.network.authentication.Authenticator; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.ServerSocket; +import java.net.Socket; + +/** + * Created by Jonas on 21/06/18. + */ +public class NetworkExtensionsProducer implements ExtensionProducer { + + private ServerSocket serverSocket; + + @Override + public void startProducing(ExtensionProducerObserver observer) { +// serverSocket = new ServerSocket(0); + int port = 9092; + boolean serverSetup = false; + while (!serverSetup) { + serverSetup = createServer(port); + port++; + } + + + new Thread(() -> { + try { + while (!serverSocket.isClosed()) { + Socket extensionSocket = serverSocket.accept(); + + new Thread(() -> { + try { + synchronized (extensionSocket) { + extensionSocket.getOutputStream().write((new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes()); + } + + InputStream inputStream = extensionSocket.getInputStream(); + DataInputStream dIn = new DataInputStream(inputStream); + + while (!extensionSocket.isClosed()) { + + int length = dIn.readInt(); + byte[] headerandbody = new byte[length + 4]; + + int amountRead = 0; + while (amountRead < length) { + amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); + } + + HPacket packet = new HPacket(headerandbody); + packet.fixLength(); + + if (packet.headerId() == NetworkExtensionInfo.INCOMING_MESSAGES_IDS.EXTENSIONINFO) { + NetworkExtension gEarthExtension = new NetworkExtension( + packet, + extensionSocket + ); + + if (Authenticator.evaluate(gEarthExtension)) { + observer.onExtensionConnect(gEarthExtension); + } + else { + gEarthExtension.close(); //you shall not pass... + } + + break; + } + } + + } catch (IOException ignored) {} + }).start(); + } + } catch (IOException e) {e.printStackTrace();} + }).start(); + } + + private boolean createServer(int port) { + try { + serverSocket = new ServerSocket(port); + return true; + } catch (IOException e) { + return false; + } + } + + public int getPort() { + return serverSocket.getLocalPort(); + } +} diff --git a/G-Earth/src/main/java/gearth/ui/extensions/authentication/Authenticator.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/authentication/Authenticator.java similarity index 88% rename from G-Earth/src/main/java/gearth/ui/extensions/authentication/Authenticator.java rename to G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/authentication/Authenticator.java index d0fc38b..302b6a8 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/authentication/Authenticator.java +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/authentication/Authenticator.java @@ -1,10 +1,7 @@ -package gearth.ui.extensions.authentication; +package gearth.services.extensionserver.extensions.network.authentication; -import gearth.extensions.Extension; import gearth.misc.ConfirmationDialog; -import gearth.ui.extensions.GEarthExtension; -import gearth.ui.extensions.executer.ExtensionRunner; -import gearth.ui.extensions.executer.ExtensionRunnerFactory; +import gearth.services.extensionserver.extensions.network.NetworkExtension; import javafx.application.Platform; import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; @@ -27,7 +24,7 @@ public class Authenticator { return cookie; } - public static boolean evaluate(GEarthExtension extension) { + public static boolean evaluate(NetworkExtension extension) { if (extension.isInstalledExtension()) { return claimSession(extension.getFileName(), extension.getCookie()); } @@ -52,7 +49,7 @@ public class Authenticator { private static volatile boolean rememberOption = false; //for not-installed extensions, popup a dialog - private static boolean askForPermission(GEarthExtension extension) { + private static boolean askForPermission(NetworkExtension extension) { boolean[] allowConnection = {true}; final String connectExtensionKey = "allow_extension_connection"; diff --git a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExecutionInfo.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExecutionInfo.java similarity index 96% rename from G-Earth/src/main/java/gearth/ui/extensions/executer/ExecutionInfo.java rename to G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExecutionInfo.java index d13203d..0effac4 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExecutionInfo.java +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExecutionInfo.java @@ -1,4 +1,4 @@ -package gearth.ui.extensions.executer; +package gearth.services.extensionserver.extensions.network.executer; import java.util.ArrayList; import java.util.HashMap; diff --git a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunner.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunner.java similarity index 82% rename from G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunner.java rename to G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunner.java index 4d1f1ee..f864684 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunner.java +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunner.java @@ -1,4 +1,4 @@ -package gearth.ui.extensions.executer; +package gearth.services.extensionserver.extensions.network.executer; /** * Created by Jonas on 21/09/18. diff --git a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunnerFactory.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunnerFactory.java similarity index 81% rename from G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunnerFactory.java rename to G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunnerFactory.java index 5d4a5d8..2717b44 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/executer/ExtensionRunnerFactory.java +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/ExtensionRunnerFactory.java @@ -1,4 +1,4 @@ -package gearth.ui.extensions.executer; +package gearth.services.extensionserver.extensions.network.executer; /** * Created by Jonas on 22/09/18. diff --git a/G-Earth/src/main/java/gearth/ui/extensions/executer/NormalExtensionRunner.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/NormalExtensionRunner.java similarity index 97% rename from G-Earth/src/main/java/gearth/ui/extensions/executer/NormalExtensionRunner.java rename to G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/NormalExtensionRunner.java index bb4bdce..5309b44 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/executer/NormalExtensionRunner.java +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/network/executer/NormalExtensionRunner.java @@ -1,7 +1,7 @@ -package gearth.ui.extensions.executer; +package gearth.services.extensionserver.extensions.network.executer; import gearth.Main; -import gearth.ui.extensions.authentication.Authenticator; +import gearth.services.extensionserver.extensions.network.authentication.Authenticator; import java.io.BufferedReader; import java.io.File; diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/ExampleExtension.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/ExampleExtension.java new file mode 100644 index 0000000..d02be61 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/ExampleExtension.java @@ -0,0 +1,111 @@ +package gearth.services.extensionserver.extensions.simple; + +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.services.extensionserver.extensions.GEarthExtension; + +import java.util.Arrays; + +public class ExampleExtension extends GEarthExtension { + @Override + public String getAuthor() { + return "sirjonasxx"; + } + + @Override + public String getDescription() { + return "example internal extension"; + } + + @Override + public String getTitle() { + return "Example"; + } + + @Override + public String getVersion() { + return "0.1"; + } + + @Override + public boolean isFireButtonUsed() { + return false; + // with this button, you could for example open an UI, or use it if your extension has a single purpose + // that needs a click to be executed + + // will only be visible if you return True here + } + + @Override + public boolean isDeleteButtonVisible() { + return false; + + // can you delete this extension? Can be useful to disable this if you want to provide a built-in tool + // in G-Earth + } + + @Override + public boolean isLeaveButtonVisible() { + return false; + + // can you disconnect from the extension? (will be connected again when re-opening G-Earth or clicking the + // "refresh" button in the Extensions GUI) + } + + @Override + public boolean isInstalledExtension() { + return false; + + // is this an extension that is located under the /Extensions folder? + } + + @Override + public void doubleclick() { + System.out.println("wtf dont click me"); + } + + private void intercept(HMessage message) { + message.getPacket().replaceAllStrings( + "What is this extension?", + "It's an example extension showing how extensions work internally" + ); + } + + @Override + public void packetIntercept(HMessage hMessage) { + intercept(hMessage); + // every packetIntercept needs to be responded with the manipulated version, even if you didn't change anything + sendManipulatedPacket(hMessage); + } + + @Override + public void provideFlags(String[] flags) { + // If you call "requestFlags()", this function will provide you the execution flags for G-Earth + // in case you want to do something with it + } + + @Override + public void connectionStart(String host, int port, String hotelVersion, String harbleMessagesPath) { + // a new habbo client has connected + System.out.println("Connected to " + host); + } + + @Override + public void connectionEnd() { + // the habbo connection has ended + } + + @Override + public void init() { + System.out.println("Example extension is connected to G-Earth"); + // the extension is now connected with G-Earth + } + + @Override + public void close() { + // if this function is called, its a heads up that you should close your extension + + // finish up and call "hasClosed()" + hasClosed(); + } +} diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/README.md b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/README.md new file mode 100644 index 0000000..b46d8e3 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/README.md @@ -0,0 +1,6 @@ + + +This package is an example implementation for a new type of extensions (just like Network extensions), +in this case, pure Java classes included in the G-Earth source + +If you maintain a fork of G-Earth and want a builtin feature, this is the place to add it \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/SimpleExtensionProducer.java b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/SimpleExtensionProducer.java new file mode 100644 index 0000000..fbf9a7c --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionserver/extensions/simple/SimpleExtensionProducer.java @@ -0,0 +1,15 @@ +package gearth.services.extensionserver.extensions.simple; + +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducerObserver; + +public class SimpleExtensionProducer implements ExtensionProducer { + + @Override + public void startProducing(ExtensionProducerObserver observer) { + + // uncomment the next line if you want to see an embedded example extension in G-Earth + // observer.onExtensionConnect(new ExampleExtension()); + + } +} diff --git a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainer.java b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainer.java index 7474c49..e84e7e9 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainer.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainer.java @@ -1,5 +1,7 @@ package gearth.ui.extensions; +import gearth.services.extensionserver.extensions.GEarthExtension; +import javafx.application.Platform; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -10,10 +12,10 @@ import javafx.scene.paint.Paint; import javafx.scene.text.Font; import gearth.misc.ConfirmationDialog; import gearth.ui.buttons.*; -import gearth.ui.extensions.executer.ExecutionInfo; -import gearth.ui.extensions.executer.ExtensionRunner; -import gearth.ui.extensions.executer.ExtensionRunnerFactory; -import gearth.ui.extensions.executer.NormalExtensionRunner; +import gearth.services.extensionserver.extensions.network.executer.ExecutionInfo; +import gearth.services.extensionserver.extensions.network.executer.ExtensionRunner; +import gearth.services.extensionserver.extensions.network.executer.ExtensionRunnerFactory; +import gearth.services.extensionserver.extensions.network.executer.NormalExtensionRunner; import java.nio.file.Paths; @@ -166,7 +168,7 @@ public class ExtensionItemContainer extends GridPane { clickButton.addEventHandler(MouseEvent.MOUSE_CLICKED, onClick); ExtensionItemContainer this2 = this; - item.onDelete(observable -> { + item.onDelete(observable -> Platform.runLater(() -> { if (item.isInstalledExtension()) { setBackground(new Background(new BackgroundFill(Paint.valueOf("#cccccc"),null, null))); getChildren().remove(buttonsBox); @@ -176,7 +178,7 @@ public class ExtensionItemContainer extends GridPane { else { parent.getChildren().remove(this2); } - }); + })); } void hasReconnected(GEarthExtension extension) { diff --git a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainerProducer.java b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainerProducer.java index e7cdde9..636f915 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainerProducer.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionItemContainerProducer.java @@ -1,5 +1,7 @@ package gearth.ui.extensions; +import gearth.services.extensionserver.extensions.GEarthExtension; +import gearth.services.extensionserver.extensions.network.NetworkExtension; import javafx.scene.Node; import javafx.scene.control.ScrollPane; import javafx.scene.layout.VBox; @@ -26,7 +28,7 @@ public class ExtensionItemContainerProducer { for (Node n : parent.getChildren()) { if (n instanceof ExtensionItemContainer) { ExtensionItemContainer container = (ExtensionItemContainer) n; - if (container.getExtensionFileName().equals(extension.getFileName())) { + if (container.getExtensionFileName() != null && container.getExtensionFileName().equals(extension.getFileName())) { container.hasReconnected(extension); return; } diff --git a/G-Earth/src/main/java/gearth/ui/extensions/Extensions.java b/G-Earth/src/main/java/gearth/ui/extensions/Extensions.java index 345b2fc..47c9af8 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/Extensions.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/Extensions.java @@ -1,6 +1,12 @@ package gearth.ui.extensions; -import gearth.misc.harble_api.HarbleAPIFetcher; +import gearth.services.extensionserver.ExtensionServer; +import gearth.services.extensionserver.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extensionserver.extensions.network.NetworkExtensionsProducer; +import gearth.services.extensionserver.extensions.network.executer.ExecutionInfo; +import gearth.services.extensionserver.extensions.network.executer.ExtensionRunner; +import gearth.services.extensionserver.extensions.network.executer.ExtensionRunnerFactory; +import gearth.ui.SubForm; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.scene.control.Button; @@ -9,100 +15,14 @@ import javafx.scene.control.TextField; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.FileChooser; -import gearth.Main; -import gearth.protocol.*; -import gearth.ui.SubForm; -import gearth.ui.extensions.executer.ExecutionInfo; -import gearth.ui.extensions.executer.ExtensionRunner; -import gearth.ui.extensions.executer.ExtensionRunnerFactory; -import gearth.ui.scheduler.ScheduleItem; import java.io.File; -import java.io.IOException; -import java.util.*; +import java.util.function.Predicate; /** * Created by Jonas on 06/04/18. */ - -/** - * THE EXTENSION COMMUNCATION PRINCIPLES & PROTOCOL: - * - * You will be able to write extensions in ANY language you want, but we will only provide an interface - * for Java so if you write your own in for example Python, make SURE you do it correctly or it could fuck G-Earth. - * - * Also, don't let the method where you manipulate the packets block. Similiar as how you must not block things in an UI thread. - * Why? Because Habbo relies on the TCP protocol, which ENSURES that packets get received in the right order, so we will not be fucking that up. - * That means that all packets following the packet you're manipulating in your extension will be blocked from being sent untill you're done. - * TIP: If you're trying to replace a packet in your extension but you know it will take time, just block the packet, end the method, and let something asynchronous send - * the editted packet when you're done. - * - * - * You may ignore everything beneath this line if you're extending the abstract Extension class we provide in Java. - * ----------------------------------------------------------------------------------------------------------------- - * - * (0. We recommend to use a cross-platform language for your extension) - * - * 1. An extension will run as a seperate process on your device and has to be called with the flag "-p ", - * where is a random port where the G-Earth local extension server will run on. Your extension has to connect with this server. - * - * 2. G-Earth will open your program only ONCE, that is on the boot of G-Earth or when you install the exension. - * Same story goes for closing the connection between the program and G-Earth, only once (on uninstall or close of G-Earth). - * - * You may also run your extension completely seperate from G-Earth for debugging purpose for example, then it won't be installed in G-Earth - * (but you have to configure the port yourself, which will be displayed in the extension page) - * - * 3. Once a connection is made, your extension will have to deal with the following incoming & outgoing messages as described (follows the same protocol structure as Habbo communication does): - * (if an object is sent; the object will be sent with its String representation from the StringifyAble interface, so the object's class must implement that) - * - * INCOMING MESSAGES: (marked with * if you're required to correctly respond or take action, ** if it's a response on something you requested) - * ----------------------------------------------------------------------------------------------------- - * | ID | TITLE | BODY & DESCRIPTION | - * ----------------------------------------------------------------------------------------------------- - * | 1 | ON-DOUBLECLICK | No body, the extension has been double clicked from within G-Earth | ( <- typically for tanji-module-like extensions you will open the UI here) - * ----------------------------------------------------------------------------------------------------- - * | 2 | INFO-REQUEST* | Needs response with extension info (name, desc, author, version, ..), | - * | | | exact implementation is found in the Java abstract Extension class | - * ----------------------------------------------------------------------------------------------------- - * | 3 | PACKET-INTERCEPT* | Includes the whole HMessage as body, needs response with the | - * | | | manipulated HMessage (OUTGOING id: 2) | - * ----------------------------------------------------------------------------------------------------- - * | 4 | FLAGS-CHECK** | Body: String with G-Earth's boot flags (args from static gearth method) | - * ----------------------------------------------------------------------------------------------------- - * | 5 | CONNECTION START | just a note that a new connection has been made, | - * | | | you could check this yourself as well (listen to out:4000 packet) | - * | | | host/port, hotel version | - * ----------------------------------------------------------------------------------------------------- - * | 6 | CONNECTION END | Empty body, just a note that a connection has ended | -* ----------------------------------------------------------------------------------------------------- - * | 7 | INIT | Empty body, a connection with G-Earth has been set up | - * ----------------------------------------------------------------------------------------------------- - * | 99 | FREE FLOW | extension-specific body | - * ----------------------------------------------------------------------------------------------------- - * - * OUTGOING MESSAGES: (marked with * if that is a response to one of the msgs above) - * ----------------------------------------------------------------------------------------------------- - * | ID | TITLE | BODY & DESCRIPTION | - * ----------------------------------------------------------------------------------------------------- - * | 1 | EXTENSION-INFO* | Response for INFO-REQUEST | - * ----------------------------------------------------------------------------------------------------- - * | 2 | MANIPULATED-PACKET*| Response for PACKET-INTERCEPT | - * ----------------------------------------------------------------------------------------------------- - * | 3 | REQUEST-FLAGS | Request G-Earth's flags, results in incoming FLAGS-CHECK response | - * ----------------------------------------------------------------------------------------------------- - * | 4 | SEND-MESSAGE | Body: HMessage object. Sends the HPacket wrapped in the HMessage | - * | | | to the client/server | - * ----------------------------------------------------------------------------------------------------- - * | 99 | FREE FLOW | extension-specific body | - * ----------------------------------------------------------------------------------------------------- - * - * 4. Your extension will only appear in the extension list once the EXTENSION-INFO has been received by G-Earth - * - * - */ - - public class Extensions extends SubForm { @@ -114,32 +34,9 @@ public class Extensions extends SubForm { public ScrollPane scroller; private ExtensionRunner extensionRunner = null; - private GEarthExtensionsRegistrer extensionsRegistrer = null; + private ExtensionServer extensionServer; + private NetworkExtensionsProducer networkExtensionsProducer; // needed for port - public static class OUTGOING_MESSAGES_IDS { - public static final int ONDOUBLECLICK = 1; - public static final int INFOREQUEST = 2; // backend: implemented - public static final int PACKETINTERCEPT = 3; // backend: implemented - public static final int FLAGSCHECK = 4; // backend: implemented - public static final int CONNECTIONSTART = 5; // backend: implemented - public static final int CONNECTIONEND = 6; // backend: implemented - public static final int INIT = 7; // backend: implemented - public static final int FREEFLOW = 99; // no implementation needed yet - } - - - public static class INCOMING_MESSAGES_IDS { - public static final int EXTENSIONINFO = 1; // backend: implemented - public static final int MANIPULATEDPACKET = 2; // backend: implemented - public static final int REQUESTFLAGS = 3; // backend: implemented - public static final int SENDMESSAGE = 4; // backend: implemented - public static final int EXTENSIONCONSOLELOG = 98; - public static final int FREEFLOW = 99; // no implementation needed yet - } - - - - private final List gEarthExtensions = new ArrayList<>(); public void initialize() { scroller.widthProperty().addListener(observable -> header_ext.setPrefWidth(scroller.getWidth())); @@ -147,206 +44,35 @@ public class Extensions extends SubForm { protected void onParentSet() { ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller); + extensionServer = new ExtensionServer(getHConnection()); + extensionServer.onExtensionConnected((e -> { + Platform.runLater(() -> producer.extensionConnected(e)); + })); - getHConnection().addStateChangeListener((oldState, newState) -> { - if (newState == HConnection.State.CONNECTED) { - HarbleAPIFetcher.fetch(getHConnection().getHotelVersion()); - synchronized (gEarthExtensions) { - for (GEarthExtension extension : gEarthExtensions) { - extension.sendMessage( - new HPacket(OUTGOING_MESSAGES_IDS.CONNECTIONSTART) - .appendString(getHConnection().getDomain()) - .appendInt(getHConnection().getServerPort()) - .appendString(getHConnection().getHotelVersion()) - .appendString(HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath()) - ); - } - } - } - if (oldState == HConnection.State.CONNECTED) { - synchronized (getHConnection()) { - for (GEarthExtension extension : gEarthExtensions) { - extension.sendMessage(new HPacket(OUTGOING_MESSAGES_IDS.CONNECTIONEND)); - } - } - } - }); - - getHConnection().addTrafficListener(1, message -> { - Set collection; - synchronized (gEarthExtensions) { - collection = new HashSet<>(gEarthExtensions); - } - - String stringified = message.stringify(); - HPacket manipulatePacketRequest = new HPacket(OUTGOING_MESSAGES_IDS.PACKETINTERCEPT); - manipulatePacketRequest.appendLongString(stringified); - - HMessage result = new HMessage(message); - - boolean[] isblock = new boolean[1]; - synchronized (collection) { - for (GEarthExtension extension : collection) { - GEarthExtension.ReceiveMessageListener respondCallback = new GEarthExtension.ReceiveMessageListener() { - @Override - public void act(HPacket packet) { - if (packet.headerId() == INCOMING_MESSAGES_IDS.MANIPULATEDPACKET) { - String stringifiedresponse = packet.readLongString(6); - HMessage responseMessage = new HMessage(stringifiedresponse); - if (responseMessage.getDestination() == message.getDestination() && responseMessage.getIndex() == message.getIndex()) { - synchronized (result) { - if (!message.equals(responseMessage)) { - result.constructFromString(stringifiedresponse); - } - if (responseMessage.isBlocked()) { - isblock[0] = true; - } - synchronized (collection) { - collection.remove(extension); - } - - extension.removeOnReceiveMessageListener(this); - } - } - } - - } - }; - extension.addOnReceiveMessageListener(respondCallback); - extension.sendMessage(manipulatePacketRequest); - } - } - - //block untill all extensions have responded - List willdelete = new ArrayList<>(); - while (true) { - synchronized (collection) { - if (collection.isEmpty()) { - break; - } - - synchronized (gEarthExtensions) { - for (GEarthExtension extension : collection) { - if (!gEarthExtensions.contains(extension)) willdelete.add(extension); - } - } - - for (int i = willdelete.size() - 1; i >= 0; i--) { - collection.remove(willdelete.get(i)); - willdelete.remove(i); - } - } + //noinspection OptionalGetWithoutIsPresent + networkExtensionsProducer + = (NetworkExtensionsProducer) extensionServer.getExtensionProducers().stream() + .filter(producer1 -> producer1 instanceof NetworkExtensionsProducer) + .findFirst().get(); - try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();} - } - - message.constructFromHMessage(result); - - if (isblock[0]) { - message.setBlocked(true); - } - }); - - - HashMap messageListeners = new HashMap<>(); - try { - extensionsRegistrer = new GEarthExtensionsRegistrer(new GEarthExtensionsRegistrer.ExtensionRegisterObserver() { - @Override - public void onConnect(GEarthExtension extension) { - synchronized (gEarthExtensions) { - gEarthExtensions.add(extension); - } - - GEarthExtension.ReceiveMessageListener receiveMessageListener = message -> { - if (message.headerId() == INCOMING_MESSAGES_IDS.REQUESTFLAGS) { // no body - HPacket packet = new HPacket(OUTGOING_MESSAGES_IDS.FLAGSCHECK); - packet.appendInt(Main.args.length); - for (String arg : Main.args) { - packet.appendString(arg); - } - extension.sendMessage(packet); - } - else if (message.headerId() == INCOMING_MESSAGES_IDS.SENDMESSAGE) { - Byte side = message.readByte(); - int byteLength = message.readInteger(); - byte[] packetAsByteArray = message.readBytes(byteLength); - - HPacket packet = new HPacket(packetAsByteArray); - if (!packet.isCorrupted()) { - if (side == 0) { // toclient - getHConnection().sendToClientAsync(packet); - } - else if (side == 1) { // toserver - getHConnection().sendToServerAsync(packet); - } - } - } - }; - synchronized (messageListeners) { - messageListeners.put(extension, receiveMessageListener); - } - extension.addOnReceiveMessageListener(receiveMessageListener); - - extension.sendMessage(new HPacket(OUTGOING_MESSAGES_IDS.INIT)); - if (getHConnection().getState() == HConnection.State.CONNECTED) { - extension.sendMessage( - new HPacket(OUTGOING_MESSAGES_IDS.CONNECTIONSTART) - .appendString(getHConnection().getDomain()) - .appendInt(getHConnection().getServerPort()) - .appendString(getHConnection().getHotelVersion()) - .appendString(HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath()) - ); - } - - extension.onRemoveClick(observable -> { - try { - extension.getConnection().close(); - } catch (IOException e) { - e.printStackTrace(); - } - }); - extension.onClick(observable -> extension.sendMessage(new HPacket(OUTGOING_MESSAGES_IDS.ONDOUBLECLICK))); - - Platform.runLater(() -> producer.extensionConnected(extension)); - } - - @Override - public void onDisconnect(GEarthExtension extension) { - synchronized (gEarthExtensions) { - gEarthExtensions.remove(extension); - } - - synchronized (messageListeners) { - extension.removeOnReceiveMessageListener(messageListeners.get(extension)); - messageListeners.remove(extension); - } - Platform.runLater(extension::delete); - } - }); - } catch (IOException e) { - e.printStackTrace(); - } - - producer.setPort(extensionsRegistrer.getPort()); - ext_port.setText(extensionsRegistrer.getPort()+""); + producer.setPort(networkExtensionsProducer.getPort()); + ext_port.setText(networkExtensionsProducer.getPort()+""); // System.out.println("Extension server registered on port: " + extensionsRegistrer.getPort()); extensionRunner = ExtensionRunnerFactory.get(); - extensionRunner.runAllExtensions(extensionsRegistrer.getPort()); + extensionRunner.runAllExtensions(networkExtensionsProducer.getPort()); } public void installBtnClicked(ActionEvent actionEvent) { - List list = new ArrayList<>(); - FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Install extension"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("G-Earth extensions", ExecutionInfo.ALLOWEDEXTENSIONTYPES)); File selectedFile = fileChooser.showOpenDialog(parentController.getStage()); if (selectedFile != null) { - extensionRunner.installAndRunExtension(selectedFile.getPath(), extensionsRegistrer.getPort()); + extensionRunner.installAndRunExtension(selectedFile.getPath(), networkExtensionsProducer.getPort()); } } } diff --git a/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtension.java b/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtension.java deleted file mode 100644 index 1d0d7a8..0000000 --- a/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtension.java +++ /dev/null @@ -1,263 +0,0 @@ -package gearth.ui.extensions; - -import javafx.beans.InvalidationListener; -import gearth.protocol.HPacket; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import gearth.ui.extensions.authentication.Authenticator; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Jonas on 21/06/18. - */ -public class GEarthExtension { - - private String title; - private String author; - private String version; - private String description; - - private boolean fireEventButtonVisible; - private boolean leaveButtonVisible; - private boolean deleteButtonVisible; - - private boolean isInstalledExtension; // <- extension is in the extensions directory - private String fileName; - private String cookie; - - private Socket connection; - - //calls callback when the extension is creatd - static void create(Socket connection, OnCreatedCallback callback, OnDisconnectedCallback onDisconnectedCallback) { - - new Thread(() -> { - try { - synchronized (connection) { - connection.getOutputStream().write((new HPacket(Extensions.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes()); - } - - InputStream inputStream = connection.getInputStream(); - DataInputStream dIn = new DataInputStream(inputStream); - - while (!connection.isClosed()) { - - int length = dIn.readInt(); - byte[] headerandbody = new byte[length + 4]; - - int amountRead = 0; - while (amountRead < length) { - amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); - } - - HPacket packet = new HPacket(headerandbody); - packet.fixLength(); - - if (packet.headerId() == Extensions.INCOMING_MESSAGES_IDS.EXTENSIONINFO) { - GEarthExtension gEarthExtension = new GEarthExtension( - packet, - connection, - onDisconnectedCallback - ); - - if (Authenticator.evaluate(gEarthExtension)) { - callback.act(gEarthExtension); - } - else { - gEarthExtension.closeConnection(); //you shall not pass... - } - - break; - } - } - - } catch (IOException ignored) {} - }).start(); - - } - - private GEarthExtension(HPacket extensionInfo, Socket connection, OnDisconnectedCallback onDisconnectedCallback) { - this.title = extensionInfo.readString(); - this.author = extensionInfo.readString(); - this.version = extensionInfo.readString(); - this.description = extensionInfo.readString(); - this.fireEventButtonVisible = extensionInfo.readBoolean(); - - this.isInstalledExtension = extensionInfo.readBoolean(); - this.fileName = extensionInfo.readString(); - this.cookie = extensionInfo.readString(); - - this.leaveButtonVisible = extensionInfo.readBoolean(); - this.deleteButtonVisible = extensionInfo.readBoolean(); - - this.connection = connection; - - GEarthExtension selff = this; - new Thread(() -> { - try { - InputStream inputStream = connection.getInputStream(); - DataInputStream dIn = new DataInputStream(inputStream); - - while (!connection.isClosed()) { - int length = dIn.readInt(); - byte[] headerandbody = new byte[length + 4]; - - int amountRead = 0; - while (amountRead < length) { - amountRead += dIn.read(headerandbody, 4 + amountRead, Math.min(dIn.available(), length - amountRead)); - } - - HPacket packet = new HPacket(headerandbody); - packet.fixLength(); - - synchronized (receiveMessageListeners) { - for (int i = receiveMessageListeners.size() - 1; i >= 0; i--) { - receiveMessageListeners.get(i).act(packet); - packet.setReadIndex(6); - } - } - - } - - } catch (IOException e) { - // An extension disconnected, which is OK - } finally { - onDisconnectedCallback.act(selff); - if (!connection.isClosed()) { - try { - connection.close(); - } catch (IOException e) { -// e.printStackTrace(); - } - } - } - }).start(); - - - } - - public Socket getConnection() { - return connection; - } - - public String getAuthor() { - return author; - } - public String getDescription() { - return description; - } - public String getTitle() { - return title; - } - public String getVersion() { - return version; - } - public boolean isFireButtonUsed() { - return fireEventButtonVisible; - } - public String getFileName() { - return fileName; - } - public String getCookie() { - return cookie; - } - public boolean isDeleteButtonVisible() { - return deleteButtonVisible; - } - public boolean isLeaveButtonVisible() { - return leaveButtonVisible; - } - - public boolean isInstalledExtension() { - return isInstalledExtension; - } - - public boolean closeConnection() { - try { - connection.close(); - return true; - } catch (IOException e) { - return false; - } - } - - public boolean sendMessage(HPacket message) { - try { - synchronized (this) { - connection.getOutputStream().write(message.toBytes()); - } - return true; - } catch (IOException e) { - return false; - } - } - - - private final List receiveMessageListeners = new ArrayList<>(); - public void addOnReceiveMessageListener(ReceiveMessageListener receiveMessageListener) { - synchronized (receiveMessageListeners) { - receiveMessageListeners.add(receiveMessageListener); - } - } - public void removeOnReceiveMessageListener(ReceiveMessageListener receiveMessageListener) { - synchronized (receiveMessageListeners) { - receiveMessageListeners.remove(receiveMessageListener); - } - } - - public interface ReceiveMessageListener { - void act(HPacket message); - } - public interface OnCreatedCallback { - void act(GEarthExtension extension); // returns itself - } - public interface OnDisconnectedCallback { - void act(GEarthExtension extension); // returns itself - } - - - private final List onRemoveClickListener = new ArrayList<>(); - public void onRemoveClick(InvalidationListener listener) { - synchronized (onRemoveClickListener) { - onRemoveClickListener.add(listener); - } - } - public void isRemoveClickTrigger() { - synchronized (onRemoveClickListener) { - for (int i = onRemoveClickListener.size() - 1; i >= 0; i--) { - onRemoveClickListener.get(i).invalidated(null); - } - } - } - - private final List onClickListener = new ArrayList<>(); - public void onClick(InvalidationListener listener) { - synchronized (onClickListener) { - onClickListener.add(listener); - } - } - public void isClickTrigger() { - synchronized (onClickListener) { - for (int i = onClickListener.size() - 1; i >= 0; i--) { - onClickListener.get(i).invalidated(null); - } - } - } - - private final List onDeleteListeners = new ArrayList<>(); - public void onDelete(InvalidationListener listener) { - synchronized (onDeleteListeners) { - onDeleteListeners.add(listener); - } - } - public void delete() { - synchronized (onDeleteListeners) { - for (int i = onDeleteListeners.size() - 1; i >= 0; i--) { - onDeleteListeners.get(i).invalidated(null); - } - } - } -} \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtensionsRegistrer.java b/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtensionsRegistrer.java deleted file mode 100644 index 083c181..0000000 --- a/G-Earth/src/main/java/gearth/ui/extensions/GEarthExtensionsRegistrer.java +++ /dev/null @@ -1,52 +0,0 @@ -package gearth.ui.extensions; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -/** - * Created by Jonas on 21/06/18. - */ -public class GEarthExtensionsRegistrer { - - private ServerSocket serverSocket; - - GEarthExtensionsRegistrer(ExtensionRegisterObserver observer) throws IOException { - -// serverSocket = new ServerSocket(0); - int port = 9092; - boolean serverSetup = false; - while (!serverSetup) { - serverSetup = createServer(port); - port++; - } - - - new Thread(() -> { - try { - while (!serverSocket.isClosed()) { - Socket extensionSocket = serverSocket.accept(); - GEarthExtension.create(extensionSocket, observer::onConnect, observer::onDisconnect); - } - } catch (IOException e) {e.printStackTrace();} - }).start(); - } - - private boolean createServer(int port) { - try { - serverSocket = new ServerSocket(port); - return true; - } catch (IOException e) { - return false; - } - } - - public int getPort() { - return serverSocket.getLocalPort(); - } - - public interface ExtensionRegisterObserver { - void onConnect(GEarthExtension extension); - void onDisconnect(GEarthExtension extension); - } -}