extension suppoer & abstract Extension class implemented, not tested yet.

This commit is contained in:
sirjonasxx 2018-06-24 01:21:53 +02:00
parent 2b9c553c9c
commit fd781afb18
11 changed files with 714 additions and 6 deletions

View File

@ -38,8 +38,10 @@ public class Main extends Application {
}
public static String[] args;
public static void main(String[] args) {
Main.args = args;
launch(args);
}
}

View File

@ -0,0 +1,210 @@
package main.extensions;
import main.protocol.HMessage;
import main.protocol.HPacket;
import main.protocol.packethandler.PayloadBuffer;
import main.ui.extensions.Extensions;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created by Jonas on 23/06/18.
*/
public abstract class Extension {
public interface MessageListener {
void act(HMessage message);
}
public interface FlagsCheckListener {
void act(String[] args);
}
private static final String[] PORT_FLAG = {"--port", "-p"};
private OutputStream out = null;
private Map<Integer, List<MessageListener>> incomingMessageListeners = new HashMap<>();
private Map<Integer, List<MessageListener>> outgoingMessageListeners = new HashMap<>();
private FlagsCheckListener flagRequestCallback = null;
public Extension(String[] args) {
//obtain port
int port = 0;
outerloop:
for (int i = 0; i < args.length - 1; i++) {
for (String str : PORT_FLAG) {
if (args[i].equals(str)) {
port = Integer.parseInt(args[i+1]);
break outerloop;
}
}
}
try {
Socket GEarthExtensionServer = new Socket("localhost", port);
InputStream in = GEarthExtensionServer.getInputStream();
out = GEarthExtensionServer.getOutputStream();
PayloadBuffer buffer = new PayloadBuffer();
while (!GEarthExtensionServer.isClosed()) {
if (in.available() > 0) {
byte[] data = new byte[in.available()];
in.read(data);
buffer.push(data);
HPacket[] packets = buffer.receive();
for (HPacket packet : packets) {
if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INFOREQUEST) {
HPacket response = new HPacket(Extensions.INCOMING_MESSAGES_IDS.EXTENSIONINFO);
response.appendString(getTitle())
.appendString(getAuthor())
.appendString(getVersion())
.appendString(getDescription());
writeToStream(response.toBytes());
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.CONNECTIONSTART) {
onStartConnection();
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.CONNECTIONEND) {
onEndConnection();
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.FLAGSCHECK) {
// body = an array of G-Earths main flags
if (flagRequestCallback != null) {
int arraysize = packet.readInteger();
String[] gEarthArgs = new String[arraysize];
for (int i = 0; i < gEarthArgs.length; i++) {
gEarthArgs[i] = packet.readString();
}
flagRequestCallback.act(gEarthArgs);
}
flagRequestCallback = null;
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.INIT) {
init();
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.FREEFLOW) {
// nothing to be done yet
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.ONDOUBLECLICK) {
onDoubleClick();
}
else if (packet.headerId() == Extensions.OUTGOING_MESSAGES_IDS.PACKETINTERCEPT) {
HMessage habboMessage = new HMessage(packet.readString());
HPacket habboPacket = habboMessage.getPacket();
Map<Integer, List<MessageListener>> listeners =
habboMessage.getDestination() == HMessage.Side.TOCLIENT ?
incomingMessageListeners :
outgoingMessageListeners;
if (listeners.containsKey(-1)) { // registered on all packets
for (int i = listeners.get(-1).size() - 1; i >= 0; i--) {
listeners.get(-1).get(i).act(habboMessage);
}
}
if (listeners.containsKey(habboPacket.headerId())) {
for (int i = listeners.get(habboPacket.headerId()).size() - 1; i >= 0; i--) {
listeners.get(habboPacket.headerId()).get(i).act(habboMessage);
}
}
HPacket response = new HPacket(Extensions.INCOMING_MESSAGES_IDS.MANIPULATEDPACKET);
response.appendString(habboMessage.stringify());
writeToStream(response.toBytes());
}
}
}
Thread.sleep(1);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
private void writeToStream(byte[] bytes) throws IOException {
synchronized (out) {
out.write(bytes);
out.flush();
}
}
//methods returns if succeed
protected boolean sendToClient(HPacket packet) {
return send(packet, HMessage.Side.TOCLIENT);
}
protected boolean sendToServer(HPacket packet) {
return send(packet, HMessage.Side.TOSERVER);
}
private boolean send(HPacket packet, HMessage.Side side) {
HPacket packet1 = new HPacket(Extensions.INCOMING_MESSAGES_IDS.SENDMESSAGE);
packet1.appendByte(side == HMessage.Side.TOCLIENT ? (byte)0 : (byte)1);
packet1.appendInt(packet.getBytesLength());
packet1.appendBytes(packet.toBytes());
try {
writeToStream(packet1.toBytes());
return true;
} catch (IOException e) {
return false;
}
}
protected void intercept(HMessage.Side side, int headerId, MessageListener messageListener) {
Map<Integer, List<MessageListener>> listeners =
side == HMessage.Side.TOCLIENT ?
incomingMessageListeners :
outgoingMessageListeners;
if (!listeners.containsKey(headerId)) {
listeners.put(headerId, new ArrayList<>());
}
listeners.get(headerId).add(messageListener);
}
protected void intercept(HMessage.Side side, MessageListener messageListener) {
intercept(side, -1, messageListener);
}
//returns "false" if another flag requester was busy
protected boolean requestFlags(FlagsCheckListener flagRequestCallback) {
if (this.flagRequestCallback != null) return false;
this.flagRequestCallback = flagRequestCallback;
return true;
}
protected void writeToConsole(String s) {
HPacket packet = new HPacket(Extensions.INCOMING_MESSAGES_IDS.EXTENSIONCONSOLELOG);
packet.appendString(s);
try {
writeToStream(packet.toBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
// All methods under here block the stream, use threads if needed.
protected abstract void init();
protected abstract void onDoubleClick();
protected abstract void onStartConnection();
protected abstract void onEndConnection();
protected abstract String getTitle();
protected abstract String getDescription();
protected abstract String getVersion();
protected abstract String getAuthor();
}

View File

@ -1,6 +1,8 @@
package main.protocol;
public class HMessage {
import main.misc.StringifyAble;
public class HMessage implements StringifyAble {
public enum Side {
TOSERVER,
@ -13,6 +15,9 @@ public class HMessage {
private boolean isBlocked;
public HMessage(String fromString) {
constructFromString(fromString);
}
public HMessage(HPacket packet, Side side, int index) {
this.side = side;
@ -42,4 +47,47 @@ public class HMessage {
public boolean isCorrupted() {
return hPacket.isCorrupted();
}
@Override
public String stringify() {
String s = (isBlocked ? "1" : "0") + "\t" + index + "\t" + side.name() + "\t" + hPacket.stringify();
return s;
}
@Override
public void constructFromString(String str) {
String[] parts = str.split("\t");
this.isBlocked = parts[0].equals("1");
this.index = Integer.parseInt(parts[1]);
this.side = parts[2].equals("TOCLIENT") ? Side.TOCLIENT : Side.TOSERVER;
HPacket p = new HPacket(new byte[0]);
p.constructFromString(parts[3]);
this.hPacket = p;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof HMessage)) return false;
HMessage message = (HMessage) obj;
return message.hPacket.equals(hPacket) && (side == message.side) && (index == message.index);
}
// public static void main(String[] args) {
// HPacket packet3 = new HPacket(81, new byte[]{0,0,0,1,0,0});
//
// HPacket packet = new HPacket(82, new byte[]{0,0,0,1,0,0});
// HMessage message = new HMessage(packet, Side.TOSERVER, 5);
//
// String stringed = message.stringify();
//
// HMessage message2 = new HMessage(stringed);
// HPacket packet1 = message2.getPacket();
//
// System.out.println(message.equals(message2));
// System.out.println(packet.equals(packet1));
// System.out.println(packet.equals(packet3));
// }
}

View File

@ -1,5 +1,7 @@
package main.protocol;
import main.misc.StringifyAble;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@ -8,7 +10,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class HPacket {
public class HPacket implements StringifyAble {
// te komen: toExpressions (+impl. expressies)
private boolean isEdited = false;
private byte[] packetInBytes;
@ -403,7 +405,6 @@ public class HPacket {
}
public HPacket replaceAllString(String oldS, String newS) {
while (replaceFirstString(oldS, newS)) {}
return this;
}
@ -737,7 +738,7 @@ public class HPacket {
resultTest[j] = "{b:"+(packetInBytes[j] == 1 ? "true" : "false")+"}";
}
else {
resultTest[j] = "{b:"+packetInBytes[j]+"}";
resultTest[j] = "{b:"+((((int)packetInBytes[j])+256)%256)+"}";
}
}
opeenvolging = 0;
@ -761,4 +762,23 @@ public class HPacket {
return expression.toString().replace("{i:0}{b:false}{b:true}", "{s:}{i:1}");
}
@Override
public String stringify() {
String st = (isEdited ? "1" : "0") + this.toString();
return st;
}
@Override
public void constructFromString(String str) {
this.isEdited = str.charAt(0) == '1';
packetInBytes = fromStringToBytes(str.substring(1));
}
@Override
public boolean equals(Object object) {
if (!(object instanceof HPacket)) return false;
HPacket packet2 = (HPacket) object;
return Arrays.equals(packetInBytes, packet2.packetInBytes) && (isEdited == packet2.isEdited);
}
}

View File

@ -1,7 +1,13 @@
package main.ui.extensions;
import main.Main;
import main.protocol.*;
import main.ui.SubForm;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.*;
/**
* Created by Jonas on 06/04/18.
*/
@ -47,7 +53,7 @@ import main.ui.SubForm;
* | | | 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) (blank body = no manipulation) |
* | | | manipulated HMessage (OUTGOING id: 2) |
* -----------------------------------------------------------------------------------------------------
* | 4 | FLAGS-CHECK** | Body: String with G-Earth's boot flags (args from static main method) |
* -----------------------------------------------------------------------------------------------------
@ -55,6 +61,10 @@ import main.ui.SubForm;
* | | | you could check this yourself as well (listen to out:4000 packet) |
* -----------------------------------------------------------------------------------------------------
* | 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)
@ -70,17 +80,168 @@ import main.ui.SubForm;
* | 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 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 List<GEarthExtension> gEarthExtensions = new ArrayList<>();
public void initialize() {
}
protected void onParentSet() {
getHConnection().addStateChangeListener((oldState, newState) -> {
if (newState == HConnection.State.CONNECTED) {
for (GEarthExtension extension : gEarthExtensions) {
extension.sendMessage(new HPacket(OUTGOING_MESSAGES_IDS.CONNECTIONSTART));
}
}
if (oldState == HConnection.State.CONNECTED) {
for (GEarthExtension extension : gEarthExtensions) {
extension.sendMessage(new HPacket(OUTGOING_MESSAGES_IDS.CONNECTIONEND));
}
}
});
getHConnection().addTrafficListener(1, message -> {
Set<GEarthExtension> collection = new HashSet<>(gEarthExtensions);
String stringified = message.stringify();
HPacket manipulatePacketRequest = new HPacket(OUTGOING_MESSAGES_IDS.PACKETINTERCEPT);
manipulatePacketRequest.appendString(stringified);
boolean[] isblock = new boolean[1];
for (GEarthExtension extension : gEarthExtensions) {
GEarthExtension.ReceiveMessageListener respondCallback = new GEarthExtension.ReceiveMessageListener() {
@Override
public void act(HPacket packet) {
if (packet.headerId() == INCOMING_MESSAGES_IDS.MANIPULATEDPACKET) {
String stringifiedresponse = packet.readString();
HMessage responseMessage = new HMessage(stringifiedresponse);
if (responseMessage.getDestination() == message.getDestination() && responseMessage.getIndex() == message.getIndex()) {
if (!message.equals(responseMessage)) {
message.constructFromString(stringifiedresponse);
if (responseMessage.isBlocked()) {
isblock[0] = true;
}
}
collection.remove(extension);
extension.removeOnReceiveMessageListener(this);
}
}
}
};
extension.addOnReceiveMessageListener(respondCallback);
extension.sendMessage(manipulatePacketRequest);
}
//block untill all extensions have responded
List<GEarthExtension> willdelete = new ArrayList<>();
while (!collection.isEmpty()) {
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();}
}
if (isblock[0]) {
message.setBlocked(true);
}
});
GEarthExtensionsRegistrer extensionsRegistrer = null;
HashMap<GEarthExtension, GEarthExtension.ReceiveMessageListener> messageListeners = new HashMap<>();
try {
extensionsRegistrer = new GEarthExtensionsRegistrer(new GEarthExtensionsRegistrer.ExtensionRegisterObserver() {
@Override
public void onConnect(GEarthExtension extension) {
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);
}
}
}
};
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));
}
}
@Override
public void onDisconnect(GEarthExtension extension) {
gEarthExtensions.remove(extension);
extension.removeOnReceiveMessageListener(messageListeners.get(extension));
messageListeners.remove(extension);
}
});
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Extension server registered on port: " + extensionsRegistrer.getPort());
}
}

