diff --git a/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/BukkitPlugin.java b/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/BukkitPlugin.java
index 1575722..7dddfe1 100644
--- a/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/BukkitPlugin.java
+++ b/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/BukkitPlugin.java
@@ -1,19 +1,20 @@
package me.confuser.banmanager.webenhancer.bukkit;
import lombok.Getter;
+import me.confuser.banmanager.bukkit.BMBukkitPlugin;
import me.confuser.banmanager.bukkit.BukkitCommand;
import me.confuser.banmanager.bukkit.BukkitScheduler;
import me.confuser.banmanager.bukkit.PluginLogger;
-import org.bstats.bukkit.Metrics;
import me.confuser.banmanager.common.commands.CommonCommand;
import me.confuser.banmanager.common.configs.PluginInfo;
import me.confuser.banmanager.common.configuration.ConfigurationSection;
import me.confuser.banmanager.common.configuration.file.YamlConfiguration;
+import me.confuser.banmanager.webenhancer.bukkit.listeners.LogServerAppender;
import me.confuser.banmanager.webenhancer.bukkit.listeners.ReportListener;
import me.confuser.banmanager.webenhancer.common.WebEnhancerPlugin;
-import me.confuser.banmanager.webenhancer.bukkit.listeners.LogServerAppender;
import org.apache.logging.log4j.LogManager;
-import org.bukkit.event.Listener;
+import org.bstats.bukkit.Metrics;
+import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
@@ -35,23 +36,31 @@ public class BukkitPlugin extends JavaPlugin {
@Override
public void onEnable() {
+ BMBukkitPlugin banManager = (BMBukkitPlugin) Bukkit.getPluginManager().getPlugin("BanManager");
+ if (banManager == null || banManager.getPlugin() == null) {
+ getLogger().severe("BanManager is not enabled — cannot start BanManager-WebEnhancer.");
+ getPluginLoader().disablePlugin(this);
+ return;
+ }
+
PluginInfo pluginInfo;
try {
pluginInfo = setupConfigs();
} catch (IOException e) {
getPluginLoader().disablePlugin(this);
- e.printStackTrace();
+ getLogger().log(java.util.logging.Level.WARNING, "Failed to set up plugin configuration", e);
return;
}
metrics = new Metrics(this, 8838);
- plugin = new WebEnhancerPlugin(pluginInfo, new PluginLogger(getLogger()), getDataFolder(), new BukkitScheduler(this), new BukkitMetrics(metrics));
+ plugin = new WebEnhancerPlugin(pluginInfo, new PluginLogger(getLogger()), getDataFolder(),
+ new BukkitScheduler(this), new BukkitMetrics(metrics), banManager.getPlugin());
try {
plugin.enable();
} catch (Exception e) {
getPluginLoader().disablePlugin(this);
- e.printStackTrace();
+ getLogger().log(java.util.logging.Level.WARNING, "Failed to enable BanManager-WebEnhancer", e);
return;
}
@@ -102,7 +111,7 @@ private PluginInfo setupConfigs() throws IOException {
public void setupCommands() {
for (CommonCommand cmd : plugin.getCommands()) {
try {
- getCommand(cmd.getCommandName()).setExecutor(new BukkitCommand(cmd));
+ getCommand(cmd.getCommandName()).setExecutor(new BukkitCommand(plugin.getBanManagerPlugin(), cmd));
} catch (NullPointerException e) {
plugin.getLogger().severe("Failed to register /" + cmd.getCommandName() + " command");
}
@@ -113,10 +122,6 @@ public void setupListeners() {
appender = new LogServerAppender(plugin);
((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()).addAppender(appender);
- registerEvent(new ReportListener(this));
- }
-
- private void registerEvent(Listener listener) {
- getServer().getPluginManager().registerEvents(listener, this);
+ new ReportListener(this);
}
}
diff --git a/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/listeners/ReportListener.java b/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/listeners/ReportListener.java
index 8dac0ea..bfe7fc5 100644
--- a/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/listeners/ReportListener.java
+++ b/bukkit/src/main/java/me/confuser/banmanager/webenhancer/bukkit/listeners/ReportListener.java
@@ -1,50 +1,54 @@
package me.confuser.banmanager.webenhancer.bukkit.listeners;
-import me.confuser.banmanager.common.ormlite.stmt.DeleteBuilder;
-import me.confuser.banmanager.bukkit.api.events.PlayerBannedEvent;
-import me.confuser.banmanager.bukkit.api.events.PlayerReportDeletedEvent;
-import me.confuser.banmanager.bukkit.api.events.PlayerReportedEvent;
-import me.confuser.banmanager.bukkit.api.events.PlayerDeniedEvent;
-import me.confuser.banmanager.bukkit.api.events.PluginReloadedEvent;
-import me.confuser.banmanager.common.util.Message;
+import me.confuser.banmanager.api.event.player.PlayerReportDeletedEvent;
+import me.confuser.banmanager.api.event.player.PlayerReportedEvent;
+import me.confuser.banmanager.api.event.player.PluginReloadedEvent;
+import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.common.data.PlayerReportData;
+import me.confuser.banmanager.common.ormlite.stmt.DeleteBuilder;
import me.confuser.banmanager.webenhancer.bukkit.BukkitPlugin;
import me.confuser.banmanager.webenhancer.common.data.LogData;
import me.confuser.banmanager.webenhancer.common.data.ReportLogData;
-import me.confuser.banmanager.webenhancer.common.listeners.CommonPlayerDeniedListener;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
-import org.bukkit.event.Listener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
-import me.confuser.banmanager.common.BanManagerPlugin;
-public class ReportListener implements Listener {
+/**
+ * Bridges Bukkit's report lifecycle into WebEnhancer storage.
+ *
+ *
Subscribes to the cross-platform {@link me.confuser.banmanager.api.event.EventBus}
+ * — the per-platform {@code PlayerDeniedEvent}/{@code PlayerBannedEvent}
+ * handlers that used to live here have been consolidated into
+ * {@link me.confuser.banmanager.webenhancer.common.listeners.CommonPlayerDeniedListener}.
+ */
+public class ReportListener {
private final BukkitPlugin plugin;
- private CommonPlayerDeniedListener listener;
+ private final BanManagerPlugin banManagerPlugin;
public ReportListener(BukkitPlugin plugin) {
this.plugin = plugin;
- this.listener = new CommonPlayerDeniedListener(plugin.getPlugin());
+ this.banManagerPlugin = plugin.getPlugin().getBanManagerPlugin();
+
+ var events = plugin.getPlugin().getBanManagerService().events();
+ events.subscribe(PlayerReportedEvent.class, this::notifyOnReport);
+ events.subscribe(PlayerReportDeletedEvent.class, this::reportDeleted);
+ events.subscribe(PluginReloadedEvent.class, e -> plugin.getPlugin().setupConfigs());
}
- @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
- public void notifyOnReport(PlayerReportedEvent event) {
+ private void notifyOnReport(PlayerReportedEvent event) {
List logs;
Queue queue = plugin.getAppender().getQueue();
synchronized (queue) {
logs = new ArrayList<>(queue);
}
- final int reportId = event.getReport().getId();
+ final int reportId = event.report().id();
- BanManagerPlugin.getInstance().getScheduler().runAsync(() -> {
+ banManagerPlugin.getScheduler().runAsync(() -> {
try {
- PlayerReportData report = BanManagerPlugin.getInstance()
- .getPlayerReportStorage().queryForId(reportId);
+ PlayerReportData report = banManagerPlugin.getPlayerReportStorage().queryForId(reportId);
if (report == null) return;
@@ -53,14 +57,13 @@ public void notifyOnReport(PlayerReportedEvent event) {
plugin.getPlugin().getReportLogStorage().create(new ReportLogData(report, log));
}
} catch (SQLException e) {
- e.printStackTrace();
+ plugin.getPlugin().getLogger().warning("Failed to attach report logs: " + e.getMessage());
}
});
}
- @EventHandler
- public void reportDeleted(PlayerReportDeletedEvent event) {
- int id = event.getReport().getId();
+ private void reportDeleted(PlayerReportDeletedEvent event) {
+ int id = event.report().id();
DeleteBuilder builder = plugin.getPlugin().getReportLogStorage().deleteBuilder();
@@ -68,29 +71,7 @@ public void reportDeleted(PlayerReportDeletedEvent event) {
builder.where().eq("report_id", id);
builder.delete();
} catch (SQLException e) {
- e.printStackTrace();
plugin.getLogger().warning("Failed to delete report associations for " + id);
}
}
-
- @EventHandler
- public void onDeny(PlayerDeniedEvent event) {
- listener.handlePin(event.getPlayer(), event.getMessage());
- }
-
- @EventHandler(priority = EventPriority.MONITOR)
- public void onBanned(PlayerBannedEvent event) {
- try {
- Message kickMessage = event.getKickMessage();
- if (kickMessage != null) {
- listener.handlePin(event.getBan().getPlayer(), kickMessage);
- }
- } catch (NoSuchMethodError ignored) {
- }
- }
-
- @EventHandler
- public void onReload(PluginReloadedEvent event) {
- plugin.getPlugin().setupConfigs();
- }
}
diff --git a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeeCommand.java b/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeeCommand.java
index 9b96c6a..cf86279 100644
--- a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeeCommand.java
+++ b/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeeCommand.java
@@ -45,10 +45,11 @@ public void execute(CommandSender sender, String[] args) {
}
private CommonSender getSender(CommandSender source) {
+ BanManagerPlugin banManagerPlugin = plugin.getPlugin().getBanManagerPlugin();
if (source instanceof ProxiedPlayer) {
- return new BungeePlayer((ProxiedPlayer) source, BanManagerPlugin.getInstance().getConfig().isOnlineMode());
+ return new BungeePlayer(banManagerPlugin, (ProxiedPlayer) source, banManagerPlugin.getConfig().isOnlineMode());
} else {
- return new BungeeSender(BanManagerPlugin.getInstance(), source);
+ return new BungeeSender(banManagerPlugin, source);
}
}
diff --git a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeePlugin.java b/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeePlugin.java
index 6e2a7ca..286d337 100644
--- a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeePlugin.java
+++ b/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/BungeePlugin.java
@@ -1,20 +1,22 @@
package me.confuser.banmanager.webenhancer.bungee;
import lombok.Getter;
+import me.confuser.banmanager.bungee.BMBungeePlugin;
import me.confuser.banmanager.bungee.BungeeScheduler;
import me.confuser.banmanager.bungee.PluginLogger;
import me.confuser.banmanager.common.commands.CommonCommand;
import me.confuser.banmanager.common.configs.PluginInfo;
import me.confuser.banmanager.common.configuration.ConfigurationSection;
import me.confuser.banmanager.common.configuration.file.YamlConfiguration;
-import me.confuser.banmanager.webenhancer.bungee.BungeeCommand;
import me.confuser.banmanager.webenhancer.common.WebEnhancerPlugin;
-import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
-import me.confuser.banmanager.webenhancer.bungee.listeners.*;
-import java.io.*;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.nio.file.Files;
public class BungeePlugin extends Plugin {
@@ -29,25 +31,31 @@ public class BungeePlugin extends Plugin {
@Override
public void onEnable() {
+ BMBungeePlugin banManager = (BMBungeePlugin) getProxy().getPluginManager().getPlugin("BanManager");
+ if (banManager == null || banManager.getPlugin() == null) {
+ getLogger().severe("BanManager is not enabled — cannot start BanManager-WebEnhancer.");
+ return;
+ }
+
PluginInfo pluginInfo;
try {
pluginInfo = setupConfigs();
} catch (IOException e) {
- e.printStackTrace();
+ getLogger().log(java.util.logging.Level.WARNING, "Failed to set up plugin configuration", e);
return;
}
metrics = new Metrics(this, 14539);
- plugin = new WebEnhancerPlugin(pluginInfo, new PluginLogger(getLogger()), getDataFolder(), new BungeeScheduler(this), new BungeeMetrics(metrics));
+ plugin = new WebEnhancerPlugin(pluginInfo, new PluginLogger(getLogger()), getDataFolder(),
+ new BungeeScheduler(this), new BungeeMetrics(metrics), banManager.getPlugin());
try {
plugin.enable();
} catch (Exception e) {
- e.printStackTrace();
+ getLogger().log(java.util.logging.Level.WARNING, "Failed to enable BanManager-WebEnhancer", e);
return;
}
- setupListeners();
setupCommands();
}
@@ -101,12 +109,4 @@ private void setupCommands() {
getLogger().info("Registered commands");
}
-
- public void setupListeners() {
- registerEvent(new BanListener(this));
- }
-
- private void registerEvent(Listener listener) {
- getProxy().getPluginManager().registerListener(this, listener);
- }
}
diff --git a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/listeners/BanListener.java b/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/listeners/BanListener.java
deleted file mode 100644
index 6f0e91f..0000000
--- a/bungee/src/main/java/me/confuser/banmanager/webenhancer/bungee/listeners/BanListener.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package me.confuser.banmanager.webenhancer.bungee.listeners;
-
-import me.confuser.banmanager.bungee.api.events.PlayerBannedEvent;
-import me.confuser.banmanager.bungee.api.events.PlayerDeniedEvent;
-import me.confuser.banmanager.bungee.api.events.PluginReloadedEvent;
-import me.confuser.banmanager.common.util.Message;
-import me.confuser.banmanager.webenhancer.bungee.BungeePlugin;
-import net.md_5.bungee.api.plugin.Listener;
-import net.md_5.bungee.event.EventHandler;
-import me.confuser.banmanager.webenhancer.common.listeners.CommonPlayerDeniedListener;
-
-public class BanListener implements Listener {
- private final BungeePlugin plugin;
- private final CommonPlayerDeniedListener listener;
-
- public BanListener(BungeePlugin plugin) {
- this.plugin = plugin;
- this.listener = new CommonPlayerDeniedListener(plugin.getPlugin());
- }
-
- @EventHandler
- public void onDeny(PlayerDeniedEvent event) {
- listener.handlePin(event.getPlayer(), event.getMessage());
- }
-
- @EventHandler
- public void onBanned(PlayerBannedEvent event) {
- try {
- Message kickMessage = event.getKickMessage();
- if (kickMessage != null) {
- listener.handlePin(event.getBan().getPlayer(), kickMessage);
- }
- } catch (NoSuchMethodError ignored) {
- }
- }
-
- @EventHandler
- public void onReload(PluginReloadedEvent event) {
- plugin.getPlugin().setupConfigs();
- }
-}
diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index 09242c1..61949b4 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -8,6 +8,7 @@ applyPlatformAndCoreConfiguration()
dependencies {
api(project(":BanManagerWebEnhancerLibs"))
+ api("me.confuser.banmanager:BanManagerAPI:8.0.0-SNAPSHOT")
api("me.confuser.banmanager:BanManagerCommon:8.0.0-SNAPSHOT")
api("me.confuser.banmanager.BanManagerLibs:BanManagerLibs:8.0.0-SNAPSHOT")
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/WebEnhancerPlugin.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/WebEnhancerPlugin.java
index bbed957..cfb9e40 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/WebEnhancerPlugin.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/WebEnhancerPlugin.java
@@ -1,42 +1,59 @@
package me.confuser.banmanager.webenhancer.common;
import lombok.Getter;
+import me.confuser.banmanager.api.BanManager;
+import me.confuser.banmanager.api.BanManagerService;
+import me.confuser.banmanager.api.database.DatabaseKind;
+import me.confuser.banmanager.api.database.MigrationService;
import me.confuser.banmanager.common.*;
-import me.confuser.banmanager.common.api.BmAPI;
import me.confuser.banmanager.common.commands.CommonCommand;
import me.confuser.banmanager.common.configs.Config;
import me.confuser.banmanager.common.configs.PluginInfo;
-import me.confuser.banmanager.common.storage.migration.MigrationRunner;
import me.confuser.banmanager.webenhancer.common.commands.PinCommand;
import me.confuser.banmanager.webenhancer.common.configs.DefaultConfig;
import me.confuser.banmanager.webenhancer.common.configs.MessagesConfig;
+import me.confuser.banmanager.webenhancer.common.listeners.CommonPlayerDeniedListener;
+import me.confuser.banmanager.webenhancer.common.runnables.ExpiresSync;
import me.confuser.banmanager.webenhancer.common.storage.LogStorage;
import me.confuser.banmanager.webenhancer.common.storage.PlayerPinStorage;
import me.confuser.banmanager.webenhancer.common.storage.ReportLogStorage;
-import me.confuser.banmanager.webenhancer.common.runnables.ExpiresSync;
import java.io.File;
import java.sql.SQLException;
import java.time.Duration;
+import java.util.Objects;
+/**
+ * Common WebEnhancer entry point. Each platform's bootstrap looks up the
+ * already-enabled BanManager plugin instance and passes it here, alongside
+ * platform glue (logger, scheduler, metrics).
+ *
+ * The {@link BanManagerPlugin} reference is required because WebEnhancer
+ * shares BanManager's shaded ORMLite + entity layer (e.g.
+ * {@link me.confuser.banmanager.common.data.PlayerData}); running them as
+ * fully-isolated plugins would force WebEnhancer to maintain duplicate
+ * entities. WebEnhancer uses {@link BanManager#get()} for everything that
+ * is covered by the public API (events, migrations).
+ */
public class WebEnhancerPlugin {
- private static WebEnhancerPlugin self;
@Getter
- private PluginInfo pluginInfo;
+ private final PluginInfo pluginInfo;
@Getter
private final CommonLogger logger;
@Getter
private final CommonMetrics metrics;
+ @Getter
+ private final BanManagerPlugin banManagerPlugin;
+ @Getter
+ private final BanManagerService banManagerService;
- // Configs
@Getter
- private File dataFolder;
+ private final File dataFolder;
@Getter
private DefaultConfig config;
@Getter
- private CommonScheduler scheduler;
+ private final CommonScheduler scheduler;
- // Storage
@Getter
private LogStorage logStorage;
@Getter
@@ -44,42 +61,46 @@ public class WebEnhancerPlugin {
@Getter
private PlayerPinStorage playerPinStorage;
- public WebEnhancerPlugin(PluginInfo pluginInfo, CommonLogger logger, File dataFolder, CommonScheduler scheduler, CommonMetrics metrics) {
- this.pluginInfo = pluginInfo;
- this.logger = logger;
- this.dataFolder = dataFolder;
- this.scheduler = scheduler;
- this.metrics = metrics;
- self = this;
+ public WebEnhancerPlugin(PluginInfo pluginInfo,
+ CommonLogger logger,
+ File dataFolder,
+ CommonScheduler scheduler,
+ CommonMetrics metrics,
+ BanManagerPlugin banManagerPlugin) {
+ this.pluginInfo = Objects.requireNonNull(pluginInfo, "pluginInfo");
+ this.logger = Objects.requireNonNull(logger, "logger");
+ this.dataFolder = Objects.requireNonNull(dataFolder, "dataFolder");
+ this.scheduler = Objects.requireNonNull(scheduler, "scheduler");
+ this.metrics = Objects.requireNonNull(metrics, "metrics");
+ this.banManagerPlugin = Objects.requireNonNull(banManagerPlugin, "banManagerPlugin");
+ this.banManagerService = BanManager.get();
}
public final void enable() throws Exception {
setupConfigs();
- MigrationRunner webMigrations = new MigrationRunner(
- BanManagerPlugin.getInstance(),
- BmAPI.getLocalConnection(),
- BanManagerPlugin.getInstance().getConfig().getLocalDb(),
+ banManagerService.migrations().run(new MigrationService.MigrationConfig(
+ DatabaseKind.LOCAL,
"webenhancer",
- "logs",
- WebEnhancerPlugin.class.getClassLoader());
- webMigrations.migrate();
+ "db/webenhancer",
+ WebEnhancerPlugin.class.getClassLoader(),
+ "logs"));
try {
setupStorage();
} catch (SQLException e) {
- e.printStackTrace();
- throw new Exception("An error occurred attempting to make a database connection, please see stack trace below");
+ throw new Exception("An error occurred attempting to make a database connection", e);
}
- // Schedule pin cleanup every 60 seconds
+ new CommonPlayerDeniedListener(this).register();
+
scheduler.runAsyncRepeating(new ExpiresSync(this), Duration.ofSeconds(60), Duration.ofSeconds(60));
}
public void setupConfigs() {
loadConfigCompat(new MessagesConfig(dataFolder, logger));
- config = new DefaultConfig(dataFolder, logger);
+ config = new DefaultConfig(dataFolder, logger, banManagerPlugin);
loadConfigCompat(config);
}
@@ -96,14 +117,14 @@ private void loadConfigCompat(Config config) {
}
public void setupStorage() throws SQLException {
- logStorage = new LogStorage(BmAPI.getLocalConnection());
- reportLogStorage = new ReportLogStorage(BmAPI.getLocalConnection());
- playerPinStorage = new PlayerPinStorage(BmAPI.getLocalConnection());
+ logStorage = new LogStorage(banManagerPlugin);
+ reportLogStorage = new ReportLogStorage(banManagerPlugin);
+ playerPinStorage = new PlayerPinStorage(banManagerPlugin);
}
public CommonCommand[] getCommands() {
return new CommonCommand[]{
- new PinCommand(BanManagerPlugin.getInstance(), this)
+ new PinCommand(banManagerPlugin, this)
};
}
}
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/configs/DefaultConfig.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/configs/DefaultConfig.java
index 9bb2a15..9f28a73 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/configs/DefaultConfig.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/configs/DefaultConfig.java
@@ -1,10 +1,10 @@
package me.confuser.banmanager.webenhancer.common.configs;
-import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig;
import lombok.Getter;
import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.common.CommonLogger;
import me.confuser.banmanager.common.configs.Config;
+import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig;
import me.confuser.banmanager.webenhancer.common.data.LogData;
import me.confuser.banmanager.webenhancer.common.data.PlayerPinData;
import me.confuser.banmanager.webenhancer.common.data.ReportLogData;
@@ -18,12 +18,14 @@
public class DefaultConfig extends Config {
- private static HashMap tables = new HashMap() {{
+ private static final HashMap> TABLES = new HashMap<>() {{
put("logs", LogData.class);
put("reportLogs", ReportLogData.class);
put("playerPins", PlayerPinData.class);
}};
+ private final BanManagerPlugin banManagerPlugin;
+
@Getter
private ArrayList patterns;
@Getter
@@ -31,14 +33,15 @@ public class DefaultConfig extends Config {
@Getter
private int amount;
- public DefaultConfig(File dataFolder, CommonLogger logger) {
+ public DefaultConfig(File dataFolder, CommonLogger logger, BanManagerPlugin banManagerPlugin) {
super(dataFolder, "config.yml", logger);
+ this.banManagerPlugin = banManagerPlugin;
}
@Override
public void afterLoad() {
- for (Map.Entry entry : tables.entrySet()) {
- BanManagerPlugin.getInstance().getConfig().getLocalDb()
+ for (Map.Entry> entry : TABLES.entrySet()) {
+ banManagerPlugin.getConfig().getLocalDb()
.addTable(entry.getKey(), new DatabaseTableConfig<>(entry.getValue(), conf.getString("tables." + entry.getKey()), null));
}
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListener.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListener.java
index cee7fa8..183fc5b 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListener.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListener.java
@@ -1,25 +1,65 @@
package me.confuser.banmanager.webenhancer.common.listeners;
+import me.confuser.banmanager.api.event.EventBus;
+import me.confuser.banmanager.api.event.player.PlayerBannedEvent;
+import me.confuser.banmanager.api.event.player.PlayerDeniedEvent;
+import me.confuser.banmanager.common.data.PlayerData;
+import me.confuser.banmanager.common.util.UUIDUtils;
import me.confuser.banmanager.webenhancer.common.WebEnhancerPlugin;
import me.confuser.banmanager.webenhancer.common.data.PlayerPinData;
-import me.confuser.banmanager.common.data.PlayerData;
-import me.confuser.banmanager.common.util.Message;
+import java.sql.SQLException;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Subscribes to BanManager's typed event bus and injects the {@code }
+ * placeholder into kick / deny messages so operators can recover access via
+ * the web UI.
+ *
+ * BanManager applies any entries added to {@code event.placeholders()} to
+ * the kick message template before rendering, so this listener never needs
+ * to touch BanManager's internal {@code Message} class.
+ */
public class CommonPlayerDeniedListener {
- private WebEnhancerPlugin plugin;
+ private final WebEnhancerPlugin plugin;
public CommonPlayerDeniedListener(WebEnhancerPlugin plugin) {
this.plugin = plugin;
}
- public void handlePin(PlayerData player, Message message) {
- String template = message.getRawTemplate();
- if (template == null || !template.contains("")) return;
+ public void register() {
+ EventBus events = plugin.getBanManagerService().events();
+ events.subscribe(PlayerDeniedEvent.class, this::onDenied);
+ events.subscribe(PlayerBannedEvent.class, this::onBanned);
+ }
- PlayerPinData pin = plugin.getPlayerPinStorage().getValidPin(player);
+ private void onDenied(PlayerDeniedEvent event) {
+ if (event.uuid().isEmpty()) return;
+ PlayerData player = lookupPlayer(event.uuid().get());
+ if (player == null) return;
+ injectPin(player, event.placeholders());
+ }
+ private void onBanned(PlayerBannedEvent event) {
+ PlayerData player = lookupPlayer(event.ban().player().uuid());
+ if (player == null) return;
+ injectPin(player, event.placeholders());
+ }
+
+ private PlayerData lookupPlayer(UUID uuid) {
+ try {
+ return plugin.getBanManagerPlugin().getPlayerStorage().queryForId(UUIDUtils.toBytes(uuid));
+ } catch (SQLException e) {
+ plugin.getLogger().warning("Failed to look up player " + uuid + " for pin injection: " + e.getMessage());
+ return null;
+ }
+ }
+
+ private void injectPin(PlayerData player, Map placeholders) {
+ PlayerPinData pin = plugin.getPlayerPinStorage().getValidPin(player);
if (pin != null) {
- message.set("pin", String.valueOf(pin.getGeneratedPin()));
+ placeholders.put("pin", String.valueOf(pin.getGeneratedPin()));
}
}
}
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/LogStorage.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/LogStorage.java
index 9dff03d..2b833ae 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/LogStorage.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/LogStorage.java
@@ -1,11 +1,10 @@
package me.confuser.banmanager.webenhancer.common.storage;
+import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.common.ormlite.dao.BaseDaoImpl;
import me.confuser.banmanager.common.ormlite.stmt.QueryBuilder;
-import me.confuser.banmanager.common.ormlite.support.ConnectionSource;
import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig;
import me.confuser.banmanager.common.ormlite.table.TableUtils;
-import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.webenhancer.common.data.LogData;
import java.sql.SQLException;
@@ -13,12 +12,13 @@
public class LogStorage extends BaseDaoImpl {
- public LogStorage(ConnectionSource connection) throws SQLException {
- super(connection, (DatabaseTableConfig) BanManagerPlugin.getInstance().getConfig().getLocalDb()
- .getTable("logs"));
+ @SuppressWarnings("unchecked")
+ public LogStorage(BanManagerPlugin banManagerPlugin) throws SQLException {
+ super(banManagerPlugin.getLocalConn(),
+ (DatabaseTableConfig) banManagerPlugin.getConfig().getLocalDb().getTable("logs"));
if (!isTableExists()) {
- TableUtils.createTable(connection, tableConfig);
+ TableUtils.createTable(banManagerPlugin.getLocalConn(), tableConfig);
}
}
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/PlayerPinStorage.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/PlayerPinStorage.java
index 023c274..1d084bd 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/PlayerPinStorage.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/PlayerPinStorage.java
@@ -1,15 +1,14 @@
package me.confuser.banmanager.webenhancer.common.storage;
-import me.confuser.banmanager.webenhancer.common.google.guava.cache.Cache;
-import me.confuser.banmanager.webenhancer.common.google.guava.cache.CacheBuilder;
+import me.confuser.banmanager.common.BanManagerPlugin;
+import me.confuser.banmanager.common.data.PlayerData;
import me.confuser.banmanager.common.ormlite.dao.BaseDaoImpl;
import me.confuser.banmanager.common.ormlite.stmt.DeleteBuilder;
-import me.confuser.banmanager.common.ormlite.support.ConnectionSource;
import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig;
import me.confuser.banmanager.common.ormlite.table.TableUtils;
-import me.confuser.banmanager.common.BanManagerPlugin;
-import me.confuser.banmanager.common.data.PlayerData;
import me.confuser.banmanager.webenhancer.common.data.PlayerPinData;
+import me.confuser.banmanager.webenhancer.common.google.guava.cache.Cache;
+import me.confuser.banmanager.webenhancer.common.google.guava.cache.CacheBuilder;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
@@ -19,18 +18,18 @@
public class PlayerPinStorage extends BaseDaoImpl {
private static final int RATE_LIMIT_SECONDS = 30;
- private Cache rateLimitCache = CacheBuilder.newBuilder()
+ private final Cache rateLimitCache = CacheBuilder.newBuilder()
.expireAfterWrite(RATE_LIMIT_SECONDS, TimeUnit.SECONDS)
.maximumSize(200)
.build();
- public PlayerPinStorage(ConnectionSource connection) throws SQLException {
- super(connection, (DatabaseTableConfig) BanManagerPlugin.getInstance().getConfig()
- .getLocalDb()
- .getTable("playerPins"));
+ @SuppressWarnings("unchecked")
+ public PlayerPinStorage(BanManagerPlugin banManagerPlugin) throws SQLException {
+ super(banManagerPlugin.getLocalConn(),
+ (DatabaseTableConfig) banManagerPlugin.getConfig().getLocalDb().getTable("playerPins"));
if (!this.isTableExists()) {
- TableUtils.createTable(connection, tableConfig);
+ TableUtils.createTable(banManagerPlugin.getLocalConn(), tableConfig);
}
}
diff --git a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/ReportLogStorage.java b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/ReportLogStorage.java
index eb1e878..aaa6af8 100644
--- a/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/ReportLogStorage.java
+++ b/common/src/main/java/me/confuser/banmanager/webenhancer/common/storage/ReportLogStorage.java
@@ -1,22 +1,22 @@
package me.confuser.banmanager.webenhancer.common.storage;
+import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.common.ormlite.dao.BaseDaoImpl;
-import me.confuser.banmanager.common.ormlite.support.ConnectionSource;
import me.confuser.banmanager.common.ormlite.table.DatabaseTableConfig;
import me.confuser.banmanager.common.ormlite.table.TableUtils;
-import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.webenhancer.common.data.ReportLogData;
import java.sql.SQLException;
public class ReportLogStorage extends BaseDaoImpl {
- public ReportLogStorage(ConnectionSource connection) throws SQLException {
- super(connection, (DatabaseTableConfig) BanManagerPlugin.getInstance().getConfig().getLocalDb()
- .getTable("reportLogs"));
+ @SuppressWarnings("unchecked")
+ public ReportLogStorage(BanManagerPlugin banManagerPlugin) throws SQLException {
+ super(banManagerPlugin.getLocalConn(),
+ (DatabaseTableConfig) banManagerPlugin.getConfig().getLocalDb().getTable("reportLogs"));
if (!isTableExists()) {
- TableUtils.createTable(connection, tableConfig);
+ TableUtils.createTable(banManagerPlugin.getLocalConn(), tableConfig);
}
}
diff --git a/common/src/test/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListenerTest.java b/common/src/test/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListenerTest.java
index 1bbcb1f..5b03399 100644
--- a/common/src/test/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListenerTest.java
+++ b/common/src/test/java/me/confuser/banmanager/webenhancer/common/listeners/CommonPlayerDeniedListenerTest.java
@@ -1,24 +1,51 @@
package me.confuser.banmanager.webenhancer.common.listeners;
+import me.confuser.banmanager.api.event.player.PlayerDeniedEvent;
+import me.confuser.banmanager.common.BanManagerPlugin;
import me.confuser.banmanager.common.data.PlayerData;
-import me.confuser.banmanager.common.util.Message;
+import me.confuser.banmanager.common.storage.PlayerStorage;
+import me.confuser.banmanager.common.util.UUIDUtils;
import me.confuser.banmanager.webenhancer.common.WebEnhancerPlugin;
import me.confuser.banmanager.webenhancer.common.data.PlayerPinData;
import me.confuser.banmanager.webenhancer.common.storage.PlayerPinStorage;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
-import static org.mockito.Mockito.*;
-
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.UUID;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Verifies the WebEnhancer listener mutates the API event's
+ * {@link PlayerDeniedEvent#placeholders()} map rather than touching
+ * BanManager's internal {@code Message} class — BanManager itself applies
+ * those placeholders to the kick template before rendering.
+ */
@ExtendWith(MockitoExtension.class)
public class CommonPlayerDeniedListenerTest {
@Mock
private WebEnhancerPlugin plugin;
+ @Mock
+ private BanManagerPlugin banManagerPlugin;
+
+ @Mock
+ private PlayerStorage playerStorage;
+
@Mock
private PlayerPinStorage playerPinStorage;
@@ -30,56 +57,66 @@ public class CommonPlayerDeniedListenerTest {
private CommonPlayerDeniedListener listener;
- @BeforeEach
- public void setUp() {
+ private final UUID uuid = UUID.fromString("00000000-0000-0000-0000-000000000001");
+
+ private void wireMocks() throws Exception {
+ lenient().when(plugin.getBanManagerPlugin()).thenReturn(banManagerPlugin);
lenient().when(plugin.getPlayerPinStorage()).thenReturn(playerPinStorage);
+ lenient().when(banManagerPlugin.getPlayerStorage()).thenReturn(playerStorage);
+ lenient().when(playerStorage.queryForId(any(byte[].class))).thenReturn(player);
listener = new CommonPlayerDeniedListener(plugin);
}
+ /** Reflection: the {@code onDenied} method is package-private. */
+ private void invokeOnDenied(PlayerDeniedEvent event) throws Exception {
+ Method m = CommonPlayerDeniedListener.class
+ .getDeclaredMethod("onDenied", PlayerDeniedEvent.class);
+ m.setAccessible(true);
+ m.invoke(listener, event);
+ }
+
@Test
- public void handlePin_replacesPlaceholderWithPin() {
+ public void onDenied_addsPinPlaceholder() throws Exception {
+ wireMocks();
when(playerPinStorage.getValidPin(player)).thenReturn(pinData);
when(pinData.getGeneratedPin()).thenReturn(123456);
- Message message = mock(Message.class);
- when(message.getRawTemplate()).thenReturn("Your login pin is: ");
+ Map placeholders = new HashMap<>();
+ PlayerDeniedEvent event = mock(PlayerDeniedEvent.class);
+ when(event.uuid()).thenReturn(Optional.of(uuid));
+ when(event.placeholders()).thenReturn(placeholders);
- listener.handlePin(player, message);
+ invokeOnDenied(event);
- verify(message).set("pin", "123456");
+ assertEquals("123456", placeholders.get("pin"));
}
@Test
- public void handlePin_ignoresMessagesWithoutPlaceholder() {
- Message message = mock(Message.class);
- when(message.getRawTemplate()).thenReturn("You have been banned!");
-
- listener.handlePin(player, message);
-
- verify(playerPinStorage, never()).getValidPin(any());
- verify(message, never()).set(anyString(), anyString());
- }
+ public void onDenied_skipsWhenNoUuid() throws Exception {
+ wireMocks();
- @Test
- public void handlePin_ignoresMessagesWithNullTemplate() {
- Message message = mock(Message.class);
- when(message.getRawTemplate()).thenReturn(null);
+ Map placeholders = new HashMap<>();
+ PlayerDeniedEvent event = mock(PlayerDeniedEvent.class);
+ when(event.uuid()).thenReturn(Optional.empty());
- listener.handlePin(player, message);
+ invokeOnDenied(event);
verify(playerPinStorage, never()).getValidPin(any());
- verify(message, never()).set(anyString(), anyString());
+ assertTrue(placeholders.isEmpty());
}
@Test
- public void handlePin_handlesNullPinGracefully() {
+ public void onDenied_skipsWhenNoPin() throws Exception {
+ wireMocks();
when(playerPinStorage.getValidPin(player)).thenReturn(null);
- Message message = mock(Message.class);
- when(message.getRawTemplate()).thenReturn("Your login pin is: ");
+ Map placeholders = new HashMap<>();
+ PlayerDeniedEvent event = mock(PlayerDeniedEvent.class);
+ when(event.uuid()).thenReturn(Optional.of(uuid));
+ when(event.placeholders()).thenReturn(placeholders);
- listener.handlePin(player, message);
+ invokeOnDenied(event);
- verify(message, never()).set(anyString(), anyString());
+ assertTrue(placeholders.isEmpty());
}
}
diff --git a/e2e/platforms/bukkit/configs/banmanager-webenhancer/messages.yml b/e2e/platforms/bukkit/configs/banmanager-webenhancer/messages.yml
index 4ea98a0..9c7e905 100644
--- a/e2e/platforms/bukkit/configs/banmanager-webenhancer/messages.yml
+++ b/e2e/platforms/bukkit/configs/banmanager-webenhancer/messages.yml
@@ -1,3 +1,6 @@
+# Message templates use MiniMessage formatting: https://docs.advntr.dev/minimessage/format.html
+# Placeholders use angle brackets, e.g. , ,
+
messages:
pin:
notify: 'Your pin expires in '
diff --git a/e2e/platforms/bungee/configs/banmanager-webenhancer/config.yml b/e2e/platforms/bungee/configs/banmanager-webenhancer/config.yml
index 58c055b..3dd1a91 100644
--- a/e2e/platforms/bungee/configs/banmanager-webenhancer/config.yml
+++ b/e2e/platforms/bungee/configs/banmanager-webenhancer/config.yml
@@ -1,16 +1,18 @@
-#
-# Number of lines to log with each report
tables:
logs: bm_server_logs
reportLogs: bm_report_logs
playerPins: bm_player_pins
+# Number of lines to log with each report
lines: 30
+# Allows using regex to filter logs
+ignorePatterns:
+# Basic contain checks
ignoreContains:
- 'issued server command: /report'
-- has been reported by
+- 'has been reported by'
- '[BanManager]'
- '[PlugMan]'
-- Metrics
-- For help, type "help"
+- 'Metrics'
+- 'For help, type "help"'
- 'HikariDataSource'
- 'TableUtils'
diff --git a/e2e/platforms/bungee/configs/banmanager-webenhancer/messages.yml b/e2e/platforms/bungee/configs/banmanager-webenhancer/messages.yml
index 4ea98a0..9c7e905 100644
--- a/e2e/platforms/bungee/configs/banmanager-webenhancer/messages.yml
+++ b/e2e/platforms/bungee/configs/banmanager-webenhancer/messages.yml
@@ -1,3 +1,6 @@
+# Message templates use MiniMessage formatting: https://docs.advntr.dev/minimessage/format.html
+# Placeholders use angle brackets, e.g. , ,
+
messages:
pin:
notify: 'Your pin expires in '
diff --git a/e2e/platforms/bungee/configs/banmanager/config.yml b/e2e/platforms/bungee/configs/banmanager/config.yml
index 6f77dac..115a2f2 100644
--- a/e2e/platforms/bungee/configs/banmanager/config.yml
+++ b/e2e/platforms/bungee/configs/banmanager/config.yml
@@ -1,5 +1,8 @@
-#
+#
# Aliases will be found and blocked automatically, e.g. msg will block tell
+locale:
+ default: en
+ perPlayer: true
debug: false
databases:
local:
diff --git a/e2e/platforms/bungee/configs/banmanager/messages/messages_en.yml b/e2e/platforms/bungee/configs/banmanager/messages/messages_en.yml
new file mode 100644
index 0000000..1930ce4
--- /dev/null
+++ b/e2e/platforms/bungee/configs/banmanager/messages/messages_en.yml
@@ -0,0 +1,474 @@
+# Message templates use MiniMessage formatting: https://docs.advntr.dev/minimessage/format.html
+# Placeholders use angle brackets, e.g. , , , ,
+
+tokens:
+ appeal_url: 'https://yourdomain.com/appeal'
+ server_name: 'My Server'
+ discord_url: 'https://discord.gg/example'
+ rules_url: 'https://yourdomain.com/rules'
+
+messages:
+ duplicateIP: 'Warning: has the same IP as the following banned players:\n'
+ duplicateIPAlts: 'Warning: has the same IP as the following players:\n'
+ configReloaded: 'Configuration reloaded successfully!'
+ deniedNotify:
+ player: 'Warning: attempted to join the server but was denied due to '
+ ip: 'Warning: attempted to join the server but was denied due to '
+ deniedMaxIp: 'Too many players with your ip address online'
+ deniedMultiaccounts: 'Too many players with your ip address logged in recently'
+ deniedCountry: 'You may not connect from your region'
+
+ time:
+ now: 'now'
+ year: 'year'
+ years: 'years'
+ month: 'month'
+ months: 'months'
+ week: 'week'
+ weeks: 'weeks'
+ day: 'day'
+ days: 'days'
+ hour: 'hour'
+ hours: 'hours'
+ minute: 'minute'
+ minutes: 'minutes'
+ second: 'second'
+ seconds: 'seconds'
+ never: 'never'
+ error:
+ invalid: 'Your time length is invalid'
+ limit: 'You cannot perform this action for that length of time'
+
+ none: 'none'
+ # General command text
+ sender:
+ error:
+ notFound: ' not found, are you sure they exist?'
+ offline: ' is offline'
+ noSelf: 'You cannot perform that action on yourself!'
+ exception: 'An error occurred whilst attempting to perform this command. Please check the console for further details.'
+ invalidIp: 'Invalid IP address, expecting w.x.y.z format'
+ offlinePermission: 'You are not allowed to perform this action on an offline player'
+ ambiguousPlayer: 'Multiple players match "". Please use their full name.'
+ exempt: ' is exempt from that action'
+ noPermission: 'You do not have permission to perform that action'
+ noConsole: 'This command cannot be used from console'
+ invalidReason: ' is not a valid reason.'
+ # Commands
+ alts:
+ header: 'Possible alts found:'
+ entry: '">'
+ separator: ', '
+
+ names:
+ header: 'Known names for :'
+ row: ' (first: , last: )'
+ interactive: ' (first: , last: )">">'
+ separator: ', '
+ dateTimeFormat: 'dd-MM-yyyy'
+ none: 'No name history found'
+
+ export:
+ error:
+ inProgress: 'An export is already in progress, please wait'
+ player:
+ started: 'Player ban export started'
+ finished: 'Player ban export finished, file created'
+ ip:
+ started: 'IP ban export started'
+ finished: 'IP ban export finished, file created'
+
+ import:
+ error:
+ inProgress: 'An import is already in progress, please wait'
+ player:
+ started: 'Player ban import started'
+ finished: 'Player ban import finished'
+ ip:
+ started: 'IP ban import started'
+ finished: 'IP ban import finished'
+ advancedban:
+ started: 'AdvancedBan import started'
+ finished: 'AdvancedBan import finished'
+ h2:
+ started: 'H2 import started'
+ finished: 'H2 import finished, please restart the server'
+ litebans:
+ started: 'LiteBans import started'
+ finished: 'LiteBans import finished'
+
+ info:
+ error:
+ invalidIndex: 'Invalid player option used'
+ indexRequired: 'Multiple players named found, please select a player by providing an index between 1 and , e.g. /bminfo 1'
+ index: '# - - '
+ stats:
+ player: ' has been banned times, muted times, kicked times and warned
+ times ( Points), has notes and been reported times'
+ ip: 'This ip has been banned times, muted times and range banned times'
+ connection: 'Their last connection was with on '
+ geoip: 'Country: City: '
+ ban:
+ permanent: 'Currently banned for by at '
+ temporary: 'Currently banned for by at which expires in '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ ipban:
+ permanent: 'Currently banned for by at '
+ temporary: 'Currently banned for by at which expires in '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ iprangeban:
+ permanent: ' - banned for by at '
+ temporary: ' - banned for by at which expires in '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ ipmute:
+ permanent: 'Currently muted for by at '
+ temporary: 'Currently muted for by at which expires in '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ mute:
+ permanent: 'Currently muted for by at '
+ temporary: 'Currently muted for by at which expires in '
+ temporaryOnline: 'Currently muted for by at which expires in (online time)'
+ temporaryOnlinePaused: 'Currently muted for by at with remaining (online time, paused)'
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ website:
+ player: 'https://yourdomain.com/player/'
+ ip: 'http://yourdomain.com/index.php?action=viewip&ip=&server=0'
+ history:
+ row: '# [] - '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+ noResults: 'No results found'
+ ips:
+ row: ' - - '
+ dateTimeFormat: 'dd-MM-yyyy HH:mm:ss'
+
+ kick:
+ player:
+ noReason: 'You have been kicked'
+ reason: 'You have been kicked for