fix: locate stats directory under Paper's dimensions layout

Rankings returned "sem dados" because World#getWorldFolder points at the
dimension directory in Paper's newer layout, so <worldFolder>/players/stats
does not exist. The real path is <worldContainer>/<level-name>/players/stats.
Try that first, fall back to the older locations, then climb out of the
dimension folder, and warn instead of silently returning nothing.

Also fixes "1 dias e 0 horas" — durations now agree in number and drop a
zero remainder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ActvLGJApdxEAd2yfKPwqv
This commit is contained in:
marcos
2026-08-04 20:52:38 +00:00
parent 801b43610f
commit 18ac5a5d17
2 changed files with 46 additions and 12 deletions
@@ -45,16 +45,23 @@ final class Msg {
.append(Component.text(value, NamedTextColor.AQUA)));
}
/** Renders a duration in ticks as "3 dias e 4 horas" / "5 horas" / "12 minutos". */
/** Renders a duration in ticks as "3 dias e 4 horas" / "1 hora" / "12 minutos". */
static String duration(long ticks) {
long minutes = ticks / 20L / 60L;
if (minutes < 60) {
return minutes + " minutos";
return plural(minutes, "minuto", "minutos");
}
long hours = minutes / 60;
if (hours < 24) {
return hours + " horas";
return plural(hours, "hora", "horas");
}
return hours / 24 + " dias e " + hours % 24 + " horas";
long days = hours / 24;
long rest = hours % 24;
String text = plural(days, "dia", "dias");
return rest == 0 ? text : text + " e " + plural(rest, "hora", "horas");
}
private static String plural(long value, String singular, String plural) {
return value + " " + (value == 1 ? singular : plural);
}
}
@@ -120,19 +120,46 @@ final class OfflineStats {
}
/**
* Paper writes to {@code <world>/players/stats}; older layouts used
* {@code <world>/stats}. Try both rather than assume.
* Locates the statistics directory.
*
* <p>Not as simple as it looks. Paper's newer layout puts dimensions under
* {@code <world>/dimensions/…}, and {@code World#getWorldFolder} points at
* the dimension rather than the world root — so {@code getWorldFolder() +
* "/players/stats"} does not exist. The real path is
* {@code <worldContainer>/<level-name>/players/stats}. Older servers used
* {@code <world>/stats}. Try each in turn, then walk up from the dimension
* folder as a last resort.
*/
private File statsDirectory() {
if (Bukkit.getWorlds().isEmpty()) {
return null;
}
File world = Bukkit.getWorlds().get(0).getWorldFolder();
File modern = new File(world, "players/stats");
if (modern.isDirectory()) {
return modern;
String levelName = Bukkit.getWorlds().get(0).getName();
File worldFolder = Bukkit.getWorlds().get(0).getWorldFolder();
File[] candidates = {
new File(new File(Bukkit.getWorldContainer(), levelName), "players/stats"),
new File(worldFolder, "players/stats"),
new File(new File(Bukkit.getWorldContainer(), levelName), "stats"),
new File(worldFolder, "stats"),
};
for (File candidate : candidates) {
if (candidate.isDirectory()) {
return candidate;
}
File legacy = new File(world, "stats");
return legacy.isDirectory() ? legacy : null;
}
// Last resort: climb out of the dimension folder looking for players/stats.
File current = worldFolder;
for (int depth = 0; depth < 4 && current != null; depth++) {
File found = new File(current, "players/stats");
if (found.isDirectory()) {
return found;
}
current = current.getParentFile();
}
plugin.getLogger().warning("Não encontrei o diretório de estatísticas a partir de "
+ worldFolder.getAbsolutePath());
return null;
}
}