Skip to content

Commit 811d1c1

Browse files
committed
Improve the dublicate message for menu module
Make it easier to set your own key for the placeholder.
1 parent 3804a81 commit 811d1c1

5 files changed

Lines changed: 131 additions & 61 deletions

File tree

Menu Library/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ dependencies {
1919
api(project(":title-update"))
2020
api(project(":log-and-validate"))
2121
api(project(":version"))
22+
api(project(":serialize-utility"))
2223

2324
compileOnly(libs.org.spigotmc.spigotapi)
2425
compileOnly(libs.com.google.code.gson.gson)

Menu Library/src/main/java/org/broken/arrow/library/menu/CheckItemsInsideMenu.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,13 @@ private void addItemsBackToPlayer(final Location location) {
287287

288288
itemStack.setAmount(amount);
289289
final OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(mapEntry.getKey());
290-
if (offlinePlayer.getPlayer() != null) {
291-
final HashMap<Integer, ItemStack> ifInventorFull = offlinePlayer.getPlayer().getInventory().addItem(itemStack);
292-
if (!ifInventorFull.isEmpty() && offlinePlayer.getPlayer().getLocation().getWorld() != null)
293-
offlinePlayer.getPlayer().getLocation().getWorld().dropItemNaturally(offlinePlayer.getPlayer().getLocation(), ifInventorFull.get(0));
290+
final Player player = offlinePlayer.getPlayer();
291+
if (player != null) {
292+
final HashMap<Integer, ItemStack> ifInventorFull = player.getInventory().addItem(itemStack);
293+
if (!ifInventorFull.isEmpty() && player.getLocation().getWorld() != null)
294+
player.getLocation().getWorld().dropItemNaturally(player.getLocation(), ifInventorFull.get(0));
294295

295-
this.registerMenuAPI.getMessages().sendDuplicatedMessage(offlinePlayer.getPlayer(), new SendMsgDuplicatedItems.DuplicatedItemWrapper(itemStack, mapEntry.getValue().size(), amount));
296+
this.registerMenuAPI.getMessages().sendDuplicatedMessage(player, new SendMsgDuplicatedItems.DuplicatedItemWrapper(itemStack, mapEntry.getValue().size(), amount));
296297
} else if (location != null && location.getWorld() != null)
297298
location.getWorld().dropItemNaturally(location, itemStack);
298299
}

Menu Library/src/main/java/org/broken/arrow/library/menu/messages/SendMsgDuplicatedItems.java

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import net.md_5.bungee.api.ChatColor;
55
import org.broken.arrow.library.color.TextTranslator;
66
import org.broken.arrow.library.menu.utility.DuplicateMessage;
7+
import org.broken.arrow.library.serialize.utility.converters.PlaceholderTranslator;
78
import org.bukkit.entity.Player;
89
import org.bukkit.inventory.ItemStack;
910

@@ -89,64 +90,71 @@ public void setBlacklistMessage(final Function<ItemStack, String> blacklistMessa
8990
}
9091

9192
/**
92-
* Set message for when player have added item some are duplicated.
93-
* Support both hex and &amp; color codes.
93+
* Sets a static message format for duplicated item handling.
9494
*
95-
* <p><b>Supported color formats:</b></p>
95+
* <p>This is a convenience method equivalent to:</p>
96+
* <pre>
97+
* setDuplicatedMessage(wrapper -> "your message");
98+
* </pre>
99+
*
100+
* <p>The message supports placeholders and color codes and will be processed automatically.</p>
101+
*
102+
* <p><b>Formatting support:</b></p>
96103
* <ul>
97-
* <li>Hex colors: {@code <#8000ff>} or gradients like {@code <#8000ff:#ff0080>} (requires color conversion module).</li>
98-
* <li>Fallback to Spigot's legacy format: {@code &x&6&6&6&6&6&6}, or for white: {@code &x&F&F&F&F&F&F}, if color conversion is not enabled.</li>
104+
* <li>Legacy color codes: {@code &a}, {@code &f}, etc.</li>
105+
* <li>Legacy hex format (Spigot-compatible): {@code &x&R&R&G&G&B&B}</li>
106+
* <li>Hex colors: {@code <#RRGGBB>} and gradients like {@code <#RRGGBB:#RRGGBB>}
107+
* (supported when the internal formatter is available)</li>
99108
* </ul>
100109
*
101-
* <p><b>Available placeholders:</b></p>
110+
* <p>Formatting is handled automatically using the best available implementation
111+
* (Spigot-compatible or internal formatter).</p>
112+
*
113+
* <p><b>Available indexed placeholders:</b></p>
102114
* <ul>
103-
* <li>{@code {0}} – The item type (e.g., {@code DIAMOND_SWORD})</li>
104-
* <li>{@code {1}} – Total number of duplicated stacks</li>
105-
* <li>{@code {2}} – Total number of duplicated items</li>
115+
* <li>{@code {0}} – Item type (e.g. {@code DIAMOND_SWORD})</li>
116+
* <li>{@code {1}} – Total duplicated stacks</li>
117+
* <li>{@code {2}} – Total duplicated item amount</li>
106118
* </ul>
107119
*
108-
* @param duplicatedMessage set a message.
120+
* <p><b>Note:</b> If {@link DuplicatedItemWrapper#getPlaceholderWrapper()} is used, those take priority over indexed placeholders.</p>
121+
*
122+
* @param duplicatedMessage message format string
109123
*/
110124
public void setDuplicatedMessage(String duplicatedMessage) {
111-
this.duplicatedMessage = (itemStack, size, itemAmount) -> duplicatedMessage;
125+
this.duplicatedMessage = (wrapper) -> duplicatedMessage;
112126
}
113127

114128
/**
115-
* Sets the message format to display when a player attempts to add items and some are duplicates.
129+
* Sets a custom message generator for duplicated item handling.
116130
*
117-
* <p>
118-
* Supports both hex color codes and legacy {@code &} codes. This function allows you to handle placeholder
119-
* replacement yourself instead of using a predefined message format, giving you more dynamic control, particularly useful
120-
* if you're using a localization file or want the text to be updated externally with minimal code changes.
121-
* </p>
131+
* <p>This allows full control over the final message, including placeholders and formatting.
132+
* The provided function receives a {@link DuplicatedItemWrapper}, and the returned string
133+
* is processed automatically.</p>
122134
*
123-
* <p><b>Supported color formats:</b></p>
135+
* <p>If not set, a default message format is used.</p>
136+
*
137+
* <p><b>Formatting support:</b></p>
124138
* <ul>
125-
* <li>Hex colors: {@code <#8000ff>} or gradients like {@code <#8000ff:#ff0080>} (requires the color conversion module).</li>
126-
* <li>Fallback to Spigot's legacy format: {@code &x&6&6&6&6&6&6}, or for white: {@code &x&F&F&F&F&F&F}, if color conversion is not enabled.</li>
139+
* <li>Legacy color codes: {@code &a}, {@code &f}, etc.</li>
140+
* <li>Legacy hex format (Spigot-compatible): {@code &x&R&R&G&G&B&B}</li>
141+
* <li>Hex colors: {@code <#RRGGBB>} and gradients like {@code <#RRGGBB:#RRGGBB>}
142+
* (supported when the internal formatter is available)</li>
127143
* </ul>
128144
*
129-
* <p><b>Available placeholders:</b></p>
145+
* <p>Formatting is handled automatically using the best available implementation
146+
* (Spigot-compatible or internal formatter).</p>
147+
*
148+
* <p><b>Available indexed placeholders:</b></p>
130149
* <ul>
131-
* <li>{@code {0}} – The item type (e.g., {@code DIAMOND_SWORD})</li>
132-
* <li>{@code {1}} – Total number of duplicated stacks</li>
133-
* <li>{@code {2}} – Total number of duplicated items</li>
150+
* <li>{@code {0}} – Item type (e.g. {@code DIAMOND_SWORD})</li>
151+
* <li>{@code {1}} – Total duplicated stacks</li>
152+
* <li>{@code {2}} – Total duplicated item amount</li>
134153
* </ul>
135154
*
136-
* <p>
137-
* The returned string will be further processed after this function is called — including color conversion
138-
* and placeholder replacement (e.g., {@code {0}}, {@code {1}}, {@code {2}}). You only need to provide the
139-
* base message with the desired format and placeholders.
140-
* </p>
141-
*
142-
* <p>
143-
* You may also choose to handle placeholder translation yourself by including resolved values directly
144-
* in the lambda. If you want full control over the output, including color formatting, simply return
145-
* an empty string or {@code null} to skip the default message generation and formatting.
146-
* </p>
155+
* <p><b>Note:</b> If {@link DuplicatedItemWrapper#getPlaceholderWrapper()} is used, those take priority over indexed placeholders.</p>
147156
*
148-
* @param duplicatedMessage a function that takes the item type, duplicated {@link ItemStack}, and finally duplicated item count,
149-
* and returns the base message string to be processed.
157+
* @param duplicatedMessage function that generates the base message from duplicated item data.
150158
*/
151159
public void setDuplicatedMessage(final DuplicateMessage duplicatedMessage) {
152160
this.duplicatedMessage = duplicatedMessage;
@@ -156,7 +164,7 @@ public void setDuplicatedMessage(final DuplicateMessage duplicatedMessage) {
156164
* Sends a raw message to the specified player.
157165
*
158166
* @param player the player to send the message to
159-
* @param msg the message string to send
167+
* @param msg the message string to send
160168
*/
161169
public void sendMessage(Player player, String msg) {
162170
player.sendMessage(msg);
@@ -166,13 +174,17 @@ public void sendMessage(Player player, String msg) {
166174
* Sends the blacklist message to the player regarding the specified blacklisted item.
167175
* The message supports color codes and placeholder replacement.
168176
*
169-
* @param player the player to send the message to
177+
* @param player the player to send the message to
170178
* @param itemStack the blacklisted item stack triggering the message
171179
*/
172180
public void sendBlacklistMessage(Player player, ItemStack itemStack) {
173181
String message;
174-
if (blacklistMessage == null) message = "&fThis item&6 {0}&f are blacklisted and you get the items back.";
175-
else message = blacklistMessage.apply(itemStack.clone());
182+
if (blacklistMessage == null) {
183+
message = "&fThis item&6 {0}&f are blacklisted and you get the items back.";
184+
} else {
185+
message = blacklistMessage.apply(itemStack.clone());
186+
}
187+
176188
if (message == null || message.isEmpty())
177189
return;
178190

@@ -186,31 +198,38 @@ public void sendBlacklistMessage(Player player, ItemStack itemStack) {
186198
* Sends the duplicated item message to the player, using data from the provided placeholder wrapper.
187199
* The message supports color codes and placeholder replacement.
188200
*
189-
* @param player the player to send the message to
201+
* @param player the player to send the message to
190202
* @param placeholderData wrapper containing duplicated item data for placeholders
191203
*/
192204
public void sendDuplicatedMessage(@Nonnull final Player player, @Nonnull final DuplicatedItemWrapper placeholderData) {
193205
String message;
194206
if (duplicatedMessage == null) {
195207
message = "&fYou can't add more if this &6 {0} &ftype, you get back &6 {2}&f items.You have added totally &4{1}&f extra itemstacks";
196208
} else {
197-
message = duplicatedMessage.apply(placeholderData.getItemStack().clone(), placeholderData.getSize(), placeholderData.getItemAmount());
209+
message = duplicatedMessage.apply(placeholderData);
198210
}
199211

200212
if (message == null || message.isEmpty())
201213
return;
202214

215+
final PlaceholderTranslator.PlaceholderWrapper wrapper = placeholderData.getPlaceholderWrapper();
216+
if (!wrapper.getPlaceholders().isEmpty())
217+
message = PlaceholderTranslator.translateText(message, wrapper);
218+
else {
219+
message = PlaceholderTranslator.translateText(message, placeholderData.retrieveAsPlaceholderData());
220+
}
221+
203222
if (notFoundTextTranslator)
204-
player.sendMessage(ChatColor.translateAlternateColorCodes('&', (translatePlaceholders(message, placeholderData.retrieveAsPlaceholderData()))));
223+
player.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
205224
else
206-
player.sendMessage(TextTranslator.toSpigotFormat(translatePlaceholders(message, placeholderData.retrieveAsPlaceholderData())));
225+
player.sendMessage(TextTranslator.toSpigotFormat(message));
207226
}
208227

209228
/**
210229
* Replaces placeholders of the form {@code {0}, {1}, ...} in the given text with
211230
* the string representation of the provided placeholder objects.
212231
*
213-
* @param rawText the text containing placeholders to replace
232+
* @param rawText the text containing placeholders to replace
214233
* @param placeholders the objects to insert into the placeholders
215234
* @return the text with placeholders replaced by their corresponding values
216235
*/
@@ -225,19 +244,20 @@ public String translatePlaceholders(String rawText, Object... placeholders) {
225244
* Wrapper class holding information about duplicated items for placeholder substitution.
226245
*/
227246
public static class DuplicatedItemWrapper {
228-
247+
private final PlaceholderTranslator.PlaceholderWrapper placeholderWrapper;
229248
private final ItemStack itemStack;
230249
private final int size;
231250
private final int itemAmount;
232251

233252
/**
234253
* Constructs a new wrapper containing duplicated item data.
235254
*
236-
* @param itemStack the duplicated item stack
237-
* @param size the total number of duplicated stacks
255+
* @param itemStack the duplicated item stack
256+
* @param size the total number of duplicated stacks
238257
* @param itemAmount the total number of duplicated items
239258
*/
240259
public DuplicatedItemWrapper(final ItemStack itemStack, final int size, final int itemAmount) {
260+
this.placeholderWrapper = new PlaceholderTranslator.PlaceholderWrapper();
241261
this.itemStack = itemStack;
242262
this.size = size;
243263
this.itemAmount = itemAmount;
@@ -249,7 +269,7 @@ public DuplicatedItemWrapper(final ItemStack itemStack, final int size, final in
249269
* @return the item stack
250270
*/
251271
public ItemStack getItemStack() {
252-
return itemStack;
272+
return itemStack.clone();
253273
}
254274

255275
/**
@@ -270,6 +290,39 @@ public int getItemAmount() {
270290
return itemAmount;
271291
}
272292

293+
/**
294+
* Returns a {@link PlaceholderTranslator.PlaceholderWrapper} for defining custom
295+
* key-based placeholders instead of using the default indexed placeholder system.
296+
*
297+
* <p>If no custom placeholders are provided, the system falls back to
298+
* {@link #retrieveAsPlaceholderData()}, which uses ordered placeholders such as
299+
* {@code {0}}, {@code {1}}, {@code {2}}.</p>
300+
*
301+
* <p>When this wrapper contains entries, it is used instead and allows named
302+
* placeholders.</p>
303+
*
304+
* <p><b>Example (default indexed placeholders):</b></p>
305+
* <pre>
306+
* "&fYou can't add more if this &6 {0} &ftype, you get back &6 {2} &fitems.
307+
* You have added totally &4 {1} &fextra itemstacks"
308+
* </pre>
309+
*
310+
* <p><b>Example (custom named placeholders):</b></p>
311+
* <pre>
312+
* wrapper.put("{type}", itemStack.getType())
313+
* .put("{addedStacks}", size)
314+
* .put("{returnedItems}", itemAmount);
315+
*
316+
* "&fYou can't add more if this &6 {type} &ftype, you get back &6 {returnedItems} &fitems.
317+
* You have added totally &4 {addedStacks} &fextra itemstacks"
318+
* </pre>
319+
*
320+
* @return the {@link PlaceholderTranslator.PlaceholderWrapper} for custom placeholders
321+
*/
322+
public PlaceholderTranslator.PlaceholderWrapper getPlaceholderWrapper() {
323+
return placeholderWrapper;
324+
}
325+
273326
/**
274327
* Returns an array of objects to be used as placeholders in messages:
275328
* item type, duplicated stacks count, and duplicated items count.
Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
package org.broken.arrow.library.menu.utility;
22

3+
import org.broken.arrow.library.menu.messages.SendMsgDuplicatedItems;
34
import org.bukkit.inventory.ItemStack;
45

6+
import javax.annotation.Nonnull;
7+
58
/**
69
* Represents a duplicate message that takes three arguments and produces a result.
710
*
811
*/
912
public interface DuplicateMessage {
1013

1114
/**
12-
* Applies this message to the given arguments.
15+
* Applies this message to the given argument.
1316
*
14-
* @param item the itemStack that is duplicate
15-
* @param amount the amount of items that is duplicated.
16-
* @param itemAmount the amount of items you get back.
17-
* @return the function result
17+
* @param duplicatedWrapper The instance of the wrapper to retrieve the data on the item that is a duplicate.
18+
* @return the text set that placeholders will be translated and color codes.
1819
*/
19-
String apply(final ItemStack item, final int amount, final int itemAmount );
20+
String apply(@Nonnull final SendMsgDuplicatedItems.DuplicatedItemWrapper duplicatedWrapper);
2021

2122
}

Serialize Utility/src/main/java/org/broken/arrow/library/serialize/utility/converters/PlaceholderTranslator.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.broken.arrow.library.logging.Logging;
44
import org.broken.arrow.library.serialize.utility.Pair;
55

6+
import javax.annotation.Nonnull;
67
import java.util.*;
78
import java.util.function.Consumer;
89
import java.util.logging.Level;
@@ -37,6 +38,19 @@ public static String translateText(final String text, final Consumer<Placeholder
3738
return applyPlaceholderMap(text, placeholderMap);
3839
}
3940

41+
/**
42+
* Translates placeholders in a raw text by replacing them with corresponding values.
43+
* The placeholders are replaced in the order specified by the placeholders array.
44+
*
45+
* @param text The raw text to translate.
46+
* @param placeholderWrapper The placeholders where you provide the placeholder data.
47+
* @return The translated text.
48+
*/
49+
public static String translateText(final String text, @Nonnull final PlaceholderWrapper placeholderWrapper) {
50+
final Map<String, Object> placeholderMap = placeholderWrapper.getPlaceholders();
51+
return applyPlaceholderMap(text, placeholderMap);
52+
}
53+
4054
/**
4155
* Translates placeholders in a raw text by replacing them with corresponding values.
4256
* The placeholders are replaced in the order specified by the placeholders array.

0 commit comments

Comments
 (0)