diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..07e1fc5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,47 @@
+.idea
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### Maven ###
+target/
+pom.xml.tag
+pom.xml.releaseBackup
+pom.xml.versionsBackup
+pom.xml.next
+release.properties
+dependency-reduced-pom.xml
+buildNumber.properties
+.mvn/timing.properties
+# https://github.com/takari/maven-wrapper#usage-without-binary-jar
+.mvn/wrapper/maven-wrapper.jar
+
+# Eclipse m2e generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
+# End of https://www.toptal.com/developers/gitignore/api/java,maven
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2fd2f18
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# BanHammer
+BanHammer is a extension to ban players on Origins Habbo Hotel, the intention of this Extensions is because the initial version of the Hotel yet doesn't contain a ban command.
+
+# How to Use
+- Ban players with :ban NICKNAME
+- Unban players with the UI of the Extension
+
+# Note
+The Bans are cached inside G-Earth's folder so if you update your G-Earth you should move your Cache folder to the new G-Earth's version folder.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..570bd5a
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,97 @@
+
+
+ 4.0.0
+
+ gearthextensions
+ EventSender
+ jar
+ 0.1
+
+
+
+
+ false
+ src/main/resources
+
+ **/*.fxml
+ **/*.png
+ **/*.css
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.5
+
+ ${project.build.directory}/bin
+
+
+ true
+ true
+ BanHammerLauncher
+ false
+ lib/
+ true
+
+
+ ${project.artifactId}
+
+
+
+
+ maven-assembly-plugin
+ 2.5
+
+
+ package
+
+ single
+
+
+
+
+ ${project.build.directory}/bin
+
+
+ BanHammerLauncher
+
+
+
+ jar-with-dependencies
+
+ ${project.artifactId}
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 1.8
+
+
+
+
+
+
+
+ G-Earth
+ G-Earth
+ 1.5.4
+
+
+ org.json
+ json
+ 20240205
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/BanHammer.java b/src/main/java/BanHammer.java
new file mode 100644
index 0000000..51bd63a
--- /dev/null
+++ b/src/main/java/BanHammer.java
@@ -0,0 +1,245 @@
+import entities.Player;
+import gearth.extensions.ExtensionForm;
+import gearth.extensions.ExtensionInfo;
+import gearth.misc.Cacher;
+import gearth.protocol.HMessage;
+import gearth.protocol.HPacket;
+import gearth.protocol.packethandler.shockwave.packets.ShockPacketIncoming;
+import gearth.protocol.packethandler.shockwave.packets.ShockPacketOutgoing;
+import javafx.application.Platform;
+import javafx.event.ActionEvent;
+import javafx.fxml.Initializable;
+import javafx.scene.control.*;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import parsers.OHEntity;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.ResourceBundle;
+
+@ExtensionInfo(
+ Title = "Ban Hammer",
+ Description = "While not implemented, ban trolls!",
+ Version = "1.0",
+ Author = "Thauan"
+)
+
+public class BanHammer extends ExtensionForm implements Initializable {
+ public static BanHammer RUNNING_INSTANCE;
+ public Button buttonUnban;
+ public ListView playerListView;
+ public Label labelInfo;
+ public Button buttonClearList;
+ public Label labelRoomName;
+ protected List playerList = new ArrayList<>();
+ public static String habboUserName;
+ public String roomName;
+ public String roomId;
+
+ @Override
+ protected void onStartConnection() {
+ System.out.println("BanHammer started!");
+ }
+
+ @Override
+ protected void onShow() {
+
+ }
+
+ @Override
+ public void initialize(URL location, ResourceBundle resources) {
+ setupCache();
+ }
+
+ @Override
+ protected void initExtension() {
+ RUNNING_INSTANCE = this;
+
+ onConnect((host, port, APIVersion, versionClient, client) -> {
+ if (!Objects.equals(versionClient, "SHOCKWAVE")) {
+ System.exit(0);
+ }
+ });
+
+ intercept(HMessage.Direction.TOCLIENT, "USERS", this::onUsersOrigin);
+ intercept(HMessage.Direction.TOCLIENT, "USER_OBJ", this::onUserObject);
+ intercept(HMessage.Direction.TOCLIENT, "LOGOUT", this::onUserRemove);
+ intercept(HMessage.Direction.TOSERVER, "CHAT", this::onChat);
+ intercept(HMessage.Direction.TOSERVER, "SHOUT", this::onChat);
+ intercept(HMessage.Direction.TOCLIENT, "FLATINFO", this::onFlatInfo);
+
+ }
+
+ private void onFlatInfo(HMessage hMessage) {
+ HPacket hPacket = hMessage.getPacket();
+ hPacket.readBoolean();
+ roomId = hPacket.readString();
+ hPacket.readString();
+ roomName = hPacket.readString();
+ String sanitizedRoomId = roomId.replaceAll("[^a-zA-Z]", "");
+
+ playerList.clear();
+ Platform.runLater(() -> {
+ labelRoomName.setText(roomName + " BAN LIST:");
+ playerListView.getItems().clear();
+ });
+
+ new Thread(() -> {
+ loadRoomCache(sanitizedRoomId);
+ }).start();
+ }
+
+ private void onChat(HMessage hMessage) {
+ HPacket hPacket = hMessage.getPacket();
+ String message = hPacket.readString(StandardCharsets.ISO_8859_1);
+
+ if (message.contains(":ban") && message.split(" ").length > 0) {
+ if(roomId == null) {
+ sendToServer(new ShockPacketOutgoing("{out:WHISPER}{s:\" Please reload the room to ban a player!\"}"));
+ hMessage.setBlocked(true);
+ return;
+ }
+ String playerName = message.split(" ")[1].trim();
+ System.out.println("Banning " + playerName + "...");
+ sendToServer(new ShockPacketOutgoing("{out:KICKUSER}{s:\"" + playerName + "\"}"));
+
+ Platform.runLater(() -> {
+ playerListView.getItems().add(playerName);
+ });
+ new Thread(this::updateRoomCache).start();
+ hMessage.setBlocked(true);
+ }
+ }
+
+ private void onUserRemove(HMessage hMessage) {
+ HPacket hPacket = hMessage.getPacket();
+
+ final byte[] dataRemainder = hPacket.readBytes(hPacket.getBytesLength() - hPacket.getReadIndex());
+ final String data = new String(dataRemainder, StandardCharsets.ISO_8859_1);
+ int index = Integer.parseInt(data);
+
+ Player player = findPlayerByIndex(index);
+
+ if (player != null) {
+ playerList.remove(player);
+ }
+ }
+
+ private void onUserObject(HMessage hMessage) {
+ HPacket hPacket = hMessage.getPacket();
+ final byte[] dataRemainder = hPacket.readBytes(hPacket.length() - hPacket.getReadIndex());
+ final String data = new String(dataRemainder, StandardCharsets.ISO_8859_1);
+
+ String[] pairs = data.split("\r");
+
+ String nameValue = null;
+
+ for (String pair : pairs) {
+ String[] keyValue = pair.split("=");
+ if (keyValue.length == 2 && keyValue[0].equals("name")) {
+ nameValue = keyValue[1];
+ break;
+ }
+ }
+
+ habboUserName = nameValue;
+ }
+
+
+ private void onUsersOrigin(HMessage hMessage) {
+ new Thread(() -> {
+ try {
+ HPacket hPacket = hMessage.getPacket();
+ OHEntity[] roomUsersList = OHEntity.parse(hPacket);
+
+ for (OHEntity hEntity : roomUsersList) {
+ String playerName = hEntity.getName();
+ if (playerName.equals(habboUserName)) {
+ continue;
+ }
+
+ if(playerListView.getItems().contains(playerName)) {
+ sendToServer(new ShockPacketOutgoing("{out:KICKUSER}{s:\"" + playerName + "\"}"));
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+
+
+ protected Player findPlayerByIndex(int index) {
+ return playerList.stream().filter(player -> player.getIndex() == index).findFirst().orElse(null);
+ }
+
+ protected Player findPlayerByUserName(String userName) {
+ return playerList.stream().filter(player -> Objects.equals(player.getName(), userName)).findFirst().orElse(null);
+ }
+
+ public void unbanPlayer(ActionEvent actionEvent) {
+ String playerName = playerListView.getSelectionModel().getSelectedItem();
+ if (playerName == null) {
+ Platform.runLater(() -> {
+ labelInfo.setText("Select a player to unban!");
+ });
+ return;
+ }
+
+ Platform.runLater(() -> {
+ playerListView.getItems().remove(playerName);
+ updateRoomCache();
+ });
+ }
+
+
+ private void loadRoomCache(String sanitizedRoomId) {
+ JSONObject cache = Cacher.getCacheContents();
+
+ if(cache.has(sanitizedRoomId)) {
+ JSONArray jsonArray = (JSONArray) Cacher.get(sanitizedRoomId);
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject bannedPlayer = jsonArray.getJSONObject(i);
+ Platform.runLater(() -> {
+ playerListView.getItems().add(bannedPlayer.getString("name"));
+ });
+ }
+ }
+ }
+
+ private void setupCache() {
+ File extDir = null;
+ try {
+ extDir = (new File(BanHammer.class.getProtectionDomain().getCodeSource().getLocation().toURI())).getParentFile();
+ if (extDir.getName().equals("Extensions")) {
+ extDir = extDir.getParentFile();
+ }
+ } catch (URISyntaxException ignored) {
+ }
+
+ Cacher.setCacheDir(extDir + File.separator + "Cache");
+ }
+
+ public void updateRoomCache() {
+ JSONArray jsonBannedList = new JSONArray();
+ for (String name : playerListView.getItems()) {
+ JSONObject jsonPlayer = new JSONObject();
+ jsonPlayer.put("name", name);
+ jsonBannedList.put(jsonPlayer);
+ }
+ Cacher.put(roomId, jsonBannedList);
+ }
+
+ public void clearList(ActionEvent actionEvent) {
+ Cacher.put(roomId, new JSONArray());
+ Platform.runLater(() -> {
+ playerListView.getItems().clear();
+ });
+ }
+}
diff --git a/src/main/java/BanHammerLauncher.java b/src/main/java/BanHammerLauncher.java
new file mode 100644
index 0000000..85d0dec
--- /dev/null
+++ b/src/main/java/BanHammerLauncher.java
@@ -0,0 +1,28 @@
+import gearth.extensions.ExtensionForm;
+import gearth.extensions.ExtensionFormCreator;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+
+public class BanHammerLauncher extends ExtensionFormCreator {
+
+ @Override
+ public ExtensionForm createForm(Stage primaryStage) throws Exception {
+ FXMLLoader loader = new FXMLLoader(getClass().getResource("BanHammer.fxml"));
+ Parent root = loader.load();
+
+ primaryStage.setTitle("Ban Hammer!");
+ primaryStage.setScene(new Scene(root));
+ primaryStage.setResizable(false);
+ primaryStage.setAlwaysOnTop(true);
+ primaryStage.getIcons().add(new Image("icon_white_bg.png"));
+
+ return loader.getController();
+ }
+
+ public static void main(String[] args) {
+ runExtensionForm(args, BanHammerLauncher.class);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/entities/Player.java b/src/main/java/entities/Player.java
new file mode 100644
index 0000000..c706cff
--- /dev/null
+++ b/src/main/java/entities/Player.java
@@ -0,0 +1,74 @@
+package entities;
+
+
+public class Player
+{
+ private int id;
+
+ private int index;
+ private String name;
+ private int coordX = -1;
+ private int coordY = -1;
+ private String figureId = "";
+ private boolean isBot = false;
+ public Player(Integer id, Integer index, String name)
+ {
+ this.id = id;
+ this.index = index;
+ this.name = name;
+ this.coordX = 0;
+ this.coordY = 0;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setCoordX(int coordX) {
+ this.coordX = coordX;
+ }
+
+ public void setCoordY(int coordY) {
+ this.coordY = coordY;
+ }
+
+ public boolean isBot() {
+ return isBot;
+ }
+
+ public void setBot(boolean bot) {
+ isBot = bot;
+ }
+
+ public int getCoordX() {
+ return coordX;
+ }
+
+ public int getCoordY() {
+ return coordY;
+ }
+
+ public String getFigureId() {
+ return figureId;
+ }
+
+ public void setFigureId(String figureId) {
+ this.figureId = figureId;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/parsers/OHEntity.java b/src/main/java/parsers/OHEntity.java
new file mode 100644
index 0000000..94e3f63
--- /dev/null
+++ b/src/main/java/parsers/OHEntity.java
@@ -0,0 +1,140 @@
+package parsers;
+
+
+import gearth.extensions.parsers.HEntityType;
+import gearth.protocol.HPacket;
+
+public class OHEntity {
+ private int id;
+ private int index;
+ private String name;
+ private String figureId;
+ private String gender;
+ private String motto;
+ private int x;
+ private int y;
+ private String z;
+ private String poolFigure;
+ private String badgeCode;
+ private HEntityType entityType;
+ public OHEntity(HPacket packet) {
+ this.index = packet.readInteger();
+ this.name = packet.readString();
+ this.figureId = packet.readString();
+ this.gender = packet.readString();
+ this.motto = packet.readString();
+ this.x = packet.readInteger();
+ this.y = packet.readInteger();
+ this.z = packet.readString();
+ this.poolFigure = packet.readString();
+ this.badgeCode = packet.readString();
+ int entityTypeId = packet.readInteger();
+ this.entityType = HEntityType.valueOf(entityTypeId);
+ }
+
+ public static OHEntity[] parse(HPacket packet) {
+ OHEntity[] entities = new OHEntity[packet.readInteger()];
+
+ for(int i = 0; i < entities.length; ++i) {
+ entities[i] = new OHEntity(packet);
+ }
+
+ return entities;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ public void setIndex(int index) {
+ this.index = index;
+ }
+
+ public String getGender() {
+ return gender;
+ }
+
+ public void setGender(String gender) {
+ this.gender = gender;
+ }
+
+ public String getMotto() {
+ return motto;
+ }
+
+ public void setMotto(String motto) {
+ this.motto = motto;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public String getZ() {
+ return z;
+ }
+
+ public void setZ(String z) {
+ this.z = z;
+ }
+
+ public String getPoolFigure() {
+ return poolFigure;
+ }
+
+ public void setPoolFigure(String poolFigure) {
+ this.poolFigure = poolFigure;
+ }
+
+ public String getBadgeCode() {
+ return badgeCode;
+ }
+
+ public void setBadgeCode(String badgeCode) {
+ this.badgeCode = badgeCode;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getFigureId() {
+ return figureId;
+ }
+
+ public void setFigureId(String figureId) {
+ this.figureId = figureId;
+ }
+
+ public HEntityType getEntityType() {
+ return entityType;
+ }
+
+ public void setEntityType(HEntityType entityType) {
+ this.entityType = entityType;
+ }
+}
diff --git a/src/main/resources/BanHammer.fxml b/src/main/resources/BanHammer.fxml
new file mode 100644
index 0000000..8085aa2
--- /dev/null
+++ b/src/main/resources/BanHammer.fxml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png
new file mode 100644
index 0000000..de887a0
Binary files /dev/null and b/src/main/resources/icon.png differ
diff --git a/src/main/resources/icon_white_bg.png b/src/main/resources/icon_white_bg.png
new file mode 100644
index 0000000..de887a0
Binary files /dev/null and b/src/main/resources/icon_white_bg.png differ