Skip to content

Native Extension

WiredSpast edited this page Apr 5, 2023 · 14 revisions

Development environment

Any IDE with Java support can be used to create a G-Earth extension using the G-Earth Native Extension framework, for this guide we'll be using IntelliJ.

Environment setup

Creating a new Maven project

G-Earth is written as a Maven project, this allows G-Earth and it's extension framework to be used as dependencies in other projects. For this reason it's best to also create your extension as a Maven project.

  1. Create a new Maven project with Java 1.8 as the Project JDK (Java 1.8 is recommended since G-Earth itself was also build using Java 1.8)

  1. Give your project a name, editing the GroupId, ArtifactId and Version is optional

IntelliJ will generate the following project files for you


Setting up your Maven Environment

Adding G-Earth to your local Maven repository

Note: You only have to do this one time, unless you want to add a new version of G-Earth to the repository.
If you've already have G-Earth in your local Maven repository you can skip to the next step.

  1. Copy G-Earth.jar into your project at root level, you will find the jar file in the folder where you have G-Earth installed

  1. Open the Maven side panel in IntelliJ which you will find in the right sidebar and click the icon

  1. Copy the following Maven command and paste it into the window that opened after clicking the icon and press enter to run it
mvn install:install-file -Dfile=G-Earth.jar -DgroupId=G-Earth -DartifactId=G-Earth -Dversion=1.5.3 -Dpackaging=jar

Note 1: The mvn part of the command is already present in the window, make sure not to put it in twice
Note 2: Make sure to change the -DVersion value if you happen to be installing a different version of the G-Earth.jar

  1. Now you can delete the G-Earth.jar file from your project

Setting up the pom file

One of the generated files is the pom.xml file which should currently contain the following:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>G-Earth.Extensions</groupId>
    <artifactId>TestExtension</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

</project>
  1. Add G-Earth as a dependency by adding the following under the <properties>...</properties> part of your pom file
<dependencies>
    <dependency>
        <groupId>G-Earth</groupId>
        <artifactId>G-Earth</artifactId>
        <version>1.5.3</version>
    </dependency>
</dependencies>

Note: If you have a different version of G-Earth added to your local Maven repository make sure to change the version in the <version>...</version> tag.

You're pom file should now look like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>G-Earth.Extensions</groupId>
    <artifactId>TestExtension</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>G-Earth</groupId>
            <artifactId>G-Earth</artifactId>
            <version>1.5.3</version>
        </dependency>
    </dependencies>
</project>

Creating your extension base

The Native Extension framework allows you to create two types of extension, console extensions and GUI extensions.

Note: The next section will focus on the setup of a console extension, if you want to setup a GUI extension you can jump over this section.

Creating a console extension

  1. Create a new Java Class file in the main/java folder and name it after your extension, in my case ExampleExtension

  1. Add the ExtensionInfo annotation by placing the following right above the public class declaration in the file you just created and change the values to match your extension
@ExtensionInfo(
        Title = "Example extension",
        Description = "This is just an example",
        Version = "0.1",
        Author = "WiredSpast"
)
  1. Make the class extend the Extension class by adding extends Extension right behind the public class ExampleExtension declaration
public class ExampleExtension extends Extension {
  1. Add a constructor to your class which matches the super class
public ExampleExtension(String[] args) {
    super(args);
}
  1. Add the following main method to your file, this will allow you to run your extension
public static void main(String[] args) {
    new ExampleExtension(args).run();
}
  1. In case your IDE hasn't done it for you yet, add the following imports at the top of the file:
import gearth.extensions.Extension;
import gearth.extensions.ExtensionInfo;

Note: Make sure to change ExampleExtension in step 4 and 5 to the name of your class file

Your class file should now look like this:

import gearth.extensions.Extension;
import gearth.extensions.ExtensionInfo;

@ExtensionInfo(
        Title = "Example extension",
        Description = "This is just an example",
        Version = "0.1",
        Author = "WiredSpast"
)
public class ExampleExtension extends Extension {
    public ExampleExtension(String[] args) {
        super(args);
    }

    public static void main(String[] args) {
        new ExampleExtension(args).run();
    }
}

Your base is now all set up, to test if your base works you can jump to the "Running your extension from your IDE" section.


Creating a GUI extension

A GUI extension requires a base of 3 files

Creating your controller class

  1. Create a new Java Class file in the main/java folder and name it after your extension, in my case ExampleExtension

  1. Add the ExtensionInfo annotation by placing the following right above the public class declaration in the file you just created and change the values to match your extension
@ExtensionInfo(
        Title = "Example extension",
        Description = "This is just an example",
        Version = "0.1",
        Author = "WiredSpast"
)
  1. Make the class extend the ExtensionForm class by adding extends ExtensionForm right behind the public class ExampleExtension declaration
public class ExampleExtension extends ExtensionForm {
  1. In case your IDE hasn't done it for you yet, add the following imports at the top of the file:
import gearth.extensions.ExtensionForm;
import gearth.extensions.ExtensionInfo;

Your controller class should now look like this:

import gearth.extensions.ExtensionForm;
import gearth.extensions.ExtensionInfo;

@ExtensionInfo(
        Title = "Example extension",
        Description = "This is just an example",
        Version = "0.1",
        Author = "WiredSpast"
)
public class ExampleExtension extends ExtensionForm {
}

Creating your FXML file

  1. Create a new FXML file in the main/resources folder and name it same way as your controller class, in my case ExampleExtension.fxml

