feat: add format lore that replaces several placeholders with a map

This commit is contained in:
sebampuero
2025-03-14 22:46:51 +01:00
committed by hyperdefined
parent d5324b5db6
commit d675549209
3 changed files with 106 additions and 15 deletions

View File

@@ -27,6 +27,7 @@ import org.bukkit.persistence.PersistentDataType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ItemLore {
@@ -767,8 +768,8 @@ public class ItemLore {
}
container.remove(toolStats.flightTime);
if (meta.hasLore()) {
String oldFlightTimeFormatted = toolStats.numberFormat.formatDouble(flightTime);
Component lineToRemove = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightTimeFormatted);
Map<String, String> oldFlightTimeFormatted = toolStats.numberFormat.formatTime(flightTime);
Component lineToRemove = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", oldFlightTimeFormatted);
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
meta.lore(newLore);
}
@@ -815,10 +816,10 @@ public class ItemLore {
}
container.set(toolStats.flightTime, PersistentDataType.LONG, flightTime + duration);
String oldFlightFormatted = toolStats.numberFormat.formatTime(flightTime);
String newFlightFormatted = toolStats.numberFormat.formatTime(flightTime + duration);
Component oldLine = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightFormatted);
Component newLine = toolStats.configTools.formatLore("flight-time", "{time}", newFlightFormatted);
Map<String, String> oldFlightFormatted = toolStats.numberFormat.formatTime(flightTime);
Map<String, String> newFlightFormatted = toolStats.numberFormat.formatTime(flightTime + duration);
Component oldLine = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", oldFlightFormatted);
Component newLine = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", newFlightFormatted);
if (oldLine == null || newLine == null) {
return null;
}

View File

@@ -23,7 +23,9 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class NumberFormat {
@@ -138,39 +140,57 @@ public class NumberFormat {
}
/**
* Formats time in milliseconds in a human readable format.
* @param time The time in milliseconds to format.
* @return The time in a human readable format.
* Returns a human readable form of time in milliseconds.
* E.g. given 3752348000L outputs 1 years, 5 months, 2 weeks, 3 days, 14 hours, 12 minutes, 28 seconds.
* @param time The time in ms.
* @return Map with units as keys and time value, e.g. "years" (key) -> 1 (value)
*/
public String formatTime(Long time) {
public Map<String, String> formatTime(Long time) {
final int SECONDS_PER_MINUTE = 60;
final int MINUTES_PER_HOUR = 60;
final int HOURS_PER_DAY = 24;
final int DAYS_PER_WEEK = 7;
final int DAYS_PER_MONTH = 30; // Approximation
final int DAYS_PER_YEAR = 365; // Approximation
long totalSeconds = time / 1000;
Map<String, String> timeUnits = new HashMap<>();
long years = totalSeconds / (DAYS_PER_YEAR * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
if (years > 0) {
timeUnits.put("years", Long.toString(years));
}
totalSeconds %= (DAYS_PER_YEAR * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
long months = totalSeconds / (DAYS_PER_MONTH * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
if (months > 0) {
timeUnits.put("months", Long.toString(months));
}
totalSeconds %= (DAYS_PER_MONTH * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
long weeks = totalSeconds / (DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
totalSeconds %= (DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
long days = totalSeconds / (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
if (days > 0) {
timeUnits.put("days", Long.toString(days));
}
totalSeconds %= (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
long hours = totalSeconds / (MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
if (hours > 0) {
timeUnits.put("hours", Long.toString(hours));
}
totalSeconds %= (MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
long minutes = totalSeconds / SECONDS_PER_MINUTE;
if (minutes > 0) {
timeUnits.put("minutes", Long.toString(minutes));
}
totalSeconds %= SECONDS_PER_MINUTE;
long seconds = totalSeconds;
return "";
if (seconds > 0 || timeUnits.isEmpty()) { // Always include seconds if everything else is zero
timeUnits.put("seconds", Long.toString(seconds));
}
return timeUnits;
}
}

View File

@@ -27,6 +27,7 @@ import org.bukkit.Material;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -133,6 +134,75 @@ public class ConfigTools {
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
}
/**
* Format a string with several placeholders to be ready for lore usage.
* @param configName The message to use.
* @param placeHoldersValues Map containing placeholders names as keys and values.
* @return Formatted string, null if the configName doesn't exist.
*/
public Component formatLoreMultiplePlaceholders(String configName, Map<String, String> placeHoldersValues) {
String lore = toolStats.config.getString("messages." + configName);
if (lore == null) {
toolStats.logger.warning("Unable to find config message for: messages." + configName);
return null;
}
// if the config message is empty, don't send it
if (lore.isEmpty()) {
return null;
}
Pattern pattern = Pattern.compile("\\{([^}]+)\\}");
Matcher matcher = pattern.matcher(lore);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String placeholder = matcher.group(1);
if (placeHoldersValues.containsKey(placeholder)) {
matcher.appendReplacement(result, placeHoldersValues.get(placeholder));
} else {
// Placeholder not found in our time values, so remove it and any unit suffix
// Find the next non-alphanumeric character after this placeholder to remove the unit
int end = matcher.end();
while (end < lore.length() &&
!Character.isWhitespace(lore.charAt(end)) &&
!lore.substring(end, end + 1).matches("[^a-zA-Z]")) {
end++;
}
matcher.appendReplacement(result, "");
// Remove trailing space if there is one
if (end < lore.length() && Character.isWhitespace(lore.charAt(end))) {
// Skip this space in the next append
end++;
}
// Adjust region to char after skipped placeholder
matcher.region(end, lore.length());
}
}
matcher.appendTail(result);
Component component;
// Clean output text
String outputText = result.toString().replaceAll("\\s+", " ").trim();
// if we match the old color codes, then format them as so
Matcher hexMatcher = CONFIG_HEX_PATTERN.matcher(outputText);
Matcher colorMatcher = COLOR_CODES.matcher(outputText);
if (hexMatcher.find() || colorMatcher.find()) {
component = LegacyComponentSerializer.legacyAmpersand().deserialize(outputText);
} else {
// otherwise format them normally
component = MiniMessage.miniMessage().deserialize(outputText);
}
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
}
/**
* Format a string from the config.
*