Skip to content

Commit d675549

Browse files
sebampuerohyperdefined
authored andcommitted
feat: add format lore that replaces several placeholders with a map
1 parent d5324b5 commit d675549

File tree

3 files changed

+106
-15
lines changed

3 files changed

+106
-15
lines changed

src/main/java/lol/hyper/toolstats/tools/ItemLore.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import java.util.ArrayList;
2929
import java.util.List;
30+
import java.util.Map;
3031

3132
public class ItemLore {
3233

@@ -767,8 +768,8 @@ public ItemMeta updateFlightTime(ItemStack elytra, long duration) {
767768
}
768769
container.remove(toolStats.flightTime);
769770
if (meta.hasLore()) {
770-
String oldFlightTimeFormatted = toolStats.numberFormat.formatDouble(flightTime);
771-
Component lineToRemove = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightTimeFormatted);
771+
Map<String, String> oldFlightTimeFormatted = toolStats.numberFormat.formatTime(flightTime);
772+
Component lineToRemove = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", oldFlightTimeFormatted);
772773
List<Component> newLore = removeLore(meta.lore(), lineToRemove);
773774
meta.lore(newLore);
774775
}
@@ -815,10 +816,10 @@ public ItemMeta updateFlightTime(ItemStack elytra, long duration) {
815816
}
816817

817818
container.set(toolStats.flightTime, PersistentDataType.LONG, flightTime + duration);
818-
String oldFlightFormatted = toolStats.numberFormat.formatTime(flightTime);
819-
String newFlightFormatted = toolStats.numberFormat.formatTime(flightTime + duration);
820-
Component oldLine = toolStats.configTools.formatLore("flight-time", "{time}", oldFlightFormatted);
821-
Component newLine = toolStats.configTools.formatLore("flight-time", "{time}", newFlightFormatted);
819+
Map<String, String> oldFlightFormatted = toolStats.numberFormat.formatTime(flightTime);
820+
Map<String, String> newFlightFormatted = toolStats.numberFormat.formatTime(flightTime + duration);
821+
Component oldLine = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", oldFlightFormatted);
822+
Component newLine = toolStats.configTools.formatLoreMultiplePlaceholders("flight-time", newFlightFormatted);
822823
if (oldLine == null || newLine == null) {
823824
return null;
824825
}

src/main/java/lol/hyper/toolstats/tools/NumberFormat.java

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
import java.text.DecimalFormatSymbols;
2424
import java.text.SimpleDateFormat;
2525
import java.util.Date;
26+
import java.util.HashMap;
2627
import java.util.Locale;
28+
import java.util.Map;
2729

