diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..3df6a91
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: ['https://paypal.me/LinsaFTW']
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..96a13c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.project
+.vscode
+.settings
+.factorypath
+.classpath
+/target
+
+pom.xml
diff --git a/README.md b/README.md
index 5c77929..2b1b4a1 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,29 @@
-# ExploitFixer
-Advanced plugin to maintain your server safe from multiple exploits.
+# AkarinFixerExploit
+
+AkarinFixerExploit is a high performance packet filter that prevents many kinds of crashers, laggers and exploits from being used in your Spigot/Paper server.
+
+You can download the prebuilt jar binaries in the link below.
+
+## Links
+
+discord
+https://discord.gg/PeS8h8eJZJ
+
+download
+https://github.com/LuciaNishimiya/AkarinFixerExploits/releases
+
+## How To (Server Admins)
+
+Download a copy of ExploitFixer. https://github.com/LuciaNishimiya/AkarinFixerExploits/releases
+Install in your Spigot/Paper plugins folder.
+
+This plugin requires HamsterAPI installed in your Spigot/paper servers. This is a custom High Performance packet listener.
+https://www.spigotmc.org/resources/2ls-hamsterapi.78831/updates
+
+
+
+## How To (Compiling From Source)
+
+To compile ExploitFixer, you need [JDK8](https://adoptopenjdk.net/releases.html), [maven](https://maven.apache.org/download.cgi), and an internet connection.
+
+Clone this repo, run ```mvn clean install``` from *bash*.
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..4287a29
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,80 @@
+
+ 4.0.0
+
+ dev._2lstudios
+ ExploitFixer
+ jar
+
+ ExploitFixer
+ Advanced anti-exploit security plugin for Spigot servers.
+ 2.0.8
+ https://www.spigotmc.org/resources/62842/
+
+
+ 2LS
+ UTF-8
+
+
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+ 2lstudios
+ https://ci.2lstudios.dev/plugin/repository/everything/
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+
+
+ org.spigotmc
+ spigot-api
+ 1.19-R0.1-SNAPSHOT
+ provided
+
+
+ com.github.2lstudios-mc
+ HamsterAPI
+ e3215bcc5a
+
+
+
+
+ ${artifactId}
+ src/main/java
+ clean install
+
+
+ src/main/resources
+ true
+
+ plugin.yml
+ bungee.yml
+
+
+
+ src/main/resources
+
+ plugin.yml
+ bungee.yml
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.1
+
+ 1.8
+ 1.8
+
+
+
+
+
diff --git a/src/bungee.yml b/src/bungee.yml
deleted file mode 100644
index fe3b842..0000000
--- a/src/bungee.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-name: ExploitFixer
-main: twolovers.exploitfixer.bungee.ExploitFixer
-version: 0.6.1
-author: 2LS
-commands:
- exploitfixer:
- description: The main ExploitFixer command
- usage: /exploitfixer
- aliases: [ef]
\ No newline at end of file
diff --git a/src/config.yml b/src/config.yml
deleted file mode 100644
index 5fc5ea3..0000000
--- a/src/config.yml
+++ /dev/null
@@ -1,107 +0,0 @@
-# Shows notifications on detection.
-notifications:
- # Do you want to enable notifications?
- enabled: true
-
- # Placeholders: %player% %ping%
- # %check% gets replaced dynamically by the check.
- message: "&c&lEF: &e%player% &f| &6%check% &f| &c%ping%ms"
-
-# Prevents exploits with custompayload packets.
-custompayload:
- # Do you want to enable this module?
- enabled: true
-
- # Kick the player on detection?
- # Set to false to only cancel.
- kick: true
-
- # Maximum custom payload packets that can be sent per second.
- limit:
- normal: 6
- # Increase if you have false positives in BungeeCord.
- other: 400
-
- # Placeholders: %player%
- punish_commands:
- - ""
-
-# Checks if players have a invalid UUID.
-uuidspoof:
- # Do you want to enable this module?
- enabled: true
-
- # Placeholders: %player%
- punish_commands:
- - ""
-
-# Checks if players try to crash/exploit the server with commands.
-commands:
- # Do you want to enable this module?
- enabled: true
-
- # Kick the player on detection?
- # Set to false to only cancel.
- kick: false
-
- # Commands to detect.
- commands:
- - "//calc"
- - "//calculate"
- - "//eval"
- - "//evaluate"
- - "//solve"
- - "/hd readtext"
- - "/holographicdisplays readtext"
- - "/pex promote"
- - "/pex demote"
- - "/promote"
- - "/demote"
-
- # Placeholders: %player%
- punish_commands:
- - ""
-
-##############################
-# Bukkit only #
-##############################
-
-# Limits packets with ProtocolLib to avoid exploits.
-packets:
- # Do you want to enable this module?
- enabled: true
-
- # Max packets per second.
- INTERACT: 80
- INTERACT_BOOKS: 5
- ABILITIES: 250
- ARM_ANIMATION: 80
- BLOCK_DIG: 120
- BLOCK_PLACE: 80
- KEEP_ALIVE: 5
- POSITION: 250
- WINDOW_CLICK: 80
-
-# Prevents duplication with Signs/Books.
-duplication:
- # Do you want to enable this module?
- enabled: true
-
- # Kick the player on detection? If you dont it will only cancel.
- kick: false
-
- # Placeholders: %player%
- punish_commands:
- - ""
-
-# Prevents exploits with hacked creative items.
-items:
- # Do you want to enable this module?
- enabled: true
-
- # Kick the player on detection? If you dont it will only cancel.
- kick: false
-
- # Placeholders: %player%
- punish_commands:
- - ""
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/ExploitFixer.java b/src/main/java/dev/_2lstudios/exploitfixer/ExploitFixer.java
new file mode 100644
index 0000000..2fff527
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/ExploitFixer.java
@@ -0,0 +1,115 @@
+package dev._2lstudios.exploitfixer;
+
+import java.util.logging.Logger;
+
+import org.bukkit.Server;
+import org.bukkit.plugin.PluginManager;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitScheduler;
+
+import dev._2lstudios.exploitfixer.commands.ExploitFixerCommand;
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+import dev._2lstudios.exploitfixer.listener.ListenerInitializer;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.tasks.ExploitFixerRepeatingTask;
+import dev._2lstudios.exploitfixer.utils.BukkitConfigurationUtil;
+
+public class ExploitFixer extends JavaPlugin {
+ private static ExploitFixer instance;
+ private BukkitConfigurationUtil configurationUtil;
+ private ModuleManager moduleManager;
+ private ListenerInitializer listenerInitializer;
+
+ public static synchronized ExploitFixer getInstance() {
+ return instance;
+ }
+
+ public static synchronized void setInstance(ExploitFixer exploitFixer) {
+ ExploitFixer.instance = exploitFixer;
+ }
+
+ @Override
+ public void onEnable() {
+ setInstance(this);
+
+ if (!checkHamsterAPI()) {
+ throw new IllegalStateException(
+ "ExploitFixer requires HamsterAPI to listen to packets and block exploits! Download: https://www.spigotmc.org/resources/78831/");
+ }
+
+ configurationUtil = new BukkitConfigurationUtil(this);
+
+ createConfigurations();
+
+ IConfiguration configYml = configurationUtil.get("%datafolder%/config.yml");
+
+ moduleManager = new ModuleManager(configurationUtil, this);
+ moduleManager.reload(configYml);
+ listenerInitializer = new ListenerInitializer(this, moduleManager);
+
+ register();
+ }
+
+ @Override
+ public void onDisable() {
+ unregister();
+ }
+
+ public void reload() {
+ createConfigurations();
+
+ IConfiguration configYml = configurationUtil.get("%datafolder%/config.yml");
+
+ moduleManager.reload(configYml);
+
+ unregister();
+ register();
+ }
+
+ private boolean checkHamsterAPI() {
+ Server server = this.getServer();
+ PluginManager pluginManager = server.getPluginManager();
+
+ return pluginManager.isPluginEnabled("HamsterAPI");
+ }
+
+ private void createConfigurations() {
+ configurationUtil.create("%datafolder%/config.yml", "config.yml");
+ }
+
+ public void unregister() {
+ Server server = this.getServer();
+ BukkitScheduler scheduler = server.getScheduler();
+
+ scheduler.cancelTasks(this);
+ server.getMessenger().unregisterIncomingPluginChannel(this);
+
+ if (this.listenerInitializer != null) {
+ this.listenerInitializer.unregister();
+ }
+ }
+
+ private void register() {
+ Server server = this.getServer();
+ BukkitScheduler scheduler = server.getScheduler();
+ Logger logger = this.getLogger();
+
+ getCommand("exploitfixer").setExecutor(new ExploitFixerCommand(this, moduleManager));
+
+ logger.info("Successfully registered commands!");
+
+ if (this.listenerInitializer.isRegistered()) {
+ this.listenerInitializer.unregister();
+ }
+
+ this.listenerInitializer.register();
+
+ logger.info("Successfully registered listeners!");
+ logger.info("Successfully hooked with HamsterAPI!");
+
+ scheduler.runTaskTimerAsynchronously(this,
+ new ExploitFixerRepeatingTask(moduleManager.getNotificationsModule()), 20L, 20L);
+
+ logger.info("Successfully registered tasks!");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/commands/ExploitFixerCommand.java b/src/main/java/dev/_2lstudios/exploitfixer/commands/ExploitFixerCommand.java
new file mode 100644
index 0000000..b1617aa
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/commands/ExploitFixerCommand.java
@@ -0,0 +1,60 @@
+package dev._2lstudios.exploitfixer.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import dev._2lstudios.exploitfixer.ExploitFixer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+
+public class ExploitFixerCommand implements CommandExecutor {
+ private ExploitFixer exploitFixer;
+ private MessagesModule messagesModule;
+ private NotificationsModule notificationsModule;
+ private ExploitPlayerManager exploitPlayerManager;
+
+ public ExploitFixerCommand(ExploitFixer exploitFixer, ModuleManager moduleManager) {
+ this.exploitFixer = exploitFixer;
+ this.messagesModule = moduleManager.getMessagesModule();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label,
+ String[] args) {
+ int length = args.length;
+ String lang = null;
+
+ if (sender instanceof Player) {
+ lang = exploitPlayerManager.get((Player) sender).getLocale();
+ }
+
+ if (length < 1 || args[0].equalsIgnoreCase("help")) {
+ HelpCommand helpCommand = new HelpCommand(messagesModule, lang);
+
+ helpCommand.onCommand(sender, command, label, args);
+ } else if (args[0].equalsIgnoreCase("reload")) {
+ ReloadCommand reloadCommand = new ReloadCommand(exploitFixer, messagesModule, lang);
+
+ reloadCommand.onCommand(sender, command, label, args);
+ } else if (args[0].equalsIgnoreCase("stats")) {
+ StatsCommand statsCommand = new StatsCommand(exploitPlayerManager, messagesModule, lang);
+
+ statsCommand.onCommand(sender, command, label, args);
+ } else if (args[0].equalsIgnoreCase("notifications")) {
+ NotificationsCommand notificationsCommand = new NotificationsCommand(notificationsModule,
+ messagesModule, lang);
+
+ notificationsCommand.onCommand(sender, command, label, args);
+ } else {
+ sender.sendMessage(messagesModule.getUnknown(lang));
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/commands/HelpCommand.java b/src/main/java/dev/_2lstudios/exploitfixer/commands/HelpCommand.java
new file mode 100644
index 0000000..3a4663b
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/commands/HelpCommand.java
@@ -0,0 +1,24 @@
+package dev._2lstudios.exploitfixer.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+
+public class HelpCommand implements CommandExecutor {
+ private MessagesModule messagesModule;
+ private String lang;
+
+ public HelpCommand(MessagesModule messagesModule, String lang) {
+ this.messagesModule = messagesModule;
+ this.lang = lang;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ sender.sendMessage(messagesModule.getHelp(lang).replace("%command%", label));
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/commands/NotificationsCommand.java b/src/main/java/dev/_2lstudios/exploitfixer/commands/NotificationsCommand.java
new file mode 100644
index 0000000..503a87e
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/commands/NotificationsCommand.java
@@ -0,0 +1,45 @@
+package dev._2lstudios.exploitfixer.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class NotificationsCommand implements CommandExecutor {
+ private NotificationsModule notificationsModule;
+ private MessagesModule messagesModule;
+ private String lang;
+
+ public NotificationsCommand(NotificationsModule notificationsModule, MessagesModule messagesModule, String lang) {
+ this.notificationsModule = notificationsModule;
+ this.messagesModule = messagesModule;
+ this.lang = lang;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (sender instanceof Player) {
+ if (sender.hasPermission("exploitfixer.admin")
+ || sender.hasPermission("exploitfixer.notifications")) {
+ String playerName = sender.getName();
+
+ if (!notificationsModule.isNotifications(playerName)) {
+ notificationsModule.setNotifications(playerName, true);
+ sender.sendMessage(messagesModule.getEnable(lang));
+ } else {
+ notificationsModule.setNotifications(playerName, false);
+ sender.sendMessage(messagesModule.getDisable(lang));
+ }
+ } else {
+ sender.sendMessage(messagesModule.getPermission(lang));
+ }
+ } else {
+ sender.sendMessage(messagesModule.getConsole(lang));
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/commands/ReloadCommand.java b/src/main/java/dev/_2lstudios/exploitfixer/commands/ReloadCommand.java
new file mode 100644
index 0000000..f9d986d
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/commands/ReloadCommand.java
@@ -0,0 +1,32 @@
+package dev._2lstudios.exploitfixer.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+import dev._2lstudios.exploitfixer.ExploitFixer;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+
+public class ReloadCommand implements CommandExecutor {
+ private ExploitFixer exploitFixer;
+ private MessagesModule messagesModule;
+ private String lang;
+
+ public ReloadCommand(ExploitFixer exploitFixer, MessagesModule messagesModule, String lang) {
+ this.exploitFixer = exploitFixer;
+ this.messagesModule = messagesModule;
+ this.lang = lang;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (sender.hasPermission("exploitfixer.admin")) {
+ exploitFixer.reload();
+ sender.sendMessage(messagesModule.getReload(lang));
+ } else {
+ sender.sendMessage(messagesModule.getPermission(lang));
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/commands/StatsCommand.java b/src/main/java/dev/_2lstudios/exploitfixer/commands/StatsCommand.java
new file mode 100644
index 0000000..2d6c3a9
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/commands/StatsCommand.java
@@ -0,0 +1,33 @@
+package dev._2lstudios.exploitfixer.commands;
+
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+
+public class StatsCommand implements CommandExecutor {
+ private ExploitPlayerManager exploitPlayerManager;
+ private MessagesModule messagesModule;
+ private String lang;
+
+ public StatsCommand(ExploitPlayerManager exploitPlayerManager, MessagesModule messagesModule, String lang) {
+ this.exploitPlayerManager = exploitPlayerManager;
+ this.messagesModule = messagesModule;
+ this.lang = lang;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (sender.hasPermission("exploitfixer.admin")) {
+ sender.sendMessage(messagesModule.getStats(lang)
+ .replace("%players_punished%", String.valueOf(exploitPlayerManager.getKicked()))
+ .replace("%players_cached%", String.valueOf(exploitPlayerManager.getSize())));
+ } else {
+ sender.sendMessage(messagesModule.getPermission(lang));
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/configuration/BukkitConfiguration.java b/src/main/java/dev/_2lstudios/exploitfixer/configuration/BukkitConfiguration.java
new file mode 100644
index 0000000..c411b60
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/configuration/BukkitConfiguration.java
@@ -0,0 +1,88 @@
+package dev._2lstudios.exploitfixer.configuration;
+
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+public class BukkitConfiguration implements IConfiguration {
+ private ConfigurationSection configuration;
+
+ public BukkitConfiguration(ConfigurationSection configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public IConfiguration getSection(String path) {
+ Object section = configuration.get(path);
+
+ if (section instanceof ConfigurationSection) {
+ return new BukkitConfiguration((ConfigurationSection) section);
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Collection getKeys() {
+ return configuration.getKeys(false);
+ }
+
+ @Override
+ public Collection getStringList(String path) {
+ if (configuration.contains(path)) {
+ return new HashSet<>(configuration.getStringList(path));
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public String getString(String path) {
+ return configuration.getString(path);
+ }
+
+ @Override
+ public String getString(String path, String def) {
+ if (contains(path)) {
+ return getString(path);
+ } else {
+ return def;
+ }
+ }
+
+ @Override
+ public double getDouble(String path) {
+ return configuration.getDouble(path);
+ }
+
+ @Override
+ public long getLong(String path) {
+ return configuration.getLong(path);
+ }
+
+ @Override
+ public int getInt(String path) {
+ return configuration.getInt(path);
+ }
+
+ @Override
+ public boolean getBoolean(String path) {
+ return configuration.getBoolean(path);
+ }
+
+ @Override
+ public boolean getBoolean(String path, boolean def) {
+ return configuration.getBoolean(path, def);
+ }
+
+ @Override
+ public boolean contains(String path) {
+ return configuration.contains(path);
+ }
+
+ @Override
+ public Object getObject() {
+ return configuration;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/configuration/IConfiguration.java b/src/main/java/dev/_2lstudios/exploitfixer/configuration/IConfiguration.java
new file mode 100644
index 0000000..8d719fe
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/configuration/IConfiguration.java
@@ -0,0 +1,29 @@
+package dev._2lstudios.exploitfixer.configuration;
+
+import java.util.Collection;
+
+public interface IConfiguration {
+ public IConfiguration getSection(String string);
+
+ public Collection getKeys();
+
+ public Collection getStringList(String string);
+
+ public String getString(String path);
+
+ public String getString(String path, String def);
+
+ public double getDouble(String path);
+
+ public long getLong(String path);
+
+ public int getInt(String path);
+
+ public boolean getBoolean(String path);
+
+ public boolean getBoolean(String path, boolean def);
+
+ public Object getObject();
+
+ public boolean contains(String string);
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/enums/CheckItemResult.java b/src/main/java/dev/_2lstudios/exploitfixer/enums/CheckItemResult.java
new file mode 100644
index 0000000..6cc4cbf
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/enums/CheckItemResult.java
@@ -0,0 +1,22 @@
+package dev._2lstudios.exploitfixer.enums;
+
+public enum CheckItemResult {
+ // General Items
+ INVALID_ITEM,
+ INVALID_ITEM_NAME,
+ INVALID_ITEM_LORE,
+ // Fireworks
+ INVALID_FIREWORK_POWER,
+ INVALID_FIREWORK_EFFECTS,
+ INVALID_FIREWORK_COLORS,
+ INVALID_FIREWORK_FADE_COLORS,
+ // Books
+ INVALID_BOOK_TITLE,
+ INVALID_BOOK_AUTHOR,
+ INVALID_BOOK_PAGES,
+ INVALID_BOOK_SIZE,
+ // Blocks
+ INVALID_BLOCK,
+ // Valid
+ VALID_ITEM
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/exploit/BukkitExploitPlayer.java b/src/main/java/dev/_2lstudios/exploitfixer/exploit/BukkitExploitPlayer.java
new file mode 100644
index 0000000..60bd953
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/exploit/BukkitExploitPlayer.java
@@ -0,0 +1,223 @@
+package dev._2lstudios.exploitfixer.exploit;
+
+import java.lang.invoke.MethodHandle;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.UUID;
+
+import org.bukkit.Server;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitScheduler;
+
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.IModule;
+import dev._2lstudios.exploitfixer.modules.IViolationModule;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.utils.ReflectionUtil;
+import dev._2lstudios.hamsterapi.enums.PacketType;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayer;
+import dev._2lstudios.hamsterapi.wrappers.PacketWrapper;
+
+public class BukkitExploitPlayer extends ExploitPlayer {
+ private Plugin plugin;
+ private ExploitPlayerManager exploitPlayerManager;
+ private MessagesModule messagesModule;
+ private NotificationsModule notificationsModule;
+
+ // Permission to bypass the items fix module
+ private boolean bypassItemsFix = false;
+
+ public BukkitExploitPlayer(Plugin plugin, ModuleManager moduleManager, UUID uuid) {
+ super(uuid);
+ this.plugin = plugin;
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ this.messagesModule = moduleManager.getMessagesModule();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ }
+
+ public void addVls(Cancellable event, HamsterPlayer hamsterPlayer,
+ IViolationModule module, double amount) {
+ addVls(event, null, hamsterPlayer, module, amount);
+ }
+
+ public void addVls(Cancellable event, PacketWrapper packet,
+ HamsterPlayer hamsterPlayer, IViolationModule module, double amount) {
+ addVls(event, packet, hamsterPlayer, module, amount, "Other");
+ }
+
+ public void addVls(Cancellable event, PacketWrapper packet,
+ HamsterPlayer hamsterPlayer, IViolationModule module, double amount, String category) {
+ Violations violations = (Violations) module.getViolations();
+
+ if (violations != null) {
+ Map violationsMap = getViolations();
+ long currentTime = System.currentTimeMillis();
+ long lastViolation = getLastViolation();
+
+ if (currentTime - lastViolation >= 1000) {
+ clearPackets();
+ clearPunishments();
+ setLastViolation(currentTime);
+
+ for (IViolationModule violationModule : new HashSet<>(violationsMap.keySet())) {
+ double vls = violationsMap.get(violationModule) - violationModule.getReduceVls();
+
+ if (vls <= 0) {
+ violationsMap.remove(violationModule);
+ } else {
+ violationsMap.put(violationModule, vls);
+ }
+ }
+ }
+
+ double newVls = getViolations(module) + amount;
+
+ violationsMap.put(module, newVls);
+ notificationsModule.addCategorizedVl(category, amount);
+
+ if (event instanceof Cancellable && module.getCancelVls() <= newVls) {
+ ((Cancellable) event).setCancelled(true);
+ }
+
+ for (int threshold : violations.getViolations()) {
+ if (threshold <= newVls) {
+ Collection commands = violations.getCommands(threshold);
+
+ punish(module, hamsterPlayer, packet, commands, (int) newVls);
+ }
+ }
+ }
+ }
+
+ public void punish(IModule module, HamsterPlayer hamsterPlayer, PacketWrapper packet,
+ Collection punishments, int newVls) {
+ Collection> punishmentsDone = this.getPunishments();
+
+ if (!punishments.isEmpty() && !punishmentsDone.contains(punishments)) {
+ Server server = plugin.getServer();
+ CommandSender consoleSender = server.getConsoleSender();
+ BukkitScheduler scheduler = server.getScheduler();
+ Player player = hamsterPlayer.getPlayer();
+ String playerName = player.getName();
+
+ for (String punishment : punishments) {
+ if (punishment.equals("kick")) {
+ String locale = getLocale();
+ String kickMessage = messagesModule.getKickMessage(module, locale);
+
+ hamsterPlayer.disconnect(kickMessage);
+
+ // When kicked, count as punishment
+ exploitPlayerManager.addKicked();
+ } else if (punishment.equals("notification")) {
+ String moduleName = module.getName();
+
+ if (packet != null) {
+ PacketType packetType = packet.getType();
+
+ if (packetType != null) {
+ notificationsModule.sendNotification(packetType.toString(), this, newVls);
+ } else {
+ notificationsModule.sendNotification(moduleName, this, newVls);
+ }
+ } else {
+ notificationsModule.sendNotification(moduleName, this, newVls);
+ }
+ } else {
+ String check_name = packet != null ? packet.getType().toString() : module.getName();
+ int ping = getPing();
+ String replacement_punishment = punishment
+ .replace("%player%", playerName)
+ .replace("%vls%", String.valueOf(newVls))
+ .replace("%ping%", String.valueOf(ping))
+ .replace("%check%", check_name);
+ if (server.isPrimaryThread()) {
+ server.dispatchCommand(consoleSender, replacement_punishment);
+ } else {
+ scheduler.runTask(plugin, () -> {
+ server.dispatchCommand(consoleSender, replacement_punishment);
+ });
+ }
+ }
+
+ // Add this punishment as already done to not execute twice
+ punishmentsDone.add(punishments);
+ }
+ }
+ }
+
+ public Player getPlayer() {
+ return plugin.getServer().getPlayer(getUUID());
+ }
+
+ public String getLocale() {
+ Player player = getPlayer();
+ String locale = null;
+
+ if (player != null && player.isOnline()) {
+ MethodHandle getLocaleMethod = ReflectionUtil.getLocalePlayerMethod();
+
+ try {
+ if (getLocaleMethod != null) {
+ locale = getLocaleMethod.invoke(player).toString();
+ } else {
+ getLocaleMethod = ReflectionUtil.getLocaleSpigotMethod();
+ if (getLocaleMethod != null) {
+ locale = getLocaleMethod.invoke(player.spigot()).toString();
+ }
+ }
+ } catch (Throwable t) {
+ // The player is invalid, ignore
+ }
+
+
+ if (locale != null && locale.length() > 1) {
+ locale = locale.substring(0, 2);
+ }
+ }
+
+ return locale;
+ }
+
+ public int getPing() {
+ Player player = getPlayer();
+
+ if (player != null && player.isOnline()) {
+ try {
+ MethodHandle getHandleMethod = ReflectionUtil.getHandleMethod();
+
+ Object playerHandle = getHandleMethod.invoke(player);
+
+ return (int) ReflectionUtil.getPingField(playerHandle).get(playerHandle);
+ } catch (Throwable ignored) {
+ // Ignored
+ }
+ }
+
+ return 0;
+ }
+
+ public String getName() {
+ Player player = plugin.getServer().getPlayer(getUUID());
+
+ if (player != null && player.isOnline()) {
+ return player.getName();
+ }
+
+ return "N/A";
+ }
+
+ public boolean isBypassItemsFix() {
+ return bypassItemsFix;
+ }
+
+ public void setBypassItemsFix(boolean bypassItemsFix) {
+ this.bypassItemsFix = bypassItemsFix;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/exploit/ExploitPlayer.java b/src/main/java/dev/_2lstudios/exploitfixer/exploit/ExploitPlayer.java
new file mode 100644
index 0000000..0f2d775
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/exploit/ExploitPlayer.java
@@ -0,0 +1,96 @@
+package dev._2lstudios.exploitfixer.exploit;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.UUID;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+import dev._2lstudios.exploitfixer.modules.IViolationModule;
+
+public abstract class ExploitPlayer {
+ private UUID uuid;
+ private Map violations = new HashMap<>();
+ private Map packets = new ConcurrentHashMap<>();
+ private Collection> punishments = new HashSet<>();
+ private int packetCount = 0;
+ private long lastViolation = 0;
+ private long lastPortalUse = 0;
+ private long lastShear = 0;
+
+ public ExploitPlayer(UUID uuid) {
+ this.uuid = uuid;
+ }
+
+ public void clearPunishments() {
+ this.punishments.clear();
+ }
+
+ public Collection> getPunishments() {
+ return punishments;
+ }
+
+ public long getLastViolation() {
+ return lastViolation;
+ }
+
+ public void setLastViolation(long lastPortalUse) {
+ this.lastViolation = lastPortalUse;
+ }
+
+ public long getLastPortalUse() {
+ return lastPortalUse;
+ }
+
+ public void setLastPortalUse(long lastPortalUse) {
+ this.lastPortalUse = lastPortalUse;
+ }
+
+ public Map getViolations() {
+ return violations;
+ }
+
+ public double getViolations(IViolationModule module) {
+ return violations.getOrDefault(module, 0D);
+ }
+
+ public UUID getUUID() {
+ return uuid;
+ }
+
+ public abstract String getName();
+
+ public abstract int getPing();
+
+ public abstract String getLocale();
+
+ public boolean hasShearCooldown() {
+ return System.currentTimeMillis() - lastShear <= 1000;
+ }
+
+ public void setShearCooldown() {
+ lastShear = System.currentTimeMillis();
+ }
+
+ public void addPacket(String packetName) {
+ packetCount++;
+ packets.put(packetName, packets.getOrDefault(packetName, 0) + 1);
+ }
+
+ public void clearPackets() {
+ packetCount = 0;
+ packets.clear();
+ }
+
+ public String getPacketsText() {
+ StringBuilder stringBuilder = new StringBuilder("Received Packets (x" + packetCount + "):");
+
+ for (Entry packet : packets.entrySet()) {
+ stringBuilder.append("\n- x" + packet.getValue() + " " + packet.getKey());
+ }
+
+ return stringBuilder.toString();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/exploit/Violations.java b/src/main/java/dev/_2lstudios/exploitfixer/exploit/Violations.java
new file mode 100644
index 0000000..6963d99
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/exploit/Violations.java
@@ -0,0 +1,30 @@
+package dev._2lstudios.exploitfixer.exploit;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public class Violations {
+ private Map> violations = new HashMap<>();
+
+ public Violations(IConfiguration configuration) {
+ if (configuration != null) {
+ for (String key : configuration.getKeys()) {
+ try {
+ this.violations.put(Integer.parseInt(key), configuration.getStringList(key));
+ } catch (NumberFormatException ignored) {
+ }
+ }
+ }
+ }
+
+ public Collection getCommands(int violations) {
+ return this.violations.getOrDefault(violations, null);
+ }
+
+ public Collection getViolations() {
+ return violations.keySet();
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockBreakListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockBreakListener.java
new file mode 100644
index 0000000..3e57cca
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockBreakListener.java
@@ -0,0 +1,30 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+
+public class BlockBreakListener implements Listener {
+ private EventsModule eventsModule;
+
+ BlockBreakListener(ModuleManager moduleManager) {
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
+ public void onBlockBreak(BlockBreakEvent event) {
+ // Check if enabled
+ if (eventsModule.isInventoryExploit()) {
+ // Get the player
+ Player player = event.getPlayer();
+
+ // Close the inventory
+ player.closeInventory();
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockDispenseListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockDispenseListener.java
new file mode 100644
index 0000000..85d2db5
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/BlockDispenseListener.java
@@ -0,0 +1,59 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockFace;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockDispenseEvent;
+import org.bukkit.material.Dispenser;
+import org.bukkit.material.MaterialData;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class BlockDispenseListener implements Listener {
+ private NotificationsModule notificationsModule;
+ private EventsModule eventsModule;
+
+ BlockDispenseListener(ModuleManager moduleManager) {
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ // This listener only runs in 1.12 servers otherwise exception will be thrown
+ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
+ public void onBlockDispense(BlockDispenseEvent event) {
+ if (eventsModule.isDispenserCrash()) {
+ Block block = event.getBlock();
+ Location location = block.getLocation();
+ World world = location.getWorld();
+
+ if (world != null) {
+ int maxHeight = world.getMaxHeight() - 1;
+ MaterialData materialData = block.getState().getData();
+
+ if (materialData instanceof Dispenser) {
+ Dispenser dispenser = (Dispenser) materialData;
+ BlockFace face = dispenser.getFacing();
+ double y = location.getY();
+
+ // Return if it's not a shulker
+ if (!event.getItem().getType().toString().contains("SHULKER")) {
+ return;
+ }
+
+ if ((y >= maxHeight && face == BlockFace.UP
+ || y <= 1 && face == BlockFace.DOWN)) {
+ event.setCancelled(true);
+ notificationsModule
+ .debug("[Events] A dispenser tried to drop objects from invalid locations.");
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/EntityDamageByEntityListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/EntityDamageByEntityListener.java
new file mode 100644
index 0000000..f85ab56
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/EntityDamageByEntityListener.java
@@ -0,0 +1,40 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class EntityDamageByEntityListener implements Listener {
+ private NotificationsModule notificationsModule;
+ private EventsModule eventsModule;
+
+ EntityDamageByEntityListener(ModuleManager moduleManager) {
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onEntityDamageByEntity(EntityDamageByEntityEvent event) {
+ if (!eventsModule.isSelfDamage()) {
+ return;
+ }
+
+ Entity entity = event.getEntity();
+
+ if (entity instanceof Player) {
+ Entity damager = event.getDamager();
+
+ if (damager instanceof Player && entity == damager) {
+ event.setCancelled(true);
+ notificationsModule.debug("[Events] Cancelled self damage by " + entity.getName() + "!");
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/ListenerInitializer.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/ListenerInitializer.java
new file mode 100644
index 0000000..aacbc82
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/ListenerInitializer.java
@@ -0,0 +1,59 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Server;
+import org.bukkit.event.HandlerList;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.plugin.PluginManager;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.utils.ExploitUtil;
+
+public class ListenerInitializer {
+ private Plugin plugin;
+ private ModuleManager moduleManager;
+ private boolean registered = false;
+
+ public ListenerInitializer(Plugin plugin, ModuleManager moduleManager) {
+ this.plugin = plugin;
+ this.moduleManager = moduleManager;
+ }
+
+ public void register() {
+ this.registered = true;
+
+ Server server = plugin.getServer();
+ PluginManager pluginManager = server.getPluginManager();
+ ExploitUtil exploitUtil = new ExploitUtil(moduleManager);
+
+ // Only register this listener on 1.12
+ if (Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3].startsWith("v1_12")) {
+ pluginManager.registerEvents(new BlockDispenseListener(moduleManager), plugin);
+ }
+
+ pluginManager.registerEvents(new BlockBreakListener(moduleManager), plugin);
+ pluginManager.registerEvents(new EntityDamageByEntityListener(moduleManager), plugin);
+ pluginManager.registerEvents(new MapInitializeListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PacketDecodeListener(exploitUtil, moduleManager), plugin);
+ pluginManager.registerEvents(new PacketReceiveListener(exploitUtil, moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerCommandListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerInteractListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerLoginListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerMoveListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerQuitListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerShearEntityListener(moduleManager), plugin);
+ pluginManager.registerEvents(new PlayerTeleportListener(moduleManager), plugin);
+ pluginManager.registerEvents(new ProjectileLaunchListener(), plugin);
+ pluginManager.registerEvents(new StructureGrowListener(moduleManager), plugin);
+ }
+
+ public void unregister() {
+ this.registered = false;
+
+ HandlerList.unregisterAll(plugin);
+ }
+
+ public boolean isRegistered() {
+ return this.registered;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/MapInitializeListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/MapInitializeListener.java
new file mode 100644
index 0000000..4d7249e
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/MapInitializeListener.java
@@ -0,0 +1,31 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.server.MapInitializeEvent;
+import org.bukkit.map.MapView;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+
+public class MapInitializeListener implements Listener {
+ private ModuleManager moduleManager;
+
+ public MapInitializeListener(ModuleManager moduleManager) {
+ this.moduleManager = moduleManager;
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onMapInitialize(MapInitializeEvent event) {
+ if (moduleManager.getEventsModule().isDisableTracking()) {
+ MapView mapView = event.getMap();
+
+ try {
+ if (mapView.isTrackingPosition()) {
+ mapView.setTrackingPosition(false);
+ }
+ } catch (NoSuchMethodError ex) {
+ // Some versions won't support this method :(
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketDecodeListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketDecodeListener.java
new file mode 100644
index 0000000..b091fdd
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketDecodeListener.java
@@ -0,0 +1,131 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import java.nio.charset.Charset;
+import java.util.regex.Pattern;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.PacketsModule;
+import dev._2lstudios.exploitfixer.utils.ExploitUtil;
+import dev._2lstudios.exploitfixer.utils.ProtocolUtil;
+import dev._2lstudios.hamsterapi.events.PacketDecodeEvent;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayer;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.EmptyByteBuf;
+
+public class PacketDecodeListener implements Listener {
+ private static String PREFIX = "[Decoder|Data] ";
+ private static String PREFIX_OFFLINE = "[Decoder|Offline] ";
+
+ // Pattern for detecting an amount of tags used
+ private Pattern tagPattern;
+
+ private ExploitUtil exploitUtil;
+ private PacketsModule packetsModule;
+
+ PacketDecodeListener(ExploitUtil exploitUtil, ModuleManager moduleManager) {
+ this.exploitUtil = exploitUtil;
+ this.packetsModule = moduleManager.getPacketsModule();
+ this.tagPattern = Pattern.compile("(.)[{]{" + packetsModule.getDataMaxFlags() + ",}(.)");
+ }
+
+ private String getName(Player player) {
+ if (player == null) {
+ return "unknown";
+ }
+ return player.getName();
+ }
+
+ private void onPacketDecode(Cancellable event, HamsterPlayer hamsterPlayer, Player player,
+ ByteBuf byteBuf) {
+ if (player != null && player.isOnline()) {
+ String bypassPermission = packetsModule.getBypassPermission();
+
+ if (bypassPermission != null && player.hasPermission(bypassPermission)) {
+ return;
+ }
+
+ String playerName = player.getName();
+ double dataVls = packetsModule.getDataVls();
+ double dataVlMultiplier = packetsModule.getDataVlMultiplier();
+ int dataBytes = packetsModule.getDataMaxSize();
+ int refCnt = byteBuf.refCnt();
+ int capacity = byteBuf.capacity();
+
+ if (capacity < 0) {
+ String reason = PREFIX + playerName
+ + " sent a packet with invalid capacity! capacity: " + capacity + " Vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Bad Capacity");
+ } else if (refCnt < 1) {
+ String reason = PREFIX + playerName + " sent a packet with invalid refCnt! refCnt: "
+ + refCnt + " Vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Bad Reference Count");
+ } else {
+ int readableBytes = byteBuf.readableBytes();
+ String reason = null;
+
+ if (capacity > dataBytes) {
+ reason = PREFIX + playerName + " sent a packet that exceeds capacity bytes limit! " + capacity
+ + "/" + dataBytes + " Vls: " + dataVls;
+ } else if (readableBytes > dataBytes) {
+ reason = PREFIX + playerName + " sent a packet that exceeds readable bytes limit! " + readableBytes
+ + "/" + dataBytes + " Vls: " + dataVls;
+ } else {
+ if (!(byteBuf instanceof EmptyByteBuf)) {
+ int oldReaderIndex = byteBuf.readerIndex();
+ int packetId = ProtocolUtil.readVarInt(byteBuf);
+
+ byteBuf.readerIndex(oldReaderIndex);
+
+ if (packetId == 18) {
+ return;
+ } else {
+ // Check if it's a edit book packet
+ String content = byteBuf.toString(Charset.defaultCharset());
+
+ byteBuf.readerIndex(oldReaderIndex);
+
+ if (packetsModule.getDataMaxFlags() > 0 && tagPattern.matcher(content).find()) {
+ reason = PREFIX + playerName
+ + " sent a packet with too many tags! Vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Many Tags");
+ }
+ }
+ }
+
+ if (dataVlMultiplier > 0) {
+ exploitUtil.addVls(event, hamsterPlayer, player, capacity * dataVlMultiplier, "Too Much Data");
+ }
+
+ return;
+ }
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Too Big Packet");
+ }
+ } else if (packetsModule.isOffline()) {
+ String reason = PREFIX_OFFLINE + getName(player) + " sent a packet while being offline!";
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, 0, "Offline");
+ }
+ }
+
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
+ public void onPacketDecode(PacketDecodeEvent event) {
+ if (!packetsModule.isEnabled()) {
+ return;
+ }
+
+ HamsterPlayer hamsterPlayer = event.getHamsterPlayer();
+ ByteBuf byteBuf = event.getByteBuf().get();
+
+ onPacketDecode(event, hamsterPlayer, hamsterPlayer.getPlayer(), byteBuf);
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketReceiveListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketReceiveListener.java
new file mode 100644
index 0000000..352e93f
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PacketReceiveListener.java
@@ -0,0 +1,345 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.bukkit.Bukkit;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryView;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+
+import dev._2lstudios.exploitfixer.enums.CheckItemResult;
+import dev._2lstudios.exploitfixer.exploit.BukkitExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.ItemsFixModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.modules.PacketsModule;
+import dev._2lstudios.exploitfixer.utils.ExploitUtil;
+import dev._2lstudios.hamsterapi.enums.PacketType;
+import dev._2lstudios.hamsterapi.events.PacketReceiveEvent;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayer;
+import dev._2lstudios.hamsterapi.wrappers.PacketWrapper;
+
+public class PacketReceiveListener implements Listener {
+ private ExploitUtil exploitUtil;
+ private ExploitPlayerManager exploitPlayerManager;
+ private ItemsFixModule itemsFixModule;
+ private NotificationsModule notificationsModule;
+ private PacketsModule packetsModule;
+ private String version;
+ private boolean checkLectern;
+ private ItemStack AIR = new ItemStack(Material.AIR);
+
+ PacketReceiveListener(ExploitUtil exploitUtil, ModuleManager moduleManager) {
+ this.exploitUtil = exploitUtil;
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ this.itemsFixModule = moduleManager.getItemsFixModule();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.packetsModule = moduleManager.getPacketsModule();
+ this.version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
+ this.checkLectern = version.equals("v1_18_R1")
+ || version.startsWith("v1_17") || version.startsWith("v1_16")
+ || version.startsWith("v1_15") || version.startsWith("v1_14")
+ || version.startsWith("v1_13") || version.startsWith("v1_12")
+ || version.startsWith("v1_11") || version.startsWith("v1_10");
+ }
+
+ public void onPacketReceive(Cancellable event, HamsterPlayer hamsterPlayer, Player player,
+ PacketWrapper packetWrapper) {
+ String bypassPermission = packetsModule.getBypassPermission();
+
+ if (bypassPermission != null && player.hasPermission(bypassPermission)) {
+ return;
+ }
+
+ PacketType packetType = packetWrapper.getType();
+ String packetName = packetWrapper.getName();
+ String playerName = player.getName();
+ Map strings = packetWrapper.getStrings();
+ BukkitExploitPlayer exploitPlayer = exploitPlayerManager.get(player);
+ double dataVls = packetsModule.getDataVls();
+ Map integers = packetWrapper.getIntegers();
+ InventoryView inventoryView = player.getOpenInventory();
+ double windowClick = packetsModule.getWindowClick();
+ double setCreativeSlot = packetsModule.getSetCreativeSlot();
+
+ exploitPlayer.addVls(event, null, hamsterPlayer, packetsModule, packetsModule.getMultiplier(packetName),
+ "Packet Rate");
+
+ if (packetsModule.isBlacklisted(packetName)) {
+ String reason = "[" + packetName + "] " + playerName + " sent a Blacklisted packet!";
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, 0, "Blacklist");
+ return;
+ }
+
+ if (packetType == PacketType.PacketPlayInCustomPayload) {
+ if (!strings.isEmpty()) {
+ String tag = strings.values().iterator().next();
+ double tagVls = packetsModule.getTagVls();
+ double bookVls = packetsModule.getBookVls();
+
+ if (tagVls > 0 && (tag == null || tag.trim().isEmpty())) {
+ String reason = "[" + packetName + "|Tag] " + playerName
+ + " sent a CustomPayload packet without TAG! Added vls: " + tagVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, tagVls, "Invalid Payload");
+ return;
+ } else if (bookVls > 0
+ && (tag.equals("MC|BEdit") || tag.equals("MC|BSign") || tag.equals("MC|BOpen"))) {
+ PlayerInventory playerInventory = player.getInventory();
+ ItemStack itemInHand = playerInventory.getItem(playerInventory.getHeldItemSlot());
+
+ if (itemInHand != null && !itemInHand.getType().toString().contains("BOOK")) {
+ String reason = "[" + packetName + "|Book] " + playerName + " tried to send a " + tag
+ + " CustomPayload packet without a book in hand!";
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, bookVls, "Payload No Book");
+ return;
+ }
+ } else if (tag.equals("MC|AdvCdm") && !player.isOp()) {
+ String reason = "[" + packetName + "|Command Block] " + playerName + " tried to send a " + tag
+ + " CustomPayload packet without being op!";
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, bookVls, "Payload Not Operator");
+ return;
+ }
+
+ exploitPlayer.addVls(event, hamsterPlayer, packetsModule, packetsModule.getMultiplier(tag));
+ } else {
+ notificationsModule.debug("[" + packetName + "] " + playerName + " sent a empty CustomPayload packet!");
+
+ event.setCancelled(true);
+ }
+ }
+
+ // Check if Window Click vls is more than 0
+ if (windowClick > 0) {
+ // Check if the packet is Window Click
+ if (packetType == PacketType.PacketPlayInWindowClick) {
+ // Get the top inventory
+ Inventory topInventory = inventoryView.getTopInventory();
+
+ // Check if the top inventory is lectern
+ if (topInventory.getType().name().equals("LECTERN")) {
+ if (this.checkLectern) {
+ if (integers.containsKey("a") && integers.get("a") == 1) {
+ String reason = "[" + packetName + "|WindowClick] " + playerName
+ + " tried to quick move on a lectern! Added vls: "
+ + windowClick;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, windowClick,
+ "Lectern Crash");
+ return;
+ } else {
+ if (integers.containsKey("b") && integers.get("b") == 1) {
+ String reason = "[" + packetName + "|WindowClick] " + playerName
+ + " tried to quick move on a lectern! Added vls: "
+ + windowClick;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, windowClick,
+ "Lectern Crash");
+ return;
+ }
+ }
+ }
+ }
+
+ // Check if the slot variable is set
+ boolean containsSlot = integers.containsKey("slot");
+
+ // Sometimes, d is used as slot
+ boolean containsD = integers.containsKey("d");
+
+ if (containsSlot || containsD) {
+ // Get the clicked slot
+ int slot = containsSlot ? integers.get("slot") : integers.get("d");
+
+ // Get the maximum slot index
+ int maxSlots = inventoryView.countSlots() - 1;
+
+ // -999 and -1 are legit slots
+ if (slot != -999 && slot != -1) {
+ // Check if the slot is negative
+ if (slot < 0) {
+ String reason = "[" + packetName + "|WindowClick] " + playerName
+ + " sent a slot less than 0 and not [-999 or -1]! Slot: " + slot + " Added vls: "
+ + windowClick;
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, windowClick,
+ "Invalid Slot");
+ return;
+ } else {
+ // Try to get an item in that slot
+ // If this check generates an exception, it means it blocked the exploit
+ try {
+ inventoryView.getItem(slot);
+ } catch (Exception ex) {
+ String reason = "[" + packetName + "|WindowClick] " + playerName
+ + " exceeded max available slots! (" + slot + "/" + maxSlots + ") Added vls: "
+ + windowClick;
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, windowClick,
+ "Large/Short Slot");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (setCreativeSlot > 0 && packetType == PacketType.PacketPlayInSetCreativeSlot
+ && player.getGameMode() != GameMode.CREATIVE) {
+ String reason = "[" + packetName + "|SetCreativeSlot] " + playerName
+ + " sent SET_CREATIVE_SLOT without CREATIVE! Added vls: " + setCreativeSlot;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, setCreativeSlot,
+ "Creative Packet No Creative");
+ return;
+ }
+
+ if (dataVls > 0) {
+ if (packetType == PacketType.PacketPlayInUpdateSign
+ && !exploitUtil.checkSign(strings.values().toArray(new String[0]))) {
+ String reason = "[" + packetName + "|Data] " + playerName
+ + " has sent a too big sign packet! Added vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Too Big Sign");
+ return;
+ }
+ }
+
+ if (dataVls > 0) {
+ for (Double val : packetWrapper.getDouble().values()) {
+ if (!Double.isFinite(val)) {
+ String reason = "[" + packetName + "|Data] " + playerName
+ + " sent an invalid double: " + val + "! Added vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Invalid Double");
+ return;
+ }
+ }
+
+ for (Float val : packetWrapper.getFloats().values()) {
+ if (!Float.isFinite(val)) {
+ String reason = "[" + packetName + "|Data] " + playerName
+ + " sent an invalid float: " + val + "! Added vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Invalid Float");
+ return;
+ }
+ }
+ }
+
+ boolean creativeMode = packetType == PacketType.PacketPlayInSetCreativeSlot && itemsFixModule.isEnabled();
+
+ for (Entry itemsEntry : packetWrapper.getItems().entrySet()) {
+ ItemStack item = itemsEntry.getValue();
+
+ if (item != null) {
+ if (dataVls > 0) {
+ CheckItemResult result = exploitUtil.checkItem(item);
+
+ if (result != CheckItemResult.VALID_ITEM) {
+ String reason = "[" + packetName + "] " + playerName
+ + " sent an invalid item! (" + result + ") Added vls: " + dataVls;
+
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, dataVls, "Invalid Item");
+ return;
+ }
+ }
+
+ if (creativeMode && !exploitPlayer.isBypassItemsFix()) {
+ String blacklisted = exploitUtil.clearIfBlacklisted(item);
+
+ if (blacklisted != null) {
+ packetWrapper.write(itemsEntry.getKey(), AIR);
+ notificationsModule.debug(
+ playerName + " had a creative item blacklisted by ExploitFixer! (" + blacklisted + ")");
+ } else {
+ // Check if whitelist system is enabled
+ if (itemsFixModule.isWhitelistItems()) {
+ // Create the slot variable
+ int slot = 0;
+
+ // Get the slot from a packet integer
+ if (integers.size() == 1) {
+ for (int value : integers.values()) {
+ slot = value;
+ }
+ }
+
+ // Check the slot item and if exists, whitelist it
+ if (slot != -1 && slot != -999) {
+ // Check for window click exploit
+ try {
+ // Get the item by the slot
+ ItemStack inventoryItem = inventoryView.getItem(slot);
+
+ if (inventoryItem != null && inventoryItem.getType() != Material.AIR) {
+ // Whitelist already created item
+ itemsFixModule.setWhitelisted(inventoryItem);
+ return;
+ }
+ } catch (IndexOutOfBoundsException ex) {
+ // Window click exploit detected
+ String reason = "[" + packetName + "|PacketPlayInSetCreativeSlot] " + playerName
+ + " exceeded max available slots! (" + slot + "/"
+ + inventoryView.countSlots()
+ + ") Added vls: "
+ + windowClick;
+
+ // Cancel and notify
+ exploitUtil.cancelExploit(event, hamsterPlayer, player, reason, windowClick,
+ "Large/Short Slot");
+ return;
+ }
+ }
+
+ // Check if the item is not whitelisted
+ if (!itemsFixModule.isWhitelisted(item)) {
+ // Fix the newly created item
+ packetWrapper.write(itemsEntry.getKey(), itemsFixModule.fixItem(item));
+
+ // Notify about this
+ notificationsModule.debug(playerName + " had a creative item fixed by ExploitFixer!");
+ return;
+ }
+ } else { // Whitelist system not enabled
+ // Fix the item
+ packetWrapper.write(itemsEntry.getKey(), itemsFixModule.fixItem(item));
+
+ // Notify about this
+ notificationsModule.debug(playerName + " had a creative item fixed by ExploitFixer!");
+ }
+ }
+ }
+ }
+ }
+
+ // Add the packet to the player instance to count it
+ exploitPlayer.addPacket(String.valueOf(packetType));
+
+ // Add the packet to the notifications module to count it
+ notificationsModule.addPacketDebug(String.valueOf(packetType));
+ }
+
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
+ public void onPacketReceive(PacketReceiveEvent event) {
+ if (!packetsModule.isEnabled()) {
+ return;
+ }
+
+ HamsterPlayer hamsterPlayer = event.getHamsterPlayer();
+
+ onPacketReceive(event, hamsterPlayer, hamsterPlayer.getPlayer(), event.getPacket());
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerCommandListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerCommandListener.java
new file mode 100644
index 0000000..540c3e6
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerCommandListener.java
@@ -0,0 +1,53 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+
+import dev._2lstudios.exploitfixer.exploit.BukkitExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.CommandsModule;
+import dev._2lstudios.hamsterapi.HamsterAPI;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayer;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayerManager;
+
+public class PlayerCommandListener implements Listener {
+ private HamsterPlayerManager hamsterPlayerManager;
+ private CommandsModule commandsModule;
+ private ExploitPlayerManager exploitPlayerManager;
+
+ PlayerCommandListener(ModuleManager moduleManager) {
+ this.hamsterPlayerManager = HamsterAPI.getInstance().getHamsterPlayerManager();
+ this.commandsModule = moduleManager.getCommandsModule();
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onPlayerCommand(PlayerCommandPreprocessEvent event) {
+ Player player = event.getPlayer();
+
+ // Commands while offline/dead wont be executed
+ if (!player.isOnline() || player.isDead()) {
+ event.setCancelled(true);
+ return;
+ }
+
+ String bypassPermission = commandsModule.getBypassPermission();
+
+ if (bypassPermission != null && player.hasPermission(bypassPermission)) {
+ return;
+ }
+
+ BukkitExploitPlayer exploitPlayer = exploitPlayerManager.get(player);
+
+ if (commandsModule.isEnabled() && commandsModule.isCommand(event.getMessage())) {
+ HamsterPlayer hamsterPlayer = hamsterPlayerManager.get(player);
+
+ exploitPlayer.punish(commandsModule, hamsterPlayer, null, commandsModule.getPunishments(), 1);
+ event.setCancelled(true);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerInteractListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerInteractListener.java
new file mode 100644
index 0000000..fdc660b
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerInteractListener.java
@@ -0,0 +1,56 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.ItemFrame;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.PlayerInventory;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.MapMeta;
+import org.bukkit.map.MapView;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+
+public class PlayerInteractListener implements Listener {
+ private ModuleManager moduleManager;
+
+ public PlayerInteractListener(ModuleManager moduleManager) {
+ this.moduleManager = moduleManager;
+ }
+
+ @EventHandler(ignoreCancelled = true)
+ public void onPlayerInteract(PlayerInteractEntityEvent event) {
+ if (moduleManager.getEventsModule().isDisableTracking()) {
+ Entity rightClicked = event.getRightClicked();
+
+ if (rightClicked instanceof ItemFrame) {
+ ItemFrame frame = (ItemFrame) rightClicked;
+
+ if (frame.isEmpty()) {
+ PlayerInventory inventory = event.getPlayer().getInventory();
+ ItemStack itemInHand = inventory.getItem(inventory.getHeldItemSlot());
+
+ if (itemInHand != null && itemInHand.hasItemMeta()) {
+ ItemMeta itemMeta = itemInHand.getItemMeta();
+
+ if (itemMeta instanceof MapMeta) {
+ MapMeta mapMeta = (MapMeta) itemMeta;
+ MapView mapView = mapMeta.getMapView();
+
+ try {
+ if (mapView.isTrackingPosition()) {
+ mapView.setTrackingPosition(false);
+ itemInHand.setItemMeta(mapMeta);
+ }
+ } catch (NoSuchMethodError ex) {
+ // Some version won't support this method :(
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerLoginListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerLoginListener.java
new file mode 100644
index 0000000..bdf5089
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerLoginListener.java
@@ -0,0 +1,61 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import java.net.InetAddress;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerLoginEvent;
+
+import dev._2lstudios.exploitfixer.exploit.ExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.modules.ConnectionModule;
+import dev._2lstudios.exploitfixer.modules.ItemsFixModule;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+
+public class PlayerLoginListener implements Listener {
+ private ExploitPlayerManager exploitPlayerManager;
+ private NotificationsModule notificationsModule;
+ private MessagesModule messagesModule;
+ private ConnectionModule connectionModule;
+ private ItemsFixModule itemsFixModule;
+
+ PlayerLoginListener(ModuleManager moduleManager) {
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ this.connectionModule = moduleManager.getConnectionModule();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.messagesModule = moduleManager.getMessagesModule();
+ this.itemsFixModule = moduleManager.getItemsFixModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
+ public void onPlayerLogin(PlayerLoginEvent event) {
+ Player player = event.getPlayer();
+ String playerName = player.getName();
+ InetAddress address = event.getAddress();
+
+ if (connectionModule.isNullAddressEnabled() && address == null) {
+ ExploitPlayer exploitPlayer = exploitPlayerManager.get(player);
+ String nullAddressKickMessage = messagesModule.getKickMessage("nulladdress", exploitPlayer.getLocale());
+
+ event.setKickMessage(nullAddressKickMessage);
+ event.setResult(PlayerLoginEvent.Result.KICK_OTHER);
+ }
+
+ if (player.hasPermission("exploitfixer.notifications")) {
+ notificationsModule.setNotifications(playerName, true);
+ }
+
+ // Get the items fix bypass permission
+ String itemsFixBypassPermission = itemsFixModule.getBypassPermission();
+
+ // Check if the permission is configured
+ if (itemsFixBypassPermission != null) {
+ // Set the player as bypassing items fix check
+ exploitPlayerManager.get(player).setBypassItemsFix(player.hasPermission(itemsFixBypassPermission));
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerMoveListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerMoveListener.java
new file mode 100644
index 0000000..ef7e5d8
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerMoveListener.java
@@ -0,0 +1,39 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.Chunk;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class PlayerMoveListener implements Listener {
+ private NotificationsModule notificationsModule;
+ private EventsModule eventsModule;
+
+ PlayerMoveListener(ModuleManager moduleManager) {
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
+ public void onPlayerMove(PlayerMoveEvent event) {
+ if (!eventsModule.isNullChunk()) {
+ return;
+ }
+
+ Location to = event.getTo();
+ World world = to.getWorld();
+ Chunk chunk = to.getChunk();
+
+ if (chunk == null || !world.isChunkLoaded(chunk)) {
+ event.setCancelled(true);
+ notificationsModule.debug("[Events] Cancelled movement to null chunk by " + event.getPlayer().getName() + "!");
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerQuitListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerQuitListener.java
new file mode 100644
index 0000000..456de01
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerQuitListener.java
@@ -0,0 +1,28 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class PlayerQuitListener implements Listener {
+ private ExploitPlayerManager exploitPlayerManager;
+ private NotificationsModule notificationsModule;
+
+ PlayerQuitListener(ModuleManager variables) {
+ this.exploitPlayerManager = variables.getExploitPlayerManager();
+ this.notificationsModule = variables.getNotificationsModule();
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ Player player = event.getPlayer();
+
+ exploitPlayerManager.remove(player);
+ notificationsModule.setNotifications(player.getName(), false);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerShearEntityListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerShearEntityListener.java
new file mode 100644
index 0000000..b1fd8f2
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerShearEntityListener.java
@@ -0,0 +1,31 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerShearEntityEvent;
+
+import dev._2lstudios.exploitfixer.exploit.ExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+
+public class PlayerShearEntityListener implements Listener {
+ private ExploitPlayerManager exploitPlayerManager;
+
+ public PlayerShearEntityListener(ModuleManager moduleManager) {
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ }
+
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
+ public void onPlayerShearEntity(PlayerShearEntityEvent event) {
+ ExploitPlayer exploitPlayer = exploitPlayerManager.get(event.getPlayer());
+
+ if (exploitPlayer != null) {
+ if (exploitPlayer.hasShearCooldown()) {
+ event.setCancelled(true);
+ } else {
+ exploitPlayer.setShearCooldown();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerTeleportListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerTeleportListener.java
new file mode 100644
index 0000000..2edad80
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/PlayerTeleportListener.java
@@ -0,0 +1,49 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerTeleportEvent;
+
+import dev._2lstudios.exploitfixer.exploit.ExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class PlayerTeleportListener implements Listener {
+ private ExploitPlayerManager exploitPlayerManager;
+ private NotificationsModule notificationsModule;
+ private EventsModule eventsModule;
+
+ PlayerTeleportListener(ModuleManager moduleManager) {
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
+ public void onPlayerTeleport(PlayerTeleportEvent event) {
+ if (eventsModule.isPortalCrash()) {
+ String cause = event.getCause().name();
+
+ if (cause.equals("END_PORTAL") || cause.equals("END_GATEWAY") || cause.equals("NETHER_PORTAL")) {
+ Player player = event.getPlayer();
+ ExploitPlayer exploitPlayer = exploitPlayerManager.get(player);
+
+ if (exploitPlayer != null) {
+ long currentTime = System.currentTimeMillis();
+ long lastPortalUse = currentTime - exploitPlayer.getLastPortalUse();
+
+ if (lastPortalUse <= 1000) {
+ event.setCancelled(true);
+ notificationsModule.debug("[Events] Too fast portal teleport was cancelled.");
+ } else {
+ exploitPlayer.setLastPortalUse(currentTime);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/ProjectileLaunchListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/ProjectileLaunchListener.java
new file mode 100644
index 0000000..8ddfcc3
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/ProjectileLaunchListener.java
@@ -0,0 +1,22 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.entity.Entity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.ProjectileLaunchEvent;
+
+public class ProjectileLaunchListener implements Listener {
+ @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
+ public void onProjectileLaunch(ProjectileLaunchEvent event) {
+ Entity entity = event.getEntity();
+
+ if (entity == null) {
+ return;
+ }
+
+ if (entity.getVelocity().lengthSquared() > 15) {
+ event.setCancelled(true);
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/listener/StructureGrowListener.java b/src/main/java/dev/_2lstudios/exploitfixer/listener/StructureGrowListener.java
new file mode 100644
index 0000000..d93186c
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/listener/StructureGrowListener.java
@@ -0,0 +1,41 @@
+package dev._2lstudios.exploitfixer.listener;
+
+import org.bukkit.World;
+import org.bukkit.block.Block;
+import org.bukkit.block.BlockState;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.world.StructureGrowEvent;
+
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class StructureGrowListener implements Listener {
+ private NotificationsModule notificationsModule;
+ private EventsModule eventsModule;
+
+ StructureGrowListener(ModuleManager moduleManager) {
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.eventsModule = moduleManager.getEventsModule();
+ }
+
+ @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
+ public void onStructureGrow(StructureGrowEvent event) {
+ if (eventsModule.isEnderPortalBreak()) {
+ World world = event.getWorld();
+
+ for (BlockState blockBefore : event.getBlocks()) {
+ Block block = world.getBlockAt(blockBefore.getLocation());
+ String type = block.getType().name();
+
+ if (type.equals("ENDER_PORTAL_FRAME")
+ || type.equals("END_PORTAL_FRAME")) {
+ event.setCancelled(true);
+ notificationsModule.debug("[Events] A mushroom tried to break an ender portal frame.");
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/managers/ExploitPlayerManager.java b/src/main/java/dev/_2lstudios/exploitfixer/managers/ExploitPlayerManager.java
new file mode 100644
index 0000000..02021b0
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/managers/ExploitPlayerManager.java
@@ -0,0 +1,70 @@
+package dev._2lstudios.exploitfixer.managers;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+
+import dev._2lstudios.exploitfixer.exploit.BukkitExploitPlayer;
+
+public class ExploitPlayerManager {
+ private Plugin plugin;
+ private Server server;
+ private ModuleManager moduleManager;
+ private Map exploitPlayers = new HashMap<>();
+ private int kicked = 0;
+
+ ExploitPlayerManager(Plugin plugin, Server server, ModuleManager moduleManager) {
+ this.plugin = plugin;
+ this.server = server;
+ this.moduleManager = moduleManager;
+ }
+
+ public BukkitExploitPlayer get(UUID uuid) {
+ BukkitExploitPlayer exploitPlayer;
+
+ if (exploitPlayers.containsKey(uuid)) {
+ exploitPlayer = exploitPlayers.get(uuid);
+ } else {
+ exploitPlayer = new BukkitExploitPlayer(this.plugin, moduleManager, uuid);
+ exploitPlayers.put(uuid, exploitPlayer);
+ }
+
+ return exploitPlayer;
+ }
+
+ public BukkitExploitPlayer get(Player player) {
+ return get(player.getUniqueId());
+ }
+
+ public void remove(UUID uuid) {
+ exploitPlayers.remove(uuid);
+ }
+
+ public void remove(Player player) {
+ remove(player.getUniqueId());
+ }
+
+ public void reload() {
+ exploitPlayers.clear();
+
+ for (Player player : server.getOnlinePlayers()) {
+ get(player);
+ }
+ }
+
+ public int getSize() {
+ return exploitPlayers.size();
+ }
+
+ public int getKicked() {
+ return kicked;
+ }
+
+ public int addKicked() {
+ return kicked++;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/managers/ModuleManager.java b/src/main/java/dev/_2lstudios/exploitfixer/managers/ModuleManager.java
new file mode 100644
index 0000000..c082cd8
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/managers/ModuleManager.java
@@ -0,0 +1,101 @@
+package dev._2lstudios.exploitfixer.managers;
+
+import java.io.File;
+import java.util.logging.Logger;
+
+import org.bukkit.Server;
+import org.bukkit.plugin.Plugin;
+
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.modules.EventsModule;
+import dev._2lstudios.exploitfixer.modules.ItemsFixModule;
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+import dev._2lstudios.exploitfixer.modules.CommandsModule;
+import dev._2lstudios.exploitfixer.modules.ConnectionModule;
+import dev._2lstudios.exploitfixer.modules.MessagesModule;
+import dev._2lstudios.exploitfixer.modules.PacketsModule;
+import dev._2lstudios.exploitfixer.utils.IConfigurationUtil;
+
+public class ModuleManager {
+ private Plugin plugin;
+ private CommandsModule commandsModule;
+ private ConnectionModule connectionModule;
+ private EventsModule eventsModule;
+ private ItemsFixModule itemsFixModule;
+ private MessagesModule messagesModule;
+ private NotificationsModule notificationsModule;
+ private PacketsModule packetsModule;
+ private ExploitPlayerManager exploitPlayerManager;
+
+ public ModuleManager(IConfigurationUtil configurationUtil, Plugin plugin) {
+ this.plugin = plugin;
+
+ Server server = plugin.getServer();
+ Logger logger = plugin.getLogger();
+
+ this.commandsModule = new CommandsModule();
+ this.connectionModule = new ConnectionModule();
+ this.eventsModule = new EventsModule();
+ this.itemsFixModule = new ItemsFixModule(plugin);
+ this.messagesModule = new MessagesModule(configurationUtil, logger, plugin.getDescription().getVersion());
+ this.notificationsModule = new NotificationsModule(server, logger);
+ this.packetsModule = new PacketsModule();
+ this.exploitPlayerManager = new ExploitPlayerManager(plugin, server, this);
+ }
+
+ public void reload(IConfiguration configYml) {
+ try {
+ File localeFolder = new File(plugin.getDataFolder() + "/locales/");
+
+ localeFolder.mkdirs();
+
+ this.commandsModule.reload(configYml);
+ this.connectionModule.reload(configYml);
+ this.eventsModule.reload(configYml);
+ this.itemsFixModule.reload(configYml);
+ this.messagesModule.reload(configYml, localeFolder);
+ this.notificationsModule.reload(configYml);
+ this.packetsModule.reload(configYml);
+ this.exploitPlayerManager.reload();
+ } catch (NullPointerException exception) {
+ NullPointerException newException = new NullPointerException(
+ "Your ExploitFixer configuration is wrong, please reset it or the plugin wont work!");
+
+ newException.setStackTrace(exception.getStackTrace());
+
+ throw newException;
+ }
+ }
+
+ public CommandsModule getCommandsModule() {
+ return commandsModule;
+ }
+
+ public ConnectionModule getConnectionModule() {
+ return connectionModule;
+ }
+
+ public EventsModule getEventsModule() {
+ return eventsModule;
+ }
+
+ public ItemsFixModule getItemsFixModule() {
+ return itemsFixModule;
+ }
+
+ public MessagesModule getMessagesModule() {
+ return messagesModule;
+ }
+
+ public NotificationsModule getNotificationsModule() {
+ return notificationsModule;
+ }
+
+ public ExploitPlayerManager getExploitPlayerManager() {
+ return exploitPlayerManager;
+ }
+
+ public PacketsModule getPacketsModule() {
+ return packetsModule;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/CommandsModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/CommandsModule.java
new file mode 100644
index 0000000..c154c42
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/CommandsModule.java
@@ -0,0 +1,56 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public class CommandsModule implements IPunishmentModule {
+ private static Pattern SYNTAX_PATTERN = Pattern.compile("[a-z0-9-]{1,}:");
+
+ private Collection commands;
+ private Collection punishments;
+ private String name;
+ private boolean enabled;
+ private String bypassPermission;
+
+ public void reload(IConfiguration configYml) {
+ this.name = "Commands";
+ this.enabled = configYml.getBoolean("commands.enabled");
+ this.commands = configYml.getStringList("commands.commands");
+ this.punishments = configYml.getStringList("commands.punishments");
+ this.bypassPermission = configYml.getString("commands.bypass-permission");
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public boolean isCommand(String rawMessage) {
+ if (rawMessage != null) {
+ String message = SYNTAX_PATTERN.matcher(rawMessage).replaceAll("").toLowerCase();
+
+ for (String command : commands) {
+ if (message.startsWith(command)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public Collection getPunishments() {
+ return punishments;
+ }
+
+ public String getBypassPermission() {
+ return bypassPermission;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/ConnectionModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/ConnectionModule.java
new file mode 100644
index 0000000..d7705a1
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/ConnectionModule.java
@@ -0,0 +1,36 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public class ConnectionModule implements IPunishmentModule {
+ private boolean nullAddressEnabled;
+ private String name;
+ private Collection punishments;
+
+ public void reload(IConfiguration configYml) {
+ this.nullAddressEnabled = configYml.getBoolean("connection.null_address");
+ this.name = "Connection";
+ this.punishments = configYml.getStringList("connection.punishments");
+ }
+
+ public boolean isNullAddressEnabled() {
+ return nullAddressEnabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public Collection getPunishments() {
+ return this.punishments;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/EventsModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/EventsModule.java
new file mode 100644
index 0000000..d7a4da1
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/EventsModule.java
@@ -0,0 +1,63 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public class EventsModule implements IModule {
+ private boolean nullChunk;
+ private boolean selfDamage;
+ private boolean disableTracking;
+ private boolean enderPortalBreak;
+ private boolean dispenserCrash;
+ private boolean portalCrash;
+ private boolean inventoryExploit;
+
+ public void reload(IConfiguration configYml) {
+ String name = getName().toLowerCase();
+
+ nullChunk = configYml.getBoolean(name + ".null_chunk", true);
+ selfDamage = configYml.getBoolean(name + ".self_damage", true);
+ disableTracking = configYml.getBoolean(name + ".disable_tracking", true);
+ enderPortalBreak = configYml.getBoolean(name + ".ender_portal_break", true);
+ dispenserCrash = configYml.getBoolean(name + ".dispenser_crash", true);
+ portalCrash = configYml.getBoolean(name + ".portal_crash", true);
+ inventoryExploit = configYml.getBoolean(name + ".inventory_exploit", true);
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ public String getName() {
+ return "events";
+ }
+
+ public boolean isSelfDamage() {
+ return selfDamage;
+ }
+
+ public boolean isNullChunk() {
+ return nullChunk;
+ }
+
+ public boolean isDisableTracking() {
+ return disableTracking;
+ }
+
+ public boolean isEnderPortalBreak() {
+ return enderPortalBreak;
+ }
+
+ public boolean isDispenserCrash() {
+ return dispenserCrash;
+ }
+
+ public boolean isPortalCrash() {
+ return portalCrash;
+ }
+
+ public boolean isInventoryExploit() {
+ return inventoryExploit;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/IMessagesModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/IMessagesModule.java
new file mode 100644
index 0000000..b58a269
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/IMessagesModule.java
@@ -0,0 +1,5 @@
+package dev._2lstudios.exploitfixer.modules;
+
+public interface IMessagesModule {
+ String getString(String locale, String path);
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/IModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/IModule.java
new file mode 100644
index 0000000..7d2de31
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/IModule.java
@@ -0,0 +1,7 @@
+package dev._2lstudios.exploitfixer.modules;
+
+public interface IModule {
+ public boolean isEnabled();
+
+ public String getName();
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/INotificationsModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/INotificationsModule.java
new file mode 100644
index 0000000..7f09ae6
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/INotificationsModule.java
@@ -0,0 +1,7 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import dev._2lstudios.exploitfixer.exploit.ExploitPlayer;
+
+public interface INotificationsModule extends IModule {
+ public void sendNotification(String check, ExploitPlayer player, int violations);
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/IPunishmentModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/IPunishmentModule.java
new file mode 100644
index 0000000..c50735b
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/IPunishmentModule.java
@@ -0,0 +1,7 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+
+public interface IPunishmentModule extends IModule {
+ public Collection getPunishments();
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/IViolationModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/IViolationModule.java
new file mode 100644
index 0000000..210528a
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/IViolationModule.java
@@ -0,0 +1,9 @@
+package dev._2lstudios.exploitfixer.modules;
+
+public interface IViolationModule extends IModule {
+ public double getCancelVls();
+
+ public double getReduceVls();
+
+ public Object getViolations();
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/ItemsFixModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/ItemsFixModule.java
new file mode 100644
index 0000000..83f3365
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/ItemsFixModule.java
@@ -0,0 +1,185 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.BannerMeta;
+import org.bukkit.inventory.meta.BookMeta;
+import org.bukkit.inventory.meta.EnchantmentStorageMeta;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.LeatherArmorMeta;
+import org.bukkit.inventory.meta.SkullMeta;
+import org.bukkit.plugin.Plugin;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public class ItemsFixModule implements IModule {
+ private Plugin plugin;
+ private boolean enabled;
+ private int enchantLimit;
+ private int maxStackSize;
+ private Collection blacklist;
+ private String bypassPermission;
+ private boolean useGameProfile;
+
+ // Whitelist already created items
+ private boolean whitelistItems;
+ private Collection fixed = new HashSet<>();
+
+ public ItemsFixModule(Plugin plugin) {
+ this.plugin = plugin;
+ String version = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
+ this.useGameProfile =
+ !version.startsWith("v1_17") && !version.startsWith("v1_16")
+ && !version.startsWith("v1_15") && !version.startsWith("v1_14")
+ && !version.startsWith("v1_13") && !version.startsWith("v1_12")
+ && !version.startsWith("v1_11") && !version.startsWith("v1_10")
+ && !version.startsWith("v1_9") && !version.startsWith("v1_8");
+ }
+
+ public void reload(IConfiguration configYml) {
+ String prefix = "creative-items-fix.";
+
+ this.enabled = configYml.getBoolean(prefix + "enabled");
+ this.enchantLimit = configYml.getInt(prefix + "enchant-limit");
+ this.maxStackSize = configYml.getInt(prefix + "max-stack-size");
+ this.blacklist = configYml.getStringList(prefix + "blacklist");
+ this.whitelistItems = configYml.getBoolean(prefix + "whitelist-items");
+ this.bypassPermission = configYml.getString(prefix + "bypass-permission");
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public String getName() {
+ return "CreativeItemsFix";
+ }
+
+ public int getEnchantLimit() {
+ return enchantLimit;
+ }
+
+ public int getMaxStackSize() {
+ return maxStackSize;
+ }
+
+ public Collection getBlacklist() {
+ return blacklist;
+ }
+
+ public String getBypassPermission() {
+ return bypassPermission;
+ }
+
+ public void setWhitelisted(ItemStack inventoryItem) {
+ fixed.add(inventoryItem);
+ }
+
+ public boolean isWhitelisted(ItemStack item) {
+ return fixed.contains(item);
+ }
+
+ public boolean isWhitelistItems() {
+ return whitelistItems;
+ }
+
+ public ItemStack fixItem(ItemStack item) {
+ Material material = Material.getMaterial(item.getType().name());
+ ItemMeta newItemMeta = plugin.getServer().getItemFactory().getItemMeta(material);
+ short durability = item.getDurability();
+
+ if (item.hasItemMeta()) {
+ ItemMeta oldItemMeta = item.getItemMeta();
+ String displayName = oldItemMeta.getDisplayName();
+ List lore = oldItemMeta.getLore();
+ // This applies vanilla levels to enchants
+ boolean ignoreLevelRestriction = enchantLimit > 0;
+
+ try {
+ if (oldItemMeta.hasCustomModelData()) {
+ newItemMeta.setCustomModelData(oldItemMeta.getCustomModelData());
+ }
+ } catch (NoSuchMethodError ex) {
+ // Does not support customModelData
+ }
+
+ if (oldItemMeta instanceof EnchantmentStorageMeta) {
+ EnchantmentStorageMeta enchantmentStorageMeta = (EnchantmentStorageMeta) oldItemMeta;
+ EnchantmentStorageMeta newEnchantmentStorageMeta = (EnchantmentStorageMeta) newItemMeta;
+
+ for (Entry entry : enchantmentStorageMeta.getStoredEnchants().entrySet()) {
+ Enchantment enchantment = entry.getKey();
+ int level = Math.min(entry.getValue(), enchantLimit > 0 ? enchantLimit : 5);
+
+ if (enchantLimit > -1 && level > -1) {
+ newEnchantmentStorageMeta.addStoredEnchant(enchantment, level, ignoreLevelRestriction);
+ }
+ }
+ } else {
+ for (Entry entry : item.getEnchantments().entrySet()) {
+ Enchantment enchantment = entry.getKey();
+ int level = Math.min(entry.getValue(), enchantLimit > 0 ? enchantLimit : 5);
+
+ if (enchantLimit > -1 && level > -1) {
+ newItemMeta.addEnchant(enchantment, level, ignoreLevelRestriction);
+ }
+ }
+ }
+
+ if (newItemMeta instanceof BookMeta) {
+ BookMeta oldBookMeta = (BookMeta) oldItemMeta;
+ BookMeta newBookMeta = (BookMeta) newItemMeta;
+
+ newBookMeta.setTitle(oldBookMeta.getTitle());
+ newBookMeta.setAuthor(oldBookMeta.getAuthor());
+ newBookMeta.setPages(oldBookMeta.getPages());
+ } else if (newItemMeta instanceof SkullMeta) {
+ SkullMeta oldSkullMeta = (SkullMeta) oldItemMeta;
+ SkullMeta newSkullMeta = (SkullMeta) newItemMeta;
+
+ if (useGameProfile) {
+ newSkullMeta.setOwnerProfile(oldSkullMeta.getOwnerProfile());
+ } else {
+ newSkullMeta.setOwner(oldSkullMeta.getOwner());
+ }
+ } else if (newItemMeta instanceof BannerMeta) {
+ BannerMeta oldBannerMeta = (BannerMeta) oldItemMeta;
+ BannerMeta newBannerMeta = (BannerMeta) newItemMeta;
+
+ newBannerMeta.setBaseColor(oldBannerMeta.getBaseColor());
+ } else if (newItemMeta instanceof LeatherArmorMeta) {
+ LeatherArmorMeta oldLeatherArmorMeta = (LeatherArmorMeta) oldItemMeta;
+ LeatherArmorMeta newLeatherArmorMeta = (LeatherArmorMeta) newItemMeta;
+
+ newLeatherArmorMeta.setColor(oldLeatherArmorMeta.getColor());
+ }
+
+ if (displayName != null && displayName.getBytes().length < 128) {
+ newItemMeta.setDisplayName(displayName);
+ }
+
+ if (lore != null && lore.toString().getBytes().length < 1024) {
+ newItemMeta.setLore(lore);
+ }
+ }
+
+ if (maxStackSize > 0 && item.getAmount() > maxStackSize) {
+ item.setAmount(maxStackSize);
+ }
+
+ item.setType(material);
+ item.setItemMeta(newItemMeta);
+ item.setDurability(durability);
+
+ return item;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/MessagesModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/MessagesModule.java
new file mode 100644
index 0000000..6ec4129
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/MessagesModule.java
@@ -0,0 +1,161 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+import dev._2lstudios.exploitfixer.utils.IConfigurationUtil;
+
+public class MessagesModule implements IMessagesModule {
+ private static String NOTFOUND_STRING = "";
+ private IConfigurationUtil configurationUtil;
+ private Logger logger;
+ private String version;
+ private Map> locales = new HashMap<>();
+ private Collection defaultLocales = new HashSet<>();
+ private String web;
+ private String defaultLocale;
+
+ public MessagesModule(IConfigurationUtil configurationUtil, Logger logger, String version) {
+ this.configurationUtil = configurationUtil;
+ this.logger = logger;
+ this.version = version;
+
+ defaultLocales.add("de");
+ defaultLocales.add("en");
+ defaultLocales.add("es");
+ defaultLocales.add("fr");
+ defaultLocales.add("he");
+ defaultLocales.add("hu");
+ defaultLocales.add("it");
+ defaultLocales.add("ja");
+ defaultLocales.add("ko");
+ defaultLocales.add("nl");
+ defaultLocales.add("pl");
+ defaultLocales.add("pt");
+ defaultLocales.add("ro");
+ defaultLocales.add("ru");
+ defaultLocales.add("th");
+ defaultLocales.add("tr");
+ defaultLocales.add("zh");
+ defaultLocales.add("zhtw");
+ }
+
+ public void putSection(IConfiguration langFile, Map locale, String currentPath) {
+ for (String key : langFile.getKeys()) {
+ IConfiguration section = langFile.getSection(key);
+
+ if (section != null) {
+ if (currentPath.isEmpty()) {
+ putSection(section, locale, key);
+ } else {
+ putSection(section, locale, currentPath + "." + key);
+ }
+ } else {
+ if (currentPath.isEmpty()) {
+ locale.put(key, langFile.getString(key));
+ } else {
+ locale.put(currentPath + "." + key, langFile.getString(key));
+ }
+ }
+ }
+ }
+
+ public void reload(IConfiguration configYml, File localeFolder) {
+ web = configYml.getString("web", " (Reset ExploitFixer config file)");
+ defaultLocale = configYml.getString("locale", "en").toLowerCase();
+
+ for (String locale : defaultLocales) {
+ configurationUtil.create("%datafolder%/locales/" + locale + ".yml", "locales/" + locale + ".yml");
+ }
+
+ for (File file : localeFolder.listFiles()) {
+ String fileName = file.getName();
+
+ try {
+ IConfiguration langFile = configurationUtil.get(file.toPath().toString());
+ Map locale = new HashMap<>();
+
+ putSection(langFile, locale, "");
+
+ locales.put(fileName.substring(0, 2).toLowerCase(), locale);
+ } catch (Exception ex) {
+ logger.info(
+ "Wasn't able to load locale " + fileName + " because of a " + ex.getClass().getName() + "!");
+ }
+ }
+ }
+
+ public boolean isEnabled() {
+ return true;
+ }
+
+ public String getName() {
+ return "Messages";
+ }
+
+ @Override
+ public String getString(String locale, String path) {
+ String string;
+
+ if (locale != null && locales.containsKey(locale)) {
+ string = locales.get(locale).getOrDefault(path, NOTFOUND_STRING.replace("%PATH%", path.toUpperCase()));
+ } else if (locales.containsKey(defaultLocale)) {
+ string = locales.get(defaultLocale).getOrDefault(path, NOTFOUND_STRING.replace("%PATH%", path.toUpperCase()));
+ } else if (locale != null) {
+ string = NOTFOUND_STRING.replace("%PATH%", locale.toUpperCase());
+ } else {
+ string = NOTFOUND_STRING.replace("%PATH%", "");
+ }
+
+ return string.replace("%version%", version).replace("%web%", web).replace('&', '\u00a7');
+ }
+
+ public String getReload(String locale) {
+ return getString(locale, "commands.reload");
+ }
+
+ public String getHelp(String locale) {
+ return getString(locale, "commands.help");
+ }
+
+ public String getUnknown(String locale) {
+ return getString(locale, "commands.error.unknown");
+ }
+
+ public String getPermission(String locale) {
+ return getString(locale, "commands.error.permission");
+ }
+
+ public String getConsole(String locale) {
+ return getString(locale, "commands.error.console");
+ }
+
+ public String getEnable(String locale) {
+ return getString(locale, "commands.notifications.enable");
+ }
+
+ public String getDisable(String locale) {
+ return getString(locale, "commands.notifications.disable");
+ }
+
+ public String getKickMessage(IModule module, String locale) {
+ return getString(locale, "modules." + module.getName().toLowerCase() + ".kick_message");
+ }
+
+ public String getKickMessage(String module, String locale) {
+ return getString(locale, "modules." + module.toLowerCase() + ".kick_message");
+ }
+
+ public String getStats(String locale) {
+ return getString(locale, "commands.stats");
+ }
+
+ public String getMojangDown(String locale) {
+ return getString(locale, "mojang_down");
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/NotificationsModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/NotificationsModule.java
new file mode 100644
index 0000000..8b15ad5
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/NotificationsModule.java
@@ -0,0 +1,180 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.logging.Logger;
+
+import org.bukkit.Server;
+import org.bukkit.entity.Player;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+import dev._2lstudios.exploitfixer.exploit.ExploitPlayer;
+import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.TextComponent;
+
+public class NotificationsModule implements INotificationsModule {
+ private Server server;
+ private Logger logger;
+ private Map packetDebug = new HashMap<>();
+ private Collection notifications = new HashSet<>();
+ private boolean enabled, debug;
+ private String message;
+
+ // Violations done per category
+ private Map categorizedVls = new HashMap<>();
+
+ public void clearCategorizedVls() {
+ this.categorizedVls.clear();
+ }
+
+ public Map getCategorizedVls() {
+ return categorizedVls;
+ }
+
+ public void addCategorizedVl(String name, double amount) {
+ if (amount <= 0) {
+ return;
+ }
+
+ this.categorizedVls.put(name, this.categorizedVls.getOrDefault(name, 0D) + amount);
+ }
+
+ public NotificationsModule(Server server, Logger logger) {
+ this.server = server;
+ this.logger = logger;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void reload(IConfiguration configYml) {
+ this.enabled = configYml.getBoolean("notifications.enabled");
+ this.debug = configYml.getBoolean("notifications.debug");
+ this.message = configYml.getString("notifications.message").replace('&', '\u00A7');
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void addPacketDebug(String packetType) {
+ if (this.debug) {
+ packetDebug.put(packetType, packetDebug.getOrDefault(packetType, 0) + 1);
+ }
+ }
+
+ public void debugPackets() {
+ if (!this.debug) {
+ return;
+ }
+
+ if (!categorizedVls.isEmpty()) {
+ StringBuilder stringBuilder = new StringBuilder();
+ Set> entries = new HashSet<>(categorizedVls.entrySet());
+ int total = 0;
+
+ stringBuilder.append("Categorized Violations (x%total%):");
+
+ categorizedVls.clear();
+
+ for (Entry vlsCategory : entries) {
+ double value = (double) ((int) (vlsCategory.getValue() * 1000)) / 1000;
+
+ if (value <= 0) {
+ continue;
+ }
+
+ if (total++ != 0) {
+ stringBuilder.append(",");
+ }
+
+ stringBuilder.append(" " + vlsCategory.getKey() + " " + value);
+ }
+
+ debug(stringBuilder.toString().replace("%total%", String.valueOf(total)));
+ }
+
+ int total = 0;
+
+ if (!packetDebug.isEmpty()) {
+ StringBuilder stringBuilder = new StringBuilder();
+ Set> entries = new HashSet<>(packetDebug.entrySet());
+ stringBuilder.append("Received Packets (x%total%):");
+
+ packetDebug.clear();
+
+ for (Entry packetEntry : entries) {
+ String packetType = packetEntry.getKey();
+ int amount = packetEntry.getValue();
+
+ if (total != 0) {
+ stringBuilder.append(",");
+ }
+
+ stringBuilder.append(" x").append(amount).append(" ").append(packetType);
+ total += amount;
+ }
+
+ debug(stringBuilder.toString().replace("%total%", String.valueOf(total)));
+ }
+ }
+
+ public void debug(String message) {
+ if (this.debug) {
+ this.logger.info(message);
+ }
+ }
+
+ public void setNotifications(String playerName, boolean input) {
+ if (input) {
+ notifications.add(playerName);
+ } else {
+ notifications.remove(playerName);
+ }
+ }
+
+ public boolean isNotifications(String playerName) {
+ return notifications.contains(playerName);
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+
+ public Collection getNotifications() {
+ return notifications;
+ }
+
+ public void sendNotification(String check, ExploitPlayer player, int violations) {
+ if (isEnabled() && player != null) {
+ int ping = player.getPing();
+ String notification = getMessage().replace("%player%", player.getName()).replace("%check%", check)
+ .replace("%ping%", String.valueOf(ping)).replace("%vls%", String.valueOf(violations));
+ String packets = player.getPacketsText();
+ TextComponent textNotification = new TextComponent(notification);
+ textNotification.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(packets).create()));
+
+ server.getConsoleSender().sendMessage(notification);
+
+ for (String notificationPlayerName : getNotifications()) {
+ Player notificationPlayer = server.getPlayer(notificationPlayerName);
+
+ if (notificationPlayer != null) {
+ notificationPlayer.spigot().sendMessage(textNotification);
+ }
+ }
+ }
+ }
+
+ @Override
+ public String getName() {
+ return "Notifications";
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/modules/PacketsModule.java b/src/main/java/dev/_2lstudios/exploitfixer/modules/PacketsModule.java
new file mode 100644
index 0000000..3bd17be
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/modules/PacketsModule.java
@@ -0,0 +1,143 @@
+package dev._2lstudios.exploitfixer.modules;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+import dev._2lstudios.exploitfixer.exploit.Violations;
+
+public class PacketsModule implements IViolationModule {
+ private Map rateMultipliers = new HashMap<>();
+ private Collection blacklist = new HashSet<>();
+ private Violations violations;
+ private double dataVls, bookVls, tagVls, blockDigVls, blockPlaceVls, setCreativeSlot, windowClick, cancelVls,
+ reduceVls, byteMultiplier;
+ private int dataMaxSizeBook, dataMaxSizeSign, dataMaxSize, dataMaxFlags, dataMaxFireworkFlags;
+ private boolean enabled, offline;
+ private String bypassPermission;
+
+ public void reload(IConfiguration configYml) {
+ String name = getName().toLowerCase();
+ IConfiguration rateMultipliersSection = configYml.getSection(name + ".rate_multipliers");
+
+ this.enabled = configYml.getBoolean(name + ".enabled");
+ this.cancelVls = configYml.getDouble(name + ".cancel_vls");
+ this.reduceVls = configYml.getDouble(name + ".reduce_vls");
+ this.offline = configYml.getBoolean(name + ".offline");
+ this.dataVls = configYml.getDouble(name + ".data.vls");
+ this.bookVls = configYml.getDouble(name + ".book");
+ this.tagVls = configYml.getDouble(name + ".tag");
+ this.dataMaxSize = configYml.getInt(name + ".data.max_size");
+ this.dataMaxSizeBook = configYml.getInt(name + ".data.max_size_book");
+ this.dataMaxSizeSign = configYml.getInt(name + ".data.max_size_sign");
+ this.dataMaxFlags = configYml.getInt(name + ".data.max_flags");
+ this.dataMaxFireworkFlags = configYml.getInt(name + ".data.max_firework_flags");
+ this.byteMultiplier = configYml.getDouble(name + ".byte_multiplier");
+ this.windowClick = configYml.getDouble(name + ".window_click");
+ this.blockPlaceVls = configYml.getDouble(name + ".block_place");
+ this.blockDigVls = configYml.getDouble(name + ".block_dig");
+ this.setCreativeSlot = configYml.getDouble(name + ".set_creative_slot");
+ this.bypassPermission = configYml.getString(name + ".bypass-permission");
+ this.blacklist = new HashSet<>(configYml.getStringList(name + ".blacklist"));
+ this.violations = new Violations(configYml.getSection(name + ".violations"));
+
+ for (String key : rateMultipliersSection.getKeys()) {
+ rateMultipliers.put(key, rateMultipliersSection.getDouble(key));
+ }
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public String getName() {
+ return "Packets";
+ }
+
+ @Override
+ public double getCancelVls() {
+ return cancelVls;
+ }
+
+ @Override
+ public double getReduceVls() {
+ return reduceVls;
+ }
+
+ @Override
+ public Violations getViolations() {
+ return violations;
+ }
+
+ public double getMultiplier(String packetName) {
+ return rateMultipliers.getOrDefault(packetName, rateMultipliers.getOrDefault("PacketPlayInOther", 1D));
+ }
+
+ public double getWindowClick() {
+ return windowClick;
+ }
+
+ public double getSetCreativeSlot() {
+ return setCreativeSlot;
+ }
+
+ public double getBlockDigVls() {
+ return blockDigVls;
+ }
+
+ public double getBlockPlaceVls() {
+ return blockPlaceVls;
+ }
+
+ public double getDataVls() {
+ return dataVls;
+ }
+
+ public double getBookVls() {
+ return bookVls;
+ }
+
+ public double getTagVls() {
+ return tagVls;
+ }
+
+ public int getDataMaxSize() {
+ return dataMaxSize;
+ }
+
+ public int getDataMaxSizeBook() {
+ return dataMaxSizeBook;
+ }
+
+ public int getDataMaxSizeSign() {
+ return dataMaxSizeSign;
+ }
+
+ public double getDataVlMultiplier() {
+ return byteMultiplier;
+ }
+
+ public boolean isOffline() {
+ return offline;
+ }
+
+ public boolean isBlacklisted(String packetName) {
+ return blacklist.contains(packetName);
+ }
+
+ public int getDataMaxFireworkFlags() {
+ return dataMaxFireworkFlags;
+ }
+
+ public int getDataMaxFlags() {
+ return dataMaxFlags;
+ }
+
+ public String getBypassPermission() {
+ return bypassPermission;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/tasks/ExploitFixerRepeatingTask.java b/src/main/java/dev/_2lstudios/exploitfixer/tasks/ExploitFixerRepeatingTask.java
new file mode 100644
index 0000000..b07cbb5
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/tasks/ExploitFixerRepeatingTask.java
@@ -0,0 +1,25 @@
+package dev._2lstudios.exploitfixer.tasks;
+
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+
+public class ExploitFixerRepeatingTask implements Runnable {
+ private NotificationsModule notificationsModule;
+ int seconds = 0;
+
+ public ExploitFixerRepeatingTask(NotificationsModule notificationsModule) {
+ this.notificationsModule = notificationsModule;
+ }
+
+ @Override
+ public void run() {
+ if (seconds > 300) {
+ seconds = 0;
+ } else {
+ if (seconds % 10 == 9) {
+ notificationsModule.debugPackets();
+ }
+
+ seconds++;
+ }
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/utils/BukkitConfigurationUtil.java b/src/main/java/dev/_2lstudios/exploitfixer/utils/BukkitConfigurationUtil.java
new file mode 100644
index 0000000..aaa525c
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/utils/BukkitConfigurationUtil.java
@@ -0,0 +1,101 @@
+package dev._2lstudios.exploitfixer.utils;
+
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitScheduler;
+
+import dev._2lstudios.exploitfixer.configuration.BukkitConfiguration;
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.logging.Logger;
+
+public class BukkitConfigurationUtil implements IConfigurationUtil {
+ private static String DATA_FOLDER_PLACEHOLDER = "%datafolder%";
+ private String dataFolderPath;
+ private Logger logger;
+ private Plugin plugin;
+ private BukkitScheduler scheduler;
+ private ClassLoader classLoader;
+
+ public BukkitConfigurationUtil(Plugin plugin) {
+ this.plugin = plugin;
+ this.scheduler = plugin.getServer().getScheduler();
+ this.logger = plugin.getLogger();
+ this.classLoader = plugin.getClass().getClassLoader();
+ this.dataFolderPath = plugin.getDataFolder().toString();
+ }
+
+ private void createParentFolder(File file) {
+ File parentFile = file.getParentFile();
+
+ if (parentFile != null) {
+ parentFile.mkdirs();
+ }
+ }
+
+ public IConfiguration get(String path) {
+ File file = new File(path.replace(DATA_FOLDER_PLACEHOLDER, dataFolderPath));
+
+ if (file.exists()) {
+ return new BukkitConfiguration(YamlConfiguration.loadConfiguration(file));
+ } else {
+ return new BukkitConfiguration(new YamlConfiguration());
+ }
+ }
+
+ public void create(String rawPath, String resourcePath) {
+ String path = rawPath.replace(DATA_FOLDER_PLACEHOLDER, dataFolderPath);
+
+ try {
+ File configFile = new File(path);
+
+ if (!configFile.exists()) {
+ InputStream inputStream = classLoader.getResourceAsStream(resourcePath);
+
+ createParentFolder(configFile);
+
+ if (inputStream != null) {
+ Files.copy(inputStream, configFile.toPath());
+ } else {
+ configFile.createNewFile();
+ }
+
+ logger.info("File '" + path + "' has been created!");
+ }
+ } catch (IOException e) {
+ logger.info("Unable to create '" + path + "'!");
+ }
+ }
+
+ public void save(IConfiguration configuration, String rawPath) {
+ String path = rawPath.replace(DATA_FOLDER_PLACEHOLDER, dataFolderPath);
+
+ scheduler.runTaskAsynchronously(plugin, () -> {
+ try {
+ ((YamlConfiguration) configuration.getObject()).save(path);
+
+ logger.info("File '" + path + "' has been saved!");
+ } catch (IOException e) {
+ logger.info("Unable to save '" + path + "'!");
+ }
+ });
+ }
+
+ public void delete(String rawPath) {
+ String path = rawPath.replace(DATA_FOLDER_PLACEHOLDER, dataFolderPath);
+
+ scheduler.runTaskAsynchronously(plugin, () -> {
+ try {
+ Files.delete(new File(path).toPath());
+
+ logger.info("File '" + path + "' has been removed!");
+ } catch (IOException e) {
+ logger.info("Unable to remove '" + path + "'!");
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/utils/ExploitUtil.java b/src/main/java/dev/_2lstudios/exploitfixer/utils/ExploitUtil.java
new file mode 100644
index 0000000..268ae34
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/utils/ExploitUtil.java
@@ -0,0 +1,205 @@
+package dev._2lstudios.exploitfixer.utils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.List;
+
+import org.bukkit.FireworkEffect;
+import org.bukkit.Material;
+import org.bukkit.block.BlockState;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.inventory.InventoryHolder;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.BlockStateMeta;
+import org.bukkit.inventory.meta.BookMeta;
+import org.bukkit.inventory.meta.FireworkMeta;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import dev._2lstudios.exploitfixer.enums.CheckItemResult;
+import dev._2lstudios.exploitfixer.exploit.BukkitExploitPlayer;
+import dev._2lstudios.exploitfixer.managers.ExploitPlayerManager;
+import dev._2lstudios.exploitfixer.managers.ModuleManager;
+import dev._2lstudios.exploitfixer.modules.ItemsFixModule;
+import dev._2lstudios.exploitfixer.modules.NotificationsModule;
+import dev._2lstudios.exploitfixer.modules.PacketsModule;
+import dev._2lstudios.hamsterapi.hamsterplayer.HamsterPlayer;
+
+public class ExploitUtil {
+ private ExploitPlayerManager exploitPlayerManager;
+ private ItemsFixModule itemsFixModule;
+ private NotificationsModule notificationsModule;
+ private PacketsModule packetsModule;
+
+ public ExploitUtil(ModuleManager moduleManager) {
+ this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
+ this.itemsFixModule = moduleManager.getItemsFixModule();
+ this.notificationsModule = moduleManager.getNotificationsModule();
+ this.packetsModule = moduleManager.getPacketsModule();
+ }
+
+ private CheckItemResult checkBlockState(BlockStateMeta meta) {
+ if (meta.hasBlockState()) {
+ BlockState blockState = meta.getBlockState();
+
+ if (blockState instanceof InventoryHolder) {
+ InventoryHolder inventoryHolder = (InventoryHolder) blockState;
+
+ for (ItemStack item : inventoryHolder.getInventory().getContents()) {
+ if (checkItem(item) != CheckItemResult.VALID_ITEM) {
+ return CheckItemResult.INVALID_BLOCK;
+ }
+ }
+ }
+ }
+
+ return CheckItemResult.VALID_ITEM;
+ }
+
+ private CheckItemResult checkBook(BookMeta meta) {
+ String title = meta.getTitle();
+
+ if (title != null && title.length() > 32) {
+ return CheckItemResult.INVALID_BOOK_TITLE;
+ }
+
+ String author = meta.getAuthor();
+
+ if (author != null && author.length() > 16) {
+ return CheckItemResult.INVALID_BOOK_AUTHOR;
+ }
+
+ if (meta.getPageCount() > 50) {
+ return CheckItemResult.INVALID_BOOK_PAGES;
+ }
+
+ int dataBytesBook = packetsModule.getDataMaxSizeBook();
+
+ for (String page : meta.getPages()) {
+ int pageBytes = page.getBytes(StandardCharsets.UTF_8).length;
+
+ if (pageBytes > dataBytesBook) {
+ return CheckItemResult.INVALID_BOOK_SIZE;
+ }
+ }
+
+ return CheckItemResult.VALID_ITEM;
+ }
+
+ private CheckItemResult checkFirework(FireworkMeta meta) {
+ try {
+ if (meta.getPower() > 3) {
+ return CheckItemResult.INVALID_FIREWORK_POWER;
+ }
+ } catch (NullPointerException ex) {
+ // Power can sometimes be null
+ }
+
+ // Check if it has effects
+ if (meta.hasEffects()) {
+ // Get the allowed flags count
+ int allowedFlags = packetsModule.getDataMaxFireworkFlags();
+
+ if (meta.getEffectsSize() > allowedFlags) {
+ // Check for the amount of effects
+ return CheckItemResult.INVALID_FIREWORK_EFFECTS;
+ }
+
+ for (FireworkEffect effect : meta.getEffects()) {
+ if (effect.getColors().size() > allowedFlags) {
+ // Check for too many colors
+ return CheckItemResult.INVALID_FIREWORK_COLORS;
+ }
+
+ if (effect.getFadeColors().size() > allowedFlags) {
+ // Check for too many fade colors
+ return CheckItemResult.INVALID_FIREWORK_FADE_COLORS;
+ }
+ }
+ }
+
+ return CheckItemResult.VALID_ITEM;
+ }
+
+ public CheckItemResult checkItem(ItemStack item) {
+ try {
+ if (item != null && item.hasItemMeta()) {
+ ItemMeta itemMeta = item.getItemMeta();
+
+ if (itemMeta != null) {
+ String displayName = itemMeta.getDisplayName();
+
+ if (displayName != null && displayName.length() > 1536) {
+ return CheckItemResult.INVALID_ITEM_NAME;
+ }
+
+ List lore = itemMeta.getLore();
+
+ if (lore != null && lore.size() > 64) {
+ return CheckItemResult.INVALID_ITEM_LORE;
+ }
+
+ if (itemMeta instanceof BookMeta) {
+ return checkBook((BookMeta) itemMeta);
+ } else if (itemMeta instanceof BlockStateMeta) {
+ return checkBlockState((BlockStateMeta) itemMeta);
+ } else if (itemMeta instanceof FireworkMeta) {
+ return checkFirework((FireworkMeta) itemMeta);
+ }
+ }
+ }
+ } catch (IllegalArgumentException ex) {
+ return CheckItemResult.INVALID_ITEM;
+ }
+
+ return CheckItemResult.VALID_ITEM;
+ }
+
+ public boolean checkSign(String[] linesString) {
+ int dataBytesSign = packetsModule.getDataMaxSizeSign();
+
+ if (linesString != null && dataBytesSign > 0) {
+ if (linesString.length > 4) {
+ return true;
+ } else {
+ for (String line : linesString) {
+ if (line.getBytes(StandardCharsets.UTF_8).length > dataBytesSign) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public String clearIfBlacklisted(ItemStack itemStack) {
+ Collection blacklist = itemsFixModule.getBlacklist();
+ String materialName = itemStack.getType().toString();
+
+ if (blacklist != null && blacklist.contains(materialName)) {
+ itemStack.setType(Material.AIR);
+ itemStack.setItemMeta(null);
+ return materialName;
+ }
+
+ return null;
+ }
+
+ public void addVls(Cancellable event, HamsterPlayer hamsterPlayer, Player player,
+ double vls, String category) {
+ if (vls > 0) {
+ BukkitExploitPlayer exploitPlayer = exploitPlayerManager.get(player);
+
+ exploitPlayer.addVls(event, null, hamsterPlayer, packetsModule, vls, category);
+ }
+ }
+
+ public void cancelExploit(Cancellable event, HamsterPlayer hamsterPlayer, Player player,
+ String reason, double vls, String category) {
+ notificationsModule.debug(reason);
+ event.setCancelled(true);
+
+ addVls(event, hamsterPlayer, player, vls, category);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/utils/IConfigurationUtil.java b/src/main/java/dev/_2lstudios/exploitfixer/utils/IConfigurationUtil.java
new file mode 100644
index 0000000..bd727df
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/utils/IConfigurationUtil.java
@@ -0,0 +1,13 @@
+package dev._2lstudios.exploitfixer.utils;
+
+import dev._2lstudios.exploitfixer.configuration.IConfiguration;
+
+public interface IConfigurationUtil {
+ public IConfiguration get(String file);
+
+ public void create(String file, String resourcePath);
+
+ public void save(IConfiguration configuration, String file);
+
+ public void delete(String file);
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/utils/ProtocolUtil.java b/src/main/java/dev/_2lstudios/exploitfixer/utils/ProtocolUtil.java
new file mode 100644
index 0000000..e791b3c
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/utils/ProtocolUtil.java
@@ -0,0 +1,27 @@
+package dev._2lstudios.exploitfixer.utils;
+
+import io.netty.buffer.ByteBuf;
+
+public class ProtocolUtil {
+ private static int SEGMENT_BITS = 0x7F;
+ private static int CONTINUE_BIT = 0x80;
+
+ public static int readVarInt(ByteBuf byteBuf) {
+ int value = 0;
+ int position = 0;
+ byte currentByte;
+
+ while (byteBuf.isReadable()) {
+ currentByte = byteBuf.readByte();
+ value |= (currentByte & SEGMENT_BITS) << position;
+
+ if ((currentByte & CONTINUE_BIT) == 0) break;
+
+ position += 7;
+
+ if (position >= 32) throw new RuntimeException("VarInt is too big");
+ }
+
+ return value;
+ }
+}
diff --git a/src/main/java/dev/_2lstudios/exploitfixer/utils/ReflectionUtil.java b/src/main/java/dev/_2lstudios/exploitfixer/utils/ReflectionUtil.java
new file mode 100644
index 0000000..bc8cea7
--- /dev/null
+++ b/src/main/java/dev/_2lstudios/exploitfixer/utils/ReflectionUtil.java
@@ -0,0 +1,64 @@
+package dev._2lstudios.exploitfixer.utils;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Player.Spigot;
+
+public final class ReflectionUtil {
+ private static final MethodHandle getLocalePlayerMethod = localePlayer();
+ private static final MethodHandle getLocaleSpigotMethod = localeSpigot();
+ private static final MethodHandle getHandleMethod = handleMethod();
+ private static Field pingField = null;
+
+ private static MethodHandle localePlayer() {
+ try {
+ MethodHandles.Lookup lookup = MethodHandles.publicLookup();
+ return lookup.findVirtual(Player.class, "getLocale", MethodType.methodType(String.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ return null;
+ }
+ }
+
+ private static MethodHandle localeSpigot() {
+ try {
+ MethodHandles.Lookup lookup = MethodHandles.publicLookup();
+ return lookup.findVirtual(Spigot.class, "getLocale", MethodType.methodType(String.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ return null;
+ }
+ }
+
+ private static MethodHandle handleMethod() {
+ MethodHandles.Lookup lookup = MethodHandles.lookup();
+ try {
+ Method method = Player.class.getMethod("getHandle");
+ method.setAccessible(true);
+ return lookup.unreflect(method);
+ } catch (IllegalAccessException | NoSuchMethodException | SecurityException e) {
+ return null;
+ }
+ }
+
+ public static MethodHandle getLocalePlayerMethod() {
+ return getLocalePlayerMethod;
+ }
+
+ public static MethodHandle getLocaleSpigotMethod() {
+ return getLocaleSpigotMethod;
+ }
+
+ public static MethodHandle getHandleMethod() {
+ return getHandleMethod;
+ }
+
+ public static Field getPingField(Object playerHandle) throws NoSuchFieldException, SecurityException {
+ return pingField == null ? pingField = playerHandle.getClass().getField("ping") : pingField;
+ }
+
+ private ReflectionUtil() {}
+}
\ No newline at end of file
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
new file mode 100644
index 0000000..303fdfa
--- /dev/null
+++ b/src/main/resources/config.yml
@@ -0,0 +1,228 @@
+# ExploitFixer by LinsaFTW
+# Please help by donating, we require funds to continue with the development.
+# https://paypal.me/LinsaFTW
+#
+# INSTALL THE PLUGIN ON ALL BUKKIT SERVERS!
+#
+# Most of the check use Violations (vls), this a counter to execute different
+# actions on diverse levels to allow high customization of the modules.
+
+# Default language used by the plugin (This changes depending on the client language)
+locale: "en"
+
+# Web link used on the messages
+web: "discord.gg/gF36AT3"
+
+# Shows notifications to console and players with permissions.
+notifications:
+ enabled: true
+
+ # Placeholders: %player% %ping% %check% %vls%
+ message: "&c&lEF: &e%player% &7(&a%ping%ms&7) &ffailed &6%check% &7(&c%vls% vls&7)"
+
+ # This shows extra information about blocked packets.
+ debug: false
+
+# Prevents exploits that occur on different Bukkit events.
+events:
+ # Prevents moving if the chunk is null. (Prevents chunk overloading)
+ null_chunk: true
+
+ # Prevents players from damaging themselves and consuming ram.
+ self_damage: true
+
+ # Prevents players from breaking ender portals.
+ ender_portal_break: true
+
+ # Prevents maps from tracking fixing many crash exploits. (The map will no longer show players their location.)
+ disable_tracking: true
+
+ # Prevents crashing when using dispensers that drop items out of the bounds.
+ dispenser_crash: true
+
+ # Prevents portal teleport spam crash.
+ portal_crash: true
+
+ # Prevents duplicating items with some inventory plugins.
+ inventory_exploit: true
+
+# Prevents the use of invalid packets to crash the server.
+packets:
+ enabled: true
+
+ # Amount of vls required to start cancelling packets.
+ cancel_vls: 25
+
+ # Amount of vls to reduce per second.
+ reduce_vls: 25
+
+ # Cancels packets if the player sending them is offline.
+ # This prevents duplication with auction house plugins.
+ offline: true
+
+ # This will check if integers, floats, doubles and items have an invalid size in packets.
+ data:
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ vls: 100
+
+ # Maximum amount of bytes allowed per packet. (Set to -1 to disable)
+ max_size: 12288
+
+ # Maximum amount of bytes allowed per book page. (Set to -1 to disable)
+ max_size_book: 300
+
+ # Maximum amount of bytes allowed per sign line. (Set to -1 to disable)
+ max_size_sign: 47
+
+ # Maximum amount of flags allowed per packet. (Set to -1 to disable)
+ max_flags: 64
+
+ # Maximum amount of flags allowed per firework. (Set to -1 to disable)
+ max_firework_flags: 12
+
+ # Cancels book packets when a book is not in hand. (Can sometimes becaused by lag)
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ book: 10
+
+ # Cancels CustomPayload packets with invalid tags
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ tag: 100
+
+ # Checks if players send PacketPlayInWindowClick packets with invalid data.
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ window_click: 100
+
+ # Checks if players send PacketPlayInBlockPlace packets with invalid data.
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ block_place: 100
+
+ # Checks if players send PacketPlayInBlockDig packets with invalid data.
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ block_dig: 100
+
+ # Checks if players send PacketPlayInCreativeSlot packets without creative.
+ # Vls to add when a packet fails the check. (Set to -1 to disable)
+ set_creative_slot: 100
+
+ # This option adds vls per byte received.
+ # This works as a packet limiter feature.
+ # Violations to add per byte received. (Set to -1 to disable)
+ byte_multiplier: 0.0001
+
+ # Set a permission for some players to bypass this check. (Uncomment to enable)
+ # WARNING: Players with this permission can crash and corrupt worlds FOREVER with hacked clients.
+ #bypass-permission: "exploitfixer.packets.bypass"
+
+ # Violations to add when a normal packet is received from a player.
+ # This works as a packet limiter feature.
+ # Unlisted packets are considered as "PacketPlayInOther" by default.
+ # Remember to take a look at byte_multiplier option.
+ rate_multipliers:
+ PacketPlayInOther: 0.1
+ MC|BSign: 4.5
+ MC|BEdit: 4.5
+ MC|BOpen: 4.5
+ PacketPlayInAutoRecipe: 10
+ PacketPlayInUpdateSign: 5
+ PacketPlayInAbilities: 0.25
+ PacketPlayInArmAnimation: 0.125
+ PacketPlayInBlockDig: 0.125
+ PacketPlayInBlockPlace: 0.5
+ PacketPlayInCustomPayload: 0.01
+ PacketPlayInEntityAction: 0.5
+ PacketPlayInFlying: 0.25
+ PacketPlayInLook: 0.4
+ PacketPlayInSteerVehicle: 0.05
+ PacketPlayInPositionLook: 0.05
+ PacketPlayInPosition: 0.2
+ PacketPlayInSetCreativeSlot: 0.15
+ PacketPlayInSettings: 0.1
+ PacketPlayInTabComplete: 0.75
+ PacketPlayInUseEntity: 0.5
+ PacketPlayInUseItem: 0.1
+ PacketPlayInWindowClick: 0.45
+
+ # These packets will be completely cancelled.
+ blacklist:
+ - "PacketPlayInAutoRecipe"
+
+ # Placeholders: %player%, %vls%, %ping%, %check%
+ # Commands to run when a player reaches certain violation level.
+ violations:
+ 25:
+ - "notification"
+ 50:
+ - "notification"
+ 75:
+ - "notification"
+ 100:
+ - "notification"
+ - "kick"
+
+# Removes custom/invalid NBT tags from items to prevent harm to your server.
+# This prevents creative mode players from corrupting worlds FOREVER and crashing the server.
+# This checks only applies to creative mode players.
+creative-items-fix:
+ enabled: true
+
+ # Maximum stack size obtainable with creative packets. (-1 to allow anything)
+ max-stack-size: 64
+
+ # Maximum enchant level obtainable with creative packets.
+ # -1 removes all enchants, 0 forces vanilla values
+ enchant-limit: 0
+
+ # Whitelist already created items to improve performance and stability.
+ # Items created with commands will not be checked when enabled.
+ # Set to false if your server has already created crasher items.
+ whitelist-items: true
+
+ # List of materials that are blocked from Creative.
+ blacklist:
+ - "END_PORTAL"
+ - "NETHER_PORTAL"
+ # - "BARRIER"
+ # - "COMMAND_BLOCK"
+
+ # Set a permission for some players to bypass this check. (Uncomment to enable)
+ # WARNING: Players with this permission can crash and corrupt worlds FOREVER with hacked clients.
+ #bypass-permission: "exploitfixer.itemsfix.bypass"
+
+# Checks if players try to crash/exploit the server with commands.
+commands:
+ enabled: true
+
+ # Commands that are exempt from this server.
+ commands:
+ # WorldEdit exploit
+ - "//calc"
+ - "//calculate"
+ - "//eval"
+ - "//evaluate"
+ - "//solve"
+ # HolographicDisplays exploit
+ - "/hd readtext"
+ - "/holo readtext"
+ - "/hologram readtext"
+ - "/holograms readtext"
+ - "/holographicdisplays readtext"
+ # PermissionsEx exploit
+ - "/pex promote"
+ - "/pex demote"
+ - "/promote"
+ - "/demote"
+ - "/execute"
+ # Multiverse exploit
+ - "/mv ^"
+ - "/mv help <"
+ - "/mvhelp <"
+ - "/$"
+
+ # Set a permission for some players to bypass this check. (Uncomment to enable)
+ # WARNING: Players with this permission can crash and corrupt worlds FOREVER with hacked clients.
+ #bypass-permission: "exploitfixer.commands.bypass"
+
+ # Placeholders: %player%
+ punishments:
+ - "notification"
+ - "kick"
diff --git a/src/main/resources/locales/de.yml b/src/main/resources/locales/de.yml
new file mode 100644
index 0000000..8423527
--- /dev/null
+++ b/src/main/resources/locales/de.yml
@@ -0,0 +1,27 @@
+# German - Credits: Dominik48N / NoJokeFNA / GeGrieftGames
+commands:
+ reload: "&aPlugin ExploitFixer wurde neu geladen!"
+ help: |-
+ &aExploitFixer &b%version%&a von &bLinsaFTW&a.
+ &e /%command% help &7> &bZeigt alle verfügbaren Befehle an.
+ &e /%command% reload &7> &bLädt das Plugin neu.
+ &e /%command% notifications &7> &bSchaltet Benachrichtigungen des Plugins um.
+ &e /%command% stats &7> &bZeigt die Statistiken des Plugins an.
+ error:
+ unknown: "&cUnbekannter Befehl. Nutze /exploitfixer help um die verfügbaren Befehle aufzulisten!"
+ permission: "&cDu darfst diesen Befehl nicht ausführen!"
+ console: "&cDieser Befehl darf nicht von der Konsole ausgeführt werden!"
+ notifications:
+ enable: "&aDu hast die Benachrichtigungen aktiviert!"
+ disable: "&cDu hast die Benachrichtigungen deaktiviert!"
+ stats: "&c&lEF: &eZwischengespeicherte Spieler: &a%players_cached%&e Bestrafte Spieler: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cDu bist temporär vom Server gesperrt!\n\n&7Grund: &fZu viele CustomPayload Packete wurden erkannt.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cDu bist temporär vom Server gesperrt!\n\n&7Grund: &fBefehl Exploit erkannt.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cDu bist temporär vom Server gesperrt!\n\n&7Grund: &fPacket Exploit erkannt.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cDu bist temporär vom Server gesperrt!\n\n&7Grund: &fNulladresse erkannt.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cDie Mojang Server sind nicht verfügbar, versuche es später erneut!\n\n&7Grund: &fExploitFixer kann die UUID nicht überprüfen.\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/en.yml b/src/main/resources/locales/en.yml
new file mode 100644
index 0000000..e41654f
--- /dev/null
+++ b/src/main/resources/locales/en.yml
@@ -0,0 +1,27 @@
+# English - Credits: LinsaFTW
+commands:
+ reload: "&aPlugin ExploitFixer reloaded!"
+ help: |-
+ &aExploitFixer &b%version%&a by &bLinsaFTW&a.
+ &e /%command% help &7> &bShows available commands.
+ &e /%command% reload &7> &bReloads the plugin.
+ &e /%command% notifications &7> &bToggles notifications of the plugin.
+ &e /%command% stats &7> &bWatch the stats of the plugin.
+ error:
+ unknown: "&cUnknown command. Use /exploitfixer help to see available commands!"
+ permission: "&cYou dont have permission to use this command!"
+ console: "&cThis command cant be executed from the console!"
+ notifications:
+ enable: "&aYou have enabled notifications!"
+ disable: "&cYou have disabled notifications!"
+ stats: "&c&lEF: &ePlayers cached: &a%players_cached%&e Players punished: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fToo many CustomPayload packets detected.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fCommand exploit detected.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fPackets exploit detected.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fNull address detected.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang servers are down, please come back later!\n\n&7Reason: &fExploitFixer can't check the UUID.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/es.yml b/src/main/resources/locales/es.yml
new file mode 100644
index 0000000..b859bf1
--- /dev/null
+++ b/src/main/resources/locales/es.yml
@@ -0,0 +1,27 @@
+# Spanish - Credits: LinsaFTW
+commands:
+ reload: "&aPlugin ExploitFixer recargado!"
+ help: |-
+ &aExploitFixer &b%version%&a por &bLinsaFTW&a.
+ &e /%command% help &7> &bMuestra los comandos disponibles.
+ &e /%command% reload &7> &bRecarga el plugin.
+ &e /%command% notifications &7> &bAlterna las notificaciones del plugin.
+ &e /%command% stats &7> &bMira las estadisticas del plugin.
+ error:
+ unknown: "&cComando desconocido. Usa /exploitfixer help para ver comandos disponibles!"
+ permission: "&cNo tienes permiso para usar ese comando!"
+ console: "&cEste comando no puede ser usado desde la consola!"
+ notifications:
+ enable: "&aHabilitaste las notificaciones!"
+ disable: "&cDeshabilitaste las notificaciones!"
+ stats: "&c&lEF: &eJugadores en cache: &a%players_cached%&e Jugadores sancionados: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Razon: &fExploit de CustomPayload detectado.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Razon: &fExploit de Comandos detectado.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Razon: &fExploit de Packets detectado.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Razon: &fExploit de Red nula detectado.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang esta apagado, vuelve pronto!\n\n&7Razon: &fExploitFixer no puede confirmar la UUID.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/fr.yml b/src/main/resources/locales/fr.yml
new file mode 100644
index 0000000..61c2a02
--- /dev/null
+++ b/src/main/resources/locales/fr.yml
@@ -0,0 +1,27 @@
+# French - Credits: HaVroz
+commands:
+ reload: "&aPlugin Exploitfixer rechargé!"
+ help: |-
+ &aExploitFixer &b%version%&a par &bLinsaFTW&a.
+ &e /%command% help &7> &bAfficher les commandes disponibles.
+ &e /%command% reload &7> &bRecharger le plugin.
+ &e /%command% notifications &7> &bActiver/Désactiver les notifications du plugin.
+ &e /%command% stats &7> &bVoir les statistiques du plugin.
+ error:
+ unknown: "&cCommande inconnue. Utilisez /exploitfixer help pour voir les commandes disponibles!"
+ permission: "&cVous n’avez pas la permission d’utiliser cette commande!"
+ console: "&cCette commande ne peut pas être exécutée depuis la console!"
+ notifications:
+ enable: "&aVous avez activé les notifications!"
+ disable: "&cVous avez désactivé les notifications!"
+ stats: "&c&lEF: &eJoueurs caché: &a%players_cached%&e Joueurs sanctionné: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cVous êtes temporairement bloqué du serveur!\n\n&7Raison: &fTrop de paquets Custompayload détectés.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cVous êtes temporairement bloqué du serveur!\n\n&7Raison: &fExploit de commande détecté.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cVous êtes temporairement bloqué du serveur!\n\n&7Raison: &fExploit de Packets détecté.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cVous êtes temporairement bloqué du serveur!\n\n&7Raison: &fAdresse nulle détectée.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang est désactivé, revenez bientôt!\n\n&7Raison: &fExploitFixer ne peut pas confirmer l'UUID.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/he.yml b/src/main/resources/locales/he.yml
new file mode 100644
index 0000000..0f2774d
--- /dev/null
+++ b/src/main/resources/locales/he.yml
@@ -0,0 +1,27 @@
+# Hebrew - Credits: ItzGuy
+commands:
+ reload: "&aהפל×גין תיקון פרצות ×¨×•×¢× ×Ÿ!"
+ help: |-
+ &aExploitFixer &b%version%&a by &bLinsaFTW&a.
+ &e /%command% help &7> &b.פקודות ×–×ž×™× ×•×ª
+ &e /%command% reload &7> &b.×¨×™×¢× ×•×Ÿ הפל×גין
+ &e /%command% notifications &7> &b×©×™× ×•×™ התר×ות הפל×גין.
+ &e /%command% stats &7> &bסטטיסטיקות של הפל×גין.
+ error:
+ unknown: "&cפקודה ×œ× ×™×“×•×¢×”! השתמש ב - /exploitfixer help"
+ permission: "&c×ין לך גישה להשתמש בפקודה ×–×ת!"
+ console: "&cהפקודה ×”×–×ת ×œ× ×™×›×•×œ×” לרוץ ×‘×§×•× ×¡×•×œ!"
+ notifications:
+ enable: "&aהדלקת התר×ות!"
+ disable: "&cכיבית התר×ות!"
+ stats: "&c&lEF: &e×©×—×§× ×™× ×©×ž×•×¨×™×: &a%players_cached%&e ×©×—×§× ×™× ×©×”×•×¢× ×©×•: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&c×תה ×—×¡×•× ×–×ž× ×™×ª מהשרת ×”×–×”!\n\n&7Reason: &fToo many CustomPayload packets detected.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&c×תה ×—×¡×•× ×–×ž× ×™×ª מהשרת ×”×–×”!\n\n&7Reason: &fCommand exploit detected.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&c×תה ×—×¡×•× ×–×ž× ×™×ª מהשרת ×”×–×”!\n\n&7Reason: &fPackets exploit detected.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&c×תה ×—×¡×•× ×–×ž× ×™×ª מהשרת ×”×–×”!\n\n&7Reason: &fNull address detected.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cשרתי ×”×ימות של מוג'×× ×’ ×œ× ×¤×•×¢×œ×™×, תחזרו מ×וחר יותר!\n\n&7Reason: &fExploitFixer can't check the UUID.\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/hu.yml b/src/main/resources/locales/hu.yml
new file mode 100644
index 0000000..0f81960
--- /dev/null
+++ b/src/main/resources/locales/hu.yml
@@ -0,0 +1,26 @@
+#Hungarian
+commands:
+ reload: "&aAz ExploitFixer sikeresen újraindult!"
+ help: |-
+ &aExploitFixer &b%version%&a by &bLinsaFTW&a.
+ &e /%command% help &7> &bElérhető parancsok.
+ &e /%command% reload &7> &bPlugin újraindÃtása.
+ &e /%command% notifications &7> &bÉrtesÃtések ki/be kapcsolása.
+ &e /%command% stats &7> &bA plugin statisztikáinak megtekintése.
+ error:
+ unknown: "&cIsmeretlen parancs. Használd a /exploitfixer help parancsot!"
+ permission: "&cNincs jogod a parancs használatához!"
+ console: "&cEzt a parancsot nem bÃrod használni a konzolból!"
+ notifications:
+ enable: "&aBekapcsoltad az értesÃtéseket!"
+ disable: "&cKikapcsoltad az értesÃtéseket!"
+ stats: "&c&lEF: &eJátékosok: &a%players_cached%&e Büntetések: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cKi lettél rúgva a szerverről!\n\n&7Reason: &fTúl sok CustomPayload csomagot észleltünk.\n&7Discord: &b&n%discord%&r"
+ commands:
+ kick_message: "&cKi lettél rúgva a szerverről!\n\n&7Reason: &fParancs exploitot észleltünk.\n&7Discord: &b&n%discord%&r"
+ packets:
+ kick_message: "&cKi lettél rúgva a szerverről!\n\n&7Reason: &fCsomag exploitot észleltünk.\n&7Discord: &b&n%discord%&r"
+ nulladdress:
+ kick_message: "&cKi lettél rúgva a szerverről!\n\n&7Reason: &fNull addresst észleltünk.\n&7Discord: &b&n%discord%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/it.yml b/src/main/resources/locales/it.yml
new file mode 100644
index 0000000..8c63bf4
--- /dev/null
+++ b/src/main/resources/locales/it.yml
@@ -0,0 +1,27 @@
+# Italian
+commands:
+ reload: "&aPlugin ExploitFixer ricaricato!"
+ help: |-
+ &aExploitFixer &b%version%&a da parte di &bLinsaFTW&a.
+ &e /%command% help &7> &bMostra i commandi disponibili.
+ &e /%command% reload &7> &bRicarica il plugin.
+ &e /%command% notifications &7> &bCambia le notifiche del plugin.
+ &e /%command% stats &7> &bMostra le statistiche del plugin.
+ error:
+ unknown: "&cCommando sconosciuto. Usa /exploitfixer help per vedere i commandi disponibili!"
+ permission: "&cNon hai il permesso per questo commando!"
+ console: "&cQuesto commando non può essere eseguito dalla console!"
+ notifications:
+ enable: "&aHai abilitato le notifiche!"
+ disable: "&cHai disabilitato le notifiche!"
+ stats: "&c&lEF: &eGiocatori in cache: &a%players_cached%&e Giocatori puniti: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cSei stato temporaneamente bloccato da questo server!\n\n&7Motivo: &fTroppi pacchetti CustomPayload.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cSei stato temporaneamente bloccato da questo server!\n\n&7Motivo: &fExploit di commandi rilevati.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cSei stato temporaneamente bloccato da questo server!\n\n&7Motivo: &fExploit di pacchetti rilevati.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cSei stato temporaneamente bloccato da questo server!\n\n&7Motivo: &fIndirizzo null rilevato.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang è disattivato, torna presto!\n\n&7Reason: &fExploitFixer non può confermare l'UUID. \n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/ja.yml b/src/main/resources/locales/ja.yml
new file mode 100644
index 0000000..9057844
--- /dev/null
+++ b/src/main/resources/locales/ja.yml
@@ -0,0 +1,27 @@
+# Japanese - Credits: cosmos223
+commands:
+ reload: "&aプラグイン ExploitFixer をリãƒãƒ¼ãƒ‰ã—ã¾ã—ãŸã€‚"
+ help: |-
+ &aExploitFixer &b%version%&a by &bLinsaFTW
+ &e /%command% help &7> &b使用å¯èƒ½ãªã‚³ãƒžãƒ³ãƒ‰ã‚’表示ã—ã¾ã™ã€‚
+ &e /%command% reload &7> &bプラグインをリãƒãƒ¼ãƒ‰ã—ã¾ã™ã€‚
+ &e /%command% notifications &7> &bプラグインã®é€šçŸ¥ã‚’切り替ãˆã¾ã™ã€‚
+ &e /%command% stats &7> &bプラグインã®çµ±è¨ˆæƒ…å ±ã‚’è¡¨ç¤ºã—ã¾ã™ã€‚
+ error:
+ unknown: "&c䏿˜Žãªã‚³ãƒžãƒ³ãƒ‰ã§ã™ã€‚ /exploitfixer help ã¨å…¥åŠ›ã—ã¦ä½¿ç”¨å¯èƒ½ãªã‚³ãƒžãƒ³ãƒ‰ã‚’確èªã—ã¦ãã ã•ã„。"
+ permission: "&c権é™ãŒã‚りã¾ã›ã‚“。"
+ console: "&cã“ã®ã‚³ãƒžãƒ³ãƒ‰ã¯ã‚³ãƒ³ã‚½ãƒ¼ãƒ«ã‹ã‚‰å®Ÿè¡Œã§ãã¾ã›ã‚“。"
+ notifications:
+ enable: "&a通知を有効ã«ã—ã¾ã—ãŸã€‚"
+ disable: "&c通知を無効ã«ã—ã¾ã—ãŸã€‚"
+ stats: "&c&lEF: &eã‚ャッシュã•れãŸãƒ—レイヤー: &a%players_cached%&e ç½°ã›ã‚‰ã‚ŒãŸãƒ—レイヤー: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cã‚ãªãŸã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ä¸€æ™‚çš„ã«ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™ã€‚\n\n&7ç†ç”±: &fCustomPayloadパケットãŒå¤šã検出ã•れã¾ã—ãŸã€‚\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cã‚ãªãŸã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ä¸€æ™‚çš„ã«ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™ã€‚\n\n&7ç†ç”±: &fコマンドã®è„†å¼±æ€§ã‚’使用ã—ãŸã“ã¨ã‚’検出ã—ã¾ã—ãŸã€‚\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cã‚ãªãŸã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ä¸€æ™‚çš„ã«ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™ã€‚\n\n&7ç†ç”±: &fパケットã®è„†å¼±æ€§ã‚’使用ã—ãŸã“ã¨ã‚’検出ã—ã¾ã—ãŸã€‚\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cã‚ãªãŸã¯ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ä¸€æ™‚çš„ã«ãƒ–ãƒãƒƒã‚¯ã•れã¦ã„ã¾ã™ã€‚\n\n&7ç†ç”±: &fNullã‚¢ãƒ‰ãƒ¬ã‚¹ãŒæ¤œå‡ºã•れã¾ã—ãŸã€‚\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojangã®ã‚µãƒ¼ãƒãƒ¼ãŒå¿œç”ã—ã¦ã„ã¾ã›ã‚“。時間を置ã„ã¦ã‹ã‚‰å†åº¦æŽ¥ç¶šã—ã¦ãã ã•ã„。\n\n&7ç†ç”±: &fExploitFixer 㯠UUID を確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml
new file mode 100644
index 0000000..3033a2b
--- /dev/null
+++ b/src/main/resources/locales/ko.yml
@@ -0,0 +1,27 @@
+# Korean
+commands:
+ reload: "&aExploitFixer 플러그ì¸ì´ 리로드ë˜ì—ˆìŠµë‹ˆë‹¤!"
+ help: |-
+ &aExploitFixer &b%version%&a by &bLinsaFTW&a.
+ &e /%command% help &7> &b사용 가능 ëª…ë ¹ì–´ë¥¼ 표시합니다.
+ &e /%command% reload &7> &b플러그ì¸ì„ 다시 로드합니다.
+ &e /%command% notifications &7> &bí”ŒëŸ¬ê·¸ì¸ ì•ŒëžŒì„ í† ê¸€í•©ë‹ˆë‹¤.
+ &e /%command% stats &7> &bí”Œë ˆì´ì–´ ìž‘ë™ í˜„í™©ì„ í™•ì¸í•©ë‹ˆë‹¤.
+ error:
+ unknown: "&c알 수 없는 ëª…ë ¹ì–´ìž…ë‹ˆë‹¤. /exploitfixer help ëª…ë ¹ì–´ë¥¼ 통해 사용 가능한 ëª…ë ¹ì–´ë¥¼ 확ì¸í•˜ì„¸ìš”!"
+ permission: "&cë‹¹ì‹ ì€ ì´ ëª…ë ¹ì–´ë¥¼ ì‚¬ìš©í• ê¶Œí•œì´ ëª¨ìžëžë‹ˆë‹¤!"
+ console: "&cì´ ëª…ë ¹ì–´ëŠ” 콘솔ì—서 ìž…ë ¥í• ìˆ˜ 없습니다!"
+ notifications:
+ enable: "&aì•ŒëžŒì„ í™œì„±í™” 하였습니다!"
+ disable: "&cì•ŒëžŒì„ ë¹„í™œì„±í™” 하였습니다!"
+ stats: "&c&lEF: &eí”Œë ˆì´ì–´ ìºì‹œ: &a%players_cached%&e ì œìž¬ëœ í”Œë ˆì´ì–´: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cë‹¹ì‹ ì€ ì´ ì„œë²„ì—서 ì¼ì‹œì 으로 차단ë˜ì—ˆìŠµë‹ˆë‹¤!\n\n&7ì‚¬ìœ : &f너무 ë§Žì€ CustomPayload íŒ¨í‚·ì´ ê°ì§€ë¨.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cë‹¹ì‹ ì€ ì´ ì„œë²„ì—서 ì¼ì‹œì 으로 차단ë˜ì—ˆìŠµë‹ˆë‹¤!\n\n&7ì‚¬ìœ : &fëª…ë ¹ì–´ 취약ì ê°ì§€.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cë‹¹ì‹ ì€ ì´ ì„œë²„ì—서 ì¼ì‹œì 으로 차단ë˜ì—ˆìŠµë‹ˆë‹¤!\n\n&7ì‚¬ìœ : &f패킷 취약ì ê°ì§€.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cë‹¹ì‹ ì€ ì´ ì„œë²„ì—서 ì¼ì‹œì 으로 차단ë˜ì—ˆìŠµë‹ˆë‹¤!\n\n&7ì‚¬ìœ : &fNull address ê°ì§€.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang ì„œë²„ì— ë¬¸ì œê°€ ìƒê²¼ìŠµë‹ˆë‹¤, ë‚˜ì¤‘ì— ë‹¤ì‹œ 와주세요!\n\n&7ì‚¬ìœ : &fExploitFixer UUID를 ì°¾ì„ ìˆ˜ 없었습니다.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/nl.yml b/src/main/resources/locales/nl.yml
new file mode 100644
index 0000000..cc2ddc5
--- /dev/null
+++ b/src/main/resources/locales/nl.yml
@@ -0,0 +1,27 @@
+# Dutch - Credits: SBDeveloper
+commands:
+ reload: "&aPlugin ExploitFixer herladen!"
+ help: |-
+ &aExploitFixer &b%version%&a door &bLinsaFTW&a.
+ &e /%command% help &7> &bLaat de beschikbare commando's zien.
+ &e /%command% reload &7> &bHerlaad de plugin.
+ &e /%command% notifications &7> &bToggled notificaties van de plugin.
+ &e /%command% stats &7> &bLaat de statistieken zien van de plugin.
+ error:
+ unknown: "&cOnbekend commando. Gebruik /exploitfixer help om de beschikbare commando's te zien!"
+ permission: "&cJe hebt niet de benodigde permissie om dit commando te gebruiken!"
+ console: "&cJe kunt dit commando niet vanaf console uitvoeren!"
+ notifications:
+ enable: "&aJe hebt notificaties ingeschakeld!"
+ disable: "&cJe hebt notificaties uitgeschakeld!"
+ stats: "&c&lEF: &eSpelers gecached: &a%players_cached%&e Spelers gestraft: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cJe bent tijdelijk geblokkeerd van deze server!\n\n&7Reden &fTe veel CustomPayload packets gedetecteerd.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cJe bent tijdelijk geblokkeerd van deze server!\n\n&7Reden: &fCommando exploit gedetecteerd.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cJe bent tijdelijk geblokkeerd van deze server!\n\n&7Reden: &fPackets exploit gedetecteerd.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cJe bent tijdelijk geblokkeerd van deze server!\n\n&7Reden: &fOnbekend adres gedetecteerd.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang servers zijn offline, kom later terug!\n\n&7Reden: &fExploitFixer kan het UUID niet detecteren.\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/pl.yml b/src/main/resources/locales/pl.yml
new file mode 100644
index 0000000..c99f50d
--- /dev/null
+++ b/src/main/resources/locales/pl.yml
@@ -0,0 +1,27 @@
+# Polish - Credits: Rollczi
+commands:
+ reload: "&aExploitFixer został przeładowany!"
+ help: |-
+ &aExploitFixer &b%version%&a stworzony przez &bLinsaFTW&a.
+ &e /%command% help &7> &bLista dostępnych komend.
+ &e /%command% reload &7> &bPrzeładowanie konfiguracji pluginu.
+ &e /%command% notifications &7> &bWłącza i wyłącza powiadomienia pluginu.
+ &e /%command% stats &7> &bStatystyki pluginu.
+ error:
+ unknown: "&cNieznana komenda. Użyj /exploitfixer help, aby zobaczyć listę komend!"
+ permission: "&cNie masz permisji do tej komendy!"
+ console: "&cTego polecenia nie można wykonać z poziomu konsoli!"
+ notifications:
+ enable: "&aWłączono powiadomienia!"
+ disable: "&cWyłączono powiadomienia!"
+ stats: "&c&lEF: &eGracze w pamięci podręcznej: &a%players_cached%&e Ukarani gracze : &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cZostałeś tymczasowo zablokowany na tym serwerze!\n\n&7Powód: &fWykryto zbyt wiele pakietów CustomPayload.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cZostałeś tymczasowo zablokowany na tym serwerze!\n\n&7Powód: &fWykryto exploit poleceń.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cZostałeś tymczasowo zablokowany na tym serwerze!\n\n&7Powód: &fWykryto exploity dla pakietów.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cZostałeś tymczasowo zablokowany na tym serwerze!\n\n&7Powód: &fWykryto \"null address\".\n&7Discord: &b&n%web%&r"
+mojang_down: "&cSerwery Mojang przestały działać! Spróbuj wejść na serwer później!\n\n&7Powód: &fExploitFixer nie może sprawdzić UUID.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/pt.yml b/src/main/resources/locales/pt.yml
new file mode 100644
index 0000000..f71a658
--- /dev/null
+++ b/src/main/resources/locales/pt.yml
@@ -0,0 +1,27 @@
+# Portuguese - Credits: LucaAzalim
+commands:
+ reload: "&aPlugin ExploitFixer recarregado!"
+ help: |-
+ &aExploitFixer &b%version%&a por &bLinsaFTW&a.
+ &e /%command% help &7> &bComandos disponiveis.
+ &e /%command% reload &7> &bRecarrega o plugin.
+ &e /%command% notifications &7> &bAtiva as notificações do plugin.
+ &e /%command% stats &7> &bVer as estatÃsticas do plugin.
+ error:
+ unknown: "&cComando desconhecido. Use /exploitfixer para ver os comandos disponiveis!"
+ permission: "&cSem permissão para usar esse comando!"
+ console: "&cEsse comando não pode ser executado pelo console!"
+ notifications:
+ enable: "&cVocê ativou as notificações!"
+ disable: "&cVocê desativou as notificações!"
+ stats: "&c&lEF: &eJogadores em cache: &a%players_cached%&e Jogadores punidos: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cVocê está temporariamente bloqueado!\n\n&7Motivo: &fExploit de CustomPayload detectado.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cVocê está temporariamente bloqueado!\n\n&7Motivo: &fExploit de comandos detectado.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cVocê está temporariamente bloqueado!\n\n&7Motivo: &fExploit de packets detectado.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cVocê foi temporariamente bloqueado!\n\n&7Motivo: &fExploit de endereço nulo detectado.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cOs servidores da Mojang estão indisponÃveis. Tente novamente mais tarde.\n\n&7Motivo: &fExploitFixer não pôde confirmar UUID.\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/ro.yml b/src/main/resources/locales/ro.yml
new file mode 100644
index 0000000..1e350b0
--- /dev/null
+++ b/src/main/resources/locales/ro.yml
@@ -0,0 +1,27 @@
+# Romanian Language by DoubleNico
+commands:
+ reload: "&aPluginul ExploitFixer a fost reîncărcat!"
+ help: |-
+ &aExploitFixer &b%version%&a creat de &bLinsaFTW&a.
+ &e /%command% help &7> &bAfișează comenzile disponibile.
+ &e /%command% reload &7> &bReîncărcați pluginul.
+ &e /%command% notifications &7> &bComută notificările despre plugin.
+ &e /%command% stats &7> &bUrmăriți statisticile pluginului.
+ error:
+ unknown: "&cComanda necunoscută. Utilizați /exploitfixer help pentru a vedea comenzile disponibile!"
+ permission: "&cNu aveți permisiunea de a utiliza această comandă!"
+ console: "&cAceastă comandă nu poate fi executată din consola!"
+ notifications:
+ enable: "&aAți activat notificările!"
+ disable: "&cAveți notificările dezactivate!"
+ stats: "&c&lEF: &eJucători cached: &a%players_cached%&e Jucători pedepsiti: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cSunteți blocat temporar de pe acest server!\n\n&7Motiv: &fPrea multe pachete de tip CustomPayload.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cSunteți blocat temporar de pe acest server!\n\n&7Motiv: &fFolosirea Comenzilor Exploatabile.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cSunteți blocat temporar de pe acest server!\n\n&7Motiv: &fExploatarea pachetelor.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cSunteți blocat temporar de pe acest server!\n\n&7Motiv: &fAdresa null.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cServerele Mojang sunt offline, vă rugăm să reveniți mai târziu!\n\n&7Motiv: &fNu putem detecta adresa UUID.\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/ru.yml b/src/main/resources/locales/ru.yml
new file mode 100644
index 0000000..0a71164
--- /dev/null
+++ b/src/main/resources/locales/ru.yml
@@ -0,0 +1,27 @@
+# Russian - Credits: GreenLink
+commands:
+ reload: "&aПлагин ExploitFixer перезагружен!"
+ help: |-
+ &aExploitFixer &b%version%&a автор &bLinsaFTW&a.
+ &e /%command% help &7> &bПоказывает доÑтупные команды.
+ &e /%command% reload &7> &bПерезагружает плагин.
+ &e /%command% notifications &7> &bВключает/Отключает ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð°.
+ &e /%command% stats &7> &bПроÑмотр ÑтатиÑтики плагина.
+ error:
+ unknown: "&cÐеизвеÑÑ‚Ð½Ð°Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°. ВоÑпользуйтеÑÑŒ /exploitfixer help чтобы проÑмотреть доÑтупные команды!"
+ permission: "&cУ Ð²Ð°Ñ Ð½ÐµÑ‚ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½Ð¸Ñ Ð½Ð° иÑпользование Ñтой команды!"
+ console: "&cÐта команда не может быть выполнена из конÑоли!"
+ notifications:
+ enable: "&aÐ’Ñ‹ включили уведомлениÑ!"
+ disable: "&cÐ’Ñ‹ отключили уведомлениÑ!"
+ stats: "&c&lEF: &Сохранённых игроков: &a%players_cached%&e Ðаказанных игроков: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cÐ’Ñ‹ временно заблокированы на Ñтом Ñервере!\n\n&7Причина: &fОбнаружено Ñлишком много пользовательÑких пакетов.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cÐ’Ñ‹ временно заблокированы на Ñтом Ñервере!\n\n&7Причина: &fОбнаружена уÑзвимоÑть команды.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cÐ’Ñ‹ временно заблокированы на Ñтом Ñервере!\n\n&7Причина: &fОбнаружена уÑзвимоÑть пакетов.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cÐ’Ñ‹ временно заблокированы на Ñтом Ñервере!\n\n&7Причина: &fОбнаружен пуÑтой адреÑ.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cСервера Mojang не работают, пожалуйÑта, возвращайтеÑÑŒ позже!\n\n&7Причина: &fExploitFixer не может проверить UUID.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/th.yml b/src/main/resources/locales/th.yml
new file mode 100644
index 0000000..91f3895
--- /dev/null
+++ b/src/main/resources/locales/th.yml
@@ -0,0 +1,27 @@
+# Thai (ภาษาไทย) - Credits: Hazuki-san
+commands:
+ reload: "&aปลั๊à¸à¸à¸´à¸™ ExploitFixer ได้โหลดใหม่à¹à¸¥à¹‰à¸§!"
+ help: |-
+ &aExploitFixer &b%version%&a โดย &bLinsaFTW&a
+ &e /%command% help &7> &bà¹à¸ªà¸”งคำสั่งที่ใช้ได้
+ &e /%command% reload &7> &bโหลดปลั๊à¸à¸à¸´à¸™à¹ƒà¸«à¸¡à¹ˆ
+ &e /%command% notifications &7> &bเปิดหรืà¸à¸›à¸´à¸”à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¸‚à¸à¸‡à¸›à¸¥à¸±à¹Šà¸à¸à¸´à¸™
+ &e /%command% stats &7> &bดูสถานะขà¸à¸‡à¸›à¸¥à¸±à¹Šà¸à¸à¸´à¸™
+ error:
+ unknown: "&cไม่รู้จัà¸à¸„ำสั่งนี้ ใช้ /exploitfixer help เพื่à¸à¸”ูคำสั่งที่สามารถใช้ได้!"
+ permission: "&cคุณไม่มีสิทธิที่จะใช้คำสั่งนี้!"
+ console: "&cคำสั่งนี้ไม่สามารถใช้งานในคà¸à¸¥à¹‚ซลได้!"
+ notifications:
+ enable: "&aคุณได้เปิดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸¥à¹‰à¸§!"
+ disable: "&cคุณได้ปิดà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ืà¸à¸™à¹à¸¥à¹‰à¸§!"
+ stats: "&c&lEF: &eผู้เล่นที่à¹à¸„ชà¹à¸¥à¹‰à¸§: &a%players_cached%&e ผู้เล่นที่ลงโทษà¹à¸¥à¹‰à¸§: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cคุณถูà¸à¸šà¸¥à¹Šà¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราว!\n\n&7สาเหตุ: &fตรวจพบเจà¸à¹à¸žà¹‡à¸„เà¸à¹‡à¸— CustomPayload มาà¸à¹€à¸à¸´à¸™à¹„ป\n&7ดิสคà¸à¸£à¹Œà¸”: &b&n%web%&r"
+ commands:
+ kick_message: "&cคุณถูà¸à¸šà¸¥à¹Šà¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราว!\n\n&7สาเหตุ: &fตรวจพบà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸„ำสั่งที่มีช่à¸à¸‡à¹‚หว่\n&7ดิสคà¸à¸£à¹Œà¸”: &b&n%web%&r"
+ packets:
+ kick_message: "&cคุณถูà¸à¸šà¸¥à¹Šà¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราว!\n\n&7สาเหตุ: &fตรวจพบà¸à¸²à¸£à¹ƒà¸Šà¹‰à¹à¸žà¹‡à¸—เà¸à¹‡à¸—ที่มีช่à¸à¸‡à¹‚หว่\n&7ดิสคà¸à¸£à¹Œà¸”: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cคุณถูà¸à¸šà¸¥à¹Šà¸à¸à¸ˆà¸²à¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸à¸£à¹Œà¸Šà¸±à¹ˆà¸§à¸„ราว!\n\n&7สาเหตุ: &fตรวจพบà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸—ี่à¸à¸¢à¸¹à¹ˆà¸—ี่ไม่มีค่า\n&7ดิสคà¸à¸£à¹Œà¸”: &b&n%web%&r"
+mojang_down: "&cขณะนี้เซิร์ฟเวà¸à¸£à¹Œ Mojang ใช้งานไม่ได้ ค่à¸à¸¢à¸¡à¸²à¹ƒà¸«à¸¡à¹ˆà¸à¸µà¸à¸—ีทีหลังนะ!\n\n&7สาเหตุ: &fExploitFixer ไม่สามารถตรวจสà¸à¸š UUID ได้\n&7ดิสคà¸à¸£à¹Œà¸”: &b&n%web%&r"
diff --git a/src/main/resources/locales/tr.yml b/src/main/resources/locales/tr.yml
new file mode 100644
index 0000000..77da929
--- /dev/null
+++ b/src/main/resources/locales/tr.yml
@@ -0,0 +1,27 @@
+# Turkish
+commands:
+ reload: "&aPlugin ExploitFixer yeniden yüklendi!"
+ help: |-
+ &aExploitFixer &b%version% &ayazan &bLinsaFTW&a.
+ &e /%command% help &7> &bMevcut komutları gösterir.
+ &e /%command% reload &7> &bEklentiyi yükler.
+ &e /%command% notifications &7> &bEklentinin bildirimlerini deÄŸiÅŸtirir.
+ &e /%command% stats &7> &bEklentinin istatistiklerini izleyin.
+ error:
+ unknown: "&cBilinmeyen komut. Kullanılabilir komutları görmek için / exploitfixer yardımını kullanın!"
+ permission: "&cBu komutu kullanma izniniz yok!"
+ console: "&cBu komut konsoldan yürütülemez!"
+ notifications:
+ enable: "&aBildirimleri etkinleÅŸtirdiniz!"
+ disable: "&cBildirimleri devre dışı bıraktınız!"
+ stats: "&c&lEF: &ePlayers önbelleğe alındı: &a%players_cached%&e Players cezalandırıldı: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cBu sunucudan geçici olarak engellendiniz!\n\n&7Reason: &fBirçok CustomPayload paketi algılandı.\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cBu sunucudan geçici olarak engellendiniz!\n\n&7Reason: &fKomuttan yararlanma algılandı.\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cBu sunucudan geçici olarak engellendiniz!\n\n&7Reason: &fPackets istismar tespit edildi.\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cBu sunucudan geçici olarak engellendiniz!\n\n&7Reason: &fNull adres algılandı.\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang kapalı, kısa süre sonra tekrar gelin!\n\n&7Neden: &fExploitFixer UUID'yi doğrulayamıyor.\n&7Discord: &b&n%web%&r"
\ No newline at end of file
diff --git a/src/main/resources/locales/zh.yml b/src/main/resources/locales/zh.yml
new file mode 100644
index 0000000..2d52b2a
--- /dev/null
+++ b/src/main/resources/locales/zh.yml
@@ -0,0 +1,26 @@
+commands:
+ reload: "&aæˆåŠŸé‡è½½æ’ä»¶ExploitFixer!"
+ help: |-
+ &aExploitFixer指令:
+ &e/%command% help &7- &bS显示帮助指令!
+ &e/%command% reload &7- &bé‡è½½æ’ä»¶!
+ &e/%command% notifications &7- &b开关æ’ä»¶æ¶ˆæ¯æé†’!
+ &e/%command% stats &7- &b查看æ’件统计数æ®!
+ error:
+ unknown: "&c未知指令。请使用 /exploitfixer help æ¥æŸ¥çœ‹æŒ‡ä»¤!"
+ permission: "&cä½ æ²¡æœ‰ä½¿ç”¨è¿™ä¸ªæŒ‡ä»¤çš„æƒé™!"
+ console: "&cè¿™ä¸ªæŒ‡ä»¤æ— æ³•åœ¨æŽ§åˆ¶å°æ‰§è¡Œ!"
+ notifications:
+ enable: "&aä½ å·²å¼€å¯æé†’!"
+ disable: "&cä½ å·²å…³é—æé†’!"
+ stats: "&c&lEF: &e已缓å˜çš„玩家: &a%players_cached% &e已被惩罚的玩家: &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cä½ è¢«æš‚æ—¶å°ç¦äº†!\n\n&7åŽŸå› : &f检测到CustomPayloadå‘包过多。\n&7Discord: &b&n%web%&r"
+ commands:
+ kick_message: "&cä½ è¢«æš‚æ—¶å°ç¦äº†!\n\n&7åŽŸå› : &f检测到滥用指令。\n&7Discord: &b&n%web%&r"
+ packets:
+ kick_message: "&cä½ è¢«æš‚æ—¶å°ç¦äº†!\n\n&7åŽŸå› : &f检测到å‘包异常。\n&7Discord: &b&n%web%&r"
+ nulladdress:
+ kick_message: "&c您被暂时å°ç¦äº†!\n\n&7åŽŸå› : &f检测到空地å€ã€‚\n&7Discord: &b&n%web%&r"
+mojang_down: "&cMojang æœåŠ¡å™¨åœæœºäº†ï¼Œè¯·ç¨åŽå†æ¥çœ‹çœ‹!\n\n&7åŽŸå› : &fExploitFixer æ— æ³•æ£€æŸ¥UUID。\n&7Discord: &b&n%web%&r"
diff --git a/src/main/resources/locales/zhtw.yml b/src/main/resources/locales/zhtw.yml
new file mode 100644
index 0000000..fc0ce74
--- /dev/null
+++ b/src/main/resources/locales/zhtw.yml
@@ -0,0 +1,27 @@
+# Chinese Traditional - Credits: Flandre_tw
+commands:
+ reload: "&aExploitFixer 已經æˆåŠŸè¼‰å…¥ ï¼"
+ help: |-
+ &aExploitFixer 指令 :
+ &e /%command% help &7> &b顯示說明指令。
+ &e /%command% reload &7> &b釿–°è¼‰å…¥æ’件。
+ &e /%command% notifications &7> &bé–‹é—œæ’ä»¶æé†’通知。
+ &e /%command% stats &7> &b查看æ’件統計資料。
+ error:
+ unknown: "&c未知的指令。請使用 /exploitfixer help 查看指令說明 ï¼"
+ permission: "&cä½ æ²’æœ‰ä½¿ç”¨è©²æŒ‡ä»¤çš„æ¬Šé™ ï¼"
+ console: "&c該指令無法在控制å°åŸ·è¡Œ ï¼"
+ notifications:
+ enable: "&aä½ å·²é–‹å•Ÿæé†’通知 ï¼"
+ disable: "&cä½ å·²é—œé–‰æé†’通知 ï¼"
+ stats: "&c&lEF : &e已快å–的玩家 : &a%players_cached% &e已被懲罰的玩家 : &a%players_punished%"
+modules:
+ custompayload:
+ kick_message: "&cä½ æš«æ™‚è¢«ä¼ºæœå™¨è¸¢å‡º ï¼ \n\n&7åŽŸå› ï¼š &f檢測到éŽå¤šçš„ CustomPayload å°åŒ…。\n&7Discord : &b&n%web%&r"
+ commands:
+ kick_message: "&cä½ æš«æ™‚è¢«ä¼ºæœå™¨è¸¢å‡º ï¼ \n\n&7åŽŸå› ï¼š &f檢測到濫用指令。\n&7Discord : &b&n%web%&r"
+ packets:
+ kick_message: "&cä½ æš«æ™‚è¢«ä¼ºæœå™¨è¸¢å‡º ï¼ \n\n&7åŽŸå› ï¼š &f檢測到å°åŒ…異常。\n&7Discord : &b&n%web%&r"
+ nulladdress:
+ kick_message: "&cä½ æš«æ™‚è¢«ä¼ºæœå™¨è¸¢å‡º ï¼ \n\n&7åŽŸå› ï¼š &f檢測到空的ä½å€ã€‚\n&7Discord : &b&n%web%&r"
+mojang_down: "&cMojang 伺æœå™¨é›¢ç·šï¼Œè«‹ç¨å¾Œå†æŸ¥çœ‹ ï¼ \n\n&7åŽŸå› ï¼š &fExploitFixer 無法檢查 UUID。\n&7Discord : &b&n%web%&r"
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
new file mode 100644
index 0000000..59e7501
--- /dev/null
+++ b/src/main/resources/plugin.yml
@@ -0,0 +1,14 @@
+name: ${name}
+description: ${description}
+author: ${author}
+version: ${version}
+url: ${url}
+main: dev._2lstudios.exploitfixer.ExploitFixer
+api: "1.13"
+api-version: "1.13"
+softdepend: [HamsterAPI]
+commands:
+ exploitfixer:
+ description: The main ExploitFixer command
+ usage: /exploitfixer
+ aliases: [ef]
diff --git a/src/messages.yml b/src/messages.yml
deleted file mode 100644
index eb2a519..0000000
--- a/src/messages.yml
+++ /dev/null
@@ -1,102 +0,0 @@
-# You can create as many locales as you want,
-# The language will change depending on the player locale.
-# If the locale of the player doesnt exist in this file, english will be used.
-
-# English
-en:
- commands:
- reload: "&aPlugin ExploitFixer reloaded!"
- help: |-
- &aExploitFixer commands:
- &e/%command% help &7- &bShows available commands!
- &e/%command% reload &7- &bReloads the plugin!
- &e/%command% notifications &7- &bToggles notifications of the plugin!
- &e/%command% stats &7- &bWatch the stats of the plugin!
- error:
- unknown: "&cUnknown command. Use /exploitfixer help to see available commands!"
- permission: "&cYou dont have permission to use this command!"
- console: "&cThis command cant be executed from the console!"
- notifications:
- enable: "&aYou have enabled notifications!"
- disable: "&cYou have disabled notifications!"
- modules:
- custompayload:
- kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fToo many CustomPayload packets detected.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- uuidspoof:
- kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fUUID Spoofing detected.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- commands:
- kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fCommand exploit detected.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- duplication:
- kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fDuplication exploit detected.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- items:
- kick_message: "&cYou are temporally blocked from this server!\n\n&7Reason: &fItem exploit detected.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- stats:
- message: |-
- &fPlayers cached: &a%players_cached%
- &fPlayers punished: &a%players_punished%
-
-# Spanish
-es:
- commands:
- reload: "&aPlugin ExploitFixer recargado!"
- help: |-
- &aComandos de ExploitFixer:
- &e/%command% help &7- &bMuestra los comandos disponibles!
- &e/%command% reload &7- &bRecarga el plugin!
- &e/%command% notifications &7- &bAlterna las notificaciones del plugin!
- &e/%command% stats &7- &bMira las estadisticas del plugin!
- error:
- unknown: "&cComando desconocido. Usa /exploitfixer help para ver comandos disponibles!"
- permission: "&cNo tienes permiso para usar ese comando!"
- console: "&cEste comando no puede ser usado desde la consola!"
- notifications:
- enable: "&aHabilitaste las notificaciones!"
- disable: "&cDeshabilitaste las notificaciones!"
- modules:
- custompayload:
- kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Reason: &fExploit de CustomPayload detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- uuidspoof:
- kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Reason: &fExploit de UUID detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- commands:
- kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Reason: &fExploit de comandos detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- duplication:
- kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Reason: &fExploit de duplicacion detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- items:
- kick_message: "&cFuiste bloqueado temporalmente!\n\n&7Reason: &fExploit de items detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- stats:
- message: |-
- &fJugadores en cache: &a%players_cached%
- &fJugadores sancionados: &a%players_punished%
-
-# Portuguese
-pt:
- commands:
- reload: "&aPlugin ExploitFixer recarregado!"
- help: |-
- &aExploitFixer comandos:
- &e/%command% help &7- &bComandos disponiveis!
- &e/%command% reload &7- &bReload plugin!
- &e/%command% notifications &7- &bAtivar as notificações do plugin!
- &e/%command% stats &7- &bVer statisticas do plugin!
- error:
- unknown: "&cComando desconhecido. Use /exploitfixer para ver os comandos disponiveis!"
- permission: "&cSem permissao para usar este comando!"
- console: "&cEsse comando não pode ser executa pelo console!"
- notifications:
- enable: "&aVoce ativou as notificações!"
- disable: "&cVoce desativou as notificações!"
- modules:
- custompayload:
- kick_message: "&cVoce foi temporariamente bloqueado!\n\n&7Reason: &fExploit de CustomPayload detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- uuidspoof:
- kick_message: "&cVoce foi temporariamente bloqueado!\n\n&7Reason: &fExploit de UUID detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- commands:
- kick_message: "&cVoce foi temporariamente bloqueado!\n\n&7Reason: &fExploit de comandos detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- duplication:
- kick_message: "&cVoce foi temporariamente bloqueado!\n\n&7Reason: &fExploit de duplicação detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- items:
- kick_message: "&cVoce foi temporariamente bloqueado!\n\n&7Reason: &fExploit de items detectado.\n&7Discord: &b&nhttps://discord.gg/cjt9bPA&r"
- stats:
- message: |-
- &fJugadores em cache: &a%players_cached%
- &fJogadores punidos: &a%players_punished%
\ No newline at end of file
diff --git a/src/plugin.yml b/src/plugin.yml
deleted file mode 100644
index c9c84ba..0000000
--- a/src/plugin.yml
+++ /dev/null
@@ -1,10 +0,0 @@
-name: ExploitFixer
-main: twolovers.exploitfixer.bukkit.ExploitFixer
-version: 0.6.1
-author: 2LS
-softdepend: [ProtocolLib]
-commands:
- exploitfixer:
- description: The main ExploitFixer command
- usage: /exploitfixer
- aliases: [ef]
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/ExploitFixer.java b/src/twolovers/exploitfixer/bukkit/ExploitFixer.java
deleted file mode 100644
index 48be0bb..0000000
--- a/src/twolovers/exploitfixer/bukkit/ExploitFixer.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package twolovers.exploitfixer.bukkit;
-
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.plugin.PluginManager;
-import org.bukkit.plugin.java.JavaPlugin;
-import twolovers.exploitfixer.bukkit.adapters.initializers.AdapterInitializer;
-import twolovers.exploitfixer.bukkit.commands.ExploitFixerCommand;
-import twolovers.exploitfixer.bukkit.listeners.initializers.ListenerInitializer;
-import twolovers.exploitfixer.bukkit.managers.BukkitModuleManager;
-import twolovers.exploitfixer.bukkit.utils.ConfigurationUtil;
-import twolovers.exploitfixer.interfaces.managers.ExploitPlayerManager;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-
-public class ExploitFixer extends JavaPlugin {
- private static ExploitFixer exploitFixer;
- private ModuleManager moduleManager;
-
- public void onEnable() {
- final ConfigurationUtil configurationUtil = new ConfigurationUtil(this);
-
- configurationUtil.createConfiguration("%datafolder%/config.yml");
- configurationUtil.createConfiguration("%datafolder%/messages.yml");
-
- final YamlConfiguration configYml = configurationUtil.getConfiguration("%datafolder%/config.yml");
- final YamlConfiguration messagesYml = configurationUtil.getConfiguration("%datafolder%/messages.yml");
-
- exploitFixer = this;
- this.moduleManager = new BukkitModuleManager(configYml, messagesYml);
-
- reload();
-
- getServer().getScheduler().scheduleSyncRepeatingTask(this, () -> {
- final ExploitPlayerManager exploitPlayerManager = moduleManager.getExploitPlayerManager();
-
- if (!exploitPlayerManager.getExploitPlayers().isEmpty()) {
- exploitPlayerManager.reload();
- System.out.println("[ExploitFixer] Automatically cleared unused cached players!");
- }
- }, 9000L, 9000L);
- }
-
- public void reload() {
- final ConfigurationUtil configurationUtil = new ConfigurationUtil(this);
-
- configurationUtil.createConfiguration("%datafolder%/config.yml");
- configurationUtil.createConfiguration("%datafolder%/messages.yml");
-
- final YamlConfiguration configYml = configurationUtil.getConfiguration("%datafolder%/config.yml");
- final YamlConfiguration messagesYml = configurationUtil.getConfiguration("%datafolder%/messages.yml");
- final YamlConfiguration spigotYml = configurationUtil.getConfiguration("%datafolder%/../spigot.yml");
-
- moduleManager.reload(configYml, messagesYml, spigotYml);
-
- final PluginManager pluginManager = getServer().getPluginManager();
-
- if (pluginManager.isPluginEnabled("ProtocolLib")) {
- new AdapterInitializer(this, moduleManager);
- } else
- System.out.println("[ExploitFixer] ProtocolLib not found, enable it for better protection!");
-
- new ListenerInitializer(this, moduleManager);
-
- getCommand("exploitfixer").setExecutor(new ExploitFixerCommand(moduleManager));
- }
-
- public static ExploitFixer getInstance() {
- return exploitFixer;
- }
-}
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/adapters/AbilitiesPacketAdapter.java b/src/twolovers/exploitfixer/bukkit/adapters/AbilitiesPacketAdapter.java
deleted file mode 100644
index 6da192b..0000000
--- a/src/twolovers/exploitfixer/bukkit/adapters/AbilitiesPacketAdapter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package twolovers.exploitfixer.bukkit.adapters;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-import org.bukkit.plugin.Plugin;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-import twolovers.exploitfixer.interfaces.modules.PacketsModule;
-import twolovers.exploitfixer.shared.enums.PacketIdentity;
-
-public class AbilitiesPacketAdapter extends PacketAdapter {
- private final PacketsModule packetsModule;
-
- public AbilitiesPacketAdapter(final Plugin plugin, final ModuleManager moduleManager) {
- super(plugin, PacketType.Play.Client.ABILITIES);
- this.packetsModule = moduleManager.getPacketsModule();
- }
-
- @Override
- public void onPacketReceiving(final PacketEvent event) {
- packetsModule.checkPacket(event, PacketIdentity.ABILITIES);
- }
-}
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/adapters/ArmAnimationPacketAdapter.java b/src/twolovers/exploitfixer/bukkit/adapters/ArmAnimationPacketAdapter.java
deleted file mode 100644
index f9db41f..0000000
--- a/src/twolovers/exploitfixer/bukkit/adapters/ArmAnimationPacketAdapter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package twolovers.exploitfixer.bukkit.adapters;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-import org.bukkit.plugin.Plugin;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-import twolovers.exploitfixer.interfaces.modules.PacketsModule;
-import twolovers.exploitfixer.shared.enums.PacketIdentity;
-
-public class ArmAnimationPacketAdapter extends PacketAdapter {
- private final PacketsModule packetsModule;
-
- public ArmAnimationPacketAdapter(final Plugin plugin, final ModuleManager moduleManager) {
- super(plugin, PacketType.Play.Client.ARM_ANIMATION);
- this.packetsModule = moduleManager.getPacketsModule();
- }
-
- @Override
- public void onPacketReceiving(final PacketEvent event) {
- packetsModule.checkPacket(event, PacketIdentity.ARM_ANIMATION);
- }
-}
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/adapters/BlockDigPacketAdapter.java b/src/twolovers/exploitfixer/bukkit/adapters/BlockDigPacketAdapter.java
deleted file mode 100644
index 8ca60f6..0000000
--- a/src/twolovers/exploitfixer/bukkit/adapters/BlockDigPacketAdapter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package twolovers.exploitfixer.bukkit.adapters;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-import org.bukkit.plugin.Plugin;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-import twolovers.exploitfixer.interfaces.modules.PacketsModule;
-import twolovers.exploitfixer.shared.enums.PacketIdentity;
-
-public class BlockDigPacketAdapter extends PacketAdapter {
- private final PacketsModule packetsModule;
-
- public BlockDigPacketAdapter(final Plugin plugin, final ModuleManager moduleManager) {
- super(plugin, PacketType.Play.Client.BLOCK_DIG);
- this.packetsModule = moduleManager.getPacketsModule();
- }
-
- @Override
- public void onPacketReceiving(final PacketEvent event) {
- packetsModule.checkPacket(event, PacketIdentity.BLOCK_DIG);
- }
-}
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/adapters/BlockPlacePacketAdapter.java b/src/twolovers/exploitfixer/bukkit/adapters/BlockPlacePacketAdapter.java
deleted file mode 100644
index 3c0cc87..0000000
--- a/src/twolovers/exploitfixer/bukkit/adapters/BlockPlacePacketAdapter.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package twolovers.exploitfixer.bukkit.adapters;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketEvent;
-import org.bukkit.plugin.Plugin;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-import twolovers.exploitfixer.interfaces.modules.PacketsModule;
-import twolovers.exploitfixer.shared.enums.PacketIdentity;
-
-public class BlockPlacePacketAdapter extends PacketAdapter {
- private final PacketsModule packetsModule;
-
- public BlockPlacePacketAdapter(final Plugin plugin, final ModuleManager moduleManager) {
- super(plugin, PacketType.Play.Client.BLOCK_PLACE);
- this.packetsModule = moduleManager.getPacketsModule();
- }
-
- @Override
- public void onPacketReceiving(final PacketEvent event) {
- packetsModule.checkPacket(event, PacketIdentity.BLOCK_PLACE);
- }
-}
\ No newline at end of file
diff --git a/src/twolovers/exploitfixer/bukkit/adapters/CustomPayloadPacketAdapter.java b/src/twolovers/exploitfixer/bukkit/adapters/CustomPayloadPacketAdapter.java
deleted file mode 100644
index d0b035c..0000000
--- a/src/twolovers/exploitfixer/bukkit/adapters/CustomPayloadPacketAdapter.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package twolovers.exploitfixer.bukkit.adapters;
-
-import com.comphenix.protocol.PacketType;
-import com.comphenix.protocol.events.PacketAdapter;
-import com.comphenix.protocol.events.PacketContainer;
-import com.comphenix.protocol.events.PacketEvent;
-import io.netty.buffer.ByteBuf;
-import org.bukkit.Bukkit;
-import org.bukkit.entity.Player;
-import org.bukkit.plugin.Plugin;
-import twolovers.exploitfixer.interfaces.instanceables.ExploitPlayer;
-import twolovers.exploitfixer.interfaces.managers.ExploitPlayerManager;
-import twolovers.exploitfixer.interfaces.managers.ModuleManager;
-import twolovers.exploitfixer.interfaces.modules.CustomPayloadModule;
-import twolovers.exploitfixer.interfaces.modules.MessagesModule;
-import twolovers.exploitfixer.shared.enums.PacketIdentity;
-
-import java.util.List;
-
-public class CustomPayloadPacketAdapter extends PacketAdapter {
- private final CustomPayloadModule customPayloadModule;
- private final ExploitPlayerManager exploitPlayerManager;
- private final MessagesModule messagesModule;
- private final boolean isOneDotSeven;
-
- public CustomPayloadPacketAdapter(final Plugin plugin, final ModuleManager moduleManager) {
- super(plugin, PacketType.Play.Client.CUSTOM_PAYLOAD);
- this.customPayloadModule = moduleManager.getCustomPayloadModule();
- this.messagesModule = moduleManager.getMessagesModule();
- this.exploitPlayerManager = moduleManager.getExploitPlayerManager();
- this.isOneDotSeven = Bukkit.getServer().getVersion().contains("1.7");
- }
-
- @Override
- public void onPacketReceiving(final PacketEvent event) {
- if (!event.isCancelled() && customPayloadModule.isEnabled()) {
- final PacketContainer packetContainer = event.getPacket();
- String tag = "";
- final Player player = event.getPlayer();
- final ExploitPlayer exploitPlayer = exploitPlayerManager.getPlayer(player.getName());
-
- if (exploitPlayer != null) {
- int dataSize = 0;
- final List