Skip to content

Commit 0ab5df3

Browse files
authored
Full Messages & GUI Configuration Update
# 🛡️ WeGuardian Changelog ## Version **2.4** – Full Messages & GUI Configuration Update **Release Type:** Major Configuration & Customization Update **Focus:** Flexibility, Localization, and Server Branding --- ## ✨ New Features ### 📁 Fully Configurable `messages.yml` * Added a dedicated **`messages.yml`** file to centralize **all player-facing text** * Every message is now configurable, including: * Chat messages * Error messages * Success messages * Notifications * Titles & subtitles * Action bar messages ### 🧩 GUI Customization (100% Configurable) * All GUI elements are now configurable from `messages.yml`, including: * GUI titles * Item display names * Item lores * Buttons & descriptions * Affected GUIs: * Punishment GUI * Duration GUI * History GUI ### 🛠️ New Messages Management System * Introduced a **MessagesManager** system to: * Load and cache messages efficiently * Support placeholders across all messages * Ensure consistent formatting everywhere * Uses MiniMessage-compatible formatting for modern text styling --- ## 🔄 Improvements * Removed hardcoded strings from: * GUI classes * Input handlers (Reason input, Custom duration input) * Command feedback messages * Improved maintainability and readability of the codebase * Easier localization for non-English servers * Server owners can now fully brand WeGuardian without touching source code --- ## 🧹 Developer & Admin Benefits * No recompiling required for message changes * Clean separation between logic and presentation * Future updates will automatically support new messages via `messages.yml` --- ## ⚠️ Notes for Updating * On first startup, **`messages.yml`** will be generated automatically * Servers upgrading from older versions should review and customize the new file * Old message values previously in `config.yml` are now handled via `messages.yml` --- ### ✅ Recommended Update to **WeGuardian v2.4** to unlock full control over messages, GUIs, and server presentation.
1 parent 662079c commit 0ab5df3

File tree

12 files changed

+739
-431
lines changed

12 files changed

+739
-431
lines changed

src/main/java/me/wethink/weguardian/WeGuardian.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
import me.wethink.weguardian.manager.PunishmentManager;
1212
import me.wethink.weguardian.scheduler.SchedulerManager;
1313
import me.wethink.weguardian.web.WebServer;
14+
import me.wethink.weguardian.util.MessagesManager;
1415
import me.wethink.weguardian.webhook.DiscordWebhookManager;
1516
import org.bstats.bukkit.Metrics;
1617
import org.bukkit.plugin.java.JavaPlugin;
1718