  1. The created file will already contain a base, in this base you will find the value fx:controller replace this value by the name of your controller class (Leave out the .java)

Afterwards your file should look like this:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="ExampleExtension"
            prefHeight="400.0" prefWidth="600.0">

</AnchorPane>
  1. To create your GUI layout it's easiest to switch to IntelliJ's build in Scene Builder which you can find at the bottom of your screen when you have the FXML file open

Note 1: If you haven't used the Scene Builder before you will be asked to download the Scene Builder Kit and JavaFX
Note 2: If you are using a different IDE you can download and install the scene builder from here

  1. Using the Scene Builder you can easily drag components from the left column onto your form canvas and edit it's properties in the right column

Connecting the added components to your controller class

Adding a clicklistener to a button
  1. Add a public clicklistener method to your controller class which has exactly one parameter ActionEvent event and returns void
public class ExampleExtension extends ExtensionForm {
    ...

    public void onButtonClick(ActionEvent event) {
        // Code to run on button click
    }

    ...
}
  1. Select the button in the Scene builder and set the On Action value in the Code section of the button's properties to the name of your created clicklistener method.

Making a component accessible in the controller class
  1. Give the component a unique fx:id in the Code section of the component's properties

  1. Add a public variable with the same name and correct JavaFX class type to your controller class
public class ExampleExtension extends ExtensionForm {
    ...
    public Label myLabel;

    ...
}

Note: Info on how to interact with every different component type can be found here.


Creating your Launcher class

  1. Create a new Java class in the main/java folder and give it a name preferably ending in Launcher

  1. Make the class extend the ThemedExtensionFormCreator class by adding extends ThemedExtensionFormCreator behind the public class ExampleExtensionLauncher declaration
public class ExampleExtensionLauncher extends ThemedExtensionFormCreator {
  1. Add the following methods to your Launcher class
@Override
public String getTitle() {
    return "Example Extension";
}

@Override
protected URL getFormResource() {
    return getClass().getResource("ExampleExtension.fxml");
}

Note 1: Make sure to change the name of the FXML file and the window title to match your extension

  1. Add the following main method to your Launcher class, this main method will be used to run your extension
public static void main(String[] args) {
    runExtensionForm(args, ExampleExtensionLauncher.class);
}

Note: make sure the class given as the second parameter of the runExtensionForm method matches your launcher class

  1. In case your IDE hasn't done it for you yet make sure this import is present at the top of your Launcher class file
import gearth.extensions.ThemedExtensionFormCreator;

Now your Launcher class file should look like this:

import gearth.extensions.ThemedExtensionFormCreator;

public class ExampleExtensionLauncher extends ThemedExtensionFormCreator {

    @Override
    protected String getTitle() {
        return "Example Extension";
    }

    @Override
    protected URL getFormResource() {
        return getClass().getResource(ExampleExtension.fxml");
    }

    public static void main(String[] args) {
        runExtensionForm(args, ExampleExtensionLauncher.class);
    }
}

Now you have your base all set up.

Running the extension from your IDE

Your extension class (or launcher class for a GUI extension) has a main method, this method will be used to run your extension.

  1. Create a run configuration for your main class by right clicking the file containing your main method and click Modify Run Configuration... under More Run/Debug

  1. Put -p 9092 in the Program arguments field, then click Apply and OK

Note: 9092 is the basic port G-Earth runs its extension socket on, if this port is already occupied G-Earth uses another port, you can find the correct port in the bottom left corner of the Extensions tab in G-Earth.

  1. Now you can find a play button in the top right corner of your IDE, clicking this play button will run your extension.

  1. The first time running your extension G-Earth will ask you to confirm the connection, check the Remember my choice checkbox and click Yes

  1. The extension should now be connected to G-Earth and be shown in the Extensions tab

Note: GUI extensions will have a play button displayed at the right side, clicking this will open the GUI you created.

Intercepting and/or sending packets

Currently your extension doesn't do anything, to make it do something we have to intercept and/or send packets

Intercepting packets

To intercept a packet we permanently add a listener, adding these listeners should only happen once when the extension first gets launched. To do this the Extension class has an initExtension method which we will override in our own extension.

Packets can be intercepted in 2 directions:

  • To the hotel server: HMessage.Direction.TOSERVER
  • To your client: HMessage.Direction.TOCLIENT
  1. Override the initExtension method in your Extension class (or your Controller class for GUI extensions)
@Override
protected void initExtension() {
    ...
}
  1. Create a method which has exactly one parameter HMessage hMessage, this method will be called when the required packet passes by:
private void onPacket(HMessage hMessage) {
    HPacket packet = hMessage.getPacket();
    ...
}
  1. Link the created method to the packet header, name or hash or intercept every packet in the initExtension method.
  • Intercept every packet going to the client
@Override
protected void initExtension() {
    ...
    intercept(HMessage.Direction.TOCLIENT, this::onPacket());
    ...
}
  • Intercept a packet with a specific header ID, in the example we're intercepting the packet with id 100 being send to the server
@Override
protected void initExtension() {
    ...
    intercept(HMessage.Direction.TOSERVER, 100, this::onPacket());
    ...
}

Note: If you are planning to add your extension to the Extension Store, you're required to not intercept packets by ID but by name or hash

  • Intercept a packet with a specific header name, in the example we're intercepting the packet with the name Chat going to the client.
@Override
protected void initExtension() {
    ...
    intercept(HMessage.Direction.TOSERVER, "Chat", this::onPacket());
    ...
}

Sending packets

The same way that we can intercept packets going in both directions, we can also send packets in both directions:

  • To the hotel server: HMessage.Direction.TOSERVER
  • To your client: HMessage.Direction.TOCLIENT

Creating packets

// TODO

Manipulating packets

// TODO

Sending the created packet

Sending packets to server

sendToServer(packet);

Sending packet to client

sendToClient(packet);

Included parsers

// TODO

Build in listeners

// TODO

Packaging your extension

// TODO