LoParkour/src/main/java/dev/loki/loparkour/player/service/UserRegistry.java
loki5512344 fea15e61c8
fix: resolve 11 P1 bugs from audit (leaderboard, SQL, coop, jump, lifecycle, schematics, config)
- leaderboard: parse M:SS.mmm/H:MM:SS(.mmm), switch TIME sort to long (п.25)
- sql: single Hikari pool, non-blocking connect, adaptive init order, settings JSON round-trip (п.27-30)
- coop: remove unregister recursion via session-only leave + reentrancy guard (п.24)
- jump: perpendicular jitter for diagonal heading, valid safety-fallback on retry exhaustion (п.32-33)
- lifecycle: fall threshold relative to course, lead via generated-blocks counter, one-time rewards once (п.35-37)
- schematics: catch RuntimeExceptions, validate size, 10MB file limit (п.31)
- config merge: preserve block-sequence lists and ignored sections, fix path reversal (п.26)
- tests: +31 (score, jump, schematics, config merge) = 72 total
2026-08-06 10:49:05 +02:00

146 lines
4.6 KiB
Java

package dev.loki.loparkour.player.service;
import dev.loki.loparkour.player.core.ParkourUser;
import dev.loki.loparkour.player.core.ParkourPlayer;
import dev.loki.loparkour.api.event.session.ParkourJoinEvent;
import dev.loki.loparkour.api.event.session.ParkourLeaveEvent;
import dev.loki.loparkour.config.core.Config;
import dev.loki.loparkour.mode.base.Mode;
import dev.loki.loparkour.mode.base.Modes;
import dev.loki.loparkour.player.data.PreviousData;
import dev.loki.loparkour.session.core.Session;
import dev.loki.loparkour.storage.Storage;
import dev.loki.loparkour.world.core.Divider;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Manages registration and unregistration of parkour users.
*
* @since 5.0.0
*/
public final class UserRegistry {
private UserRegistry() {
}
private static int joinCount = 0;
public static @NotNull ParkourPlayer register(@NotNull Player player, @NotNull Session session) {
PreviousData data = null;
final ParkourUser existing = getUser(player);
if (existing != null) {
data = existing.previousData;
unregister(existing, false, false, false);
}
final ParkourPlayer pp = new ParkourPlayer(player, session, data);
joinCount++;
new ParkourJoinEvent(pp).call();
Storage.readPlayer(pp);
return pp;
}
public static void leave(@NotNull Player player) {
final ParkourUser user = getUser(player);
if (user != null) {
leave(user);
}
}
public static void leave(@NotNull ParkourUser user) {
unregister(user, true, true, false);
}
public static void unregister(@NotNull ParkourUser user, boolean restorePreviousData, boolean kickIfBungee, boolean urgent) {
if (user.unregistering) {
return;
}
user.unregistering = true;
try {
dev.loki.loparkour.adaptive.bootstrap.AdaptiveServices.unloadPlayer(user.getUUID());
new ParkourLeaveEvent(user).call();
try {
user.unregister();
resetScoreboard(user);
} catch (Exception ex) {
handleUnregisterError(user, ex);
}
if (restorePreviousData && shouldKickToBungee(kickIfBungee)) {
BungeeUtil.sendPlayerToServer(user.player, Config.CONFIG.getString("bungeecord.return_server"));
return;
}
if (restorePreviousData) {
restorePreviousState(user, urgent);
}
} finally {
user.unregistering = false;
}
}
public static boolean isUser(@Nullable Player player) {
return player != null && getUsers().stream().anyMatch(other -> other.player == player);
}
public static @Nullable ParkourUser getUser(@NotNull Player player) {
return getUsers().stream()
.filter(other -> other.getUUID().equals(player.getUniqueId()))
.findAny()
.orElse(null);
}
public static Set<ParkourUser> getUsers() {
return Divider.SECTIONS.keySet().stream()
.flatMap(session -> session.getUsers().stream())
.collect(Collectors.toSet());
}
public static int getJoinCount() {
final int count = joinCount;
joinCount = 0;
return count;
}
private static void resetScoreboard(ParkourUser user) {
if (user.board != null) {
user.player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
user.board = null;
}
}
private static void handleUnregisterError(ParkourUser user, Exception ex) {
user.player.getServer().getLogger().log(java.util.logging.Level.SEVERE,
"Error while trying to make player " + user.getName() + " leave", ex);
user.sendTranslated("other.leave_error");
}
private static boolean shouldKickToBungee(boolean kickIfBungee) {
return Config.CONFIG.getBoolean("bungeecord.enabled") && kickIfBungee;
}
private static void restorePreviousState(ParkourUser user, boolean urgent) {
user.previousData.apply(user.player, urgent);
Mode mode = user.session.generator.getMode();
if (mode == null) {
mode = Modes.DEFAULT;
}
if (user instanceof ParkourPlayer player) {
final Mode finalMode = mode;
user.previousData.onLeave.forEach(r -> r.execute(player, finalMode));
}
}
}