2830
public class NumberFormat {
2931

@@ -138,39 +140,57 @@ public String formatDate(Date date) {
138140
}
139141

140142
/**
141-
* Formats time in milliseconds in a human readable format.
142-
* @param time The time in milliseconds to format.
143-
* @return The time in a human readable format.
143+
* Returns a human readable form of time in milliseconds.
144+
* E.g. given 3752348000L outputs 1 years, 5 months, 2 weeks, 3 days, 14 hours, 12 minutes, 28 seconds.
145+
* @param time The time in ms.
146+
* @return Map with units as keys and time value, e.g. "years" (key) -> 1 (value)
144147
*/
145-
public String formatTime(Long time) {
148+
public Map<String, String> formatTime(Long time) {
146149
final int SECONDS_PER_MINUTE = 60;
147150
final int MINUTES_PER_HOUR = 60;
148151
final int HOURS_PER_DAY = 24;
149-
final int DAYS_PER_WEEK = 7;
150152
final int DAYS_PER_MONTH = 30; // Approximation
151153
final int DAYS_PER_YEAR = 365; // Approximation
152154

153155
long totalSeconds = time / 1000;
154156

157+
Map<String, String> timeUnits = new HashMap<>();
158+
155159
long years = totalSeconds / (DAYS_PER_YEAR * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
160+
if (years > 0) {
161+
timeUnits.put("years", Long.toString(years));
162+
}
156163
totalSeconds %= (DAYS_PER_YEAR * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
157164

158165
long months = totalSeconds / (DAYS_PER_MONTH * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
166+
if (months > 0) {
167+
timeUnits.put("months", Long.toString(months));
168+
}
159169
totalSeconds %= (DAYS_PER_MONTH * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
160170

161-
long weeks = totalSeconds / (DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
162-
totalSeconds %= (DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
163-
164171
long days = totalSeconds / (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
172+
if (days > 0) {
173+
timeUnits.put("days", Long.toString(days));
174+
}
165175
totalSeconds %= (HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
166176

167177
long hours = totalSeconds / (MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
178+
if (hours > 0) {
179+
timeUnits.put("hours", Long.toString(hours));
180+
}
168181
totalSeconds %= (MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
169182

170183
long minutes = totalSeconds / SECONDS_PER_MINUTE;
184+
if (minutes > 0) {
185+
timeUnits.put("minutes", Long.toString(minutes));
186+
}
171187
totalSeconds %= SECONDS_PER_MINUTE;
172188

173189
long seconds = totalSeconds;
174-
return "";
190+
if (seconds > 0 || timeUnits.isEmpty()) { // Always include seconds if everything else is zero
191+
timeUnits.put("seconds", Long.toString(seconds));
192+
}
193+
194+
return timeUnits;
175195
}
176196
}

src/main/java/lol/hyper/toolstats/tools/config/ConfigTools.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.ArrayList;
2828
import java.util.Collections;
2929
import java.util.List;
30+
import java.util.Map;
3031
import java.util.regex.Matcher;
3132
import java.util.regex.Pattern;
3233

@@ -133,6 +134,75 @@ public Component formatLore(String configName, String placeHolder, Object value)
133134
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
134135
}
135136

137+
/**
138+
* Format a string with several placeholders to be ready for lore usage.
139+
* @param configName The message to use.
140+
* @param placeHoldersValues Map containing placeholders names as keys and values.
141+
* @return Formatted string, null if the configName doesn't exist.
142+
*/
143+
public Component formatLoreMultiplePlaceholders(String configName, Map<String, String> placeHoldersValues) {
144+
String lore = toolStats.config.getString("messages." + configName);
145+
if (lore == null) {
146+
toolStats.logger.warning("Unable to find config message for: messages." + configName);
147+
return null;
148+
}
149+
150+
// if the config message is empty, don't send it
151+
if (lore.isEmpty()) {
152+
return null;
153+
}
154+
155+
Pattern pattern = Pattern.compile("\\{([^}]+)\\}");
156+
Matcher matcher = pattern.matcher(lore);
157+
158+
StringBuffer result = new StringBuffer();
159+
160+
while (matcher.find()) {
161+
String placeholder = matcher.group(1);
162+
if (placeHoldersValues.containsKey(placeholder)) {
163+
matcher.appendReplacement(result, placeHoldersValues.get(placeholder));
164+
} else {
165+
// Placeholder not found in our time values, so remove it and any unit suffix
166+
// Find the next non-alphanumeric character after this placeholder to remove the unit
167+
int end = matcher.end();
168+
while (end < lore.length() &&
169+
!Character.isWhitespace(lore.charAt(end)) &&
170+
!lore.substring(end, end + 1).matches("[^a-zA-Z]")) {
171+
end++;
172+
}
173+
174+
matcher.appendReplacement(result, "");
175+
176+
// Remove trailing space if there is one
177+
if (end < lore.length() && Character.isWhitespace(lore.charAt(end))) {
178+
// Skip this space in the next append
179+
end++;
180+
}
181+
182+
// Adjust region to char after skipped placeholder
183+
matcher.region(end, lore.length());
184+
}
185+
}
186+
187+
matcher.appendTail(result);
188+
189+
Component component;
190+
// Clean output text
191+
String outputText = result.toString().replaceAll("\\s+", " ").trim();
192+
193+
// if we match the old color codes, then format them as so
194+
Matcher hexMatcher = CONFIG_HEX_PATTERN.matcher(outputText);
195+
Matcher colorMatcher = COLOR_CODES.matcher(outputText);
196+
if (hexMatcher.find() || colorMatcher.find()) {
197+
component = LegacyComponentSerializer.legacyAmpersand().deserialize(outputText);
198+
} else {
199+
// otherwise format them normally
200+
component = MiniMessage.miniMessage().deserialize(outputText);
201+
}
202+
203+
return component.decorationIfAbsent(TextDecoration.ITALIC, TextDecoration.State.FALSE);
204+
}
205+
136206
/**
137207
* Format a string from the config.
138208
*

0 commit comments

Comments
 (0)