Merge pull request #97 from sebampuero/impr-flighttime-fmt

Elytra flight time as human readable format
This commit is contained in:
hyperdefined
2025-03-31 18:44:50 -04:00
committed by GitHub
7 changed files with 132 additions and 10 deletions

View File

@@ -210,7 +210,8 @@ public class CommandToolStats implements TabExecutor {
if (flightTime != null) {
if (toolStats.config.getBoolean("enabled.flight-time")) {
Component line = toolStats.configTools.formatLore("flight-time", "{time}", toolStats.numberFormat.formatDouble((double) flightTime / 1000));
Map<String, String> flightTimeFormatted = toolStats.numberFormat.formatTime(flightTime);
Component line = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", flightTimeFormatted);
lore.add(line);
}
}

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.formatDouble((double) flightTime / 1000);
String newFlightFormatted = toolStats.numberFormat.formatDouble((double) (flightTime + duration) / 1000);
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 {
@@ -136,4 +138,59 @@ public class NumberFormat {
public String formatDate(Date date) {
return DATE_FORMAT.format(date);
}
/**
* Returns a human readable form of time in milliseconds.
* E.g. given 3752348000L outputs 1 years, 5 months, 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 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_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 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;
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,65 @@ 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("\\{([^}]+)\\}(\\S*)\\s*");
Matcher matcher = pattern.matcher(lore);
StringBuilder result = new StringBuilder();
int lastEnd = 0;
while (matcher.find()) {
String placeholder = matcher.group(1);
String unit = matcher.group(2);
result.append(lore, lastEnd, matcher.start());
if (placeHoldersValues.containsKey(placeholder)) {
result.append(placeHoldersValues.get(placeholder)).append(unit).append(" ");
}
// Update lastEnd to end of the match
lastEnd = matcher.end();
}
if (lastEnd < lore.length()) {
result.append(lore.substring(lastEnd));
}
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.
*

View File

@@ -86,6 +86,9 @@ public class Version11 {
toolStats.logger.info("Adding enabled.damage-done.bow to config.yml");
toolStats.logger.info("Adding enabled.damage-done.mace to config.yml");
toolStats.logger.info("Updating entry for messages.flight-time");
toolStats.config.set("messages.flight-time", "&7Flight time: &8{years}y {months}m {days}d {hours}h {minutes}m {seconds}s");
// save the config and reload it
try {
toolStats.config.save("plugins" + File.separator + "ToolStats" + File.separator + "config.yml");

View File

@@ -196,7 +196,7 @@ messages:
dropped-by: "&7Dropped by: &8{name}" # name will be player/mob name
damage-taken: "&7Damage taken: &8{damage}"
arrows-shot: "&7Arrows shot: &8{arrows}"
flight-time: "&7Flight time: &8{time}"
flight-time: "&7Flight time: &8{years}y {months}m {days}d {hours}h {minutes}m {seconds}s"
damage-done: "&7Damage done: &8{damage}"
# Set display name for mobs. See: https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/EntityType.html
mobs: