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.HMessage;
|
||||||
import gearth.protocol.HPacket;
|
import gearth.protocol.HPacket;
|
||||||
|
import gearth.services.extensionserver.ExtensionServer;
|
||||||
|
import gearth.services.extensionserver.extensions.network.NetworkExtensionInfo;
|
||||||
import gearth.ui.extensions.Extensions;
|
import gearth.ui.extensions.Extensions;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -118,10 +120,10 @@ public abstract class Extension implements IExtension{
|
|||||||
packet.fixLength();
|
packet.fixLength();
|
||||||
|
|
||||||
|
|
||||||
if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INFOREQUEST) {
|
if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INFOREQUEST) {
|
||||||
ExtensionInfo info = getInfoAnnotations();
|
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())
|
response.appendString(info.Title())
|
||||||
.appendString(info.Author())
|
.appendString(info.Author())
|
||||||
.appendString(info.Version())
|
.appendString(info.Version())
|
||||||
@ -134,7 +136,7 @@ public abstract class Extension implements IExtension{
|
|||||||
.appendBoolean(canDelete);
|
.appendBoolean(canDelete);
|
||||||
writeToStream(response.toBytes());
|
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();
|
String host = packet.readString();
|
||||||
int connectionPort = packet.readInteger();
|
int connectionPort = packet.readInteger();
|
||||||
String hotelVersion = packet.readString();
|
String hotelVersion = packet.readString();
|
||||||
@ -142,10 +144,10 @@ public abstract class Extension implements IExtension{
|
|||||||
notifyConnectionListeners(host, connectionPort, hotelVersion, harbleMessagesPath);
|
notifyConnectionListeners(host, connectionPort, hotelVersion, harbleMessagesPath);
|
||||||
onStartConnection();
|
onStartConnection();
|
||||||
}
|
}
|
||||||
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.CONNECTIONEND) {
|
else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.CONNECTIONEND) {
|
||||||
onEndConnection();
|
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
|
// body = an array of G-Earths gearth flags
|
||||||
if (flagRequestCallback != null) {
|
if (flagRequestCallback != null) {
|
||||||
int arraysize = packet.readInteger();
|
int arraysize = packet.readInteger();
|
||||||
@ -157,16 +159,13 @@ public abstract class Extension implements IExtension{
|
|||||||
}
|
}
|
||||||
flagRequestCallback = null;
|
flagRequestCallback = null;
|
||||||
}
|
}
|
||||||
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INIT) {
|
else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.INIT) {
|
||||||
initExtension();
|
initExtension();
|
||||||
}
|
}
|
||||||
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.FREEFLOW) {
|
else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK) {
|
||||||
// nothing to be done yet
|
|
||||||
}
|
|
||||||
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK) {
|
|
||||||
onClick();
|
onClick();
|
||||||
}
|
}
|
||||||
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) {
|
else if (packet.headerId() == NetworkExtensionInfo.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) {
|
||||||
String stringifiedMessage = packet.readLongString();
|
String stringifiedMessage = packet.readLongString();
|
||||||
HMessage habboMessage = new HMessage(stringifiedMessage);
|
HMessage habboMessage = new HMessage(stringifiedMessage);
|
||||||
HPacket habboPacket = habboMessage.getPacket();
|
HPacket habboPacket = habboMessage.getPacket();
|
||||||
@ -200,7 +199,7 @@ public abstract class Extension implements IExtension{
|
|||||||
}
|
}
|
||||||
habboMessage.getPacket().resetReadIndex();
|
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());
|
response.appendLongString(habboMessage.stringify());
|
||||||
|
|
||||||
writeToStream(response.toBytes());
|
writeToStream(response.toBytes());
|
||||||
@ -247,7 +246,7 @@ public abstract class Extension implements IExtension{
|
|||||||
return send(packet, HMessage.Direction.TOSERVER);
|
return send(packet, HMessage.Direction.TOSERVER);
|
||||||
}
|
}
|
||||||
private boolean send(HPacket packet, HMessage.Direction direction) {
|
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.appendByte(direction == HMessage.Direction.TOCLIENT ? (byte)0 : (byte)1);
|
||||||
packet1.appendInt(packet.getBytesLength());
|
packet1.appendInt(packet.getBytesLength());
|
||||||
packet1.appendBytes(packet.toBytes());
|
packet1.appendBytes(packet.toBytes());
|
||||||
@ -307,7 +306,7 @@ public abstract class Extension implements IExtension{
|
|||||||
* @param s the text to be written
|
* @param s the text to be written
|
||||||
*/
|
*/
|
||||||
public void writeToConsole(String s) {
|
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);
|
packet.appendString(s);
|
||||||
try {
|
try {
|
||||||
writeToStream(packet.toBytes());
|
writeToStream(packet.toBytes());
|
||||||
|
@ -3,9 +3,6 @@ package gearth.extensions.parsers;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by Jeunez on 8/01/2019.
|
|
||||||
*/
|
|
||||||
public enum HEntityType {
|
public enum HEntityType {
|
||||||
HABBO(1),
|
HABBO(1),
|
||||||
PET(2),
|
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.misc.ConfirmationDialog;
|
||||||
import gearth.ui.extensions.GEarthExtension;
|
import gearth.services.extensionserver.extensions.network.NetworkExtension;
|
||||||
import gearth.ui.extensions.executer.ExtensionRunner;
|
|
||||||
import gearth.ui.extensions.executer.ExtensionRunnerFactory;
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.scene.control.Alert;
|
import javafx.scene.control.Alert;
|
||||||
import javafx.scene.control.ButtonType;
|
import javafx.scene.control.ButtonType;
|
||||||
@ -27,7 +24,7 @@ public class Authenticator {
|
|||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean evaluate(GEarthExtension extension) {
|
public static boolean evaluate(NetworkExtension extension) {
|
||||||
if (extension.isInstalledExtension()) {
|
if (extension.isInstalledExtension()) {
|
||||||
return claimSession(extension.getFileName(), extension.getCookie());
|
return claimSession(extension.getFileName(), extension.getCookie());
|
||||||
}
|
}
|
||||||
@ -52,7 +49,7 @@ public class Authenticator {
|
|||||||
|
|
||||||
private static volatile boolean rememberOption = false;
|
private static volatile boolean rememberOption = false;
|
||||||
//for not-installed extensions, popup a dialog
|
//for not-installed extensions, popup a dialog
|
||||||
private static boolean askForPermission(GEarthExtension extension) {
|
private static boolean askForPermission(NetworkExtension extension) {
|
||||||
boolean[] allowConnection = {true};
|
boolean[] allowConnection = {true};
|
||||||
|
|
||||||
final String connectExtensionKey = "allow_extension_connection";
|
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.ArrayList;
|
||||||
import java.util.HashMap;
|
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.
|
* 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.
|
* 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.Main;
|
||||||
import gearth.ui.extensions.authentication.Authenticator;
|
import gearth.services.extensionserver.extensions.network.authentication.Authenticator;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
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;
|
package gearth.ui.extensions;
|
||||||
|
|
||||||
|
import gearth.services.extensionserver.extensions.GEarthExtension;
|
||||||
|
import javafx.application.Platform;
|
||||||
import javafx.event.EventHandler;
|
import javafx.event.EventHandler;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
@ -10,10 +12,10 @@ import javafx.scene.paint.Paint;
|
|||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
import gearth.misc.ConfirmationDialog;
|
import gearth.misc.ConfirmationDialog;
|
||||||
import gearth.ui.buttons.*;
|
import gearth.ui.buttons.*;
|
||||||
import gearth.ui.extensions.executer.ExecutionInfo;
|
import gearth.services.extensionserver.extensions.network.executer.ExecutionInfo;
|
||||||
import gearth.ui.extensions.executer.ExtensionRunner;
|
import gearth.services.extensionserver.extensions.network.executer.ExtensionRunner;
|
||||||
import gearth.ui.extensions.executer.ExtensionRunnerFactory;
|
import gearth.services.extensionserver.extensions.network.executer.ExtensionRunnerFactory;
|
||||||
import gearth.ui.extensions.executer.NormalExtensionRunner;
|
import gearth.services.extensionserver.extensions.network.executer.NormalExtensionRunner;
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@ -166,7 +168,7 @@ public class ExtensionItemContainer extends GridPane {
|
|||||||
clickButton.addEventHandler(MouseEvent.MOUSE_CLICKED, onClick);
|
clickButton.addEventHandler(MouseEvent.MOUSE_CLICKED, onClick);
|
||||||
|
|
||||||
ExtensionItemContainer this2 = this;
|
ExtensionItemContainer this2 = this;
|
||||||
item.onDelete(observable -> {
|
item.onDelete(observable -> Platform.runLater(() -> {
|
||||||
if (item.isInstalledExtension()) {
|
if (item.isInstalledExtension()) {
|
||||||
setBackground(new Background(new BackgroundFill(Paint.valueOf("#cccccc"),null, null)));
|
setBackground(new Background(new BackgroundFill(Paint.valueOf("#cccccc"),null, null)));
|
||||||
getChildren().remove(buttonsBox);
|
getChildren().remove(buttonsBox);
|
||||||
@ -176,7 +178,7 @@ public class ExtensionItemContainer extends GridPane {
|
|||||||
else {
|
else {
|
||||||
parent.getChildren().remove(this2);
|
parent.getChildren().remove(this2);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void hasReconnected(GEarthExtension extension) {
|
void hasReconnected(GEarthExtension extension) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package gearth.ui.extensions;
|
package gearth.ui.extensions;
|
||||||
|
|
||||||
|
import gearth.services.extensionserver.extensions.GEarthExtension;
|
||||||
|
import gearth.services.extensionserver.extensions.network.NetworkExtension;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
@ -26,7 +28,7 @@ public class ExtensionItemContainerProducer {
|
|||||||
for (Node n : parent.getChildren()) {
|
for (Node n : parent.getChildren()) {
|
||||||
if (n instanceof ExtensionItemContainer) {
|
if (n instanceof ExtensionItemContainer) {
|
||||||
ExtensionItemContainer container = (ExtensionItemContainer) n;
|
ExtensionItemContainer container = (ExtensionItemContainer) n;
|
||||||
if (container.getExtensionFileName().equals(extension.getFileName())) {
|
if (container.getExtensionFileName() != null && container.getExtensionFileName().equals(extension.getFileName())) {
|
||||||
container.hasReconnected(extension);
|
container.hasReconnected(extension);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
package gearth.ui.extensions;
|
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.application.Platform;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
@ -9,100 +15,14 @@ import javafx.scene.control.TextField;
|
|||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.stage.FileChooser;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.util.function.Predicate;
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Jonas on 06/04/18.
|
* 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 {
|
public class Extensions extends SubForm {
|
||||||
|
|
||||||
|
|
||||||
@ -114,32 +34,9 @@ public class Extensions extends SubForm {
|
|||||||
public ScrollPane scroller;
|
public ScrollPane scroller;
|
||||||
|
|
||||||
private ExtensionRunner extensionRunner = null;
|
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() {
|
public void initialize() {
|
||||||
scroller.widthProperty().addListener(observable -> header_ext.setPrefWidth(scroller.getWidth()));
|
scroller.widthProperty().addListener(observable -> header_ext.setPrefWidth(scroller.getWidth()));
|
||||||
@ -147,206 +44,35 @@ public class Extensions extends SubForm {
|
|||||||
|
|
||||||
protected void onParentSet() {
|
protected void onParentSet() {
|
||||||
ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller);
|
ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller);
|
||||||
|
extensionServer = new ExtensionServer(getHConnection());
|
||||||
|
extensionServer.onExtensionConnected((e -> {
|
||||||
|
Platform.runLater(() -> producer.extensionConnected(e));
|
||||||
|
}));
|
||||||
|
|
||||||
getHConnection().addStateChangeListener((oldState, newState) -> {
|
//noinspection OptionalGetWithoutIsPresent
|
||||||
if (newState == HConnection.State.CONNECTED) {
|
networkExtensionsProducer
|
||||||
HarbleAPIFetcher.fetch(getHConnection().getHotelVersion());
|
= (NetworkExtensionsProducer) extensionServer.getExtensionProducers().stream()
|
||||||
synchronized (gEarthExtensions) {
|
.filter(producer1 -> producer1 instanceof NetworkExtensionsProducer)
|
||||||
for (GEarthExtension extension : gEarthExtensions) {
|
.findFirst().get();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
|
producer.setPort(networkExtensionsProducer.getPort());
|
||||||
}
|
ext_port.setText(networkExtensionsProducer.getPort()+"");
|
||||||
|
|
||||||
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()+"");
|
|
||||||
// System.out.println("Extension server registered on port: " + extensionsRegistrer.getPort());
|
// System.out.println("Extension server registered on port: " + extensionsRegistrer.getPort());
|
||||||
|
|
||||||
extensionRunner = ExtensionRunnerFactory.get();
|
extensionRunner = ExtensionRunnerFactory.get();
|
||||||
extensionRunner.runAllExtensions(extensionsRegistrer.getPort());
|
extensionRunner.runAllExtensions(networkExtensionsProducer.getPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void installBtnClicked(ActionEvent actionEvent) {
|
public void installBtnClicked(ActionEvent actionEvent) {
|
||||||
List<ScheduleItem> list = new ArrayList<>();
|
|
||||||
|
|
||||||
FileChooser fileChooser = new FileChooser();
|
FileChooser fileChooser = new FileChooser();
|
||||||
fileChooser.setTitle("Install extension");
|
fileChooser.setTitle("Install extension");
|
||||||
fileChooser.getExtensionFilters().addAll(
|
fileChooser.getExtensionFilters().addAll(
|
||||||
new FileChooser.ExtensionFilter("G-Earth extensions", ExecutionInfo.ALLOWEDEXTENSIONTYPES));
|
new FileChooser.ExtensionFilter("G-Earth extensions", ExecutionInfo.ALLOWEDEXTENSIONTYPES));
|
||||||
File selectedFile = fileChooser.showOpenDialog(parentController.getStage());
|
File selectedFile = fileChooser.showOpenDialog(parentController.getStage());
|
||||||
if (selectedFile != null) {
|
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