diff --git a/Extensions/AdminOnConnect/pom.xml b/Extensions/AdminOnConnect/pom.xml index ce4d9c4..7ba81c5 100644 --- a/Extensions/AdminOnConnect/pom.xml +++ b/Extensions/AdminOnConnect/pom.xml @@ -13,7 +13,7 @@ G-Earth G-Earth-Parent - 1.3.4 + 1.4 ../../ @@ -106,7 +106,7 @@ G-Earth G-Earth - 1.3.4 + 1.4 diff --git a/Extensions/BlockReplacePackets/pom.xml b/Extensions/BlockReplacePackets/pom.xml deleted file mode 100644 index 9a1a6fc..0000000 --- a/Extensions/BlockReplacePackets/pom.xml +++ /dev/null @@ -1,113 +0,0 @@ - - - 4.0.0 - - BlockReplacePackets - - jar - - 0.0.1-beta - - - G-Earth - G-Earth-Parent - 1.3.4 - ../../ - - - - - - - false - src/main/java - - **/*.fxml - **/*.css - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 2.5 - - ${project.build.directory}/bin - - - true - true - extensions.blockreplacepackets.BlockAndReplacePackets - false - lib/ - true - - - ${project.artifactId} - - - - - maven-assembly-plugin - 2.5 - - - package - - single - - - - - ${project.build.directory}/bin - - - extensions.blockreplacepackets.BlockAndReplacePackets - - - - jar-with-dependencies - - ${project.artifactId} - false - - - - - - maven-antrun-plugin - 3.0.0 - - - package - - - - - - - - - - run - - - - - - - - - - - G-Earth - G-Earth - 1.3.4 - - - - \ No newline at end of file diff --git a/G-Earth/pom.xml b/G-Earth/pom.xml index a93eb89..5824e8b 100644 --- a/G-Earth/pom.xml +++ b/G-Earth/pom.xml @@ -8,7 +8,7 @@ jar - 1.3.4 + 1.4 1.8 @@ -18,7 +18,7 @@ G-Earth G-Earth-Parent - 1.3.4 + 1.4 diff --git a/G-Earth/src/main/java/gearth/Main.java b/G-Earth/src/main/java/gearth/Main.java index 1fa04e6..7e4d733 100644 --- a/G-Earth/src/main/java/gearth/Main.java +++ b/G-Earth/src/main/java/gearth/Main.java @@ -28,7 +28,7 @@ import java.util.Set; public class Main extends Application { public static Application main; - public static String version = "1.3.4"; + public static String version = "1.4"; private static String gitApi = "https://api.github.com/repos/sirjonasxx/G-Earth/releases/latest"; @Override @@ -45,7 +45,7 @@ public class Main extends Application { primaryStage.setResizable(false); primaryStage.setTitle("G-Earth " + version); - primaryStage.setScene(new Scene(root, 650, 295)); + primaryStage.setScene(new Scene(root, 650, 290)); primaryStage.show(); primaryStage.getScene().getStylesheets().add(getClass().getResource("ui/bootstrap3.css").toExternalForm()); diff --git a/G-Earth/src/main/java/gearth/extensions/Extension.java b/G-Earth/src/main/java/gearth/extensions/Extension.java index c987633..4e49c0b 100644 --- a/G-Earth/src/main/java/gearth/extensions/Extension.java +++ b/G-Earth/src/main/java/gearth/extensions/Extension.java @@ -1,32 +1,22 @@ package gearth.extensions; -import gearth.misc.listenerpattern.Observable; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.protocol.connection.HClient; import gearth.services.Constants; import gearth.services.extensionhandler.extensions.implementations.network.NetworkExtensionInfo; import java.io.*; import java.net.Socket; import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; /** * Created by Jonas on 23/06/18. */ -public abstract class Extension implements IExtension { +public abstract class Extension extends ExtensionBase { - public interface MessageListener { - void act(HMessage message); - } - public interface FlagsCheckListener { - void act(String[] args); - } - - protected boolean canLeave; // can you disconnect the ext - protected boolean canDelete; // can you delete the ext (will be false for some built-in extensions) + protected FlagsCheckListener flagRequestCallback = null; private String[] args; private boolean isCorrupted = false; @@ -34,10 +24,9 @@ public abstract class Extension implements IExtension { private static final String[] FILE_FLAG = {"--filename", "-f"}; private static final String[] COOKIE_FLAG = {"--auth-token", "-c"}; // don't add a cookie or filename when debugging + protected PacketInfoManager packetInfoManager = new PacketInfoManager(new ArrayList<>()); // empty + private OutputStream out = null; - private final Map> incomingMessageListeners = new HashMap<>(); - private final Map> outgoingMessageListeners = new HashMap<>(); - private FlagsCheckListener flagRequestCallback = null; private String getArgument(String[] args, String... arg) { for (int i = 0; i < args.length - 1; i++) { @@ -55,8 +44,7 @@ public abstract class Extension implements IExtension { * @param args arguments */ public Extension(String[] args) { - canLeave = canLeave(); - canDelete = canDelete(); + super(); //obtain port this.args = args; @@ -132,18 +120,23 @@ public abstract class Extension implements IExtension { .appendBoolean(file != null) .appendString(file == null ? "": file) .appendString(cookie == null ? "" : cookie) - .appendBoolean(canLeave) - .appendBoolean(canDelete); + .appendBoolean(canLeave()) + .appendBoolean(canDelete()); writeToStream(response.toBytes()); } else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) { String host = packet.readString(); int connectionPort = packet.readInteger(); String hotelVersion = packet.readString(); - String harbleMessagesPath = packet.readString(); - String clientType = packet.readString(); - Constants.UNITY_PACKETS = clientType.toLowerCase().contains("unity"); - onConnectionObservable.fireEvent(l -> l.onConnection(host, connectionPort, hotelVersion, clientType, harbleMessagesPath)); + String clientIdentifier = packet.readString(); + HClient clientType = HClient.valueOf(packet.readString()); + packetInfoManager = PacketInfoManager.readFromPacket(packet); + + Constants.UNITY_PACKETS = clientType == HClient.UNITY; + getOnConnectionObservable().fireEvent(l -> l.onConnection( + host, connectionPort, hotelVersion, + clientIdentifier, clientType, packetInfoManager) + ); onStartConnection(); } else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONEND) { @@ -171,36 +164,8 @@ public abstract class Extension implements IExtension { else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) { String stringifiedMessage = packet.readLongString(); HMessage habboMessage = new HMessage(stringifiedMessage); - HPacket habboPacket = habboMessage.getPacket(); - Map> listeners = - habboMessage.getDestination() == HMessage.Direction.TOCLIENT ? - incomingMessageListeners : - outgoingMessageListeners; - - List correctListeners = new ArrayList<>(); - - synchronized (incomingMessageListeners) { - synchronized (outgoingMessageListeners) { - if (listeners.containsKey(-1)) { // registered on all packets - for (int i = listeners.get(-1).size() - 1; i >= 0; i--) { - correctListeners.add(listeners.get(-1).get(i)); - } - } - - if (listeners.containsKey(habboPacket.headerId())) { - for (int i = listeners.get(habboPacket.headerId()).size() - 1; i >= 0; i--) { - correctListeners.add(listeners.get(habboPacket.headerId()).get(i)); - } - } - } - } - - for(MessageListener listener : correctListeners) { - habboMessage.getPacket().resetReadIndex(); - listener.act(habboMessage); - } - habboMessage.getPacket().resetReadIndex(); + modifyMessage(habboMessage); HPacket response = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.MANIPULATEDPACKET); response.appendLongString(habboMessage.stringify()); @@ -249,6 +214,11 @@ public abstract class Extension implements IExtension { return send(packet, HMessage.Direction.TOSERVER); } private boolean send(HPacket packet, HMessage.Direction direction) { + if (packet.isCorrupted()) return false; + + if (!packet.isPacketComplete()) packet.completePacket(direction, packetInfoManager); + if (!packet.isPacketComplete()) return false; + HPacket packet1 = new HPacket(NetworkExtensionInfo.INCOMING_MESSAGES_IDS.SENDMESSAGE); packet1.appendByte(direction == HMessage.Direction.TOCLIENT ? (byte)0 : (byte)1); packet1.appendInt(packet.getBytesLength()); @@ -261,37 +231,6 @@ public abstract class Extension implements IExtension { } } - /** - * Register a listener on a specific packet Type - * @param direction ToClient or ToServer - * @param headerId the packet header ID - * @param messageListener the callback - */ - public void intercept(HMessage.Direction direction, int headerId, MessageListener messageListener) { - Map> listeners = - direction == HMessage.Direction.TOCLIENT ? - incomingMessageListeners : - outgoingMessageListeners; - - synchronized (listeners) { - if (!listeners.containsKey(headerId)) { - listeners.put(headerId, new ArrayList<>()); - } - } - - - listeners.get(headerId).add(messageListener); - } - - /** - * Register a listener on all packets - * @param direction ToClient or ToServer - * @param messageListener the callback - */ - public void intercept(HMessage.Direction direction, MessageListener messageListener) { - intercept(direction, -1, messageListener); - } - /** * Requests the flags which have been given to G-Earth when it got executed * For example, you might want this extension to do a specific thing if the flag "-e" was given @@ -310,15 +249,6 @@ public abstract class Extension implements IExtension { } } - - /** - * Write to the console in G-Earth - * @param s the text to be written - */ - public void writeToConsole(String s) { - writeToConsole("black", s, true); - } - /** * Write to the console in G-Earth * @param s the text to be written @@ -346,37 +276,12 @@ public abstract class Extension implements IExtension { } } - - private boolean isOnClickMethodUsed() { - - Class c = getClass(); - - while (c != Extension.class) { - try { - c.getDeclaredMethod("onClick"); - // if it didnt error, onClick exists - return true; - } catch (NoSuchMethodException e) { -// e.printStackTrace(); - } - - c = (Class) c.getSuperclass(); - } - - return false; - } - /** * Gets called when a connection has been established with G-Earth. * This does not imply a connection with Habbo is setup. */ protected void initExtension(){} - /** - * The application got doubleclicked from the G-Earth interface. Doing something here is optional - */ - protected void onClick(){} - /** * A connection with Habbo has been started */ @@ -395,14 +300,4 @@ public abstract class Extension implements IExtension { return true; } - ExtensionInfo getInfoAnnotations() { - return getClass().getAnnotation(ExtensionInfo.class); - } - - - private Observable onConnectionObservable = new Observable<>(); - public void onConnect(OnConnectionListener listener){ - onConnectionObservable.addListener(listener); - } - } diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java b/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java new file mode 100644 index 0000000..060b835 --- /dev/null +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionBase.java @@ -0,0 +1,130 @@ +package gearth.extensions; + +import gearth.misc.listenerpattern.Observable; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class ExtensionBase extends IExtension { + + public interface MessageListener { + void act(HMessage message); + } + public interface FlagsCheckListener { + void act(String[] args); + } + + protected final Map> incomingMessageListeners = new HashMap<>(); + protected final Map> outgoingMessageListeners = new HashMap<>(); + + /** + * Register a listener on a specific packet Type + * @param direction ToClient or ToServer + * @param headerId the packet header ID + * @param messageListener the callback + */ + public void intercept(HMessage.Direction direction, int headerId, MessageListener messageListener) { + Map> listeners = + direction == HMessage.Direction.TOCLIENT ? + incomingMessageListeners : + outgoingMessageListeners; + + synchronized (listeners) { + if (!listeners.containsKey(headerId)) { + listeners.put(headerId, new ArrayList<>()); + } + } + + + listeners.get(headerId).add(messageListener); + } + + /** + * Register a listener on all packets + * @param direction ToClient or ToServer + * @param messageListener the callback + */ + public void intercept(HMessage.Direction direction, MessageListener messageListener) { + intercept(direction, -1, messageListener); + } + + @Override + public void writeToConsole(String s) { + writeToConsole("black", s); + } + + protected boolean isOnClickMethodUsed() { + Class c = getClass(); + while (c != Extension.class) { + try { + c.getDeclaredMethod("onClick"); + // if it didnt error, onClick exists + return true; + } catch (NoSuchMethodException e) { +// e.printStackTrace(); + } + + c = (Class) c.getSuperclass(); + } + return false; + } + + public void modifyMessage(HMessage habboMessage) { + HPacket habboPacket = habboMessage.getPacket(); + + Map> listeners = + habboMessage.getDestination() == HMessage.Direction.TOCLIENT ? + incomingMessageListeners : + outgoingMessageListeners; + + List correctListeners = new ArrayList<>(); + + synchronized (incomingMessageListeners) { + synchronized (outgoingMessageListeners) { + if (listeners.containsKey(-1)) { // registered on all packets + for (int i = listeners.get(-1).size() - 1; i >= 0; i--) { + correctListeners.add(listeners.get(-1).get(i)); + } + } + + if (listeners.containsKey(habboPacket.headerId())) { + for (int i = listeners.get(habboPacket.headerId()).size() - 1; i >= 0; i--) { + correctListeners.add(listeners.get(habboPacket.headerId()).get(i)); + } + } + } + } + + for(MessageListener listener : correctListeners) { + habboMessage.getPacket().resetReadIndex(); + listener.act(habboMessage); + } + habboMessage.getPacket().resetReadIndex(); + } + + /** + * The application got doubleclicked from the G-Earth interface. Doing something here is optional + */ + @Override + void onClick() { + + } + + @Override + protected ExtensionInfo getInfoAnnotations() { + return getClass().getAnnotation(ExtensionInfo.class); + } + + private Observable onConnectionObservable = new Observable<>(); + public void onConnect(OnConnectionListener listener){ + onConnectionObservable.addListener(listener); + } + + Observable getOnConnectionObservable() { + return onConnectionObservable; + } +} diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java b/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java index 145733c..5310cd0 100644 --- a/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionForm.java @@ -8,9 +8,9 @@ import gearth.protocol.HPacket; /** * Created by Jonas on 22/09/18. */ -public abstract class ExtensionForm implements IExtension { +public abstract class ExtensionForm extends ExtensionBase { - volatile Extension extension; + volatile ExtensionBase extension; protected volatile Stage primaryStage; protected static void runExtensionForm(String[] args, Class extension) { @@ -59,7 +59,7 @@ public abstract class ExtensionForm implements IExtension { /** * The application got doubleclicked from the G-Earth interface. Doing something here is optional */ - protected void onClick(){ + public void onClick(){ Platform.runLater(() -> { primaryStage.show(); primaryStage.requestFocus(); diff --git a/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java b/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java index 0eec47b..920f02a 100644 --- a/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java +++ b/G-Earth/src/main/java/gearth/extensions/ExtensionFormLauncher.java @@ -5,7 +5,7 @@ import javafx.application.Platform; import javafx.stage.Stage; /** - * Created by Jeunez on 6/11/2018. + * Created by Jonas on 6/11/2018. */ public class ExtensionFormLauncher extends Application { @@ -19,7 +19,7 @@ public class ExtensionFormLauncher extends Application { ExtensionForm creator = extension.newInstance(); ExtensionForm extensionForm = creator.launchForm(primaryStage); - extensionForm.extension = new Extension(args) { + Extension extension = new Extension(args) { @Override protected void initExtension() { extensionForm.initExtension(); @@ -41,7 +41,7 @@ public class ExtensionFormLauncher extends Application { } @Override - ExtensionInfo getInfoAnnotations() { + protected ExtensionInfo getInfoAnnotations() { return extInfo; } @@ -55,9 +55,11 @@ public class ExtensionFormLauncher extends Application { return extensionForm.canDelete(); } }; + extensionForm.extension = extension; + extensionForm.primaryStage = primaryStage; Thread t = new Thread(() -> { - extensionForm.extension.run(); + extension.run(); //when the extension has ended, close this process System.exit(0); }); diff --git a/G-Earth/src/main/java/gearth/extensions/IExtension.java b/G-Earth/src/main/java/gearth/extensions/IExtension.java index 8040bce..3b8ae89 100644 --- a/G-Earth/src/main/java/gearth/extensions/IExtension.java +++ b/G-Earth/src/main/java/gearth/extensions/IExtension.java @@ -3,15 +3,22 @@ package gearth.extensions; import gearth.protocol.HMessage; import gearth.protocol.HPacket; -public interface IExtension { +public abstract class IExtension { - boolean sendToClient(HPacket packet); - boolean sendToServer(HPacket packet); - void intercept(HMessage.Direction direction, int headerId, Extension.MessageListener messageListener); - void intercept(HMessage.Direction direction, Extension.MessageListener messageListener); - boolean requestFlags(Extension.FlagsCheckListener flagRequestCallback); - void writeToConsole(String colorClass, String s); - void writeToConsole(String s); - void onConnect(OnConnectionListener listener); + public abstract boolean sendToClient(HPacket packet); + public abstract boolean sendToServer(HPacket packet); + public abstract void intercept(HMessage.Direction direction, int headerId, Extension.MessageListener messageListener); + public abstract void intercept(HMessage.Direction direction, Extension.MessageListener messageListener); + public abstract boolean requestFlags(Extension.FlagsCheckListener flagRequestCallback); + public abstract void writeToConsole(String colorClass, String s); + public abstract void writeToConsole(String s); + public abstract void onConnect(OnConnectionListener listener); + abstract void initExtension(); + abstract void onClick(); + abstract void onStartConnection(); + abstract void onEndConnection(); + abstract ExtensionInfo getInfoAnnotations(); + abstract boolean canLeave(); + abstract boolean canDelete(); } diff --git a/G-Earth/src/main/java/gearth/extensions/InternalExtension.java b/G-Earth/src/main/java/gearth/extensions/InternalExtension.java new file mode 100644 index 0000000..5f870d2 --- /dev/null +++ b/G-Earth/src/main/java/gearth/extensions/InternalExtension.java @@ -0,0 +1,68 @@ +package gearth.extensions; + +import gearth.Main; +import gearth.extensions.ExtensionBase; +import gearth.extensions.IExtension; +import gearth.protocol.HPacket; + + +public class InternalExtension extends ExtensionBase { + + public interface InternalExtensionCommunicator { + void sendToClient(HPacket packet); + void sendToServer(HPacket packet); + void writeToConsole(String s); + } + + private InternalExtensionCommunicator communicator = null; + + public void setCommunicator(InternalExtensionCommunicator communicator) { + this.communicator = communicator; + } + + @Override + public boolean sendToClient(HPacket packet) { + communicator.sendToClient(packet); + return true; + } + + @Override + public boolean sendToServer(HPacket packet) { + communicator.sendToServer(packet); + return true; + } + + @Override + public boolean requestFlags(FlagsCheckListener flagRequestCallback) { + flagRequestCallback.act(Main.args); + return true; + } + + @Override + public void writeToConsole(String colorClass, String s) { + String text = "[" + colorClass + "]" + getInfoAnnotations().Title() + " --> " + s; + communicator.writeToConsole(text); + } + + // to be maybe overwritten + @Override + protected void initExtension() { } + + // to be maybe overwritten + @Override + protected void onStartConnection() {} + + // to be maybe overwritten + @Override + protected void onEndConnection() {} + + @Override + protected boolean canLeave() { + return false; + } + + @Override + protected boolean canDelete() { + return false; + } +} diff --git a/G-Earth/src/main/java/gearth/extensions/InternalExtensionBuilder.java b/G-Earth/src/main/java/gearth/extensions/InternalExtensionBuilder.java new file mode 100644 index 0000000..8f90d79 --- /dev/null +++ b/G-Earth/src/main/java/gearth/extensions/InternalExtensionBuilder.java @@ -0,0 +1,145 @@ +package gearth.extensions; + + +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.protocol.connection.HClient; +import gearth.services.extensionhandler.extensions.ExtensionType; +import gearth.services.extensionhandler.extensions.GEarthExtension; + +// wraps internal GEarthExtension class to IExtension interface +// to allow internal extensions that follow the same remote-extensions interface +public class InternalExtensionBuilder extends GEarthExtension { + + private final InternalExtension extension; + + public InternalExtensionBuilder(InternalExtension extension) { + this.extension = extension; + extension.setCommunicator(new InternalExtension.InternalExtensionCommunicator() { + @Override + public void sendToClient(HPacket packet) { + sendMessage(HMessage.Direction.TOCLIENT, packet); + } + + @Override + public void sendToServer(HPacket packet) { + sendMessage(HMessage.Direction.TOSERVER, packet); + } + + @Override + public void writeToConsole(String s) { + log(s); + } + }); + } + + @Override + public String getAuthor() { + return extension.getInfoAnnotations().Author(); + } + + @Override + public String getDescription() { + return extension.getInfoAnnotations().Description(); + } + + @Override + public String getTitle() { + return extension.getInfoAnnotations().Title(); + } + + @Override + public String getVersion() { + return extension.getInfoAnnotations().Version(); + } + + @Override + public boolean isFireButtonUsed() { + Class c = extension.getClass(); + + while (c != InternalExtension.class) { + try { + c.getDeclaredMethod("onClick"); + return true; + } catch (NoSuchMethodException e) { +// e.printStackTrace(); + } + + c = (Class) c.getSuperclass(); + } + + return false; + } + + @Override + public boolean isDeleteButtonVisible() { + return extension.canDelete(); + } + + @Override + public boolean isLeaveButtonVisible() { + return extension.canLeave(); + } + + @Override + public boolean isInstalledExtension() { + return false; + } + + @Override + public void doubleclick() { + extension.onClick(); + } + + @Override + public void packetIntercept(HMessage hMessage) { + extension.modifyMessage(hMessage); + sendManipulatedPacket(hMessage); + } + + @Override + public void provideFlags(String[] flags) { + // no need + } + + @Override + public void connectionStart(String host, int port, String hotelVersion, String clientIdentifier, HClient clientType, PacketInfoManager packetInfoManager) { + extension.getOnConnectionObservable().fireEvent(l -> l.onConnection( + host, port, hotelVersion, + clientIdentifier, clientType, packetInfoManager) + ); + extension.onStartConnection(); + } + + @Override + public void connectionEnd() { + extension.onEndConnection(); + } + + @Override + public void init() { + extension.initExtension(); + } + + @Override + public void close() { + // no need in internal ext + } + + @Override + public void packetToStringResponse(String string, String expression) { + // no need in java ext + } + + @Override + public void stringToPacketResponse(HPacket packet) { + // no need in java ext + } + + @Override + public ExtensionType extensionType() { + return ExtensionType.INTERNAL; + } + +} diff --git a/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormBuilder.java b/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormBuilder.java new file mode 100644 index 0000000..ddf5358 --- /dev/null +++ b/G-Earth/src/main/java/gearth/extensions/InternalExtensionFormBuilder.java @@ -0,0 +1,78 @@ +package gearth.extensions; + +import gearth.services.extensionhandler.extensions.GEarthExtension; +import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducerObserver; +import javafx.application.Platform; +import javafx.stage.Stage; + +public class InternalExtensionFormBuilder { + + public T launch(Class extensionClass, ExtensionProducerObserver observer) { + try { + ExtensionInfo extInfo = extensionClass.getAnnotation(ExtensionInfo.class); + T creator = extensionClass.newInstance(); + + Stage stage = new Stage(); + T extensionForm = (T)(creator.launchForm(stage)); + + InternalExtension internalExtension = new InternalExtension() { + @Override + protected void initExtension() { + extensionForm.initExtension(); + } + + @Override + protected void onClick() { + extensionForm.onClick(); + } + + @Override + protected void onStartConnection() { + extensionForm.onStartConnection(); + } + + @Override + protected void onEndConnection() { + extensionForm.onEndConnection(); + } + + @Override + protected ExtensionInfo getInfoAnnotations() { + return extInfo; + } + + @Override + protected boolean canLeave() { + return extensionForm.canLeave(); + } + + @Override + protected boolean canDelete() { + return extensionForm.canDelete(); + } + }; + extensionForm.extension = internalExtension; + extensionForm.primaryStage = stage; + + GEarthExtension gEarthExtension = new InternalExtensionBuilder(internalExtension); + observer.onExtensionProduced(gEarthExtension); + + + Platform.setImplicitExit(false); + + stage.setOnCloseRequest(event -> { + event.consume(); + Platform.runLater(() -> { + stage.hide(); + extensionForm.onHide(); + }); + }); + + return extensionForm; + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } +} diff --git a/G-Earth/src/main/java/gearth/extensions/OnConnectionListener.java b/G-Earth/src/main/java/gearth/extensions/OnConnectionListener.java index cbb63b0..07b7f58 100644 --- a/G-Earth/src/main/java/gearth/extensions/OnConnectionListener.java +++ b/G-Earth/src/main/java/gearth/extensions/OnConnectionListener.java @@ -1,5 +1,8 @@ package gearth.extensions; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.connection.HClient; + public interface OnConnectionListener { - void onConnection(String host, int port, String hotelversion, String clientType, String harbleMessagesPath); + void onConnection(String host, int port, String hotelversion, String clientIdentifier, HClient clientType, PacketInfoManager packetInfoManager); } diff --git a/G-Earth/src/main/java/gearth/extensions/extra/harble/HashSupport.java b/G-Earth/src/main/java/gearth/extensions/extra/harble/HashSupport.java deleted file mode 100644 index c4dd705..0000000 --- a/G-Earth/src/main/java/gearth/extensions/extra/harble/HashSupport.java +++ /dev/null @@ -1,135 +0,0 @@ -package gearth.extensions.extra.harble; - -import gearth.extensions.Extension; -import gearth.extensions.IExtension; -import gearth.misc.harble_api.HarbleAPI; -import gearth.protocol.HMessage; -import gearth.protocol.HPacket; - -import java.io.File; -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Created by Jonas on 10/11/2018. - */ -public class HashSupport { - - private final Object lock = new Object(); - - private HarbleAPI harbleAPI = new HarbleAPI(""); //empty - private Map> incomingMessageListeners = new HashMap<>(); - private Map> outgoingMessageListeners = new HashMap<>(); - - private IExtension extension; - - public HashSupport(IExtension extension) { - this.extension = extension; - - extension.onConnect((host, port, hotelversion, clientType, cachePath) -> { -// synchronized (lock) { - harbleAPI = new HarbleAPI(new File(cachePath)); -// } - }); - - extension.intercept(HMessage.Direction.TOSERVER, message -> { -// synchronized (lock) { - HarbleAPI.HarbleMessage haMessage = harbleAPI.getHarbleMessageFromHeaderId(HMessage.Direction.TOSERVER, message.getPacket().headerId()); - if (haMessage != null) { - List listeners_hash = outgoingMessageListeners.get(haMessage.getHash()); - List listeners_name = outgoingMessageListeners.get(haMessage.getName()); - if (listeners_hash != null) { - for (Extension.MessageListener listener : listeners_hash) { - listener.act(message); - message.getPacket().resetReadIndex(); - } - } - if (listeners_name != null) { - for (Extension.MessageListener listener : listeners_name) { - listener.act(message); - message.getPacket().resetReadIndex(); - } - } - } -// } - }); - extension.intercept(HMessage.Direction.TOCLIENT, message -> { -// synchronized (lock) { - HarbleAPI.HarbleMessage haMessage = harbleAPI.getHarbleMessageFromHeaderId(HMessage.Direction.TOCLIENT, message.getPacket().headerId()); - if (haMessage != null) { - List listeners_hash = incomingMessageListeners.get(haMessage.getHash()); - List listeners_name = incomingMessageListeners.get(haMessage.getName()); - if (listeners_hash != null) { - for (Extension.MessageListener listener : listeners_hash) { - listener.act(message); - message.getPacket().resetReadIndex(); - } - } - if (listeners_name != null) { - for (Extension.MessageListener listener : listeners_name) { - listener.act(message); - message.getPacket().resetReadIndex(); - } - } - } -// } - }); - } - - public void intercept(HMessage.Direction direction, String hashOrName, Extension.MessageListener messageListener) { - Map> messageListeners = - (direction == HMessage.Direction.TOSERVER - ? outgoingMessageListeners - : incomingMessageListeners); - - messageListeners.computeIfAbsent(hashOrName, k -> new ArrayList<>()); - messageListeners.get(hashOrName).add(messageListener); - } - - private boolean send(HMessage.Direction direction, String hashOrName, Object... objects) { - int headerId; - HarbleAPI.HarbleMessage fromname = harbleAPI.getHarbleMessageFromName(direction, hashOrName); - if (fromname != null) { - headerId = fromname.getHeaderId(); - } - else { - List possibilities = harbleAPI.getHarbleMessagesFromHash(direction, hashOrName); - if (possibilities.size() == 0) return false; - headerId = possibilities.get(0).getHeaderId(); - } - - try { - HPacket packetToSend = new HPacket(headerId, objects); - - return (direction == HMessage.Direction.TOCLIENT - ? extension.sendToClient(packetToSend) - : extension.sendToServer(packetToSend)); - } - catch (InvalidParameterException e) { - return false; - } - } - - /** - * - * @return if no errors occurred (invalid hash/invalid parameters/connection error) - */ - public boolean sendToClient(String hashOrName, Object... objects) { - return send(HMessage.Direction.TOCLIENT, hashOrName, objects); - } - - /** - * - * @return if no errors occurred (invalid hash/invalid parameters/connection error) - */ - public boolean sendToServer(String hashOrName, Object... objects) { - return send(HMessage.Direction.TOSERVER, hashOrName, objects); - } - - public HarbleAPI getHarbleAPI() { - return harbleAPI; - } -} diff --git a/G-Earth/src/main/java/gearth/extensions/extra/harble/ChatConsole.java b/G-Earth/src/main/java/gearth/extensions/extra/tools/ChatConsole.java similarity index 74% rename from G-Earth/src/main/java/gearth/extensions/extra/harble/ChatConsole.java rename to G-Earth/src/main/java/gearth/extensions/extra/tools/ChatConsole.java index ddc339a..cde0c8c 100644 --- a/G-Earth/src/main/java/gearth/extensions/extra/harble/ChatConsole.java +++ b/G-Earth/src/main/java/gearth/extensions/extra/tools/ChatConsole.java @@ -1,8 +1,7 @@ -package gearth.extensions.extra.harble; +package gearth.extensions.extra.tools; import gearth.extensions.ExtensionInfo; import gearth.extensions.IExtension; -import gearth.extensions.OnConnectionListener; import gearth.misc.listenerpattern.Observable; import gearth.protocol.HMessage; import gearth.protocol.HPacket; @@ -18,31 +17,33 @@ public class ChatConsole { private volatile int chatid; private volatile String name; - private volatile HashSupport hashSupport; + private volatile PacketInfoSupport packetInfoSupport; private volatile String infoMessage; private volatile boolean firstTime = true; private volatile Observable chatInputObservable = new Observable<>(); - public ChatConsole(final HashSupport hashSupport, IExtension extension) { - this(hashSupport, extension, null); + public ChatConsole(final PacketInfoSupport packetInfoSupport, IExtension extension) { + this(packetInfoSupport, extension, null); } /** * infomessage will be used as response for :info and for initialize - * @param hashSupport + * @param packetInfoSupport * @param extension * @param infoMessage */ - public ChatConsole(final HashSupport hashSupport, IExtension extension, String infoMessage) { - this.hashSupport = hashSupport; + public ChatConsole(final PacketInfoSupport packetInfoSupport, IExtension extension, String infoMessage) { + this.packetInfoSupport = packetInfoSupport; this.name = extension.getClass().getAnnotation(ExtensionInfo.class).Title(); chatid = (this.name.hashCode() % 300000000) + 300000000; this.infoMessage = infoMessage; final boolean[] doOncePerConnection = {false}; - extension.onConnect((s, i, s1, ct, h1) -> doOncePerConnection[0] = true); + extension.onConnect((host, port, hotelversion, clientIdentifier, clientType, packetInfoManager) -> + doOncePerConnection[0] = true + ); extension.intercept(HMessage.Direction.TOSERVER, hMessage -> { // if the first packet on init is not 4000, the extension was already running, so we open the chat instantly @@ -55,7 +56,7 @@ public class ChatConsole { } }); - hashSupport.intercept(HMessage.Direction.TOCLIENT, "FriendListFragment", hMessage -> { + packetInfoSupport.intercept(HMessage.Direction.TOCLIENT, "FriendListFragment", hMessage -> { if (doOncePerConnection[0]) { doOncePerConnection[0] = false; @@ -71,7 +72,7 @@ public class ChatConsole { } }); - hashSupport.intercept(HMessage.Direction.TOSERVER, "SendMsg", hMessage -> { + packetInfoSupport.intercept(HMessage.Direction.TOSERVER, "SendMsg", hMessage -> { HPacket packet = hMessage.getPacket(); if (packet.readInteger() == chatid) { hMessage.setBlocked(true); @@ -87,7 +88,7 @@ public class ChatConsole { } private void createChat() { - hashSupport.sendToClient("FriendListUpdate", + packetInfoSupport.sendToClient("FriendListUpdate", 0, 1, 0, chatid, " [G-Earth] - " + name, 1, true, false, "ha-1015-64.hd-209-30.cc-260-64.ch-235-64.sh-305-64.lg-285-64", 0, "", 0, true, false, true, "" @@ -100,10 +101,10 @@ public class ChatConsole { public void writeOutput(String string, boolean asInvite) { if (asInvite) { - hashSupport.sendToClient("RoomInvite", chatid, string); + packetInfoSupport.sendToClient("RoomInvite", chatid, string); } else { - hashSupport.sendToClient("NewConsole", chatid, string, 0, ""); + packetInfoSupport.sendToClient("NewConsole", chatid, string, 0, ""); } } diff --git a/G-Earth/src/main/java/gearth/extensions/extra/harble/ChatInputListener.java b/G-Earth/src/main/java/gearth/extensions/extra/tools/ChatInputListener.java similarity index 65% rename from G-Earth/src/main/java/gearth/extensions/extra/harble/ChatInputListener.java rename to G-Earth/src/main/java/gearth/extensions/extra/tools/ChatInputListener.java index a9a933a..4e097a8 100644 --- a/G-Earth/src/main/java/gearth/extensions/extra/harble/ChatInputListener.java +++ b/G-Earth/src/main/java/gearth/extensions/extra/tools/ChatInputListener.java @@ -1,4 +1,4 @@ -package gearth.extensions.extra.harble; +package gearth.extensions.extra.tools; public interface ChatInputListener { void inputEntered(String input); diff --git a/G-Earth/src/main/java/gearth/extensions/extra/tools/PacketInfoSupport.java b/G-Earth/src/main/java/gearth/extensions/extra/tools/PacketInfoSupport.java new file mode 100644 index 0000000..b0d256a --- /dev/null +++ b/G-Earth/src/main/java/gearth/extensions/extra/tools/PacketInfoSupport.java @@ -0,0 +1,116 @@ +package gearth.extensions.extra.tools; + +import gearth.extensions.Extension; +import gearth.extensions.IExtension; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; + +import java.security.InvalidParameterException; +import java.util.*; + +/** + * Created by Jonas on 10/11/2018. + */ +public class PacketInfoSupport { + + private final Object lock = new Object(); + + private PacketInfoManager packetInfoManager = new PacketInfoManager(new ArrayList<>()); //empty + private Map> incomingMessageListeners = new HashMap<>(); + private Map> outgoingMessageListeners = new HashMap<>(); + + private IExtension extension; + + public PacketInfoSupport(IExtension extension) { + this.extension = extension; + + extension.onConnect((host, port, hotelversion, clientIdentifier, clientType, packetInfoManager) -> + this.packetInfoManager = packetInfoManager + ); + + extension.intercept(HMessage.Direction.TOSERVER, message -> onReceivePacket(HMessage.Direction.TOSERVER, message)); + extension.intercept(HMessage.Direction.TOCLIENT, message -> onReceivePacket(HMessage.Direction.TOCLIENT, message)); + } + + private void onReceivePacket(HMessage.Direction direction, HMessage message) { + Set callbacks = new HashSet<>(); + Map> messageListeners = + (direction == HMessage.Direction.TOSERVER + ? outgoingMessageListeners + : incomingMessageListeners); + + List packetInfos = packetInfoManager.getAllPacketInfoFromHeaderId(HMessage.Direction.TOCLIENT, message.getPacket().headerId()); + + for (PacketInfo packetInfo : packetInfos) { + List listeners_hash = messageListeners.get(packetInfo.getHash()); + List listeners_name = messageListeners.get(packetInfo.getName()); + if (listeners_hash != null) { + callbacks.addAll(listeners_hash); + } + if (listeners_name != null) { + callbacks.addAll(listeners_name); + } + } + + for (Extension.MessageListener listener : callbacks) { + listener.act(message); + message.getPacket().resetReadIndex(); + } + } + + public void intercept(HMessage.Direction direction, String hashOrName, Extension.MessageListener messageListener) { + Map> messageListeners = + (direction == HMessage.Direction.TOSERVER + ? outgoingMessageListeners + : incomingMessageListeners); + + messageListeners.computeIfAbsent(hashOrName, k -> new ArrayList<>()); + messageListeners.get(hashOrName).add(messageListener); + } + + private boolean send(HMessage.Direction direction, String hashOrName, Object... objects) { + int headerId; + PacketInfo fromname = packetInfoManager.getPacketInfoFromName(direction, hashOrName); + if (fromname != null) { + headerId = fromname.getHeaderId(); + } + else { + PacketInfo fromHash = packetInfoManager.getPacketInfoFromHash(direction, hashOrName); + if (fromHash == null) return false; + headerId = fromHash.getHeaderId(); + } + + try { + HPacket packetToSend = new HPacket(headerId, objects); + + return (direction == HMessage.Direction.TOCLIENT + ? extension.sendToClient(packetToSend) + : extension.sendToServer(packetToSend)); + } + catch (InvalidParameterException e) { + return false; + } + } + + /** + * + * @return if no errors occurred (invalid hash/invalid parameters/connection error) + */ + public boolean sendToClient(String hashOrName, Object... objects) { + return send(HMessage.Direction.TOCLIENT, hashOrName, objects); + } + + /** + * + * @return if no errors occurred (invalid hash/invalid parameters/connection error) + */ + public boolean sendToServer(String hashOrName, Object... objects) { + return send(HMessage.Direction.TOSERVER, hashOrName, objects); + } + + public PacketInfoManager getPacketInfoManager() { + return packetInfoManager; + } +} diff --git a/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPI.java b/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPI.java deleted file mode 100644 index 8333483..0000000 --- a/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPI.java +++ /dev/null @@ -1,224 +0,0 @@ -package gearth.misc.harble_api; - -import gearth.misc.Cacher; -import gearth.protocol.HMessage; -import org.json.JSONArray; -import org.json.JSONObject; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Created by Jonas on 10/11/2018. - */ -public class HarbleAPI { - - public class HarbleMessage { - private HMessage.Direction destination; - private int headerId; - private String hash; - private String name; - private String structure; - - //name can be NULL - public HarbleMessage(HMessage.Direction destination, int headerId, String hash, String name, String structure) { - this.destination = destination; - this.headerId = headerId; - this.hash = hash; - this.name = (name == null || name.equals("null") ? null : name); - this.structure = (structure == null || structure.equals("null") ? null : structure); - } - - public String getName() { - return name; - } - - public int getHeaderId() { - return headerId; - } - - public HMessage.Direction getDestination() { - return destination; - } - - public String getHash() { - return hash; - } - - public String getStructure() { - return structure; - } - - public String toString() { - String s = (headerId + ": " + "[" + hash + "][" + name + "][" + structure + "]"); - return s; - } - } - - private Map headerIdToMessage_incoming = new HashMap<>(); - private Map headerIdToMessage_outgoing = new HashMap<>(); - - private Map> hashToMessage_incoming = new HashMap<>(); - private Map> hashToMessage_outgoing = new HashMap<>(); - - private Map nameToMessage_incoming = new HashMap<>(); - private Map nameToMessage_outgoing = new HashMap<>(); - - private boolean success = false; - private String fullPath = null; - - /** - * cache file must be generated first within G-Earth, inb4 20 extensions requesting it at the same time - * - * @param hotelversion - */ - - public static HarbleAPI get(String hotelversion) { - HarbleAPI wannabe = new HarbleAPI(hotelversion); - if (!wannabe.success) { - return null; - } - return wannabe; - } - - public HarbleAPI(String hotelversion) { - String possibleCachedMessagesPath = HarbleAPIFetcher.CACHE_PREFIX + hotelversion; - if (Cacher.cacheFileExists(possibleCachedMessagesPath)) { - JSONObject object = Cacher.getCacheContents(possibleCachedMessagesPath); - success = true; - fullPath = Cacher.getCacheDir() + File.separator + possibleCachedMessagesPath; - parse(object); - } - } - - public HarbleAPI(File f) { - if (f.exists() && !f.isDirectory()) { - try { - String contents = String.join("\n", Files.readAllLines(f.toPath())); - JSONObject object = new JSONObject(contents); - success = true; - fullPath = f.getAbsolutePath(); - parse(object); - - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - private void addMessage(HMessage.Direction direction, JSONObject object) { - String name; - String hash; - try { name = object.getString("Name"); } - catch (Exception e) { name = null; } - try { hash = object.getString("Hash"); } - catch (Exception e) { hash = null; } - - - int headerId = object.getInt("Id"); - - String structure; - try { - structure = object.getString("Structure"); - } catch (Exception e) { - structure = null; - } - - - HarbleMessage message = new HarbleMessage(direction, headerId, hash, name, structure); - - - Map headerIdToMessage = - message.getDestination() == HMessage.Direction.TOCLIENT - ? headerIdToMessage_incoming : - headerIdToMessage_outgoing; - - Map> hashToMessage = - message.getDestination() == HMessage.Direction.TOCLIENT - ? hashToMessage_incoming - : hashToMessage_outgoing; - - Map nameToMessag = - message.getDestination() == HMessage.Direction.TOCLIENT - ? nameToMessage_incoming - : nameToMessage_outgoing; - - headerIdToMessage.put(message.getHeaderId(), message); - hashToMessage.computeIfAbsent(message.getHash(), k -> new ArrayList<>()); - hashToMessage.get(message.getHash()).add(message); - if (message.getName() != null && !message.getName().equals("null")) { - nameToMessag.put(message.getName(), message); - } - } - - private void parse(JSONObject object) { - try { - JSONArray incoming = object.getJSONArray("Incoming"); - JSONArray outgoing = object.getJSONArray("Outgoing"); - - if (incoming != null && outgoing != null) { - for (int i = 0; i < incoming.length(); i++) { - try { - JSONObject message = incoming.getJSONObject(i); - addMessage(HMessage.Direction.TOCLIENT, message); - } - catch (Exception e){ - e.printStackTrace(); - } - } - for (int i = 0; i < outgoing.length(); i++) { - try{ - JSONObject message = outgoing.getJSONObject(i); - addMessage(HMessage.Direction.TOSERVER, message); - } - catch (Exception e){ - e.printStackTrace(); - } - } - } - } catch (Exception e) { - success = false; - } - } - - public HarbleMessage getHarbleMessageFromHeaderId(HMessage.Direction direction, int headerId) { - Map headerIdToMessage = - (direction == HMessage.Direction.TOSERVER - ? headerIdToMessage_outgoing - : headerIdToMessage_incoming); - - return headerIdToMessage.get(headerId); - } - - public List getHarbleMessagesFromHash(HMessage.Direction direction, String hash) { - Map> hashToMessage = - (direction == HMessage.Direction.TOSERVER - ? hashToMessage_outgoing - : hashToMessage_incoming); - - List result = hashToMessage.get(hash); - return result == null ? new ArrayList<>() : result; - } - - public HarbleMessage getHarbleMessageFromName(HMessage.Direction direction, String name) { - Map nameToMessage = - (direction == HMessage.Direction.TOSERVER - ? nameToMessage_outgoing - : nameToMessage_incoming); - - return nameToMessage.get(name); - } - - public String getPath() { - if (success) { - return fullPath; - } - return "null"; - } - -} diff --git a/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPIFetcher.java b/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPIFetcher.java deleted file mode 100644 index 4c54684..0000000 --- a/G-Earth/src/main/java/gearth/misc/harble_api/HarbleAPIFetcher.java +++ /dev/null @@ -1,80 +0,0 @@ -package gearth.misc.harble_api; - -import gearth.Main; -import gearth.misc.Cacher; -import gearth.protocol.HMessage; -import org.jsoup.Connection; -import org.jsoup.Jsoup; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; - -/** - * Created by Jonas on 10/11/2018. - */ - -/** - * Ok the usage of this class is pretty shitty so I'm just gonna add some documentation here - * - * What this class does is fetching the revision (if needed) from the API, this is the only class with communication with the - * actual API. Then the result (if any) gets cached. - * - * The method "fetch(xxx);" needs to be called exactly once at the moment a new connection has been made. - * - * However, at that same moment the Extension class needs to send the "startConnection" signal to the extensions, and we want to make sure - * that the cached revision is already available at the moment the extensions get initialized with a new connection. That's why the - * fetch() method here only gets called by the extension class as that's the only way to ensure this method gets called BEFORE the extensions - * start. (bc im lazy and dont wanna rewrite code too) - * - * - * the "HARBLEAPI" object contains the latest fetched object and is ensured to be up-to-date with the current connection - */ -public class HarbleAPIFetcher { - - public static final String CACHE_PREFIX = "HARBLE_API-"; - public static final String HARBLE_API_URL = "https://api.harble.net/messages/$hotelversion$.json"; - - //latest fetched - public static HarbleAPI HARBLEAPI = null; - - public synchronized static void fetch(String hotelversion, String clientType) { - // if unity - if (clientType.toLowerCase().contains("unity")) { - try { - HARBLEAPI = new HarbleAPI( - new File(new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()) - .getParentFile(), "messages.json" - ) - ); - } catch (URISyntaxException e) { - HARBLEAPI = null; - } - return; - } - - String cacheName = CACHE_PREFIX + hotelversion; - - if (Cacher.cacheFileExists(cacheName)) { - HARBLEAPI = new HarbleAPI(hotelversion); - } - else { - Connection connection = Jsoup.connect(HARBLE_API_URL.replace("$hotelversion$", hotelversion)).ignoreContentType(true); - try { - connection.timeout(3000); - Connection.Response response = connection.execute(); - if (response.statusCode() == 200) { - String messagesBodyJson = response.body(); - Cacher.updateCache(messagesBodyJson, cacheName); - HARBLEAPI = new HarbleAPI(hotelversion); - } - else { - HARBLEAPI = null; - } - } catch (IOException e) { - HARBLEAPI = null; - } - - } - } -} \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/protocol/HConnection.java b/G-Earth/src/main/java/gearth/protocol/HConnection.java index 511494d..aa917c9 100644 --- a/G-Earth/src/main/java/gearth/protocol/HConnection.java +++ b/G-Earth/src/main/java/gearth/protocol/HConnection.java @@ -1,10 +1,11 @@ package gearth.protocol; import gearth.misc.listenerpattern.Observable; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.connection.HClient; import gearth.protocol.connection.HProxy; import gearth.protocol.connection.HState; import gearth.protocol.connection.proxy.ProxyProvider; -import gearth.protocol.connection.proxy.flash.FlashProxyProvider; import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.flash.unix.LinuxRawIpFlashProxyProvider; import gearth.protocol.connection.proxy.unity.UnityProxyProvider; @@ -137,16 +138,28 @@ public class HConnection { public boolean sendToClientAsync(HPacket message) { - if (proxy == null) { - return false; + if (proxy == null) return false; + + if (!message.isPacketComplete()) { + PacketInfoManager packetInfoManager = getPacketInfoManager(); + message.completePacket(HMessage.Direction.TOCLIENT, packetInfoManager); + + if (!message.isPacketComplete()) return false; } + proxy.getAsyncPacketSender().sendToClientAsync(message); return true; } public boolean sendToServerAsync(HPacket message) { - if (proxy == null) { - return false; + if (proxy == null) return false; + + if (!message.isPacketComplete()) { + PacketInfoManager packetInfoManager = getPacketInfoManager(); + message.completePacket(HMessage.Direction.TOSERVER, packetInfoManager); + + if (!message.isPacketComplete()) return false; } + proxy.getAsyncPacketSender().sendToServerAsync(message); return true; } @@ -172,11 +185,25 @@ public class HConnection { return proxy.getHotelVersion(); } - public String getClientType() { + public String getClientIdentifier() { if (proxy == null) { return ""; } - return proxy.getClientType(); + return proxy.getClientIdentifier(); + } + + public HClient getClientType() { + if (proxy == null) { + return null; + } + return proxy.gethClient(); + } + + public PacketInfoManager getPacketInfoManager() { + if (proxy == null) { + return null; + } + return proxy.getPacketInfoManager(); } public boolean isRawIpMode() { diff --git a/G-Earth/src/main/java/gearth/protocol/HPacket.java b/G-Earth/src/main/java/gearth/protocol/HPacket.java index 16b3205..9a2e65d 100644 --- a/G-Earth/src/main/java/gearth/protocol/HPacket.java +++ b/G-Earth/src/main/java/gearth/protocol/HPacket.java @@ -1,17 +1,17 @@ package gearth.protocol; import gearth.misc.StringifyAble; -import gearth.misc.harble_api.HarbleAPI; -import gearth.misc.harble_api.HarbleAPIFetcher; -import gearth.misc.packetrepresentation.InvalidPacketException; -import gearth.misc.packetrepresentation.PacketStringUtils; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.PacketInfoManager; +import gearth.services.packetrepresentation.InvalidPacketException; +import gearth.services.packetrepresentation.PacketStringUtils; -import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.InvalidParameterException; import java.util.Arrays; +import java.util.Optional; public class HPacket implements StringifyAble { @@ -19,6 +19,9 @@ public class HPacket implements StringifyAble { private byte[] packetInBytes; private int readIndex = 6; + // if identifier != null, this is a placeholder name for the type of packet, headerId will be "-1" + private String identifier = null; + public HPacket(byte[] packet) { packetInBytes = packet.clone(); } @@ -28,11 +31,11 @@ public class HPacket implements StringifyAble { } public HPacket(String packet) { try { - packetInBytes = PacketStringUtils.fromString(packet).packetInBytes; + HPacket packetFromString = PacketStringUtils.fromString(packet); + packetInBytes = packetFromString.packetInBytes; + identifier = packetFromString.identifier; } catch (InvalidPacketException e) { packetInBytes = new byte[0]; - // will be corrupted - // e.printStackTrace(); } } public HPacket(int header) { @@ -40,6 +43,7 @@ public class HPacket implements StringifyAble { replaceShort(4, (short)header); isEdited = false; } + public HPacket(int header, byte[] bytes) { this(header); appendBytes(bytes); @@ -53,14 +57,19 @@ public class HPacket implements StringifyAble { */ public HPacket(int header, Object... objects) throws InvalidParameterException { this(header); - for (int i = 0; i < objects.length; i++) { - Object o = objects[i]; - appendObject(o); - } - + appendObjects(objects); isEdited = false; } + public HPacket(String identifier, Object... objects) throws InvalidParameterException { + packetInBytes = new byte[]{0,0,0,2,-1,-1}; + this.identifier = identifier; + appendObjects(objects); + isEdited = false; + } + + + public String toString() { return PacketStringUtils.toString(packetInBytes); } @@ -75,6 +84,46 @@ public class HPacket implements StringifyAble { return 2; } + public void setIdentifier(String identifier) { + this.identifier = identifier; + } + + public String getIdentifier() { + return identifier; + } + + public void completePacket(HMessage.Direction direction, PacketInfoManager packetInfoManager) { + if (isCorrupted() || identifier == null) return; + + PacketInfo packetInfo = packetInfoManager.getPacketInfoFromName(direction, identifier); + if (packetInfo == null) { + packetInfo = packetInfoManager.getPacketInfoFromHash(direction, identifier); + if (packetInfo == null) return; + } + + boolean wasEdited = isEdited; + replaceShort(4, (short)(packetInfo.getHeaderId())); + identifier = null; + + isEdited = wasEdited; + } + + public boolean canComplete(HMessage.Direction direction, PacketInfoManager packetInfoManager) { + if (isCorrupted() || identifier == null) return false; + + PacketInfo packetInfo = packetInfoManager.getPacketInfoFromName(direction, identifier); + if (packetInfo == null) { + packetInfo = packetInfoManager.getPacketInfoFromHash(direction, identifier); + return packetInfo != null; + } + + return true; + } + + public boolean isPacketComplete() { + return identifier == null; + } + public byte[] toBytes() { return packetInBytes; } @@ -532,6 +581,14 @@ public class HPacket implements StringifyAble { return appendLongString(s, StandardCharsets.ISO_8859_1); } + public HPacket appendObjects(Object... objects) { + for (Object object : objects) { + appendObject(object); + } + + return this; + } + public HPacket appendObject(Object o) throws InvalidParameterException { isEdited = true; @@ -580,45 +637,52 @@ public class HPacket implements StringifyAble { isEdited = edited; } - private String getHarbleStructure(HMessage.Direction direction) { - HarbleAPI.HarbleMessage msg; - if (HarbleAPIFetcher.HARBLEAPI != null && - ((msg = HarbleAPIFetcher.HARBLEAPI.getHarbleMessageFromHeaderId(direction, headerId())) != null)) { - if (msg.getStructure() != null && structureEquals(msg.getStructure())) { - return msg.getStructure(); - } - } + private PacketInfo getPacketInfo(HMessage.Direction direction, PacketInfoManager packetInfoManager) { + if (packetInfoManager == null) return null; - return null; + // prefer packetinfo with structure information (not available in at time of writing) + Optional packetInfoMaybe = packetInfoManager.getAllPacketInfoFromHeaderId(direction, headerId()) + .stream().filter(packetInfo -> packetInfo.getStructure() != null).findFirst(); + return packetInfoMaybe.orElseGet(() -> packetInfoManager.getPacketInfoFromHeaderId(direction, headerId())); } - public String toExpression(HMessage.Direction direction) { + public String toExpression(HMessage.Direction direction, PacketInfoManager packetInfoManager, boolean removeShuffle) { if (isCorrupted()) return ""; - String structure = getHarbleStructure(direction); - if (structure != null) { - return PacketStringUtils.toExpressionFromGivenStructure(this, structure); + PacketInfo packetInfo = getPacketInfo(direction, packetInfoManager); + if (packetInfo != null && packetInfo.getStructure() != null) { + return PacketStringUtils.toExpressionFromGivenStructure(this, packetInfo.getStructure(), removeShuffle ? packetInfo : null); } - - return PacketStringUtils.predictedExpression(this); + return PacketStringUtils.predictedExpression(this, removeShuffle ? packetInfo : null); } /** * returns "" if not found or not sure enough */ - public String toExpression() { + public String toExpression(PacketInfoManager packetInfoManager, boolean removeShuffle) { if (isCorrupted()) return ""; - String structure1 = getHarbleStructure(HMessage.Direction.TOCLIENT); - String structure2 = getHarbleStructure(HMessage.Direction.TOSERVER); - if (structure1 != null && structure2 == null) { - return PacketStringUtils.toExpressionFromGivenStructure(this, structure1); + PacketInfo maybe1 = getPacketInfo(HMessage.Direction.TOCLIENT, packetInfoManager); + PacketInfo maybe2 = getPacketInfo(HMessage.Direction.TOSERVER, packetInfoManager); + + PacketInfo packetInfo = null; + + if (maybe1 != null && maybe2 == null) { + packetInfo = maybe1; } - else if (structure1 == null && structure2 != null) { - return PacketStringUtils.toExpressionFromGivenStructure(this, structure2); + else if (maybe1 == null && maybe2 != null) { + packetInfo = maybe2; } - return PacketStringUtils.predictedExpression(this); + if (packetInfo != null && packetInfo.getStructure() != null) { + return PacketStringUtils.toExpressionFromGivenStructure(this, packetInfo.getStructure(), removeShuffle ? packetInfo : null); + } + return PacketStringUtils.predictedExpression(this, removeShuffle ? packetInfo : null); + } + + public String toExpression() { + if (isCorrupted()) return ""; + return PacketStringUtils.predictedExpression(this, null); } @Override diff --git a/G-Earth/src/main/java/gearth/protocol/connection/HProxy.java b/G-Earth/src/main/java/gearth/protocol/connection/HProxy.java index 530556d..13548a3 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/HProxy.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/HProxy.java @@ -1,5 +1,6 @@ package gearth.protocol.connection; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.packethandler.PacketHandler; import java.net.ServerSocket; @@ -21,7 +22,9 @@ public class HProxy { private volatile PacketHandler outHandler = null; //connection with server (only initialized when verified habbo connection) private volatile String hotelVersion = ""; - private volatile String clientType = ""; + private volatile String clientIdentifier = ""; + private volatile PacketInfoManager packetInfoManager = null; + private volatile AsyncPacketSender asyncPacketSender = null; public HProxy(HClient hClient, String input_domain, String actual_domain, int actual_port, int intercept_port, String intercept_host) { @@ -37,16 +40,17 @@ public class HProxy { this.proxy_server = socket; } - public void verifyProxy(PacketHandler incomingHandler, PacketHandler outgoingHandler, String hotelVersion, String clientType) { + public void verifyProxy(PacketHandler incomingHandler, PacketHandler outgoingHandler, String hotelVersion, String clientIdentifier) { this.inHandler = incomingHandler; this.outHandler = outgoingHandler; this.hotelVersion = hotelVersion; - this.clientType = clientType; + this.clientIdentifier = clientIdentifier; + this.packetInfoManager = PacketInfoManager.fromHotelVersion(hotelVersion, hClient); this.asyncPacketSender = new AsyncPacketSender(this); } - public String getClientType() { - return clientType; + public String getClientIdentifier() { + return clientIdentifier; } public int getActual_port() { @@ -92,4 +96,8 @@ public class HProxy { public HClient gethClient() { return hClient; } + + public PacketInfoManager getPacketInfoManager() { + return packetInfoManager; + } } 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 b06c431..1a8ec33 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 @@ -51,9 +51,9 @@ public abstract class FlashProxyProvider implements ProxyProvider { Semaphore abort = new Semaphore(0); - outgoingHandler.addOnDatastreamConfirmedListener((hotelVersion, clientType) -> { + outgoingHandler.addOnDatastreamConfirmedListener((hotelVersion, clientIdentifier) -> { incomingHandler.setAsDataStream(); - proxy.verifyProxy(incomingHandler, outgoingHandler, hotelVersion, clientType); + proxy.verifyProxy(incomingHandler, outgoingHandler, hotelVersion, clientIdentifier); proxySetter.setProxy(proxy); datastream[0] = true; abortSemaphore = abort; diff --git a/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityCommunicator.java b/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityCommunicator.java index c59ef92..36cd806 100644 --- a/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityCommunicator.java +++ b/G-Earth/src/main/java/gearth/protocol/connection/proxy/unity/UnityCommunicator.java @@ -59,12 +59,12 @@ public class UnityCommunicator { if (maybe.getBytesLength() > 6 && maybe.headerId() == 4000) { hProxy = new HProxy(HClient.UNITY, "", "", -1, -1, ""); String ignore = maybe.readString(); - String clientType = maybe.readString(); + String clientIdentifier = maybe.readString(); hProxy.verifyProxy( new UnityPacketHandler(hConnection.getExtensionHandler(), hConnection.getTrafficObservables(), session, HMessage.Direction.TOCLIENT), new UnityPacketHandler(hConnection.getExtensionHandler(), hConnection.getTrafficObservables(), session, HMessage.Direction.TOSERVER), revision, - clientType + clientIdentifier ); proxySetter.setProxy(hProxy); stateSetter.setState(HState.CONNECTED); diff --git a/G-Earth/src/main/java/gearth/protocol/memory/habboclient/windows/WindowsHabboClient.java b/G-Earth/src/main/java/gearth/protocol/memory/habboclient/windows/WindowsHabboClient.java index 8801fef..7178b06 100644 --- a/G-Earth/src/main/java/gearth/protocol/memory/habboclient/windows/WindowsHabboClient.java +++ b/G-Earth/src/main/java/gearth/protocol/memory/habboclient/windows/WindowsHabboClient.java @@ -15,7 +15,7 @@ import java.net.URISyntaxException; import java.util.*; /** - * Created by Jeunez on 27/06/2018. + * Created by Jonas on 27/06/2018. */ public class WindowsHabboClient extends HabboClient { diff --git a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OnDatastreamConfirmedListener.java b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OnDatastreamConfirmedListener.java index 6e13dab..f7ed253 100644 --- a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OnDatastreamConfirmedListener.java +++ b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OnDatastreamConfirmedListener.java @@ -2,6 +2,6 @@ package gearth.protocol.packethandler.flash; public interface OnDatastreamConfirmedListener { - void confirm(String hotelVersion, String clientType); + void confirm(String hotelVersion, String clientIdentifier); } diff --git a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OutgoingFlashPacketHandler.java b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OutgoingFlashPacketHandler.java index a2775e6..84ddf7b 100644 --- a/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OutgoingFlashPacketHandler.java +++ b/G-Earth/src/main/java/gearth/protocol/packethandler/flash/OutgoingFlashPacketHandler.java @@ -26,9 +26,9 @@ public class OutgoingFlashPacketHandler extends FlashPacketHandler { HPacket hpacket = new HPacket(buffer); isDataStream = (hpacket.getBytesLength() > 6 && hpacket.length() < 100); if (isDataStream) { - String version = hpacket.readString(); - String clientType = hpacket.readString(); - datastreamConfirmedObservable.fireEvent(l -> l.confirm(version, clientType)); + String hotelVersion = hpacket.readString(); + String clientIdentifier = hpacket.readString(); + datastreamConfirmedObservable.fireEvent(l -> l.confirm(hotelVersion, clientIdentifier)); } } } diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/ExtensionHandler.java b/G-Earth/src/main/java/gearth/services/extensionhandler/ExtensionHandler.java index 3d12d3a..5f26af8 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/ExtensionHandler.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/ExtensionHandler.java @@ -1,7 +1,6 @@ package gearth.services.extensionhandler; import gearth.Main; -import gearth.misc.harble_api.HarbleAPIFetcher; import gearth.misc.listenerpattern.Observable; import gearth.protocol.HConnection; import gearth.protocol.HMessage; @@ -12,6 +11,7 @@ import gearth.services.extensionhandler.extensions.GEarthExtension; import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducer; import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducerFactory; import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducerObserver; +import gearth.services.packet_info.PacketInfoManager; import javafx.util.Pair; import java.io.IOException; @@ -45,18 +45,17 @@ public class ExtensionHandler { } private void initialize() { - hConnection.getStateObservable().addListener((oldState, newState) -> { if (newState == HState.CONNECTED) { - HarbleAPIFetcher.fetch(hConnection.getHotelVersion(), hConnection.getClientType()); synchronized (gEarthExtensions) { for (GEarthExtension extension : gEarthExtensions) { extension.connectionStart( hConnection.getDomain(), hConnection.getServerPort(), hConnection.getHotelVersion(), + hConnection.getClientIdentifier(), hConnection.getClientType(), - HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath() + hConnection.getPacketInfoManager() ); } } @@ -71,7 +70,7 @@ public class ExtensionHandler { }); extensionProducers = ExtensionProducerFactory.getAll(); - extensionProducers.forEach(this::initializeExtensionProducer); + extensionProducers.forEach(extensionProducer -> extensionProducer.startProducing(createExtensionProducerObserver())); } @@ -174,8 +173,8 @@ public class ExtensionHandler { - private void initializeExtensionProducer(ExtensionProducer producer) { - producer.startProducing(new ExtensionProducerObserver() { + private ExtensionProducerObserver createExtensionProducerObserver() { + return new ExtensionProducerObserver() { @Override public void onExtensionProduced(GEarthExtension extension) { synchronized (gEarthExtensions) { @@ -221,7 +220,7 @@ public class ExtensionHandler { try { s = packet.toString(); if (packet.length() < 3000) { - expression = packet.toExpression(); + expression = packet.toExpression(hConnection.getPacketInfoManager(), true); } } finally { @@ -232,6 +231,13 @@ public class ExtensionHandler { @Override protected void stringToPacketRequest(String string) { HPacket packet = new HPacket(string); + PacketInfoManager packetInfoManager = hConnection.getPacketInfoManager(); + if (packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && !packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOCLIENT, packetInfoManager); + } + else if (!packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOSERVER, packetInfoManager); + } extension.stringToPacketResponse(packet); } }; @@ -244,8 +250,9 @@ public class ExtensionHandler { hConnection.getDomain(), hConnection.getServerPort(), hConnection.getHotelVersion(), + hConnection.getClientIdentifier(), hConnection.getClientType(), - HarbleAPIFetcher.HARBLEAPI == null ? "null" : HarbleAPIFetcher.HARBLEAPI.getPath() + hConnection.getPacketInfoManager() ); } @@ -254,7 +261,7 @@ public class ExtensionHandler { observable.fireEvent(l -> l.onExtensionConnect(extension)); } - }); + }; } public List getExtensionProducers() { @@ -264,5 +271,10 @@ public class ExtensionHandler { return observable; } + public void addExtensionProducer(ExtensionProducer producer) { + producer.startProducing(createExtensionProducerObserver()); + extensionProducers.add(producer); + } + } diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/ExtensionType.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/ExtensionType.java new file mode 100644 index 0000000..00d0358 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/ExtensionType.java @@ -0,0 +1,6 @@ +package gearth.services.extensionhandler.extensions; + +public enum ExtensionType { + INTERNAL, + EXTERNAL +} diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/GEarthExtension.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/GEarthExtension.java index ce52885..a22d847 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/GEarthExtension.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/GEarthExtension.java @@ -2,14 +2,14 @@ package gearth.services.extensionhandler.extensions; import gearth.misc.listenerpattern.Observable; import gearth.misc.listenerpattern.SynchronizedObservable; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.protocol.connection.HClient; import gearth.services.extensionhandler.extensions.listeners.OmRemoveClickListener; import gearth.services.extensionhandler.extensions.listeners.OnClickListener; import gearth.services.extensionhandler.extensions.listeners.OnDeleteListener; -import java.util.function.Consumer; - public abstract class GEarthExtension { @@ -38,7 +38,7 @@ public abstract class GEarthExtension { 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 clientType, String harbleMessagesPath); + public abstract void connectionStart(String host, int port, String hotelVersion, String clientIdentifier, HClient clientType, PacketInfoManager packetInfoManager); public abstract void connectionEnd(); public abstract void init(); public abstract void close(); @@ -51,7 +51,6 @@ public abstract class GEarthExtension { // ----------------- listen to the extension --------------------- - protected final Observable extensionObservable = new SynchronizedObservable<>(); public Observable getExtensionObservable() { return extensionObservable; @@ -121,4 +120,7 @@ public abstract class GEarthExtension { } // ---------------------------------------------------------------------------------------- + + + public abstract ExtensionType extensionType(); } diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/extensionproducers/ExtensionProducerFactory.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/extensionproducers/ExtensionProducerFactory.java index d02b910..40c0b83 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/extensionproducers/ExtensionProducerFactory.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/extensionproducers/ExtensionProducerFactory.java @@ -3,6 +3,7 @@ package gearth.services.extensionhandler.extensions.extensionproducers; import gearth.services.extensionhandler.extensions.implementations.network.NetworkExtensionsProducer; import gearth.services.extensionhandler.extensions.implementations.simple.SimpleExtensionProducer; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -10,10 +11,11 @@ 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() - ); + List all = new ArrayList<>(); + all.add(new NetworkExtensionsProducer()); + all.add(new SimpleExtensionProducer()); + + return all; } diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/network/NetworkExtension.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/network/NetworkExtension.java index 707a3d2..6d6eaf4 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/network/NetworkExtension.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/network/NetworkExtension.java @@ -1,6 +1,9 @@ package gearth.services.extensionhandler.extensions.implementations.network; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.HMessage; +import gearth.protocol.connection.HClient; +import gearth.services.extensionhandler.extensions.ExtensionType; import gearth.services.extensionhandler.extensions.GEarthExtension; import gearth.protocol.HPacket; @@ -190,15 +193,16 @@ public class NetworkExtension extends GEarthExtension { } @Override - public void connectionStart(String host, int port, String hotelVersion, String clientType, String harbleMessagesPath) { - sendMessage( - new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) - .appendString(host) - .appendInt(port) - .appendString(hotelVersion) - .appendString(harbleMessagesPath) - .appendString(clientType) - ); + public void connectionStart(String host, int port, String hotelVersion, String clientIdentifier, HClient clientType, PacketInfoManager packetInfoManager) { + HPacket connectionStartPacket = new HPacket(NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) + .appendString(host) + .appendInt(port) + .appendString(hotelVersion) + .appendString(clientIdentifier) + .appendString(clientType.name()); + + packetInfoManager.appendToPacket(connectionStartPacket); + sendMessage(connectionStartPacket); } @Override @@ -236,4 +240,9 @@ public class NetworkExtension extends GEarthExtension { packet.appendLongString(packetFromString.stringify()); sendMessage(packet); } + + @Override + public ExtensionType extensionType() { + return ExtensionType.EXTERNAL; + } } \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/ExampleExtension.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/ExampleExtension.java index 909deae..a33bf9b 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/ExampleExtension.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/ExampleExtension.java @@ -1,7 +1,10 @@ package gearth.services.extensionhandler.extensions.implementations.simple; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.protocol.connection.HClient; +import gearth.services.extensionhandler.extensions.ExtensionType; import gearth.services.extensionhandler.extensions.GEarthExtension; public class ExampleExtension extends GEarthExtension { @@ -83,7 +86,7 @@ public class ExampleExtension extends GEarthExtension { } @Override - public void connectionStart(String host, int port, String hotelVersion, String clientType, String harbleMessagesPath) { + public void connectionStart(String host, int port, String hotelVersion, String clientIdentifier, HClient clientType, PacketInfoManager packetInfoManager) { // a new habbo client has connected System.out.println("Connected to " + host); } @@ -120,4 +123,9 @@ public class ExampleExtension extends GEarthExtension { public void stringToPacketResponse(HPacket packet) { } + + @Override + public ExtensionType extensionType() { + return ExtensionType.INTERNAL; + } } diff --git a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/SimpleExtensionProducer.java b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/SimpleExtensionProducer.java index 105b907..25b753c 100644 --- a/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/SimpleExtensionProducer.java +++ b/G-Earth/src/main/java/gearth/services/extensionhandler/extensions/implementations/simple/SimpleExtensionProducer.java @@ -1,7 +1,10 @@ package gearth.services.extensionhandler.extensions.implementations.simple; +import gearth.extensions.InternalExtensionFormBuilder; import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducer; import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducerObserver; +import gearth.services.internal_extensions.blockreplacepackets.BlockAndReplacePackets; +import gearth.services.internal_extensions.packetinfoexplorer.PacketInfoExplorer; public class SimpleExtensionProducer implements ExtensionProducer { @@ -9,7 +12,12 @@ public class SimpleExtensionProducer implements ExtensionProducer { 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()); +// observer.onExtensionProduced(new ExampleExtension()); + new InternalExtensionFormBuilder() + .launch(BlockAndReplacePackets.class, observer); + + new InternalExtensionFormBuilder() + .launch(PacketInfoExplorer.class, observer); } } diff --git a/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java b/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java index 34e2be0..0c839ea 100644 --- a/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java +++ b/G-Earth/src/main/java/gearth/services/gpython/GPythonVersionUtils.java @@ -10,7 +10,7 @@ import java.util.List; public class GPythonVersionUtils { - private static final String MIN_GPYTHON_VERSION = "0.1.3"; + private static final String MIN_GPYTHON_VERSION = "0.1.4"; private static final String MIN_PYTHON_VERSION = "3.2"; // returns null if python not installed diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/BlockAndReplacePackets.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/BlockAndReplacePackets.java similarity index 77% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/BlockAndReplacePackets.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/BlockAndReplacePackets.java index ad3d6ff..6838aad 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/BlockAndReplacePackets.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/BlockAndReplacePackets.java @@ -1,10 +1,14 @@ -package extensions.blockreplacepackets; +package gearth.services.internal_extensions.blockreplacepackets; -import extensions.blockreplacepackets.rules.BlockReplaceRule; -import extensions.blockreplacepackets.rules.RuleFactory; -import gearth.extensions.Extension; +import gearth.Main; +import gearth.extensions.ExtensionForm; +import gearth.extensions.ExtensionInfo; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.PacketInfoManager; import gearth.protocol.HMessage; import gearth.protocol.HPacket; +import gearth.services.internal_extensions.blockreplacepackets.rules.BlockReplaceRule; +import gearth.services.internal_extensions.blockreplacepackets.rules.RuleFactory; import gearth.ui.GEarthController; import javafx.application.Platform; import javafx.event.ActionEvent; @@ -15,11 +19,10 @@ import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; +import javafx.scene.image.Image; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; -import gearth.extensions.ExtensionForm; -import gearth.extensions.ExtensionInfo; import java.util.ArrayList; import java.util.List; @@ -32,7 +35,7 @@ import java.util.List; @ExtensionInfo( Title = "G-Manipulate", Description = "Block &/ replace packets", - Version = "0.1", + Version = "0.2", Author = "sirjonasxx" ) public class BlockAndReplacePackets extends ExtensionForm { @@ -48,9 +51,11 @@ public class BlockAndReplacePackets extends ExtensionForm { List rules = new ArrayList<>(); - public static void main(String[] args) { - runExtensionForm(args, BlockAndReplacePackets.class); - } + private PacketInfoManager packetInfoManager = PacketInfoManager.EMPTY; + +// public static void main(String[] args) { +// runExtensionForm(args, BlockAndReplacePackets.class); +// } //initialize javaFX elements public void initialize() { @@ -70,6 +75,26 @@ public class BlockAndReplacePackets extends ExtensionForm { } + private String getVal() { + String val = txt_value.getText(); + String type = cmb_type.getSelectionModel().getSelectedItem(); + String side = cmb_side.getSelectionModel().getSelectedItem(); + + if (type.endsWith("packet")) { + HMessage.Direction dir = side.equals("Outgoing") ? HMessage.Direction.TOSERVER : HMessage.Direction.TOCLIENT; + PacketInfo fromName = packetInfoManager.getPacketInfoFromName(dir, val); + PacketInfo fromHash = packetInfoManager.getPacketInfoFromHash(dir, val); + if (fromName != null) { + val = fromName.getHeaderId() +""; + } + else if (fromHash != null) { + val = fromHash.getHeaderId() +""; + } + } + + return val; + } + private void refreshOptions() { txt_replacement.setDisable(cmb_type.getSelectionModel().getSelectedItem().startsWith("Block")); if (cmb_side.getItems().size() == 2 && !cmb_type.getSelectionModel().getSelectedItem().endsWith("packet")) { @@ -83,7 +108,7 @@ public class BlockAndReplacePackets extends ExtensionForm { } boolean isValid = false; - String val = txt_value.getText(); + String val = getVal(); String repl = txt_replacement.getText(); String type = cmb_type.getSelectionModel().getSelectedItem(); String side = cmb_side.getSelectionModel().getSelectedItem(); @@ -154,7 +179,7 @@ public class BlockAndReplacePackets extends ExtensionForm { if (val.equals("")) { if (spl[1].equals("packet")) { - txt_value.setPromptText("Enter the headerID"); + txt_value.setPromptText("Enter headerID/name"); } else if (spl[1].equals("integer")) { txt_value.setPromptText("Enter an integer"); @@ -177,7 +202,7 @@ public class BlockAndReplacePackets extends ExtensionForm { @Override protected void initExtension() { - Extension.MessageListener messageListener = message -> { + MessageListener messageListener = message -> { for (BlockReplaceRule rule : rules) { rule.appendRuleToMessage(message); } @@ -185,6 +210,10 @@ public class BlockAndReplacePackets extends ExtensionForm { intercept(HMessage.Direction.TOSERVER, messageListener); intercept(HMessage.Direction.TOCLIENT, messageListener); + + onConnect((host, port, hotelversion, clientIdentifier, clientType, packetInfoManager) -> { + this.packetInfoManager = packetInfoManager; + }); } @Override @@ -196,6 +225,7 @@ public class BlockAndReplacePackets extends ExtensionForm { primaryStage.setScene(new Scene(root)); primaryStage.setResizable(false); primaryStage.getScene().getStylesheets().add(GEarthController.class.getResource("/gearth/ui/bootstrap3.css").toExternalForm()); + primaryStage.getIcons().add(new Image(Main.class.getResourceAsStream("G-EarthLogoSmaller.png"))); return loader.getController(); } @@ -206,7 +236,8 @@ public class BlockAndReplacePackets extends ExtensionForm { } public void click_btnAddRule(ActionEvent actionEvent) { - BlockReplaceRule rule = RuleFactory.getRule(cmb_type.getSelectionModel().getSelectedItem(), cmb_side.getSelectionModel().getSelectedItem(), txt_value.getText(), txt_replacement.getText()); + BlockReplaceRule rule = RuleFactory.getRule(cmb_type.getSelectionModel().getSelectedItem(), + cmb_side.getSelectionModel().getSelectedItem(), getVal(), txt_replacement.getText(), packetInfoManager); rules.add(rule); rule.onDelete(observable -> rules.remove(rule)); new RuleContainer(rule, vbox); @@ -215,6 +246,11 @@ public class BlockAndReplacePackets extends ExtensionForm { clearInput(); } + @Override + protected boolean canLeave() { + return false; + } + @Override protected boolean canDelete() { return false; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/RuleContainer.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/RuleContainer.java similarity index 93% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/RuleContainer.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/RuleContainer.java index c72ee00..4539d53 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/RuleContainer.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/RuleContainer.java @@ -1,6 +1,6 @@ -package extensions.blockreplacepackets; +package gearth.services.internal_extensions.blockreplacepackets; -import extensions.blockreplacepackets.rules.BlockReplaceRule; +import gearth.services.internal_extensions.blockreplacepackets.rules.BlockReplaceRule; import gearth.ui.buttons.DeleteButton; import javafx.geometry.Insets; import javafx.geometry.Pos; @@ -13,7 +13,7 @@ import javafx.scene.layout.VBox; import javafx.scene.text.Font; /** - * Created by Jeunez on 6/11/2018. + * Created by Jonas on 6/11/2018. */ public class RuleContainer extends GridPane { diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockPacketRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockPacketRule.java similarity index 94% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockPacketRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockPacketRule.java index d80c675..a8e22df 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockPacketRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockPacketRule.java @@ -1,4 +1,4 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockReplaceRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockReplaceRule.java similarity index 93% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockReplaceRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockReplaceRule.java index d1cc521..621cf0a 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/BlockReplaceRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/BlockReplaceRule.java @@ -1,4 +1,4 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; import javafx.beans.InvalidationListener; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceIntegerRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceIntegerRule.java similarity index 94% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceIntegerRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceIntegerRule.java index f543461..d5141ee 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceIntegerRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceIntegerRule.java @@ -1,4 +1,4 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplacePacketRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplacePacketRule.java similarity index 92% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplacePacketRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplacePacketRule.java index c6bfa0d..6134234 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplacePacketRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplacePacketRule.java @@ -1,10 +1,10 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; import gearth.protocol.HPacket; /** - * Created by Jeunez on 6/11/2018. + * Created by Jonas on 6/11/2018. */ public class ReplacePacketRule extends BlockReplaceRule { diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceStringRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceStringRule.java similarity index 94% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceStringRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceStringRule.java index fd20820..5449370 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceStringRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceStringRule.java @@ -1,4 +1,4 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceSubstringRule.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceSubstringRule.java similarity index 94% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceSubstringRule.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceSubstringRule.java index 9e56286..dbb58be 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/ReplaceSubstringRule.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/ReplaceSubstringRule.java @@ -1,4 +1,4 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; import gearth.protocol.HMessage; diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/RuleFactory.java b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/RuleFactory.java similarity index 72% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/RuleFactory.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/RuleFactory.java index ef99e03..a6dc50f 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/rules/RuleFactory.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/blockreplacepackets/rules/RuleFactory.java @@ -1,5 +1,7 @@ -package extensions.blockreplacepackets.rules; +package gearth.services.internal_extensions.blockreplacepackets.rules; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; import gearth.protocol.HPacket; /** @@ -7,7 +9,7 @@ import gearth.protocol.HPacket; */ public class RuleFactory { - public static BlockReplaceRule getRule(String type, String side, String value, String replacement) { + public static BlockReplaceRule getRule(String type, String side, String value, String replacement, PacketInfoManager packetInfoManager) { BlockReplaceRule.Option rOption = BlockReplaceRule.Option.valueOf(type.split(" ")[0].toUpperCase()); BlockReplaceRule.Type rType = BlockReplaceRule.Type.valueOf(type.split(" ")[1].toUpperCase()); BlockReplaceRule.Side rSide = BlockReplaceRule.Side.valueOf(side.toUpperCase()); @@ -24,7 +26,11 @@ public class RuleFactory { return new ReplaceIntegerRule(rSide, Integer.parseInt(value), Integer.parseInt(replacement)); } if (rType == BlockReplaceRule.Type.PACKET) { - return new ReplacePacketRule(rSide, Integer.parseInt(value), new HPacket(replacement)); + HPacket packet = new HPacket(replacement); + if (!packet.isPacketComplete()) { + packet.completePacket(rSide == BlockReplaceRule.Side.INCOMING ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER, packetInfoManager); + } + return new ReplacePacketRule(rSide, Integer.parseInt(value), packet); } if (rType == BlockReplaceRule.Type.STRING) { return new ReplaceStringRule(rSide, value, replacement); diff --git a/G-Earth/src/main/java/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.java b/G-Earth/src/main/java/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.java new file mode 100644 index 0000000..2795abe --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.java @@ -0,0 +1,189 @@ +package gearth.services.internal_extensions.packetinfoexplorer; + +import gearth.Main; +import gearth.extensions.ExtensionForm; +import gearth.extensions.ExtensionInfo; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; +import gearth.ui.GEarthController; +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.TextField; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.image.Image; +import javafx.scene.layout.GridPane; +import javafx.stage.Stage; + +import java.util.*; +import java.util.List; +import java.util.stream.Collectors; + +@ExtensionInfo( + Title = "Packet Info", + Description = "Packet info explorer", + Version = "0.1", + Author = "sirjonasxx" +) +public class PacketInfoExplorer extends ExtensionForm { + public TextField txt_filterHeaderId; + public TextField txt_filterNameHash; + public GridPane source_grid; + public CheckBox chk_toClient; + public CheckBox chk_toServer; + + private Map chk_sources = new HashMap<>(); + + private List packetInfoList = new ArrayList<>(); + + private TableView tableView; + public GridPane grid; + + @Override + public ExtensionForm launchForm(Stage primaryStage) throws Exception { + FXMLLoader loader = new FXMLLoader(PacketInfoExplorer.class.getResource("PacketInfoExplorer.fxml")); + Parent root = loader.load(); + + primaryStage.setTitle("Packet info explorer"); + primaryStage.setScene(new Scene(root)); + primaryStage.setMinWidth(430); + primaryStage.setMinHeight(260); + primaryStage.getScene().getStylesheets().add(GEarthController.class.getResource("/gearth/ui/bootstrap3.css").toExternalForm()); + primaryStage.getIcons().add(new Image(Main.class.getResourceAsStream("G-EarthLogoSmaller.png"))); + + return loader.getController(); + } + + public void initialize() { + Platform.runLater( () -> grid.requestFocus() ); + + tableView = new TableView<>(); + tableView.setTableMenuButtonVisible(true); + tableView.setStyle("-fx-focus-color: white;"); + + tableView.focusedProperty().addListener(observable -> { + if (tableView.isFocused()) { + grid.requestFocus(); + } + }); + + TableColumn headerIdColumn = new TableColumn<>("Header ID"); + headerIdColumn.setCellValueFactory(new PropertyValueFactory<>("headerId")); + + TableColumn directionColumn = new TableColumn<>("Direction"); + directionColumn.setCellValueFactory(new PropertyValueFactory<>("destination")); + directionColumn.setPrefWidth(96); + + TableColumn packetNameColumn = new TableColumn<>("Name"); + packetNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + packetNameColumn.setPrefWidth(220); + + TableColumn packetHashColumn = new TableColumn<>("Hash"); + packetHashColumn.setVisible(false); + packetHashColumn.setCellValueFactory(new PropertyValueFactory<>("hash")); + packetHashColumn.setPrefWidth(220); + + TableColumn structureColumn = new TableColumn<>("Structure"); + structureColumn.setCellValueFactory(new PropertyValueFactory<>("structure")); + structureColumn.setPrefWidth(115); + + TableColumn sourceColumn = new TableColumn<>("Source"); + sourceColumn.setCellValueFactory(new PropertyValueFactory<>("source")); + + tableView.getColumns().addAll(Arrays.asList(headerIdColumn, directionColumn, packetNameColumn, + packetHashColumn, structureColumn, sourceColumn)); + + grid.add(tableView, 0, 1); + + InvalidationListener filterValues = observable -> updateTableValues(); + txt_filterHeaderId.textProperty().addListener(filterValues); + txt_filterNameHash.textProperty().addListener(filterValues); + chk_toClient.selectedProperty().addListener(filterValues); + chk_toClient.selectedProperty().addListener(filterValues); + } + + @Override + protected void initExtension() { + onConnect((host, port, hotelversion, clientIdentifier, clientType, packetInfoManager) -> { + setPacketInfoManager(packetInfoManager); + }); + } + + @Override + protected void onEndConnection() { + setPacketInfoManager(PacketInfoManager.EMPTY); + } + + private void setPacketInfoManager(PacketInfoManager packetInfoManager) { + packetInfoList = packetInfoManager.getPacketInfoList(); + packetInfoList.sort(Comparator.comparingInt(PacketInfo::getHeaderId)); + + Platform.runLater(() -> { + source_grid.getChildren().clear(); + chk_sources.clear(); + for (PacketInfo packetInfo : packetInfoList) { + if (!chk_sources.containsKey(packetInfo.getSource())) { + CheckBox checkBox = new CheckBox(packetInfo.getSource()); + checkBox.setSelected(true); + checkBox.selectedProperty().addListener(observable -> updateTableValues()); + source_grid.add(checkBox, 0, chk_sources.size()); + chk_sources.put(packetInfo.getSource(), checkBox); + } + } + + primaryStage.setTitle("Packet info explorer | " + packetInfoList.size() + " packets"); + + updateTableValues(); + }); + + } + + private void updateTableValues() { + tableView.getItems().clear(); + + IntegerProperty doHeaderIdFilter = new SimpleIntegerProperty(-1); + if (!txt_filterHeaderId.getText().equals("")) { + try { + doHeaderIdFilter.setValue(Integer.parseInt(txt_filterHeaderId.getText())); + } + catch (Exception ignore) {} + } + + + List allPacketInfos = packetInfoList.stream() + .filter(packetInfo -> { + if (doHeaderIdFilter.get() != -1 && packetInfo.getHeaderId() != doHeaderIdFilter.get()) return false; + String filterNameHashLower = txt_filterNameHash.getText().toLowerCase(); + if (!filterNameHashLower.equals("") + && (packetInfo.getName() == null || !packetInfo.getName().toLowerCase().contains(filterNameHashLower)) + && (packetInfo.getHash() == null || !packetInfo.getHash().toLowerCase().contains(filterNameHashLower))) { + return false; + } + if ((!chk_toClient.isSelected() && packetInfo.getDestination() == HMessage.Direction.TOCLIENT) + || (!chk_toServer.isSelected() && packetInfo.getDestination() == HMessage.Direction.TOSERVER)) { + return false; + } + if (!chk_sources.get(packetInfo.getSource()).isSelected()) return false; + return true; + }).collect(Collectors.toList()); + + + tableView.getItems().addAll(allPacketInfos); + } + + @Override + protected boolean canLeave() { + return false; + } + + @Override + protected boolean canDelete() { + return false; + } +} diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/Element.java b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/Element.java similarity index 80% rename from G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/Element.java rename to G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/Element.java index 7c6d0d1..7a8169a 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/Element.java +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/Element.java @@ -1,4 +1,4 @@ -package gearth.ui.logger.loggerdisplays.uilogger; +package gearth.services.internal_extensions.uilogger; /** * Created by Jonas on 17/11/2018. diff --git a/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLogger.java b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLogger.java new file mode 100644 index 0000000..fa594f7 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLogger.java @@ -0,0 +1,108 @@ +package gearth.services.internal_extensions.uilogger; + +import gearth.extensions.ExtensionForm; +import gearth.extensions.ExtensionInfo; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HConnection; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.ui.logger.loggerdisplays.PacketLogger; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.stage.Modality; +import javafx.stage.Stage; + +@ExtensionInfo( + Title = "Packet Logger", + Description = "", + Version = "1.0", + Author = "sirjonasxx & Scott" +) +public class UiLogger extends ExtensionForm implements PacketLogger { + private UiLoggerController controller = null; + + @Override + public void start(HConnection hConnection) { +// // don't let the user close this window on their own +// stage.setOnCloseRequest(Event::consume); + +// primaryStage.show(); + } + + @Override + public void stop() { +// primaryStage.hide(); +// if (stage != null) +// stage.close(); + } + + @Override + public void appendSplitLine() { + // don't use this, we can't discern incoming/outgoing + //Platform.runLater(() -> controller.appendSplitLine()); + } + + @Override + protected void initExtension() { + onConnect((host, port, hotelversion, clientIdentifier, clientType, packetInfoManager) -> { + controller.setPacketInfoManager(packetInfoManager); + controller.onConnect(); + }); + } + + @Override + protected void onEndConnection() { + controller.onDisconnect(); + controller.setPacketInfoManager(PacketInfoManager.EMPTY); + } + + @Override + public ExtensionForm launchForm(Stage stage) throws Exception { + FXMLLoader loader = new FXMLLoader(UiLogger.class.getResource("UiLogger.fxml")); + + Parent root = loader.load(); + stage.setTitle("G-Earth | Packet Logger"); + stage.initModality(Modality.NONE); + stage.getIcons().add(new Image(getClass().getResourceAsStream("/gearth/G-EarthLogoSmaller.png"))); + + Scene scene = new Scene(root); + scene.getStylesheets().add("/gearth/ui/bootstrap3.css"); + scene.getStylesheets().add("/gearth/services/internal_extensions/uilogger/logger.css"); + controller = loader.getController(); + controller.setStage(stage); + + stage.setScene(scene); + return this; + } + + private class Elem { + HPacket packet; + int types; + Elem(HPacket packet, int types) { + this.packet = packet; + this.types = types; + } + } + + @Override + public void appendMessage(HPacket packet, int types) { + controller.appendMessage(packet, types); + } + + @Override + public void appendStructure(HPacket packet, HMessage.Direction direction) { + + } + + @Override + protected boolean canLeave() { + return false; + } + + @Override + protected boolean canDelete() { + return false; + } +} diff --git a/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLoggerController.java b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLoggerController.java new file mode 100644 index 0000000..6321745 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/internal_extensions/uilogger/UiLoggerController.java @@ -0,0 +1,400 @@ +package gearth.services.internal_extensions.uilogger; + + import gearth.misc.Cacher; + import gearth.services.packet_info.PacketInfo; + import gearth.services.packet_info.PacketInfoManager; + import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.ui.logger.loggerdisplays.PacketLogger; +import javafx.application.Platform; +import javafx.event.ActionEvent; + import javafx.fxml.Initializable; +import javafx.scene.control.CheckMenuItem; +import javafx.scene.control.Label; + import javafx.scene.control.MenuItem; + import javafx.scene.control.RadioMenuItem; + import javafx.scene.layout.BorderPane; +import javafx.scene.layout.FlowPane; + import javafx.stage.FileChooser; + import javafx.stage.Stage; +import org.fxmisc.flowless.VirtualizedScrollPane; +import org.fxmisc.richtext.StyleClassedTextArea; +import org.fxmisc.richtext.model.StyleSpansBuilder; + + import java.io.BufferedWriter; + import java.io.File; + import java.io.FileWriter; + import java.io.IOException; + import java.net.URL; +import java.util.*; + import java.util.stream.Collectors; + +public class UiLoggerController implements Initializable { + + private static final String LOGGER_SETTINGS_CACHE = "LOGGER_SETTINGS"; + + public FlowPane flowPane; + public BorderPane borderPane; + public Label lblViewIncoming; + public Label lblViewOutgoing; + + public CheckMenuItem chkViewIncoming; + public CheckMenuItem chkViewOutgoing; + public CheckMenuItem chkDisplayStructure; + public Label lblAutoScrolll; + public CheckMenuItem chkAutoscroll; + public CheckMenuItem chkSkipBigPackets; + public CheckMenuItem chkMessageName; + public CheckMenuItem chkMessageHash; + public Label lblPacketInfo; + public CheckMenuItem chkUseNewStructures; + public CheckMenuItem chkAlwaysOnTop; + + public CheckMenuItem chkOpenOnConnect; + public CheckMenuItem chkResetOnConnect; + public CheckMenuItem chkHideOnDisconnect; + public CheckMenuItem chkResetOnDisconnect; + + private final static int FILTER_AMOUNT_THRESHOLD_L = 15; + private final static int FILTER_AMOUNT_THRESHOLD_M = 9; + private final static int FILTER_AMOUNT_THRESHOLD_H = 4; + private final static int FILTER_AMOUNT_THRESHOLD_U = 2; + private final static int FILTER_TIME_THRESHOLD = 5000; + + public RadioMenuItem chkAntiSpam_none; + public RadioMenuItem chkAntiSpam_low; + public RadioMenuItem chkAntiSpam_medium; + public RadioMenuItem chkAntiSpam_high; + public RadioMenuItem chkAntiSpam_ultra; + public Label lblFiltered; + + private Map> filterTimestamps = new HashMap<>(); + + private StyleClassedTextArea area; + + private Stage stage; + private PacketInfoManager packetInfoManager = null; + + private int filteredAmount = 0; + + private volatile boolean initialized = false; + private final List appendLater = new ArrayList<>(); + + private List allMenuItems = new ArrayList<>(); + + private boolean isSelected(MenuItem item) { + if (item instanceof CheckMenuItem) { + return ((CheckMenuItem)item).isSelected(); + } + if (item instanceof RadioMenuItem) { + return ((RadioMenuItem)item).isSelected(); + } + return false; + } + + private void setSelected(MenuItem item, boolean selected) { + if (item instanceof CheckMenuItem) { + ((CheckMenuItem)item).setSelected(selected); + } + if (item instanceof RadioMenuItem) { + ((RadioMenuItem)item).setSelected(selected); + } + } + + + private void saveAllMenuItems() { + List selection = new ArrayList<>(); + for (MenuItem menuItem : allMenuItems) { + selection.add(isSelected(menuItem)); + } + + Cacher.put(LOGGER_SETTINGS_CACHE, selection); + } + + private void loadAllMenuItems() { + List selectedMenuItems = Cacher.getList(LOGGER_SETTINGS_CACHE); + if (selectedMenuItems != null) { + for (int i = 0; i < selectedMenuItems.size(); i++) { + boolean isSelected = (boolean) selectedMenuItems.get(i); + setSelected(allMenuItems.get(i), isSelected); + } + } + } + + @Override + public void initialize(URL arg0, ResourceBundle arg1) { + allMenuItems.addAll(Arrays.asList( + chkViewIncoming, chkViewOutgoing, chkDisplayStructure, chkAutoscroll, + chkSkipBigPackets, chkMessageName, chkMessageHash, chkUseNewStructures, + chkOpenOnConnect, chkResetOnConnect, chkHideOnDisconnect, chkResetOnDisconnect, + chkAntiSpam_none, chkAntiSpam_low, chkAntiSpam_medium, chkAntiSpam_high, chkAntiSpam_ultra + )); + loadAllMenuItems(); + + for (MenuItem item : allMenuItems) { + item.setOnAction(event -> { + saveAllMenuItems(); + updateLoggerInfo(); + }); + } + + area = new StyleClassedTextArea(); + area.getStyleClass().add("dark"); + area.setWrapText(true); + + VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); + borderPane.setCenter(vsPane); + + synchronized (appendLater) { + initialized = true; + if (!appendLater.isEmpty()) { + appendLog(appendLater); + appendLater.clear(); + } + } + } + + private static String cleanTextContent(String text) { +// // strips off all non-ASCII characters +// text = text.replaceAll("[^\\x00-\\x7F]", ""); +// +// // erases all the ASCII control characters + text = text.replaceAll("[\\p{Cntrl}&&[^\n\t]]", ""); + + // removes non-printable characters from Unicode +// text = text.replaceAll("\\p{C}", ""); + + return text.trim(); + } + + + private boolean checkFilter(HPacket packet) { + int headerId = packet.headerId(); + + int threshold = chkAntiSpam_none.isSelected() ? 100000000 : ( + chkAntiSpam_low.isSelected() ? FILTER_AMOUNT_THRESHOLD_L : ( + chkAntiSpam_medium.isSelected() ? FILTER_AMOUNT_THRESHOLD_M : ( + chkAntiSpam_high.isSelected() ? FILTER_AMOUNT_THRESHOLD_H : FILTER_AMOUNT_THRESHOLD_U + ) + ) + ); + + if (!filterTimestamps.containsKey(headerId)) { + filterTimestamps.put(headerId, new LinkedList<>()); + } + + long queueRemoveThreshold = System.currentTimeMillis() - FILTER_TIME_THRESHOLD; + LinkedList list = filterTimestamps.get(headerId); + while (!list.isEmpty() && list.get(0) < queueRemoveThreshold) list.removeFirst(); + + if (list.size() == threshold) list.removeFirst(); + list.add(System.currentTimeMillis()); + + return list.size() >= threshold; + } + + public void appendMessage(HPacket packet, int types) { + boolean isBlocked = (types & PacketLogger.MESSAGE_TYPE.BLOCKED.getValue()) != 0; + boolean isReplaced = (types & PacketLogger.MESSAGE_TYPE.REPLACED.getValue()) != 0; + boolean isIncoming = (types & PacketLogger.MESSAGE_TYPE.INCOMING.getValue()) != 0; + + if (isIncoming && !isBlocked && !isReplaced) { + boolean filter = checkFilter(packet); + if (filter) { + filteredAmount++; + updateLoggerInfo(); + return; + } + } + + if (isIncoming && !chkViewIncoming.isSelected()) return; + if (!isIncoming && !chkViewOutgoing.isSelected()) return; + + ArrayList elements = new ArrayList<>(); + + + boolean packetInfoAvailable = packetInfoManager.getPacketInfoList().size() > 0; + + if ((chkMessageName.isSelected() || chkMessageHash.isSelected()) && packetInfoAvailable) { + List messages = packetInfoManager.getAllPacketInfoFromHeaderId( + (isIncoming ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER), + packet.headerId() + ); + List names = messages.stream().map(PacketInfo::getName) + .filter(Objects::nonNull).distinct().collect(Collectors.toList()); + List hashes = messages.stream().map(PacketInfo::getHash) + .filter(Objects::nonNull).distinct().collect(Collectors.toList()); + + boolean addedSomething = false; + if (chkMessageName.isSelected() && names.size() > 0) { + for (String name : names) {elements.add(new Element("["+name+"]", "messageinfo")); } + addedSomething = true; + } + if (chkMessageHash.isSelected() && hashes.size() > 0) { + for (String hash : hashes) {elements.add(new Element("["+hash+"]", "messageinfo")); } + addedSomething = true; + } + + if (addedSomething) { + elements.add(new Element("\n", "")); + } + + } + + if (isBlocked) elements.add(new Element("[Blocked]\n", "blocked")); + else if (isReplaced) elements.add(new Element("[Replaced]\n", "replaced")); + + if (isIncoming) { + // handle skipped eventually + elements.add(new Element("Incoming[", "incoming")); + elements.add(new Element(String.valueOf(packet.headerId()), "")); + elements.add(new Element("]", "incoming")); + + elements.add(new Element(" <- ", "")); + if (chkSkipBigPackets.isSelected() && packet.length() > 4000) { + elements.add(new Element("", "skipped")); + } + else { + elements.add(new Element(packet.toString(), "incoming")); + } + } else { + elements.add(new Element("Outgoing[", "outgoing")); + elements.add(new Element(String.valueOf(packet.headerId()), "")); + elements.add(new Element("]", "outgoing")); + + elements.add(new Element(" -> ", "")); + + if (chkSkipBigPackets.isSelected() && packet.length() > 4000) { + elements.add(new Element("", "skipped")); + } + else { + elements.add(new Element(packet.toString(), "outgoing")); + } + } + + if (packet.length() <= 2000) { + String expr = packet.toExpression(isIncoming ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER, packetInfoManager, chkUseNewStructures.isSelected()); + String cleaned = cleanTextContent(expr); + if (cleaned.equals(expr)) { + if (!expr.equals("") && chkDisplayStructure.isSelected()) + elements.add(new Element("\n" + cleanTextContent(expr), "structure")); + } + } + + + elements.add(new Element("\n--------------------\n", "")); + + synchronized (appendLater) { + if (initialized) { + appendLog(elements); + } + else { + appendLater.addAll(elements); + } + } + + } + + private synchronized void appendLog(List elements) { + Platform.runLater(() -> { + StringBuilder sb = new StringBuilder(); + StyleSpansBuilder> styleSpansBuilder = new StyleSpansBuilder<>(0); + + for (Element element : elements) { + sb.append(element.text); + + styleSpansBuilder.add(Collections.singleton(element.className), element.text.length()); + } + + int oldLen = area.getLength(); + area.appendText(sb.toString()); +// System.out.println(sb.toString()); + area.setStyleSpans(oldLen, styleSpansBuilder.create()); + + if (chkAutoscroll.isSelected()) { +// area.moveTo(area.getLength()); + area.requestFollowCaret(); + } + }); + } + + public void setStage(Stage stage) { + this.stage = stage; + } + + public void updateLoggerInfo() { + Platform.runLater(() -> { + lblViewIncoming.setText("View Incoming: " + (chkViewIncoming.isSelected() ? "True" : "False")); + lblViewOutgoing.setText("View Outgoing: " + (chkViewOutgoing.isSelected() ? "True" : "False")); + lblAutoScrolll.setText("Autoscroll: " + (chkAutoscroll.isSelected() ? "True" : "False")); + lblFiltered.setText("Filtered: " + filteredAmount); + + boolean packetInfoAvailable = packetInfoManager.getPacketInfoList().size() > 0; + lblPacketInfo.setText("Packet info: " + (packetInfoAvailable ? "True" : "False")); + }); + } + + public void setPacketInfoManager(PacketInfoManager packetInfoManager) { + this.packetInfoManager = packetInfoManager; + Platform.runLater(this::updateLoggerInfo); + } + + public void toggleAlwaysOnTop(ActionEvent actionEvent) { + stage.setAlwaysOnTop(chkAlwaysOnTop.isSelected()); + } + + public void clearText(ActionEvent actionEvent) { + area.clear(); + } + + public void onDisconnect() { + Platform.runLater(() -> { + if (chkHideOnDisconnect.isSelected()) { + stage.hide(); + } + if (chkResetOnDisconnect.isSelected()) { + clearText(null); + } + }); + } + + public void onConnect() { + Platform.runLater(() -> { + if (chkResetOnConnect.isSelected()) { + clearText(null); + } + if (chkOpenOnConnect.isSelected()) { + stage.show(); + } + }); + } + + public void exportAll(ActionEvent actionEvent) { + FileChooser fileChooser = new FileChooser(); + + //Set extension filter + FileChooser.ExtensionFilter extFilter = + new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt"); + fileChooser.getExtensionFilters().add(extFilter); + fileChooser.setTitle("Save Packets"); + + //Show save file dialog + File file = fileChooser.showSaveDialog(stage); + + if(file != null){ + try { + FileWriter fileWriter = new FileWriter(file); + BufferedWriter out = new BufferedWriter(fileWriter); + + out.write(area.getText()); + + out.flush(); + out.close(); + fileWriter.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + + } + } +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/PacketInfo.java b/G-Earth/src/main/java/gearth/services/packet_info/PacketInfo.java new file mode 100644 index 0000000..4869fde --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/PacketInfo.java @@ -0,0 +1,50 @@ +package gearth.services.packet_info; + +import gearth.protocol.HMessage; + +public class PacketInfo { + private final HMessage.Direction destination; + private final int headerId; + private final String hash; + private final String name; + private final String structure; + + private final String source; + + public PacketInfo(HMessage.Direction destination, int headerId, String hash, String name, String structure, String source) { + this.destination = destination; + this.headerId = headerId; + this.hash = hash; + this.name = name; + this.structure = structure; + this.source = source; + } + + public String getName() { + return name; + } + + public String getHash() { + return hash; + } + + public int getHeaderId() { + return headerId; + } + + public HMessage.Direction getDestination() { + return destination; + } + + public String getStructure() { + return structure; + } + + public String getSource() { + return source; + } + + public String toString() { + return headerId + ": " + "[" + name + "][" + structure + "]"; + } +} \ No newline at end of file diff --git a/G-Earth/src/main/java/gearth/services/packet_info/PacketInfoManager.java b/G-Earth/src/main/java/gearth/services/packet_info/PacketInfoManager.java new file mode 100644 index 0000000..27904b4 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/PacketInfoManager.java @@ -0,0 +1,184 @@ +package gearth.services.packet_info; + +import gearth.services.packet_info.providers.RemotePacketInfoProvider; +import gearth.services.packet_info.providers.implementations.HarblePacketInfoProvider; +import gearth.services.packet_info.providers.implementations.SulekPacketInfoProvider; +import gearth.services.packet_info.providers.implementations.GEarthUnityPacketInfoProvider; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.protocol.connection.HClient; + +import java.util.*; +import java.util.concurrent.Semaphore; + +public class PacketInfoManager { + + private Map> headerIdToMessage_incoming = new HashMap<>(); + private Map> headerIdToMessage_outgoing = new HashMap<>(); + + private Map> hashToMessage_incoming = new HashMap<>(); + private Map> hashToMessage_outgoing = new HashMap<>(); + + private Map> nameToMessage_incoming = new HashMap<>(); + private Map> nameToMessage_outgoing = new HashMap<>(); + + private List packetInfoList; + + public PacketInfoManager(List packetInfoList) { + this.packetInfoList = packetInfoList; + for (PacketInfo packetInfo : packetInfoList) { + addMessage(packetInfo); + } + } + + private void addMessage(PacketInfo packetInfo) { + if (packetInfo.getHash() == null && packetInfo.getName() == null) return; + + Map> headerIdToMessage = + packetInfo.getDestination() == HMessage.Direction.TOCLIENT + ? headerIdToMessage_incoming : + headerIdToMessage_outgoing; + + Map> hashToMessage = + packetInfo.getDestination() == HMessage.Direction.TOCLIENT + ? hashToMessage_incoming + : hashToMessage_outgoing; + + Map> nameToMessage = + packetInfo.getDestination() == HMessage.Direction.TOCLIENT + ? nameToMessage_incoming + : nameToMessage_outgoing; + + headerIdToMessage.computeIfAbsent(packetInfo.getHeaderId(), k -> new ArrayList<>()); + + headerIdToMessage.get(packetInfo.getHeaderId()).add(packetInfo); + if (packetInfo.getHash() != null) { + hashToMessage.computeIfAbsent(packetInfo.getHash(), k -> new ArrayList<>()); + hashToMessage.get(packetInfo.getHash()).add(packetInfo); + } + if (packetInfo.getName() != null) { + nameToMessage.computeIfAbsent(packetInfo.getName(), k -> new ArrayList<>()); + nameToMessage.get(packetInfo.getName()).add(packetInfo); + } + + } + + public List getAllPacketInfoFromHeaderId(HMessage.Direction direction, int headerId) { + Map> headerIdToMessage = + (direction == HMessage.Direction.TOSERVER + ? headerIdToMessage_outgoing + : headerIdToMessage_incoming); + + return headerIdToMessage.get(headerId) == null ? new ArrayList<>() : headerIdToMessage.get(headerId); + } + + public List getAllPacketInfoFromHash(HMessage.Direction direction, String hash) { + Map> hashToMessage = + (direction == HMessage.Direction.TOSERVER + ? hashToMessage_outgoing + : hashToMessage_incoming); + + return hashToMessage.get(hash) == null ? new ArrayList<>() : hashToMessage.get(hash); + } + + public List getAllPacketInfoFromName(HMessage.Direction direction, String name) { + Map> nameToMessage = + (direction == HMessage.Direction.TOSERVER + ? nameToMessage_outgoing + : nameToMessage_incoming); + + return nameToMessage.get(name) == null ? new ArrayList<>() : nameToMessage.get(name); + } + + public PacketInfo getPacketInfoFromHeaderId(HMessage.Direction direction, int headerId) { + List all = getAllPacketInfoFromHeaderId(direction, headerId); + return all.size() == 0 ? null : all.get(0); + } + + public PacketInfo getPacketInfoFromHash(HMessage.Direction direction, String hash) { + List all = getAllPacketInfoFromHash(direction, hash); + return all.size() == 0 ? null : all.get(0); + } + + public PacketInfo getPacketInfoFromName(HMessage.Direction direction, String name) { + List all = getAllPacketInfoFromName(direction, name); + return all.size() == 0 ? null : all.get(0); + } + + public List getPacketInfoList() { + return packetInfoList; + } + + public static PacketInfoManager fromHotelVersion(String hotelversion, HClient clientType) { + List result = new ArrayList<>(); + + if (clientType == HClient.UNITY) { + result.addAll(new GEarthUnityPacketInfoProvider(hotelversion).provide()); + } + else if (clientType == HClient.FLASH) { + try { + List providers = new ArrayList<>(); + providers.add(new HarblePacketInfoProvider(hotelversion)); + providers.add(new SulekPacketInfoProvider(hotelversion)); + + Semaphore blockUntilComplete = new Semaphore(providers.size()); + blockUntilComplete.acquire(providers.size()); + + List synchronizedResult = Collections.synchronizedList(result); + for (RemotePacketInfoProvider provider : providers) { + new Thread(() -> { + synchronizedResult.addAll(provider.provide()); + blockUntilComplete.release(); + }).start(); + } + + blockUntilComplete.acquire(providers.size()); + + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + + return new PacketInfoManager(result); + } + + public static PacketInfoManager readFromPacket(HPacket hPacket) { + List packetInfoList = new ArrayList<>(); + int size = hPacket.readInteger(); + + for (int i = 0; i < size; i++) { + int headerId = hPacket.readInteger(); + String hash = hPacket.readString(); + String name = hPacket.readString(); + String structure = hPacket.readString(); + boolean isOutgoing = hPacket.readBoolean(); + String source = hPacket.readString(); + + packetInfoList.add(new PacketInfo( + isOutgoing ? HMessage.Direction.TOSERVER : HMessage.Direction.TOCLIENT, + headerId, + hash.equals("NULL") ? null : hash, + name.equals("NULL") ? null : name, + structure.equals("NULL") ? null : structure, + source)); + } + + return new PacketInfoManager(packetInfoList); + } + + public void appendToPacket(HPacket hPacket) { + hPacket.appendInt(packetInfoList.size()); + for (PacketInfo packetInfo : packetInfoList) { + hPacket.appendInt(packetInfo.getHeaderId()); + hPacket.appendString(packetInfo.getHash() == null ? "NULL" : packetInfo.getHash()); + hPacket.appendString(packetInfo.getName() == null ? "NULL" : packetInfo.getName()); + hPacket.appendString(packetInfo.getStructure() == null ? "NULL" : packetInfo.getStructure()); + hPacket.appendBoolean(packetInfo.getDestination() == HMessage.Direction.TOSERVER); + hPacket.appendString(packetInfo.getSource()); + } + } + + + public static PacketInfoManager EMPTY = new PacketInfoManager(new ArrayList<>()); +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/providers/PacketInfoProvider.java b/G-Earth/src/main/java/gearth/services/packet_info/providers/PacketInfoProvider.java new file mode 100644 index 0000000..9b1aa44 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/providers/PacketInfoProvider.java @@ -0,0 +1,39 @@ +package gearth.services.packet_info.providers; + +import gearth.services.packet_info.PacketInfo; +import org.json.JSONObject; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +public abstract class PacketInfoProvider { + + protected final String hotelVersion; + + public PacketInfoProvider(String hotelVersion) { + this.hotelVersion = hotelVersion; + } + + protected abstract File getFile(); + + public List provide() { + File file = getFile(); + if (file == null || !file.exists() || file.isDirectory()) return new ArrayList<>(); + + try { + String contents = String.join("\n", Files.readAllLines(file.toPath())); + JSONObject object = new JSONObject(contents); + return parsePacketInfo(object); + } catch (IOException e) { + e.printStackTrace(); + } + + return new ArrayList<>(); + } + + protected abstract List parsePacketInfo(JSONObject jsonObject); + +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/providers/RemotePacketInfoProvider.java b/G-Earth/src/main/java/gearth/services/packet_info/providers/RemotePacketInfoProvider.java new file mode 100644 index 0000000..5ffc589 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/providers/RemotePacketInfoProvider.java @@ -0,0 +1,40 @@ +package gearth.services.packet_info.providers; + +import gearth.misc.Cacher; +import org.jsoup.Connection; +import org.jsoup.Jsoup; + +import java.io.File; +import java.io.IOException; + +public abstract class RemotePacketInfoProvider extends PacketInfoProvider { + public RemotePacketInfoProvider(String hotelVersion) { + super(hotelVersion); + } + + protected abstract String getRemoteUrl(); + protected abstract String getCacheName(); + + @Override + protected File getFile() { + File f = new File(Cacher.getCacheDir(), getCacheName()); + if (!f.exists()) { + Connection connection = Jsoup.connect(getRemoteUrl()).ignoreContentType(true); + try { + connection.timeout(3000); + Connection.Response response = connection.execute(); + if (response.statusCode() == 200) { + String messagesBodyJson = response.body(); + Cacher.updateCache(messagesBodyJson, getCacheName()); + } + else { + return null; + } + } catch (IOException e) { + return null; + } + } + + return new File(Cacher.getCacheDir(), getCacheName()); + } +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/GEarthUnityPacketInfoProvider.java b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/GEarthUnityPacketInfoProvider.java new file mode 100644 index 0000000..64f3ee6 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/GEarthUnityPacketInfoProvider.java @@ -0,0 +1,64 @@ +package gearth.services.packet_info.providers.implementations; + +import gearth.Main; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.providers.PacketInfoProvider; +import gearth.protocol.HMessage; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.File; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +public class GEarthUnityPacketInfoProvider extends PacketInfoProvider { + + public GEarthUnityPacketInfoProvider(String hotelVersion) { + super(hotelVersion); + } + + @Override + protected File getFile() { + try { + return new File(new File(Main.class.getProtectionDomain().getCodeSource().getLocation().toURI()) + .getParentFile(), "messages.json"); + } catch (URISyntaxException e) { + e.printStackTrace(); + return null; + } + } + + private PacketInfo jsonToPacketInfo(JSONObject object, HMessage.Direction destination) { + String name = object.getString("Name"); + int headerId = object.getInt("Id"); + return new PacketInfo(destination, headerId, null, name, null, "G-Earth"); + } + + @Override + protected List parsePacketInfo(JSONObject jsonObject) { + List packetInfos = new ArrayList<>(); + + try { + JSONArray incoming = jsonObject.getJSONArray("Incoming"); + JSONArray outgoing = jsonObject.getJSONArray("Outgoing"); + + if (incoming != null && outgoing != null) { + for (int i = 0; i < incoming.length(); i++) { + JSONObject jsonInfo = incoming.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOCLIENT); + packetInfos.add(packetInfo); + } + for (int i = 0; i < outgoing.length(); i++) { + JSONObject jsonInfo = outgoing.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOSERVER); + packetInfos.add(packetInfo); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return packetInfos; + } +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/HarblePacketInfoProvider.java b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/HarblePacketInfoProvider.java new file mode 100644 index 0000000..69d34c8 --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/HarblePacketInfoProvider.java @@ -0,0 +1,79 @@ +package gearth.services.packet_info.providers.implementations; + +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.providers.RemotePacketInfoProvider; +import gearth.protocol.HMessage; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class HarblePacketInfoProvider extends RemotePacketInfoProvider { + + public static final String CACHE_PREFIX = "HARBLE_API-"; + public static final String HARBLE_API_URL = "https://api.harble.net/messages/$hotelversion$.json"; + + public HarblePacketInfoProvider(String hotelVersion) { + super(hotelVersion); + } + + @Override + protected String getRemoteUrl() { + return HARBLE_API_URL.replace("$hotelversion$", hotelVersion); + } + + @Override + protected String getCacheName() { + return CACHE_PREFIX + hotelVersion; + } + + private PacketInfo jsonToPacketInfo(JSONObject object, HMessage.Direction destination) { + String name; + String hash; + String structure; + try { + name = object.getString("Name") + .replaceAll("Composer$", ""); + } + catch (Exception e) { name = null; } + try { hash = object.getString("Hash"); } + catch (Exception e) { hash = null; } + try { structure = object.getString("Structure"); + } catch (Exception e) { structure = null; } + structure = (structure == null || structure.equals("")) ? null : structure; + + int headerId; + try {headerId = object.getInt("Id"); } + catch (Exception e) { headerId = Integer.parseInt(object.getString("Id")); } + + return new PacketInfo(destination, headerId, hash, name, structure, "Harble"); + } + + @Override + protected List parsePacketInfo(JSONObject jsonObject) { + List packetInfos = new ArrayList<>(); + + try { + JSONArray incoming = jsonObject.getJSONArray("Incoming"); + JSONArray outgoing = jsonObject.getJSONArray("Outgoing"); + + if (incoming != null && outgoing != null) { + for (int i = 0; i < incoming.length(); i++) { + JSONObject jsonInfo = incoming.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOCLIENT); + packetInfos.add(packetInfo); + } + for (int i = 0; i < outgoing.length(); i++) { + JSONObject jsonInfo = outgoing.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOSERVER); + packetInfos.add(packetInfo); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + return packetInfos; + } +} diff --git a/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/SulekPacketInfoProvider.java b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/SulekPacketInfoProvider.java new file mode 100644 index 0000000..f28427c --- /dev/null +++ b/G-Earth/src/main/java/gearth/services/packet_info/providers/implementations/SulekPacketInfoProvider.java @@ -0,0 +1,63 @@ +package gearth.services.packet_info.providers.implementations; + +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.providers.RemotePacketInfoProvider; +import gearth.protocol.HMessage; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class SulekPacketInfoProvider extends RemotePacketInfoProvider { + + public static final String CACHE_PREFIX = "SULEK_API-"; + public static final String SULEK_API_URL = "https://api.sulek.dev/releases/$hotelversion$/messages"; + + public SulekPacketInfoProvider(String hotelVersion) { + super(hotelVersion); + } + + @Override + protected String getRemoteUrl() { + return SULEK_API_URL.replace("$hotelversion$", hotelVersion); + } + + @Override + protected String getCacheName() { + return CACHE_PREFIX + hotelVersion; + } + + private PacketInfo jsonToPacketInfo(JSONObject object, HMessage.Direction destination) { + int headerId = object.getInt("id"); + String name = object.getString("name") + .replaceAll("(((Message)?Composer)|((Message)?Event))$", ""); + + return new PacketInfo(destination, headerId, null, name, null, "Sulek"); + } + + @Override + protected List parsePacketInfo(JSONObject jsonObject) { + List packetInfos = new ArrayList<>(); + + try { + JSONArray incoming = jsonObject.getJSONObject("messages").getJSONArray("incoming"); + JSONArray outgoing = jsonObject.getJSONObject("messages").getJSONArray("outgoing"); + + for (int i = 0; i < incoming.length(); i++) { + JSONObject jsonInfo = incoming.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOCLIENT); + packetInfos.add(packetInfo); + } + for (int i = 0; i < outgoing.length(); i++) { + JSONObject jsonInfo = outgoing.getJSONObject(i); + PacketInfo packetInfo = jsonToPacketInfo(jsonInfo, HMessage.Direction.TOSERVER); + packetInfos.add(packetInfo); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return packetInfos; + } +} diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/InvalidPacketException.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/InvalidPacketException.java similarity index 56% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/InvalidPacketException.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/InvalidPacketException.java index e360c58..16f144f 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/InvalidPacketException.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/InvalidPacketException.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation; +package gearth.services.packetrepresentation; public class InvalidPacketException extends Exception { } diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/PacketStringUtils.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/PacketStringUtils.java similarity index 83% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/PacketStringUtils.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/PacketStringUtils.java index a71b9b7..d83f8e8 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/PacketStringUtils.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/PacketStringUtils.java @@ -1,6 +1,7 @@ -package gearth.misc.packetrepresentation; +package gearth.services.packetrepresentation; -import gearth.misc.packetrepresentation.prediction.StructurePredictor; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packetrepresentation.prediction.StructurePredictor; import gearth.protocol.HPacket; import java.nio.ByteBuffer; @@ -48,8 +49,8 @@ public class PacketStringUtils { packet = replaceAll(packet, "\\{u:([0-9]+)}", m -> "[" + (Integer.parseInt(m.group(1))/256) + "][" + (Integer.parseInt(m.group(1)) % 256) + "]"); - packet = replaceAll(packet, "\\{h:([0-9]+)}", - m -> "[" + (Integer.parseInt(m.group(1))/256) + "][" + (Integer.parseInt(m.group(1)) % 256) + "]"); + packet = replaceAll(packet, "\\{h:(-?[0-9]+)}", + m -> toString(ByteBuffer.allocate(2).putShort(Short.parseShort(m.group(1))).array())); packet = replaceAll(packet, "\\{b:([Ff]alse|[Tt]rue)}", m -> m.group(1).toLowerCase().equals("true") ? "[1]" : "[0]"); @@ -124,13 +125,24 @@ public class PacketStringUtils { } } actualString.append(match); + String latin = new String(actualString.toString().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1); + HPacket temp = new HPacket(0); + temp.appendString(latin, StandardCharsets.ISO_8859_1); packet = packet.substring(0, start) + - toString(new HPacket(0, latin).readBytes(latin.length() + 2, 6)) + + toString(temp.readBytes(latin.length() + 2, 6)) + packet.substring(end + 2); } + String[] identifier = {null}; + if (!fixLengthLater && packet.startsWith("{")) { + packet = replaceAll(packet, "^\\{([^:{}]*)}", m -> { + identifier[0] = m.group(1); + return "[255][255]"; + }); + } + if (identifier[0] != null) fixLengthLater = true; if (packet.contains("{") || packet.contains("}")) { throw new InvalidPacketException(); @@ -160,6 +172,9 @@ public class PacketStringUtils { if (fixLengthLater) { hPacket.fixLength(); } + if (identifier[0] != null) { + hPacket.setIdentifier(identifier[0]); + } return hPacket; } public static String toString(byte[] packet) { @@ -175,14 +190,21 @@ public class PacketStringUtils { } // generates an expression for a packet from a packet structure (ex. "i(isi(b))iBd") - public static String toExpressionFromGivenStructure(HPacket packet, String struct) { + public static String toExpressionFromGivenStructure(HPacket packet, String struct, PacketInfo packetInfo) { int oldReadIndex = packet.getReadIndex(); packet.resetReadIndex(); StringBuilder builder = new StringBuilder(); - builder.append("{l}{h:").append(packet.headerId()).append("}"); + if (packetInfo != null) { + String identifier = packetInfo.getName() == null ? packetInfo.getHash() : packetInfo.getName(); + builder.append("{").append(identifier).append("}"); + } + else { + builder.append("{l}{h:").append(packet.headerId()).append("}"); + } buildExpressionFromGivenStructure(packet, struct, 0, builder); + packet.setReadIndex(oldReadIndex); return builder.toString(); } @@ -216,9 +238,12 @@ public class PacketStringUtils { else return; } } - public static String predictedExpression(HPacket packet) { + public static String predictedExpression(HPacket packet, PacketInfo packetInfo) { StructurePredictor structurePredictor = new StructurePredictor(packet); - return structurePredictor.getExpression(); + String structure = structurePredictor.getStructure(); + if (structure == null) return ""; + + return PacketStringUtils.toExpressionFromGivenStructure(packet, structure, packetInfo); } public static boolean structureEquals(HPacket packet, String struct) { @@ -241,6 +266,12 @@ public class PacketStringUtils { } public static void main(String[] args) throws InvalidPacketException { + HPacket fghdft = fromString("{l}{h:-1}{i:1}{i:0}{i:6}{i:4}{s:\"1.0\"}"); + System.out.println(fghdft); + + HPacket zed = fromString("{test}{s:\"Â¥\"}{i:0}{i:0}"); + System.out.println(zed); + HPacket p1 = fromString("{l}{h:1129}{s:\"\\\\\\\\\"}{i:0}{i:0}"); System.out.println(p1.toExpression()); HPacket p1_2 = fromString(p1.toExpression()); @@ -253,6 +284,9 @@ public class PacketStringUtils { new HPacket("{l}{h:5}{s:\"asdas\"}"), "s" )); + + HPacket p3 = fromString("{l}{h:2266}{s:\"Â¥\"}{i:0}{i:0}"); + System.out.println(p3); } } diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/StructurePredictor.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/StructurePredictor.java similarity index 81% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/StructurePredictor.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/StructurePredictor.java index 8216489..4a40d0a 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/StructurePredictor.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/StructurePredictor.java @@ -1,8 +1,7 @@ -package gearth.misc.packetrepresentation.prediction; +package gearth.services.packetrepresentation.prediction; -import gearth.misc.packetrepresentation.PacketStringUtils; -import gearth.misc.packetrepresentation.prediction.checkers.TypeChecker; -import gearth.misc.packetrepresentation.prediction.checkers.TypeCheckerProducer; +import gearth.services.packetrepresentation.prediction.checkers.TypeChecker; +import gearth.services.packetrepresentation.prediction.checkers.TypeCheckerProducer; import gearth.protocol.HPacket; import java.util.List; @@ -65,13 +64,6 @@ public class StructurePredictor { structure = stringBuilder.reverse().toString(); } - public String getExpression() { - if (structure == null) { - return ""; - } - return PacketStringUtils.toExpressionFromGivenStructure(packet, structure); - } - public String getStructure() { return structure; } diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/BooleanChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/BooleanChecker.java similarity index 93% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/BooleanChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/BooleanChecker.java index 9536316..cc94df5 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/BooleanChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/BooleanChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ByteChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ByteChecker.java similarity index 90% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ByteChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ByteChecker.java index f3e957d..2aa1cc9 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ByteChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ByteChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/IntegerChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/IntegerChecker.java similarity index 97% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/IntegerChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/IntegerChecker.java index d34c0a3..0e772be 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/IntegerChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/IntegerChecker.java @@ -1,7 +1,6 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; -import gearth.services.Constants; import java.nio.charset.StandardCharsets; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/LongChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/LongChecker.java similarity index 94% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/LongChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/LongChecker.java index 7cc3591..052f892 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/LongChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/LongChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ShortChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ShortChecker.java similarity index 95% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ShortChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ShortChecker.java index d125e3b..96fd860 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/ShortChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/ShortChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/StringChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/StringChecker.java similarity index 97% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/StringChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/StringChecker.java index 4815e32..589df8f 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/StringChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/StringChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeChecker.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeChecker.java similarity index 91% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeChecker.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeChecker.java index f467bfb..6348a47 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeChecker.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeChecker.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeCheckerProducer.java b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeCheckerProducer.java similarity index 91% rename from G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeCheckerProducer.java rename to G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeCheckerProducer.java index 11255a7..33d7d43 100644 --- a/G-Earth/src/main/java/gearth/misc/packetrepresentation/prediction/checkers/TypeCheckerProducer.java +++ b/G-Earth/src/main/java/gearth/services/packetrepresentation/prediction/checkers/TypeCheckerProducer.java @@ -1,4 +1,4 @@ -package gearth.misc.packetrepresentation.prediction.checkers; +package gearth.services.packetrepresentation.prediction.checkers; import gearth.protocol.HPacket; diff --git a/G-Earth/src/main/java/gearth/ui/GEarthController.java b/G-Earth/src/main/java/gearth/ui/GEarthController.java index 43a7f45..c405c42 100644 --- a/G-Earth/src/main/java/gearth/ui/GEarthController.java +++ b/G-Earth/src/main/java/gearth/ui/GEarthController.java @@ -87,7 +87,11 @@ public class GEarthController { private void trySetController() { if (++initcount == 2) { GEarthController self = this; - tabs.forEach(subForm -> subForm.setParentController(self)); + + extensionsController.setParentController(self); + tabs.forEach(subForm -> { + if (subForm != extensionsController) subForm.setParentController(self); + }); } } diff --git a/G-Earth/src/main/java/gearth/ui/connection/ConnectionController.java b/G-Earth/src/main/java/gearth/ui/connection/ConnectionController.java index e7b1f22..72d1a34 100644 --- a/G-Earth/src/main/java/gearth/ui/connection/ConnectionController.java +++ b/G-Earth/src/main/java/gearth/ui/connection/ConnectionController.java @@ -116,7 +116,11 @@ public class ConnectionController extends SubForm { } } + + private void updateInputUI() { + if (parentController == null) return; + grd_clientSelection.setDisable(getHConnection().getState() != HState.NOT_CONNECTED); txtfield_hotelversion.setText(getHConnection().getHotelVersion()); @@ -184,6 +188,8 @@ public class ConnectionController extends SubForm { } })); + + Platform.runLater(this::updateInputUI); } public void btnConnect_clicked(ActionEvent actionEvent) { 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 43b1158..12729b5 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,6 @@ package gearth.ui.extensions; +import gearth.services.extensionhandler.extensions.ExtensionType; import gearth.services.extensionhandler.extensions.GEarthExtension; import javafx.application.Platform; import javafx.event.EventHandler; @@ -142,6 +143,11 @@ public class ExtensionItemContainer extends GridPane { parent.getChildren().add(this); + if (item.extensionType() == ExtensionType.INTERNAL) { + setBackground(new Background(new BackgroundFill(Paint.valueOf("F0FFFF"), CornerRadii.EMPTY, Insets.EMPTY))); + } + + initExtension(); } diff --git a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java index 813599f..214acea 100644 --- a/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java +++ b/G-Earth/src/main/java/gearth/ui/extensions/ExtensionsController.java @@ -133,4 +133,8 @@ public class ExtensionsController extends SubForm { Platform.runLater(this::updateGPythonStatus); }); } + + public ExtensionHandler getExtensionHandler() { + return extensionHandler; + } } diff --git a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java index f21abc3..ee0ca80 100644 --- a/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java +++ b/G-Earth/src/main/java/gearth/ui/extra/ExtraController.java @@ -2,12 +2,10 @@ package gearth.ui.extra; import gearth.Main; import gearth.misc.Cacher; -import gearth.misc.packetrepresentation.prediction.checkers.TypeCheckerProducer; import gearth.protocol.HConnection; import gearth.protocol.connection.HState; import gearth.protocol.connection.proxy.ProxyProviderFactory; import gearth.protocol.connection.proxy.SocksConfiguration; -import gearth.services.Constants; import gearth.services.gpython.GPythonVersionUtils; import gearth.ui.SubForm; import gearth.ui.info.InfoController; diff --git a/G-Earth/src/main/java/gearth/ui/injection/InjectedPackets.java b/G-Earth/src/main/java/gearth/ui/injection/InjectedPackets.java new file mode 100644 index 0000000..df30cbc --- /dev/null +++ b/G-Earth/src/main/java/gearth/ui/injection/InjectedPackets.java @@ -0,0 +1,85 @@ +package gearth.ui.injection; + +import gearth.misc.StringifyAble; +import gearth.protocol.HMessage; +import gearth.protocol.HPacket; +import gearth.services.packet_info.PacketInfo; +import gearth.services.packet_info.PacketInfoManager; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +public class InjectedPackets implements StringifyAble { + + private String packetsAsString; + private String description; + + public InjectedPackets(String packetsAsString, int amountPackets, PacketInfoManager packetInfoManager, HMessage.Direction direction) { + String description; + if (amountPackets > 1) { + description = String.format("(packets: %d, length: %d)", amountPackets, packetsAsString.length()); + } + else { // assume 1 packet + HPacket packet = new HPacket(packetsAsString); + String identifier = null; + if (!packet.isPacketComplete()) { + identifier = packet.getIdentifier(); + } + else { + Optional maybeInfo = packetInfoManager.getAllPacketInfoFromHeaderId(direction, packet.headerId()) + .stream().filter(packetInfo -> packetInfo.getName() != null).findFirst(); + if (maybeInfo.isPresent()) { + PacketInfo packetInfo = maybeInfo.get(); + identifier = packetInfo.getName(); + } + } + + if (identifier != null) { + description = String.format("%s", identifier); + } + else { + description = String.format("(id: %d, length: %d)", packet.headerId(), packet.length()); + } + } + + this.description = description; + this.packetsAsString = packetsAsString; + } + + public InjectedPackets(String fromString) { + constructFromString(fromString); + } + + public String getPacketsAsString() { + return packetsAsString; + } + + public String getDescription() { + return description; + } + + @Override + public String stringify() { + Map info = new HashMap<>(); + info.put("packetsAsString", packetsAsString); + info.put("description", description); + + return new JSONObject(info).toString(); + } + + @Override + public void constructFromString(String str) { + JSONObject jsonObject = new JSONObject(str); + this.packetsAsString = jsonObject.getString("packetsAsString"); + this.description = jsonObject.getString("description"); + } + + @Override + public String toString() { + return description; + } +} diff --git a/G-Earth/src/main/java/gearth/ui/injection/InjectionController.java b/G-Earth/src/main/java/gearth/ui/injection/InjectionController.java index 7cc3e52..91168fe 100644 --- a/G-Earth/src/main/java/gearth/ui/injection/InjectionController.java +++ b/G-Earth/src/main/java/gearth/ui/injection/InjectionController.java @@ -1,25 +1,42 @@ package gearth.ui.injection; +import gearth.misc.Cacher; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; import gearth.protocol.connection.HState; import javafx.application.Platform; import javafx.event.ActionEvent; -import javafx.scene.control.Button; -import javafx.scene.control.TextArea; +import javafx.event.EventHandler; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import javafx.scene.paint.Paint; import javafx.scene.text.Text; -import gearth.protocol.HConnection; import gearth.protocol.HPacket; import gearth.ui.SubForm; +import sun.misc.Cache; +import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; public class InjectionController extends SubForm { + + private static final String HISTORY_CACHE_KEY = "INJECTED_HISTORY"; + private static final int historylimit = 69; + public TextArea inputPacket; public Text lbl_corrruption; public Text lbl_pcktInfo; public Button btn_sendToServer; public Button btn_sendToClient; + public ListView history; + public Label lblHistory; protected void onParentSet() { getHConnection().getStateObservable().addListener((oldState, newState) -> Platform.runLater(this::updateUI)); @@ -27,6 +44,29 @@ public class InjectionController extends SubForm { inputPacket.textProperty().addListener(event -> Platform.runLater(this::updateUI)); } + public void initialize() { + history.setOnMouseClicked(event -> { + if(event.getButton().equals(MouseButton.PRIMARY) && event.getClickCount() == 2) { + InjectedPackets injectedPackets = history.getSelectionModel().getSelectedItem(); + if (injectedPackets != null) { + Platform.runLater(() -> { + inputPacket.setText(injectedPackets.getPacketsAsString()); + updateUI(); + }); + } + } + }); + + lblHistory.setTooltip(new Tooltip("Double click a packet to restore it")); + + List rawHistory = Cacher.getList(HISTORY_CACHE_KEY); + if (rawHistory != null) { + List history = rawHistory.stream() + .map(o -> (String)o).limit(historylimit - 1).map(InjectedPackets::new).collect(Collectors.toList()); + updateHistoryView(history); + } + } + private static boolean isPacketIncomplete(String line) { boolean unmatchedBrace = false; @@ -95,9 +135,38 @@ public class InjectionController extends SubForm { } if (!dirty) { - btn_sendToClient.setDisable(getHConnection().getState() != HState.CONNECTED); - btn_sendToServer.setDisable(getHConnection().getState() != HState.CONNECTED); + PacketInfoManager packetInfoManager = getHConnection().getPacketInfoManager(); + + List unIdentifiedPackets = Arrays.stream(packets) + .filter(hPacket -> !hPacket.isPacketComplete()) + .map(HPacket::getIdentifier).collect(Collectors.toList()); + + boolean canSendToClient = unIdentifiedPackets.stream().allMatch(s -> { + if (packetInfoManager == null) return false; + return packetInfoManager.getPacketInfoFromHash(HMessage.Direction.TOCLIENT, s) != null || + packetInfoManager.getPacketInfoFromName(HMessage.Direction.TOCLIENT, s) != null; + }); + boolean canSendToServer = unIdentifiedPackets.stream().allMatch(s -> { + if (packetInfoManager == null) return false; + return packetInfoManager.getPacketInfoFromHash(HMessage.Direction.TOSERVER, s) != null || + packetInfoManager.getPacketInfoFromName(HMessage.Direction.TOSERVER, s) != null; + }); + + btn_sendToClient.setDisable(!canSendToClient || getHConnection().getState() != HState.CONNECTED); + btn_sendToServer.setDisable(!canSendToServer || getHConnection().getState() != HState.CONNECTED); if (packets.length == 1) { + + // complete packet to show correct headerId + if (!packets[0].isPacketComplete()) { + HPacket packet = packets[0]; + if (packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && !packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOCLIENT, packetInfoManager); + } + else if (!packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOSERVER, packetInfoManager); + } + } + lbl_pcktInfo.setText("header (id:" + packets[0].headerId() + ", length:" + packets[0].length() + ")"); } @@ -124,6 +193,8 @@ public class InjectionController extends SubForm { getHConnection().sendToServerAsync(packet); writeToLog(Color.BLUE, "SS -> packet with id: " + packet.headerId()); } + + addToHistory(packets, inputPacket.getText(), HMessage.Direction.TOSERVER); } public void sendToClient_clicked(ActionEvent actionEvent) { @@ -132,6 +203,40 @@ public class InjectionController extends SubForm { getHConnection().sendToClientAsync(packet); writeToLog(Color.RED, "CS -> packet with id: " + packet.headerId()); } + + addToHistory(packets, inputPacket.getText(), HMessage.Direction.TOCLIENT); + } + + private void addToHistory(HPacket[] packets, String packetsAsString, HMessage.Direction direction) { + InjectedPackets injectedPackets = new InjectedPackets(packetsAsString, packets.length, getHConnection().getPacketInfoManager(), direction); + + List newHistory = new ArrayList<>(); + newHistory.add(injectedPackets); + + List rawOldHistory = Cacher.getList(HISTORY_CACHE_KEY); + if (rawOldHistory != null) { + List history = rawOldHistory.stream() + .map(o -> (String)o).limit(historylimit - 1).map(InjectedPackets::new).collect(Collectors.toList()); + + // dont add to history if its equal to the latest added packet + if (history.size() != 0 && history.get(0).getPacketsAsString().equals(injectedPackets.getPacketsAsString())) { + return; + } + + newHistory.addAll(history); + } + + List historyAsStrings = newHistory.stream().map(InjectedPackets::stringify).collect(Collectors.toList()); + Cacher.put(HISTORY_CACHE_KEY, historyAsStrings); + + updateHistoryView(newHistory); + } + + private void updateHistoryView(List allHistoryItems) { + Platform.runLater(() -> { + history.getItems().clear(); + history.getItems().addAll(allHistoryItems); + }); } diff --git a/G-Earth/src/main/java/gearth/ui/logger/LoggerController.java b/G-Earth/src/main/java/gearth/ui/logger/LoggerController.java index 1f76d32..905cf1d 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/LoggerController.java +++ b/G-Earth/src/main/java/gearth/ui/logger/LoggerController.java @@ -1,6 +1,8 @@ package gearth.ui.logger; +import gearth.extensions.parsers.HDirection; import gearth.protocol.connection.HState; +import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducer; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.scene.control.Button; @@ -17,6 +19,7 @@ import gearth.ui.logger.loggerdisplays.PacketLogger; import gearth.ui.logger.loggerdisplays.PacketLoggerFactory; import java.util.Calendar; +import java.util.function.Predicate; public class LoggerController extends SubForm { @@ -33,16 +36,20 @@ public class LoggerController extends SubForm { private int packetLimit = 8000; - private PacketLogger packetLogger = PacketLoggerFactory.get(); + private PacketLoggerFactory packetLoggerFactory; + private PacketLogger packetLogger; public void onParentSet(){ + packetLoggerFactory = new PacketLoggerFactory(parentController.extensionsController.getExtensionHandler()); + packetLogger = packetLoggerFactory.get(); + getHConnection().getStateObservable().addListener((oldState, newState) -> Platform.runLater(() -> { if (newState == HState.PREPARING) { miniLogText(Color.ORANGE, "Connecting to "+getHConnection().getDomain() + ":" + getHConnection().getServerPort()); } if (newState == HState.CONNECTED) { miniLogText(Color.GREEN, "Connected to "+getHConnection().getDomain() + ":" + getHConnection().getServerPort()); - packetLogger.start(); + packetLogger.start(getHConnection()); } if (newState == HState.NOT_CONNECTED) { miniLogText(Color.RED, "End of connection"); @@ -69,7 +76,7 @@ public class LoggerController extends SubForm { packetLogger.appendMessage(message.getPacket(), types); if (cbx_showstruct.isSelected() && message.getPacket().length() < packetLimit) { - packetLogger.appendStructure(message.getPacket()); + packetLogger.appendStructure(message.getPacket(), message.getDestination()); } }); }); diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/LinuxTerminalLogger.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/LinuxTerminalLogger.java index a39b950..0b125a5 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/LinuxTerminalLogger.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/LinuxTerminalLogger.java @@ -1,5 +1,6 @@ package gearth.ui.logger.loggerdisplays; +import gearth.protocol.HMessage; import gearth.protocol.HPacket; import java.util.HashMap; @@ -60,8 +61,8 @@ class LinuxTerminalLogger extends SimpleTerminalLogger { } @Override - public void appendStructure(HPacket packet) { - String expr = packet.toExpression(); + public void appendStructure(HPacket packet, HMessage.Direction direction) { + String expr = packet.toExpression(direction, packetInfoManager, true); if (!expr.equals("")) { System.out.println( colorizePackets.get("EXPRESSION") + diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLogger.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLogger.java index 31dfc58..a2e1770 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLogger.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLogger.java @@ -1,5 +1,7 @@ package gearth.ui.logger.loggerdisplays; +import gearth.protocol.HConnection; +import gearth.protocol.HMessage; import gearth.protocol.HPacket; /** @@ -27,11 +29,11 @@ public interface PacketLogger { } } - void start(); + void start(HConnection hConnection); void stop(); void appendSplitLine(); void appendMessage(HPacket packet, int types); - void appendStructure(HPacket packet); + void appendStructure(HPacket packet, HMessage.Direction direction); } diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLoggerFactory.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLoggerFactory.java index 86c5994..cc1b41b 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLoggerFactory.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/PacketLoggerFactory.java @@ -1,21 +1,33 @@ package gearth.ui.logger.loggerdisplays; import gearth.Main; +import gearth.extensions.InternalExtensionFormBuilder; import gearth.misc.OSValidator; -import gearth.ui.logger.loggerdisplays.uilogger.UiLogger; +import gearth.services.extensionhandler.ExtensionHandler; +import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducer; +import gearth.services.extensionhandler.extensions.extensionproducers.ExtensionProducerObserver; +import gearth.services.internal_extensions.uilogger.UiLogger; /** * Created by Jonas on 04/04/18. */ -public class PacketLoggerFactory { +public class PacketLoggerFactory implements ExtensionProducer { + + private UiLogger uiLogger; public static boolean usesUIlogger() { return (!Main.hasFlag("-t")); } - public static PacketLogger get() { + public PacketLoggerFactory(ExtensionHandler handler) { + handler.addExtensionProducer(this); + } + + + public PacketLogger get() { if (usesUIlogger()) { - return new UiLogger(); +// return new UiLogger(); //now an extension + return uiLogger; } if (OSValidator.isUnix()) { @@ -24,4 +36,11 @@ public class PacketLoggerFactory { return new SimpleTerminalLogger(); } + @Override + public void startProducing(ExtensionProducerObserver observer) { + if (usesUIlogger()) { + uiLogger = new InternalExtensionFormBuilder() + .launch(UiLogger.class, observer); + } + } } diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/SimpleTerminalLogger.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/SimpleTerminalLogger.java index 70e5ccd..b8aaf4e 100644 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/SimpleTerminalLogger.java +++ b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/SimpleTerminalLogger.java @@ -1,13 +1,20 @@ package gearth.ui.logger.loggerdisplays; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HConnection; +import gearth.protocol.HMessage; import gearth.protocol.HPacket; /** * Created by Jonas on 04/04/18. */ class SimpleTerminalLogger implements PacketLogger { + + protected PacketInfoManager packetInfoManager = null; + @Override - public void start() { + public void start(HConnection hConnection) { + packetInfoManager = hConnection.getPacketInfoManager(); // System.out.println("-- START OF SESSION --"); } @@ -53,8 +60,8 @@ class SimpleTerminalLogger implements PacketLogger { } @Override - public void appendStructure(HPacket packet) { - String expr = packet.toExpression(); + public void appendStructure(HPacket packet, HMessage.Direction direction) { + String expr = packet.toExpression(direction, packetInfoManager, true); if (!expr.equals("")) { System.out.println(expr); } diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLogger.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLogger.java deleted file mode 100644 index 84eadef..0000000 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLogger.java +++ /dev/null @@ -1,116 +0,0 @@ -package gearth.ui.logger.loggerdisplays.uilogger; - -import gearth.protocol.HPacket; -import gearth.ui.logger.loggerdisplays.PacketLogger; -import javafx.event.ActionEvent; -import javafx.event.Event; -import javafx.fxml.FXMLLoader; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.image.Image; -import javafx.stage.Modality; -import javafx.stage.Stage; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -public class UiLogger implements PacketLogger { - private Stage stage; - private UiLoggerController controller = null; - - @Override - public void start() { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/gearth/ui/logger/uilogger/UiLogger.fxml")); - - try { - Parent root = loader.load(); - synchronized (appendLater) { - controller = loader.getController(); - for (Elem elem : appendLater) { - controller.appendMessage(elem.packet, elem.types); - } - appendLater.clear(); - } - - stage = new Stage(); - stage.setTitle("G-Earth | Packet Logger"); - stage.initModality(Modality.NONE); - stage.getIcons().add(new Image(getClass().getResourceAsStream("/gearth/G-EarthLogoSmaller.png"))); - - Scene scene = new Scene(root); - scene.getStylesheets().add("/gearth/ui/bootstrap3.css"); - scene.getStylesheets().add("/gearth/ui/logger/uilogger/logger.css"); - UiLoggerController controller = (UiLoggerController) loader.getController(); - controller.setStage(stage); - -// scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler() { -// final KeyCombination keyCombIncoming = new KeyCodeCombination(KeyCode.I, -// KeyCombination.CONTROL_DOWN); -// final KeyCombination keyCombOutgoing = new KeyCodeCombination(KeyCode.O, -// KeyCombination.CONTROL_DOWN); -// -// public void handle(KeyEvent ke) { -// if (keyCombIncoming.match(ke)) { -// controller.toggleViewIncoming(); -// ke.consume(); -// } else if (keyCombOutgoing.match(ke)) { -// controller.toggleViewOutgoing(); -// ke.consume(); -// } -// } -// }); - - stage.setScene(scene); - -// ScenicView.show(scene); - - // don't let the user close this window on their own - stage.setOnCloseRequest(Event::consume); - - stage.show(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - @Override - public void stop() { - if (stage != null) - stage.close(); - } - - @Override - public void appendSplitLine() { - // don't use this, we can't discern incoming/outgoing - //Platform.runLater(() -> controller.appendSplitLine()); - } - - private class Elem { - HPacket packet; - int types; - Elem(HPacket packet, int types) { - this.packet = packet; - this.types = types; - } - } - - private final List appendLater = new ArrayList<>(); - - @Override - public void appendMessage(HPacket packet, int types) { - synchronized (appendLater) { - if (controller == null) { - appendLater.add(new Elem(packet, types)); - } - else { - controller.appendMessage(packet, types); - } - } - } - - @Override - public void appendStructure(HPacket packet) { - - } -} diff --git a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java b/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java deleted file mode 100644 index 1422f47..0000000 --- a/G-Earth/src/main/java/gearth/ui/logger/loggerdisplays/uilogger/UiLoggerController.java +++ /dev/null @@ -1,239 +0,0 @@ -package gearth.ui.logger.loggerdisplays.uilogger; - -import gearth.misc.harble_api.HarbleAPI; -import gearth.misc.harble_api.HarbleAPIFetcher; -import gearth.protocol.HMessage; -import gearth.protocol.HPacket; -import gearth.ui.logger.loggerdisplays.PacketLogger; -import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.Initializable; -import javafx.scene.control.CheckMenuItem; -import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.FlowPane; -import javafx.stage.Stage; -import org.fxmisc.flowless.VirtualizedScrollPane; -import org.fxmisc.richtext.StyleClassedTextArea; -import org.fxmisc.richtext.model.StyleSpansBuilder; - -import java.net.URL; -import java.util.*; - -public class UiLoggerController implements Initializable { - public FlowPane flowPane; - public BorderPane borderPane; - public Label lblViewIncoming; - public Label lblViewOutgoing; - public CheckMenuItem chkViewIncoming; - public CheckMenuItem chkViewOutgoing; - public CheckMenuItem chkDisplayStructure; - public Label lblAutoScrolll; - public CheckMenuItem chkAutoscroll; - public CheckMenuItem chkSkipBigPackets; - public CheckMenuItem chkMessageName; - public CheckMenuItem chkMessageHash; - public Label lblHarbleAPI; - - private StyleClassedTextArea area; - - private Stage stage; - - private boolean viewIncoming = true; - private boolean viewOutgoing = true; - private boolean displayStructure = true; - private boolean autoScroll = true; - private boolean skiphugepackets = true; - private boolean viewMessageName = true; - private boolean viewMessageHash = false; - private boolean alwaysOnTop = false; - - private volatile boolean initialized = false; - private final List appendLater = new ArrayList<>(); - - @Override - public void initialize(URL arg0, ResourceBundle arg1) { - area = new StyleClassedTextArea(); - area.getStyleClass().add("dark"); - area.setWrapText(true); - - VirtualizedScrollPane vsPane = new VirtualizedScrollPane<>(area); - borderPane.setCenter(vsPane); - - synchronized (appendLater) { - initialized = true; - if (!appendLater.isEmpty()) { - appendLog(appendLater); - appendLater.clear(); - } - } - } - - private static String cleanTextContent(String text) { -// // strips off all non-ASCII characters -// text = text.replaceAll("[^\\x00-\\x7F]", ""); -// -// // erases all the ASCII control characters - text = text.replaceAll("[\\p{Cntrl}&&[^\n\t]]", ""); - - // removes non-printable characters from Unicode -// text = text.replaceAll("\\p{C}", ""); - - return text.trim(); - } - - public void appendMessage(HPacket packet, int types) { - boolean isBlocked = (types & PacketLogger.MESSAGE_TYPE.BLOCKED.getValue()) != 0; - boolean isReplaced = (types & PacketLogger.MESSAGE_TYPE.REPLACED.getValue()) != 0; - boolean isIncoming = (types & PacketLogger.MESSAGE_TYPE.INCOMING.getValue()) != 0; - - if (isIncoming && !viewIncoming) return; - if (!isIncoming && !viewOutgoing) return; - - ArrayList elements = new ArrayList<>(); - - lblHarbleAPI.setText("Messages: " + (HarbleAPIFetcher.HARBLEAPI == null ? "False" : "True")); - if ((viewMessageName || viewMessageHash) && HarbleAPIFetcher.HARBLEAPI != null) { - HarbleAPI api = HarbleAPIFetcher.HARBLEAPI; - HarbleAPI.HarbleMessage message = api.getHarbleMessageFromHeaderId( - (isIncoming ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER), - packet.headerId() - ); - - if ( message != null && !(viewMessageName && !viewMessageHash && message.getName() == null)) { - if (viewMessageName && message.getName() != null) { - elements.add(new Element("["+message.getName()+"]", "messageinfo")); - } - if (viewMessageHash) { - elements.add(new Element("["+message.getHash()+"]", "messageinfo")); - } - elements.add(new Element("\n", "")); - } - } - - if (isBlocked) elements.add(new Element("[Blocked]\n", "blocked")); - else if (isReplaced) elements.add(new Element("[Replaced]\n", "replaced")); - - if (isIncoming) { - // handle skipped eventually - elements.add(new Element("Incoming[", "incoming")); - elements.add(new Element(String.valueOf(packet.headerId()), "")); - elements.add(new Element("]", "incoming")); - - elements.add(new Element(" <- ", "")); - if (skiphugepackets && packet.length() > 4000) { - elements.add(new Element("", "skipped")); - } - else { - elements.add(new Element(packet.toString(), "incoming")); - } - } else { - elements.add(new Element("Outgoing[", "outgoing")); - elements.add(new Element(String.valueOf(packet.headerId()), "")); - elements.add(new Element("]", "outgoing")); - - elements.add(new Element(" -> ", "")); - - if (skiphugepackets && packet.length() > 8000) { - elements.add(new Element("", "skipped")); - } - else { - elements.add(new Element(packet.toString(), "outgoing")); - } - } - - if (packet.length() <= 2000) { - String expr = packet.toExpression(isIncoming ? HMessage.Direction.TOCLIENT : HMessage.Direction.TOSERVER); - String cleaned = cleanTextContent(expr); - if (cleaned.equals(expr)) { - if (!expr.equals("") && displayStructure) - elements.add(new Element("\n" + cleanTextContent(expr), "structure")); - } - } - - - elements.add(new Element("\n--------------------\n", "")); - - synchronized (appendLater) { - if (initialized) { - appendLog(elements); - } - else { - appendLater.addAll(elements); - } - } - - } - - private synchronized void appendLog(List elements) { - Platform.runLater(() -> { - StringBuilder sb = new StringBuilder(); - StyleSpansBuilder> styleSpansBuilder = new StyleSpansBuilder<>(0); - - for (Element element : elements) { - sb.append(element.text); - - styleSpansBuilder.add(Collections.singleton(element.className), element.text.length()); - } - - int oldLen = area.getLength(); - area.appendText(sb.toString()); -// System.out.println(sb.toString()); - area.setStyleSpans(oldLen, styleSpansBuilder.create()); - - if (autoScroll) { -// area.moveTo(area.getLength()); - area.requestFollowCaret(); - } - }); - } - - public void setStage(Stage stage) { - this.stage = stage; - } - - public void toggleViewIncoming() { - viewIncoming = !viewIncoming; - lblViewIncoming.setText("View Incoming: " + (viewIncoming ? "True" : "False")); -// chkViewIncoming.setSelected(viewIncoming); - } - - public void toggleViewOutgoing() { - viewOutgoing = !viewOutgoing; - lblViewOutgoing.setText("View Outgoing: " + (viewOutgoing ? "True" : "False")); -// chkViewOutgoing.setSelected(viewOutgoing); - } - - public void toggleDisplayStructure() { - displayStructure = !displayStructure; -// chkDisplayStructure.setSelected(displayStructure); - } - - public void toggleAutoscroll(ActionEvent actionEvent) { - autoScroll = !autoScroll; - lblAutoScrolll.setText("Autoscroll: " + (autoScroll ? "True" : "False")); - } - - public void toggleSkipPackets(ActionEvent actionEvent) { - skiphugepackets = !skiphugepackets; - } - - public void toggleMessageName(ActionEvent actionEvent) { - viewMessageName = !viewMessageName; - } - - public void toggleMessageHash(ActionEvent actionEvent) { - viewMessageHash = !viewMessageHash; - } - - public void toggleAlwaysOnTop(ActionEvent actionEvent) { - stage.setAlwaysOnTop(!alwaysOnTop); - alwaysOnTop = !alwaysOnTop; - } - - public void clearText(ActionEvent actionEvent) { - area.clear(); - } -} diff --git a/G-Earth/src/main/java/gearth/ui/scheduler/InteractableScheduleItem.java b/G-Earth/src/main/java/gearth/ui/scheduler/InteractableScheduleItem.java index 1af3246..d663145 100644 --- a/G-Earth/src/main/java/gearth/ui/scheduler/InteractableScheduleItem.java +++ b/G-Earth/src/main/java/gearth/ui/scheduler/InteractableScheduleItem.java @@ -72,8 +72,8 @@ public class InteractableScheduleItem extends ScheduleItem implements StringifyA .append("\t") .append(getDelayProperty().get().toString()) .append("\t") - .append(getPacketProperty().get().toString()) - .append("\t") +// .append(getPacketProperty().get().toString()) +// .append("\t") .append(getDestinationProperty().get().name()) .append("\t") .append(getPacketAsStringProperty().get()); @@ -87,9 +87,10 @@ public class InteractableScheduleItem extends ScheduleItem implements StringifyA int index = Integer.parseInt(parts[0]); boolean paused = parts[1].equals("true"); Interval delay = new Interval(parts[2]); - HPacket packet = new HPacket(parts[3]); - HMessage.Direction direction = parts[4].equals(HMessage.Direction.TOSERVER.name()) ? HMessage.Direction.TOSERVER : HMessage.Direction.TOCLIENT; - String packetAsString = parts[5]; +// HPacket packet = new HPacket(parts[3]); + HMessage.Direction direction = parts[3].equals(HMessage.Direction.TOSERVER.name()) ? HMessage.Direction.TOSERVER : HMessage.Direction.TOCLIENT; + String packetAsString = parts[4]; + HPacket packet = new HPacket(packetAsString); construct(index, paused, delay, packet, direction); this.packetAsStringProperty = new SimpleStringProperty(packetAsString); diff --git a/G-Earth/src/main/java/gearth/ui/scheduler/SchedulerController.java b/G-Earth/src/main/java/gearth/ui/scheduler/SchedulerController.java index b9d9cb2..8972996 100644 --- a/G-Earth/src/main/java/gearth/ui/scheduler/SchedulerController.java +++ b/G-Earth/src/main/java/gearth/ui/scheduler/SchedulerController.java @@ -216,7 +216,6 @@ public class SchedulerController extends SubForm { File file = fileChooser.showSaveDialog(parentController.getStage()); if(file != null){ - try { FileWriter fileWriter = new FileWriter(file); BufferedWriter out = new BufferedWriter(fileWriter); diff --git a/G-Earth/src/main/java/gearth/ui/tools/ToolsController.java b/G-Earth/src/main/java/gearth/ui/tools/ToolsController.java index ef62772..f6aeab4 100644 --- a/G-Earth/src/main/java/gearth/ui/tools/ToolsController.java +++ b/G-Earth/src/main/java/gearth/ui/tools/ToolsController.java @@ -1,5 +1,7 @@ package gearth.ui.tools; +import gearth.services.packet_info.PacketInfoManager; +import gearth.protocol.HMessage; import javafx.event.ActionEvent; import javafx.scene.control.Button; import javafx.scene.control.TextArea; @@ -138,11 +140,26 @@ public class ToolsController extends SubForm { } + private HPacket parseToPacket(String p) { + PacketInfoManager packetInfoManager = getHConnection().getPacketInfoManager(); + HPacket packet = new HPacket(p); + if (!packet.isPacketComplete() && packetInfoManager != null) { + if (packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && !packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOCLIENT, packetInfoManager); + } + else if (!packet.canComplete(HMessage.Direction.TOCLIENT, packetInfoManager) && packet.canComplete(HMessage.Direction.TOSERVER, packetInfoManager)) { + packet.completePacket(HMessage.Direction.TOSERVER, packetInfoManager); + } + } + + return packet; + } + public void btn_toExpr_clicked(ActionEvent actionEvent) { - txt_exprArea.setText(new HPacket(txt_packetArea.getText()).toExpression()); + txt_exprArea.setText(parseToPacket(txt_packetArea.getText()).toExpression(getHConnection().getPacketInfoManager(), true)); } public void btn_toPacket_clicked(ActionEvent actionEvent) { - txt_packetArea.setText(new HPacket(txt_exprArea.getText()).toString()); + txt_packetArea.setText(parseToPacket(txt_exprArea.getText()).toString()); } } diff --git a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/blockreplace.fxml b/G-Earth/src/main/resources/gearth/services/internal_extensions/blockreplacepackets/blockreplace.fxml similarity index 94% rename from Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/blockreplace.fxml rename to G-Earth/src/main/resources/gearth/services/internal_extensions/blockreplacepackets/blockreplace.fxml index 5df9e8a..0e135cf 100644 --- a/Extensions/BlockReplacePackets/src/main/java/extensions/blockreplacepackets/blockreplace.fxml +++ b/G-Earth/src/main/resources/gearth/services/internal_extensions/blockreplacepackets/blockreplace.fxml @@ -1,17 +1,9 @@ - - - - - - - - - - - - + + + + diff --git a/G-Earth/src/main/resources/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.fxml b/G-Earth/src/main/resources/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.fxml new file mode 100644 index 0000000..966234a --- /dev/null +++ b/G-Earth/src/main/resources/gearth/services/internal_extensions/packetinfoexplorer/PacketInfoExplorer.fxml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/G-Earth/src/main/resources/gearth/services/internal_extensions/uilogger/UiLogger.fxml b/G-Earth/src/main/resources/gearth/services/internal_extensions/uilogger/UiLogger.fxml new file mode 100644 index 0000000..a79e878 --- /dev/null +++ b/G-Earth/src/main/resources/gearth/services/internal_extensions/uilogger/UiLogger.fxml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css b/G-Earth/src/main/resources/gearth/services/internal_extensions/uilogger/logger.css similarity index 100% rename from G-Earth/src/main/resources/gearth/ui/logger/uilogger/logger.css rename to G-Earth/src/main/resources/gearth/services/internal_extensions/uilogger/logger.css diff --git a/G-Earth/src/main/resources/gearth/ui/connection/Connection.fxml b/G-Earth/src/main/resources/gearth/ui/connection/Connection.fxml index 78a27b1..96b87e5 100644 --- a/G-Earth/src/main/resources/gearth/ui/connection/Connection.fxml +++ b/G-Earth/src/main/resources/gearth/ui/connection/Connection.fxml @@ -1,19 +1,11 @@ - - - - - - - - - - - - + + + + - + @@ -148,7 +140,7 @@ - diff --git a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/UiLogger.fxml b/G-Earth/src/main/resources/gearth/ui/logger/uilogger/UiLogger.fxml deleted file mode 100644 index 1299ca7..0000000 --- a/G-Earth/src/main/resources/gearth/ui/logger/uilogger/UiLogger.fxml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index ca40e40..3535219 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ G-Earth G-Earth-Parent pom - 1.3.4 + 1.4 G-Earth-Parent https://github.com/sirjonasxx/G-Earth @@ -18,7 +18,6 @@ Extensions/AdminOnConnect - Extensions/BlockReplacePackets