View File

@ -0,0 +1,169 @@
package main.ui.extensions;
import main.protocol.HMessage;
import main.protocol.HPacket;
import main.protocol.packethandler.PayloadBuffer;
import java.io.IOException;
import java.io.InputStream;
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 Socket connection;
//calls callback when the extension is creatd
static void create(Socket connection, OnCreatedCallback callback, OnDisconnectedCallback onDisconnectedCallback) {
new Thread(() -> {
try {
connection.getOutputStream().write((new HPacket(Extensions.OUTGOING_MESSAGES_IDS.INFOREQUEST)).toBytes());
connection.getOutputStream().flush();
PayloadBuffer payloadBuffer = new PayloadBuffer();
InputStream inputStream = connection.getInputStream();
outerloop:
while (!connection.isClosed()) {
if (inputStream.available() > 0) {
byte[] incoming = new byte[inputStream.available()];
inputStream.read(incoming);
payloadBuffer.push(incoming);
}
HPacket[] hPackets = payloadBuffer.receive();
for (HPacket packet : hPackets) { // it should be only one packet
if (packet.headerId() == Extensions.INCOMING_MESSAGES_IDS.EXTENSIONINFO) {
GEarthExtension gEarthExtension = new GEarthExtension(
packet.readString(),
packet.readString(),
packet.readString(),
packet.readString(),
connection,
onDisconnectedCallback
);
callback.act(gEarthExtension);
break outerloop;
}
}
Thread.sleep(1);
}
} catch (IOException | InterruptedException ignored) {}
}).start();
}
private GEarthExtension(String title, String author, String version, String description, Socket connection, OnDisconnectedCallback onDisconnectedCallback) {
this.title = title;
this.author = author;
this.version = version;
this.description = description;
this.connection = connection;
GEarthExtension selff = this;
new Thread(() -> {
try {
PayloadBuffer payloadBuffer = new PayloadBuffer();
InputStream inputStream = connection.getInputStream();
while (!connection.isClosed()) {
if (inputStream.available() > 0) {
byte[] incoming = new byte[inputStream.available()];
inputStream.read(incoming);
payloadBuffer.push(incoming);
}
HPacket[] hPackets = payloadBuffer.receive();
for (HPacket packet : hPackets) {
for (int i = receiveMessageListeners.size() - 1; i >= 0; i--) {
receiveMessageListeners.get(i).act(packet);
}
}
Thread.sleep(1);
}
onDisconnectedCallback.act(selff);
} catch (IOException | InterruptedException 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 closeConnection() {
try {
connection.close();
return true;
} catch (IOException e) {
return false;
}
}
public boolean sendMessage(HPacket message) {
try {
connection.getOutputStream().write(message.toBytes());
connection.getOutputStream().flush();
return true;
} catch (IOException e) {
return false;
}
}
private List<ReceiveMessageListener> receiveMessageListeners = new ArrayList<>();
public void addOnReceiveMessageListener(ReceiveMessageListener receiveMessageListener) {
receiveMessageListeners.add(receiveMessageListener);
}
public void removeOnReceiveMessageListener(ReceiveMessageListener receiveMessageListener) {
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
}
}

View File

@ -0,0 +1,35 @@
package main.ui.extensions;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* Created by Jonas on 21/06/18.
*/
public class GEarthExtensionsRegistrer {
final ServerSocket serverSocket;
GEarthExtensionsRegistrer(ExtensionRegisterObserver observer) throws IOException {
serverSocket = new ServerSocket(0);
new Thread(() -> {
try {
while (!serverSocket.isClosed()) {
Socket extensionSocket = serverSocket.accept();
GEarthExtension.create(extensionSocket, observer::onConnect, observer::onDisconnect);
}
} catch (IOException e) {e.printStackTrace();}
}).start();
}
public int getPort() {
return serverSocket.getLocalPort();
}
public interface ExtensionRegisterObserver {
void onConnect(GEarthExtension extension);
void onDisconnect(GEarthExtension extension);
}
}

View File

@ -0,0 +1,19 @@
package main.ui.extensions.extensionfilemanager;
import main.ui.extensions.extensionfilemanager.extensionfile.ExtensionFile;
import java.io.File;
import java.util.List;
/**
* Created by Jonas on 21/06/18.
*/
public interface ExtensionFilesManager {
List<ExtensionFile> getAllExtensions();
ExtensionFile addExtension(File file); //returns g-earth extension file, returns null if failure
boolean removeExtension(ExtensionFile file); //returns false if not done
}

View File

@ -0,0 +1,7 @@
package main.ui.extensions.extensionfilemanager;
/**
* Created by Jonas on 21/06/18.
*/
public class ExtensionFilesManagerFactory {
}

View File

@ -0,0 +1,27 @@
package main.ui.extensions.extensionfilemanager;
import main.ui.extensions.extensionfilemanager.extensionfile.ExtensionFile;
import java.io.File;
import java.util.List;
/**
* Created by Jonas on 21/06/18.
*/
public class LinuxExtensionFilesManager implements ExtensionFilesManager {
@Override
public List<ExtensionFile> getAllExtensions() {
return null;
}
@Override
public ExtensionFile addExtension(File file) {
return null;
}
@Override
public boolean removeExtension(ExtensionFile file) {
return false;
}
}

View File

@ -0,0 +1,10 @@
package main.ui.extensions.extensionfilemanager.extensionfile;
/**
* Created by Jonas on 21/06/18.
*/
public class ExtensionFile {
}