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