Skip to content

Commit 647738c

Browse files
committed
feat!: v8 major release — MiniMessage migration, Component-first architecture
BREAKING CHANGES: - Java 17 minimum required across all modules (was Java 8) - Adventure API upgraded from 4.9.3 to 4.21.0 with MiniMessage support - Message templates now use MiniMessage format (<tag>) instead of legacy &-codes - Message.set() creates MiniMessage TagResolvers instead of [token] bracket replacement - Legacy &-code formatting in messages.yml is hard-rejected (bundled defaults used) - Removed deprecated APIs: Message.load(), CommonServer.broadcastJSON(), WebhookConfig.getType() - CommonSender.sendMessage(Message) and CommonPlayer.sendMessage(Message) are now default interface methods routing through Component - CommonPlayer.kick(Message) routes through Component - Message.toString() callers migrated to Message.sendTo(sender) New features: - MessageRenderer: central MiniMessage rendering, legacy conversion, tag escaping - Message.component(key): static convenience for resolving to Component - Message.getRawTemplate(): access unrendered template strings - Message.resolveComponent(): full Component pipeline with PAPI escaping - PaperAdventureHelper: zero-overhead native Adventure on Paper servers - NotificationsConfig: configurable staff notification channels (chat/actionbar/title/sound) - NotificationUtils: unified staff notification delivery - PaginatedView: Component-based pagination with clickable navigation - ConfirmationManager: timed action confirmation system - BmCommand: /bm confirm, /bm cancel, /bm dashboard subcommands - Duration tab-complete presets for temp punishment commands - Static tokens support in messages.yml - Platform adapters: sendActionBar(), showTitle(), playSound() on all platforms
1 parent da673fc commit 647738c

146 files changed

Lines changed: 6607 additions & 3870 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

buildSrc/src/main/kotlin/CommonConfig.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ fun Project.applyCommonConfiguration() {
2727
}
2828
}
2929

30-
// Only set Java 1.8 for non-Fabric modules
30+
// Set Java 17 for non-Fabric modules
3131
// Fabric uses toolchain configuration in its build.gradle.kts
3232
plugins.withId("java") {
3333
if (!plugins.hasPlugin("fabric-loom")) {
34-
the<JavaPluginExtension>().setSourceCompatibility("1.8")
35-
the<JavaPluginExtension>().setTargetCompatibility("1.8")
34+
the<JavaPluginExtension>().setSourceCompatibility("17")
35+
the<JavaPluginExtension>().setTargetCompatibility("17")
3636
}
3737
}
3838
}

buildSrc/src/main/kotlin/LibsConfig.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ fun Project.applyLibrariesConfiguration() {
3838
}
3939

4040
plugins.withId("java") {
41-
the<JavaPluginExtension>().setSourceCompatibility("1.8")
42-
the<JavaPluginExtension>().setTargetCompatibility("1.8")
41+
the<JavaPluginExtension>().setSourceCompatibility("17")
42+
the<JavaPluginExtension>().setTargetCompatibility("17")
4343
}
4444

4545
group = "${rootProject.group}.BanManagerLibs"
@@ -100,7 +100,7 @@ fun Project.applyLibrariesConfiguration() {
100100
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
101101
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
102102
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
103-
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8)
103+
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
104104
}
105105
outgoing.artifact(tasks.named("jar"))
106106
}
@@ -115,7 +115,7 @@ fun Project.applyLibrariesConfiguration() {
115115
attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category.LIBRARY))
116116
attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED))
117117
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements.JAR))
118-
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 8)
118+
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
119119
}
120120
outgoing.artifact(tasks.named("jar"))
121121
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Versions {
22
const val JUNIT = "4.13"
33
const val MOCKITO = "3.7.7"
4-
const val ADVENTURE = "4.9.3"
4+
const val ADVENTURE = "4.21.0"
55
}