18-
1919
public final class WeGuardian extends JavaPlugin {
2020

2121
private static final int BSTATS_PLUGIN_ID = 27046;
@@ -29,6 +29,7 @@ public final class WeGuardian extends JavaPlugin {
2929
private PunishmentDAO punishmentDAO;
3030
private DiscordWebhookManager webhookManager;
3131
private WebServer webServer;
32+
private MessagesManager messagesManager;
3233

3334
private PaperCommandManager commandManager;
3435

@@ -43,6 +44,9 @@ public void onEnable() {
4344

4445
saveDefaultConfig();
4546

47+
this.messagesManager = new MessagesManager(this);
48+
getLogger().info("Messages loaded");
49+
4650
FastInvManager.register(this);
4751

4852
me.wethink.weguardian.gui.PunishmentGUI.initializeIcons();
@@ -110,7 +114,6 @@ public void onDisable() {
110114
getLogger().info("WeGuardian disabled. Goodbye!");
111115
}
112116

113-
114117
private void initMetrics() {
115118
try {
116119
this.metrics = new Metrics(this, BSTATS_PLUGIN_ID);
@@ -148,7 +151,6 @@ private void printBanner() {
148151
}
149152
}
150153

151-
152154
private String colorize(String message) {
153155
java.util.regex.Pattern hexPattern = java.util.regex.Pattern.compile("&#([A-Fa-f0-9]{6})");
154156
java.util.regex.Matcher matcher = hexPattern.matcher(message);
@@ -169,7 +171,6 @@ private String colorize(String message) {
169171
private void registerCommands() {
170172
this.commandManager = new PaperCommandManager(this);
171173

172-
173174
try {
174175
commandManager.enableUnstableAPI("help");
175176
} catch (Exception ignored) {
@@ -189,8 +190,6 @@ private void registerListeners() {
189190
getServer().getPluginManager().registerEvents(new PlayerChatListener(this), this);
190191
}
191192

192-
193-
194193
public static WeGuardian getInstance() {
195194
return instance;
196195
}
@@ -226,4 +225,8 @@ public DiscordWebhookManager getWebhookManager() {
226225
public WebServer getWebServer() {
227226
return webServer;
228227
}
228+
229+
public MessagesManager getMessagesManager() {
230+
return messagesManager;
231+
}
229232
}

src/main/java/me/wethink/weguardian/commands/PunishmentCommands.java

Lines changed: 106 additions & 97 deletions
Large diffs are not rendered by default.

src/main/java/me/wethink/weguardian/gui/CustomDurationHandler.java

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import me.wethink.weguardian.WeGuardian;
55
import me.wethink.weguardian.model.PunishmentType;
66
import me.wethink.weguardian.util.MessageUtil;
7+
import me.wethink.weguardian.util.MessagesManager;
78
import me.wethink.weguardian.util.TimeUtil;
89
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
910
import org.bukkit.OfflinePlayer;
@@ -14,9 +15,9 @@
1415
import org.bukkit.event.Listener;
1516
import org.bukkit.event.player.PlayerQuitEvent;
1617

18+
import java.util.List;
1719
import java.util.concurrent.TimeUnit;
1820

19-
2021
public class CustomDurationHandler implements Listener {
2122

2223
private static final PlainTextComponentSerializer PLAIN_SERIALIZER = PlainTextComponentSerializer.plainText();
@@ -35,33 +36,34 @@ public CustomDurationHandler(WeGuardian plugin, Player staff, OfflinePlayer targ
3536
}
3637

3738
public void start() {
39+
MessagesManager msg = plugin.getMessagesManager();
40+
3841
plugin.getServer().getPluginManager().registerEvents(this, plugin);
3942

4043
staff.sendMessage(MessageUtil.toComponent(""));
41-
staff.sendMessage(MessageUtil.toComponent("&8&m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
42-
staff.sendMessage(MessageUtil.toComponent("&c&lWeGuardian &8» &fEnter Custom Duration"));
44+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.header")));
45+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.title")));
4346
staff.sendMessage(MessageUtil.toComponent(""));
44-
staff.sendMessage(MessageUtil.toComponent("&7Format examples:"));
45-
staff.sendMessage(MessageUtil.toComponent("&7 &e1h &7= 1 hour"));
46-
staff.sendMessage(MessageUtil.toComponent("&7 &e30m &7= 30 minutes"));
47-
staff.sendMessage(MessageUtil.toComponent("&7 &e7d &7= 7 days"));
48-
staff.sendMessage(MessageUtil.toComponent("&7 &e2w &7= 2 weeks"));
49-
staff.sendMessage(MessageUtil.toComponent("&7 &e1M &7= 1 month"));
50-
staff.sendMessage(MessageUtil.toComponent("&7 &e1y &7= 1 year"));
51-
staff.sendMessage(MessageUtil.toComponent("&7 &e2h30m &7= 2 hours 30 minutes"));
47+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.format-header")));
48+
49+
List<String> formats = msg.getMessageList("input.custom-duration.formats");
50+
for (String format : formats) {
51+
staff.sendMessage(MessageUtil.toComponent(format));
52+
}
53+
5254
staff.sendMessage(MessageUtil.toComponent(""));
53-
staff.sendMessage(MessageUtil.toComponent("&7Type your duration, or type &ccancel &7to cancel."));
54-
staff.sendMessage(MessageUtil.toComponent("&8&m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
55+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.instruction")));
56+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.header")));
5557

5658
plugin.getSchedulerManager().runAsyncLater(this::handleTimeout, 60, TimeUnit.SECONDS);
5759
}
5860

59-
6061
private void handleTimeout() {
6162
if (!completed) {
6263
cleanup();
6364
plugin.getSchedulerManager().runForEntity(staff,
64-
() -> staff.sendMessage(MessageUtil.toComponent("&cDuration input cancelled (timed out).")));
65+
() -> staff.sendMessage(MessageUtil
66+
.toComponent(plugin.getMessagesManager().getMessage("input.custom-duration.timeout"))));
6567
}
6668
}
6769

@@ -73,24 +75,26 @@ public void onAsyncChat(AsyncChatEvent event) {
7375

7476
event.setCancelled(true);
7577
String message = PLAIN_SERIALIZER.serialize(event.message());
78+
MessagesManager msg = plugin.getMessagesManager();
7679

7780
if (message.equalsIgnoreCase("cancel")) {
7881
cleanup();
79-
staff.sendMessage(MessageUtil.toComponent("&cDuration input cancelled."));
82+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.cancelled")));
8083
PunishmentGUI.openAsync(plugin, staff, target);
8184
return;
8285
}
8386

8487
long durationMs = TimeUtil.parseDuration(message);
8588
if (durationMs <= 0) {
86-
staff.sendMessage(MessageUtil.toComponent("&cInvalid duration format! Try again or type &ecancel&c."));
89+
staff.sendMessage(MessageUtil.toComponent(msg.getMessage("input.custom-duration.invalid")));
8790
return;
8891
}
8992

9093
completed = true;
9194
cleanup();
9295

93-
staff.sendMessage(MessageUtil.toComponent("&aDuration set to: &f" + TimeUtil.formatDuration(durationMs)));
96+
staff.sendMessage(MessageUtil.toComponent(
97+
msg.getMessage("input.custom-duration.success", "{duration}", TimeUtil.formatDuration(durationMs))));
9498

9599
new ReasonInputHandler(plugin, staff, target, type, durationMs).start();
96100
}

src/main/java/me/wethink/weguardian/gui/DurationGUI.java

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
import me.wethink.weguardian.WeGuardian;
66
import me.wethink.weguardian.model.PunishmentType;
77
import me.wethink.weguardian.util.MessageUtil;
8+
import me.wethink.weguardian.util.MessagesManager;
89
import me.wethink.weguardian.util.TimeUtil;
910
import org.bukkit.Material;
1011
import org.bukkit.OfflinePlayer;
1112
import org.bukkit.entity.Player;
1213
import org.bukkit.inventory.ItemStack;
1314

15+
import java.util.List;
16+
1417
public class DurationGUI extends FastInv {
1518

1619
private static ItemStack GLASS_PANE;
@@ -35,6 +38,8 @@ public class DurationGUI extends FastInv {
3538
private static long DURATION_90D;
3639

3740
public static void initializeIcons() {
41+
MessagesManager msg = WeGuardian.getInstance().getMessagesManager();
42+
3843
DURATION_1H = TimeUtil.parseDuration("1h");
3944
DURATION_6H = TimeUtil.parseDuration("6h");
4045
DURATION_1D = TimeUtil.parseDuration("1d");
@@ -47,48 +52,40 @@ public static void initializeIcons() {
4752
.name(" ")
4853
.build();
4954

55+
List<String> backLore = msg.getMessageList("gui.duration.items.back.lore");
5056
BACK_BUTTON = new ItemBuilder(Material.ARROW)
51-
.name(MessageUtil.colorize("&7&l← Back"))
52-
.lore("", MessageUtil.colorize("&e▶ Click to go back"))
57+
.name(MessageUtil.colorize(msg.getMessage("gui.duration.items.back.name")))
58+
.lore(backLore.stream().map(MessageUtil::colorize).toArray(String[]::new))
5359
.build();
5460

61+
List<String> permLore = msg.getMessageList("gui.duration.items.permanent.lore");
5562
PERM_ITEM = new ItemBuilder(Material.BEDROCK)
56-
.name(MessageUtil.colorize("&c&lPermanent"))
57-
.lore(
58-
"",
59-
MessageUtil.colorize("&7Make this punishment"),
60-
MessageUtil.colorize("&7permanent (no expiry)."),
61-
"",
62-
MessageUtil.colorize("&e▶ Click to apply"))
63+
.name(MessageUtil.colorize(msg.getMessage("gui.duration.items.permanent.name")))
64+
.lore(permLore.stream().map(MessageUtil::colorize).toArray(String[]::new))
6365
.build();
6466

67+
List<String> customLore = msg.getMessageList("gui.duration.items.custom.lore");
6568
CUSTOM_ITEM = new ItemBuilder(Material.NAME_TAG)
66-
.name(MessageUtil.colorize("&b&lCustom Duration"))
67-
.lore(
68-
"",
69-
MessageUtil.colorize("&7Enter a custom duration"),
70-
MessageUtil.colorize("&7in chat (e.g. 2h30m, 5d)"),
71-
"",
72-
MessageUtil.colorize("&e▶ Click to enter"))
69+
.name(MessageUtil.colorize(msg.getMessage("gui.duration.items.custom.name")))
70+
.lore(customLore.stream().map(MessageUtil::colorize).toArray(String[]::new))
7371
.build();
7472

75-
HOUR_1 = createDurationItem(Material.LIME_DYE, "&a1 Hour", DURATION_1H);
76-
HOURS_6 = createDurationItem(Material.YELLOW_DYE, "&e6 Hours", DURATION_6H);
77-
DAY_1 = createDurationItem(Material.ORANGE_DYE, "&61 Day", DURATION_1D);
78-
DAYS_3 = createDurationItem(Material.RED_DYE, "&c3 Days", DURATION_3D);
79-
WEEK_1 = createDurationItem(Material.PURPLE_DYE, "&51 Week", DURATION_7D);
80-
DAYS_30 = createDurationItem(Material.MAGENTA_DYE, "&d30 Days", DURATION_30D);
81-
DAYS_90 = createDurationItem(Material.BLUE_DYE, "&990 Days", DURATION_90D);
73+
HOUR_1 = createDurationItem(msg, "1h", Material.LIME_DYE, DURATION_1H);
74+
HOURS_6 = createDurationItem(msg, "6h", Material.YELLOW_DYE, DURATION_6H);
75+
DAY_1 = createDurationItem(msg, "1d", Material.ORANGE_DYE, DURATION_1D);
76+
DAYS_3 = createDurationItem(msg, "3d", Material.RED_DYE, DURATION_3D);
77+
WEEK_1 = createDurationItem(msg, "7d", Material.PURPLE_DYE, DURATION_7D);
78+
DAYS_30 = createDurationItem(msg, "30d", Material.MAGENTA_DYE, DURATION_30D);
79+
DAYS_90 = createDurationItem(msg, "90d", Material.BLUE_DYE, DURATION_90D);
8280
}
8381

84-
private static ItemStack createDurationItem(Material material, String name, long durationMs) {
82+
private static ItemStack createDurationItem(MessagesManager msg, String key, Material material, long durationMs) {
83+
String name = msg.getMessage("gui.duration.items." + key + ".name");
84+
List<String> lore = msg.getMessageList("gui.duration.items." + key + ".lore",
85+
"{duration}", TimeUtil.formatDuration(durationMs));
8586
return new ItemBuilder(material)
8687
.name(MessageUtil.colorize(name))
87-
.lore(
88-
"",
89-
MessageUtil.colorize("&7Duration: &f" + TimeUtil.formatDuration(durationMs)),
90-
"",
91-
MessageUtil.colorize("&e▶ Click to apply"))
88+
.lore(lore.stream().map(MessageUtil::colorize).toArray(String[]::new))
9289
.build();
9390
}
9491

@@ -98,7 +95,8 @@ private static ItemStack createDurationItem(Material material, String name, long
9895
private final PunishmentType type;
9996

10097
public DurationGUI(WeGuardian plugin, Player staff, OfflinePlayer target, PunishmentType type) {
101-
super(36, MessageUtil.colorize("&c&lSelect Duration &8» &e" + target.getName()));
98+
super(36, MessageUtil
99+
.colorize(plugin.getMessagesManager().getMessage("gui.duration.title", "{player}", target.getName())));
102100

103101
this.plugin = plugin;
104102
this.staff = staff;
@@ -107,6 +105,8 @@ public DurationGUI(WeGuardian plugin, Player staff, OfflinePlayer target, Punish
107105
}
108106

109107
public void build() {
108+
MessagesManager msg = plugin.getMessagesManager();
109+
110110
for (int i = 0; i < 9; i++) {
111111
setItem(i, GLASS_PANE);
112112
setItem(27 + i, GLASS_PANE);
@@ -124,7 +124,8 @@ public void build() {
124124
staff.closeInventory();
125125
PunishmentType permType = type == PunishmentType.TEMPBAN ? PunishmentType.BAN : PunishmentType.MUTE;
126126
if (!staff.hasPermission(permType.getPermission())) {
127-
staff.sendMessage(MessageUtil.toComponent("&cYou don't have permission to use permanent punishments!"));
127+
staff.sendMessage(
128+
MessageUtil.toComponent(msg.getMessage("input.custom-duration.no-permission-permanent")));
128129
return;
129130
}
130131
new ReasonInputHandler(plugin, staff, target, permType, -1).start();

0 commit comments

Comments
 (0)