bukkit/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ dependencies {
8080
"compileOnly"("org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT") {
8181
exclude("junit", "junit")
8282
}
83+
"compileOnly"("net.kyori:adventure-api:${Versions.ADVENTURE}")
84+
"compileOnly"("net.kyori:adventure-text-serializer-gson:${Versions.ADVENTURE}")
85+
"compileOnly"("net.kyori:adventure-text-serializer-json:${Versions.ADVENTURE}")
8386
"compileOnly"("me.clip:placeholderapi:2.10.9")
8487
"shadeOnly"("org.bstats:bstats-bukkit:2.2.1")
8588
"shadeOnly"("org.slf4j:slf4j-api:1.7.36")

bukkit/src/main/java/me/confuser/banmanager/bukkit/BMBukkitPlugin.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public class BMBukkitPlugin extends JavaPlugin {
3939
"exemptions.yml",
4040
"geoip.yml",
4141
"messages.yml",
42+
"notifications.yml",
4243
"reasons.yml",
4344
"schedules.yml"
4445
};

bukkit/src/main/java/me/confuser/banmanager/bukkit/BukkitPlayer.java

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,37 @@
44
import me.confuser.banmanager.common.CommonPlayer;
55
import me.confuser.banmanager.common.CommonWorld;
66
import me.confuser.banmanager.common.data.PlayerData;
7+
import me.confuser.banmanager.common.kyori.text.Component;
78
import me.confuser.banmanager.common.kyori.text.TextComponent;
89
import me.confuser.banmanager.common.kyori.text.serializer.gson.GsonComponentSerializer;
910
import me.confuser.banmanager.common.util.Message;
11+
import me.confuser.banmanager.common.util.MessageRenderer;
1012
import me.confuser.banmanager.common.util.MessageRegistry;
1113
import me.confuser.banmanager.common.util.UUIDUtils;
14+
import net.md_5.bungee.api.ChatMessageType;
1215
import net.md_5.bungee.chat.ComponentSerializer;
1316
import org.bukkit.Bukkit;
1417
import org.bukkit.Location;
18+
import org.bukkit.Sound;
1519
import org.bukkit.entity.Player;
1620

1721
import java.net.InetAddress;
1822
import java.sql.SQLException;
1923
import java.util.UUID;
2024

2125
public class BukkitPlayer implements CommonPlayer {
26+
private static final boolean PAPER_ADVENTURE;
27+
28+
static {
29+
boolean paper = false;
30+
try {
31+
Class.forName("io.papermc.paper.adventure.PaperAdventure");
32+
paper = true;
33+
} catch (ClassNotFoundException ignored) {
34+
}
35+
PAPER_ADVENTURE = paper;
36+
}
37+
2238
private Player player;
2339
private final UUID uuid;
2440
private InetAddress address;
@@ -45,6 +61,15 @@ public void kick(String message) {
4561
getPlayer().kickPlayer(BukkitServer.formatMessage(message));
4662
}
4763

64+
@Override
65+
public void kick(Component component) {
66+
if (PAPER_ADVENTURE) {
67+
PaperAdventureHelper.kick(getPlayer(), component);
68+
} else {
69+
kick(MessageRenderer.getInstance().toLegacy(component));
70+
}
71+
}
72+
4873
public void sendMessage(String message) {
4974
if(message.isEmpty()) return;
5075

@@ -55,8 +80,47 @@ public void sendMessage(String message) {
5580
}
5681
}
5782

58-
public void sendMessage(Message message) {
59-
sendMessage(message.toString());
83+
@Override
84+
public void sendMessage(Component component) {
85+
if (PAPER_ADVENTURE) {
86+
PaperAdventureHelper.sendMessage(getPlayer(), component);
87+
} else {
88+
String json = MessageRenderer.getInstance().toJson(component);
89+
getPlayer().spigot().sendMessage(ComponentSerializer.parse(json));
90+
}
91+
}
92+
93+
@Override
94+
public void sendActionBar(Component component) {
95+
if (PAPER_ADVENTURE) {
96+
PaperAdventureHelper.sendActionBar(getPlayer(), component);
97+
} else {
98+
String json = MessageRenderer.getInstance().toJson(component);
99+
getPlayer().spigot().sendMessage(ChatMessageType.ACTION_BAR, ComponentSerializer.parse(json));
100+
}
101+
}
102+
103+
@Override
104+
public void showTitle(Component title, Component subtitle, int fadeIn, int stay, int fadeOut) {
105+
if (PAPER_ADVENTURE) {
106+
PaperAdventureHelper.showTitle(getPlayer(), title, subtitle, fadeIn, stay, fadeOut);
107+
} else {
108+
MessageRenderer renderer = MessageRenderer.getInstance();
109+
String legacyTitle = title != null ? renderer.toLegacy(title) : "";
110+
String legacySubtitle = subtitle != null ? renderer.toLegacy(subtitle) : "";
111+
getPlayer().sendTitle(legacyTitle, legacySubtitle, fadeIn, stay, fadeOut);
112+
}
113+
}
114+
115+
@Override
116+
public void playSound(String sound, float volume, float pitch) {
117+
Player p = getPlayer();
118+
if (p == null) return;
119+
try {
120+
p.playSound(p.getLocation(), sound, volume, pitch);
121+
} catch (IllegalArgumentException ignored) {
122+
// Invalid sound key -- silently ignore
123+
}
60124
}
61125

62126
@Override

bukkit/src/main/java/me/confuser/banmanager/bukkit/BukkitSender.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,6 @@ public void sendMessage(String message) {
3535
sender.sendMessage(BukkitServer.formatMessage(message));
3636
}
3737

38-
@Override
39-
public void sendMessage(Message message) {
40-
sendMessage(message.toString());
41-
}
42-
4338
@Override
4439
public boolean isConsole() {
4540
return !(sender instanceof Player);

bukkit/src/main/java/me/confuser/banmanager/bukkit/BukkitServer.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import me.confuser.banmanager.common.api.events.CommonEvent;
66
import me.confuser.banmanager.common.commands.CommonSender;
77
import me.confuser.banmanager.common.data.*;
8-
import me.confuser.banmanager.common.kyori.text.TextComponent;
98
import me.confuser.banmanager.common.util.ColorUtils;
109
import me.confuser.banmanager.common.util.Message;
1110
import org.bukkit.Bukkit;
@@ -17,7 +16,6 @@
1716
import org.bukkit.entity.Player;
1817
import org.bukkit.permissions.Permissible;
1918

20-
import java.util.Arrays;
2119
import java.util.Set;
2220
import java.util.UUID;
2321
import java.util.stream.Collectors;
@@ -82,13 +80,6 @@ public void broadcast(String message, String permission) {
8280
}
8381
}
8482

85-
@Override
86-
public void broadcastJSON(TextComponent message, String permission) {
87-
Arrays.stream(getOnlinePlayers()).forEach(player -> {
88-
if (player.hasPermission(permission)) player.sendJSONMessage(message);
89-
});
90-
}
91-
9283
@Override
9384
public void broadcast(String message, String permission, CommonSender sender) {
9485
broadcast(message, permission);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package me.confuser.banmanager.bukkit;
2+
3+
import me.confuser.banmanager.common.kyori.text.Component;
4+
import me.confuser.banmanager.common.util.MessageRenderer;
5+
import net.kyori.adventure.audience.Audience;
6+
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
7+
import net.kyori.adventure.title.Title;
8+
import org.bukkit.entity.Player;
9+
10+
import java.time.Duration;
11+
12+
/**
13+
* Paper-only helper that uses native Adventure API for zero-overhead Component delivery.
14+
* Paper's Player implements Audience, so we cast to Audience for Adventure methods.
15+
* This class must only be loaded when Paper's Adventure API is on the classpath.
16+
*/
17+
class PaperAdventureHelper {
18+
19+
private static final java.lang.reflect.Method KICK_METHOD;
20+
21+
static {
22+
java.lang.reflect.Method m = null;
23+
try {
24+
m = Player.class.getMethod("kick", net.kyori.adventure.text.Component.class);
25+
} catch (NoSuchMethodException e) {
26+
// Expected on older Paper/Spigot versions without Component-based kick
27+
}
28+
KICK_METHOD = m;
29+
}
30+
31+
private PaperAdventureHelper() {}
32+
33+
static void sendMessage(Player player, Component component) {
34+
((Audience) player).sendMessage(convertToNative(component));
35+
}
36+
37+
static void sendActionBar(Player player, Component component) {
38+
((Audience) player).sendActionBar(convertToNative(component));
39+
}
40+
41+
static void showTitle(Player player, Component title, Component subtitle,
42+
int fadeIn, int stay, int fadeOut) {
43+
net.kyori.adventure.text.Component nativeTitle = title != null
44+
? convertToNative(title) : net.kyori.adventure.text.Component.empty();
45+
net.kyori.adventure.text.Component nativeSubtitle = subtitle != null
46+
? convertToNative(subtitle) : net.kyori.adventure.text.Component.empty();
47+
48+
Title.Times times = Title.Times.times(
49+
Duration.ofMillis(fadeIn * 50L),
50+
Duration.ofMillis(stay * 50L),
51+
Duration.ofMillis(fadeOut * 50L)
52+
);
53+
((Audience) player).showTitle(Title.title(nativeTitle, nativeSubtitle, times));
54+
}
55+
56+
static void kick(Player player, Component component) {
57+
if (KICK_METHOD != null) {
58+
try {
59+
KICK_METHOD.invoke(player, convertToNative(component));
60+
return;
61+
} catch (IllegalAccessException e) {
62+
java.util.logging.Logger.getLogger("BanManager").warning("Failed to invoke Paper kick method, falling back to legacy: " + e.getMessage());
63+
} catch (java.lang.reflect.InvocationTargetException e) {
64+
throw new IllegalStateException("Failed to kick player", e.getCause());
65+
}
66+
}
67+
player.kickPlayer(MessageRenderer.getInstance().toLegacy(component));
68+
}
69+
70+
private static net.kyori.adventure.text.Component convertToNative(Component component) {
71+
String json = MessageRenderer.getInstance().toJson(component);
72+
return GsonComponentSerializer.gson().deserialize(json);
73+
}
74+
}

bukkit/src/main/java/me/confuser/banmanager/bukkit/listeners/JoinListener.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ public void handlePlayerDeny(PlayerData player, Message message) {
6262
plugin.getServer().callEvent("PlayerDeniedEvent", player, message);
6363
String locale = player.getLocale() != null ? player.getLocale() : "en";
6464
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_BANNED);
65-
event.setKickMessage(BukkitServer.formatMessage(message.resolve(locale)));
65+
event.setKickMessage(BukkitServer.formatMessage(MessageRenderer.getInstance().toLegacy(message.resolveComponent(locale))));
6666
}
6767

6868
@Override
6969
public void handleDeny(Message message) {
7070
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_BANNED);
71-
event.setKickMessage(BukkitServer.formatMessage(message.toString()));
71+
event.setKickMessage(BukkitServer.formatMessage(MessageRenderer.getInstance().toLegacy(message.resolveComponent())));
7272
}
7373
}
7474

@@ -83,7 +83,8 @@ public void handlePlayerDeny(PlayerData player, Message message) {
8383

8484
@Override
8585
public void handleDeny(Message message) {
86-
event.disallow(PlayerLoginEvent.Result.KICK_BANNED, message.toString());
86+
event.disallow(PlayerLoginEvent.Result.KICK_BANNED,
87+
BukkitServer.formatMessage(MessageRenderer.getInstance().toLegacy(message.resolveComponent())));
8788
}
8889
}
8990
}

0 commit comments

Comments
 (0)