fix and migrate 70%

This commit is contained in:
loki 2026-03-05 20:28:25 +01:00
parent 8705cad578
commit d1dcf6ab57
138 changed files with 3736 additions and 4735 deletions

View file

@ -0,0 +1,162 @@
package dev.loki.loparkour.command;
import dev.loki.loparkour.LoParkour;
import dev.loki.loparkour.api.Registry;
import dev.loki.loparkour.config.Locales;
import dev.loki.loparkour.leaderboard.Leaderboard;
import dev.loki.loparkour.menu.ParkourOption;
import dev.loki.loparkour.mode.Mode;
import dev.loki.loparkour.mode.Modes;
import dev.loki.loparkour.player.ParkourPlayer;
import dev.loki.loparkour.player.ParkourUser;
import dev.loki.loparkour.player.data.InventoryData;
import dev.loki.loparkour.util.ColorUtil;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
/**
* Handles admin-only /LoParkour sub-commands:
* forcejoin, forceleave, reset, recoverinventory.
*/
public class AdminCommandHandler {
private final PlayerCommandHandler base;
public AdminCommandHandler(PlayerCommandHandler base) {
this.base = base;
}
public void handle(@NotNull String arg1, @NotNull String arg2,
@NotNull CommandSender sender, @Nullable Player player) {
if (!sender.hasPermission(ParkourOption.ADMIN.permission)) return;
switch (arg1.toLowerCase()) {
case "forcejoin" -> handleForceJoin(arg2, sender);
case "forceleave" -> handleForceLeave(arg2, sender);
case "reset" -> handleReset(arg2, sender);
case "recoverinventory" -> handleRecoverInventory(arg2, sender);
}
if (player == null) return;
if (arg1.equals("schematic")) {
SchematicCommandHandler.handleSubcommand(arg2, sender, player, base);
}
}
// ── forcejoin ──────────────────────────────────────────────────────────────
private void handleForceJoin(String target, CommandSender sender) {
if (target.equalsIgnoreCase("everyone")) {
Bukkit.getOnlinePlayers().forEach(p -> Modes.DEFAULT.create(p));
send(sender, LoParkour.PREFIX + "Force joined everyone!");
return;
}
if (target.equalsIgnoreCase("nearest")) {
Player closest = findNearest(sender);
if (closest == null) return;
Modes.DEFAULT.create(closest);
send(sender, LoParkour.PREFIX + "Force joined " + closest.getName() + "!");
return;
}
Player other = Bukkit.getPlayer(target);
if (other == null) { send(sender, LoParkour.PREFIX + "Player not online!"); return; }
Modes.DEFAULT.create(other);
}
// ── forceleave ─────────────────────────────────────────────────────────────
private void handleForceLeave(String target, CommandSender sender) {
if (target.equalsIgnoreCase("everyone")) {
ParkourPlayer.getPlayers().forEach(ParkourUser::leave);
send(sender, LoParkour.PREFIX + "Force kicked everyone!");
return;
}
Player other = Bukkit.getPlayer(target);
if (other == null) { send(sender, LoParkour.PREFIX + "Player not online!"); return; }
ParkourUser user = ParkourUser.getUser(other);
if (user == null) { send(sender, LoParkour.PREFIX + "Player isn't playing!"); return; }
ParkourUser.leave(user);
}
// ── reset ──────────────────────────────────────────────────────────────────
private void handleReset(String target, CommandSender sender) {
if (!base.cooldown(sender, "reset", 2500)) return;
if (target.equalsIgnoreCase("everyone")) {
Registry.getModes().stream()
.map(Mode::getLeaderboard)
.filter(lb -> lb != null)
.forEach(lb -> { lb.resetAll(); lb.write(true); });
send(sender, LoParkour.PREFIX + "Reset all high scores.");
return;
}
UUID uuid = resolveUUID(target);
String name = resolvePlayerName(target, uuid);
Registry.getModes().stream()
.map(Mode::getLeaderboard)
.filter(lb -> lb != null)
.forEach(lb -> { lb.remove(uuid); lb.write(true); });
send(sender, LoParkour.PREFIX + "Reset high score of " + name + ".");
}
// ── recoverinventory ───────────────────────────────────────────────────────
private void handleRecoverInventory(String target, CommandSender sender) {
if (!base.cooldown(sender, "recoverinventory", 2500)) return;
Player other = Bukkit.getPlayer(target);
if (other == null) { send(sender, LoParkour.PREFIX + "Player not online!"); return; }
new InventoryData(other).load(result -> {
if (result != null) {
send(sender, LoParkour.PREFIX + "Recovered inventory of " + other.getName() + ".");
} else {
send(sender, LoParkour.PREFIX + "<red>No saved inventory found for " + other.getName() + ".");
}
});
}
// ── helpers ────────────────────────────────────────────────────────────────
private @Nullable Player findNearest(CommandSender sender) {
Location from = sender instanceof Player p ? p.getLocation()
: sender instanceof BlockCommandSender b ? b.getBlock().getLocation()
: null;
if (from == null || from.getWorld() == null) return null;
return from.getWorld().getPlayers().stream()
.min((a, b) -> Double.compare(a.getLocation().distance(from), b.getLocation().distance(from)))
.orElse(null);
}
private UUID resolveUUID(String input) {
Player online = Bukkit.getPlayerExact(input);
if (online != null) return online.getUniqueId();
if (input.contains("-")) return UUID.fromString(input);
return Bukkit.getOfflinePlayer(input).getUniqueId();
}
private String resolvePlayerName(String input, UUID uuid) {
Player online = Bukkit.getPlayerExact(input);
if (online != null) return online.getName();
OfflinePlayer op = Bukkit.getOfflinePlayer(uuid);
return op.getName() != null ? op.getName() : input;
}
private void send(CommandSender sender, String msg) {
sender.sendMessage(ColorUtil.color(msg));
}
}

View file

@ -0,0 +1,121 @@
package dev.loki.loparkour.command;
import dev.loki.loparkour.LoParkour;
import dev.loki.loparkour.config.Config;
import dev.loki.loparkour.config.Locales;
import dev.loki.loparkour.menu.Menus;
import dev.loki.loparkour.menu.ParkourOption;
import dev.loki.loparkour.mode.Modes;
import dev.loki.loparkour.player.ParkourUser;
import dev.loki.loparkour.util.ColorUtil;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Handles all player-facing /parkour sub-commands.
* Admin commands are in {@link AdminCommandHandler}.
*/
public class PlayerCommandHandler {
private final Map<String, Long> cooldowns = new HashMap<>();
public void handleNoArgs(@NotNull CommandSender sender, @Nullable Player player) {
if (player != null && ParkourOption.MAIN.mayPerform(player)) {
Menus.MAIN.open(player);
} else {
sendHelp(sender);
}
}
public void handle(@NotNull String arg, @NotNull CommandSender sender, @Nullable Player player) {
switch (arg.toLowerCase()) {
case "help" -> sendHelp(sender);
case "reload" -> handleReload(sender, player);
}
if (player == null) return;
switch (arg.toLowerCase()) {
case "join" -> {
if (!cooldown(sender, "join", 2500)) return;
if (!ParkourOption.JOIN.mayPerform(player)) {
send(sender, Locales.getString(player, "other.no_do"));
return;
}
if (ParkourUser.getUser(player) != null) return;
Modes.DEFAULT.create(player);
}
case "leave" -> {
if (!cooldown(sender, "leave", 2500)) return;
ParkourUser.leave(player);
}
case "play" -> {
if (ParkourOption.PLAY.mayPerform(player)) Menus.PLAY.open(player);
}
case "menu", "main" -> {
if (ParkourOption.MAIN.mayPerform(player)) Menus.MAIN.open(player);
}
case "leaderboard" -> player.performCommand("LoParkour leaderboard invalid");
case "schematic" -> {
if (!player.hasPermission(ParkourOption.ADMIN.permission)) {
send(sender, Locales.getString(player, "other.no_do"));
return;
}
SchematicCommandHandler.sendHelp(player);
}
}
}
private void handleReload(@NotNull CommandSender sender, @Nullable Player player) {
if (!cooldown(sender, "reload", 2500)) return;
if (!sender.hasPermission(ParkourOption.ADMIN.permission)) {
send(sender, Locales.getString(player, "other.no_do"));
return;
}
Config.reload(false);
send(sender, LoParkour.PREFIX + "Reloaded config files.");
}
public void sendHelp(@NotNull CommandSender sender) {
send(sender, "");
send(sender, "<dark_gray><strikethrough>---------------<reset> " + LoParkour.NAME + " <dark_gray><strikethrough>---------------<reset>");
send(sender, "");
send(sender, "<gray>/parkour <dark_gray>- Main command");
if (sender.hasPermission(ParkourOption.JOIN.permission)) {
send(sender, "<gray>/parkour join [mode/player] <dark_gray>- Join a mode");
send(sender, "<gray>/parkour leave <dark_gray>- Leave parkour");
}
if (sender.hasPermission(ParkourOption.MAIN.permission))
send(sender, "<gray>/parkour menu <dark_gray>- Open the menu");
if (sender.hasPermission(ParkourOption.PLAY.permission))
send(sender, "<gray>/parkour play <dark_gray>- Mode selection menu");
if (sender.hasPermission(ParkourOption.LEADERBOARDS.permission))
send(sender, "<gray>/parkour leaderboard [type] <dark_gray>- Leaderboard");
if (sender.hasPermission(ParkourOption.ADMIN.permission)) {
send(sender, "<gray>/LoParkour schematic <dark_gray>- Schematic tools");
send(sender, "<gray>/LoParkour reload <dark_gray>- Reload configs");
send(sender, "<gray>/LoParkour reset <everyone/player> <dark_gray>- Reset scores");
send(sender, "<gray>/LoParkour forcejoin/forceleave <target> <dark_gray>- Force join/leave");
send(sender, "<gray>/LoParkour recoverinventory <player> <dark_gray>- Recover inventory");
}
send(sender, "");
}
public boolean cooldown(CommandSender sender, String key, long millis) {
String fullKey = sender.getName() + ":" + key;
long now = System.currentTimeMillis();
Long last = cooldowns.get(fullKey);
if (last != null && now - last < millis) return false;
cooldowns.put(fullKey, now);
return true;
}
private void send(CommandSender sender, String message) {
sender.sendMessage(ColorUtil.color(message));
}
}

View file

@ -0,0 +1,149 @@
package dev.loki.loparkour.command;
import dev.loki.loparkour.LoParkour;
import dev.loki.loparkour.schematic.lpschem.LPSchematic;
import dev.loki.loparkour.schematic.lpschem.LPSchematicBuilder;
import dev.loki.loparkour.util.ColorUtil;
import dev.loki.loparkour.util.Locations;
import dev.loki.loparkour.util.ParticleUtil;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.Material;
import org.bukkit.util.BoundingBox;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Handles /LoParkour schematic sub-commands (wand, pos1, pos2, save, paste, list, reload).
*/
public class SchematicCommandHandler {
public static final Map<Player, Location[]> selections = new HashMap<>();
private static final ItemStack WAND = buildWand();
private static ItemStack buildWand() {
ItemStack item = new ItemStack(Material.GOLDEN_AXE);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName(ColorUtil.color("<red><bold>LPSchematic Wand"));
meta.setLore(List.of(
ColorUtil.color("<gray>Left click: first position"),
ColorUtil.color("<gray>Right click: second position")
));
item.setItemMeta(meta);
}
return item;
}
public static ItemStack getWand() {
return WAND.clone();
}
public static void sendHelp(Player player) {
send(player, "");
send(player, "<dark_red><bold>Schematics <reset><dark_gray>-----------");
send(player, "<red>/LoParkour schematic wand <dark_gray>- <gray>Get the wand");
send(player, "<red>/LoParkour schematic pos1 <dark_gray>- <gray>Set position 1");
send(player, "<red>/LoParkour schematic pos2 <dark_gray>- <gray>Set position 2");
send(player, "<red>/LoParkour schematic save <n> <dark_gray>- <gray>Save selection");
send(player, "<red>/LoParkour schematic paste <n> <dark_gray>- <gray>Paste schematic");
send(player, "<red>/LoParkour schematic list <dark_gray>- <gray>List loaded schematics");
send(player, "<red>/LoParkour schematic reload <dark_gray>- <gray>Reload schematics");
send(player, "");
}
/** Called for 2-arg schematic commands (wand/pos1/pos2/list/reload) */
public static void handleSubcommand(String sub, CommandSender sender, Player player, PlayerCommandHandler cooldowns) {
Location playerLoc = player.getLocation();
Location[] sel = selections.get(player);
switch (sub.toLowerCase()) {
case "wand" -> {
player.getInventory().addItem(WAND.clone());
send(player, "<dark_red><bold>Schematics <reset><gray>Wand given. Left/Right click to set positions.");
}
case "pos1" -> setPos(player, sel, playerLoc, 0);
case "pos2" -> setPos(player, sel, playerLoc, 1);
case "list" -> {
send(player, "<dark_red><bold>Schematics <reset><gray>Loaded schematics:");
LoParkour.getSchematicManager().getAllSchematics().forEach((name, s) ->
send(player, "<gray>- <red>" + name + " <dark_gray>(" + s.getMetadata().getDifficulty() + ")"));
}
case "reload" -> {
if (!cooldowns.cooldown(sender, "schematic-reload", 2500)) return;
LoParkour.getSchematicManager().reload();
send(player, "<dark_red><bold>Schematics <reset><gray>Reloaded.");
}
}
}
/** Called for 3-arg schematic commands (save <n>, paste <n>) */
public static void handleSubcommandWithName(String sub, String name, CommandSender sender,
Player player, PlayerCommandHandler cooldowns) {
switch (sub.toLowerCase()) {
case "save" -> handleSave(name, sender, player, cooldowns);
case "paste" -> handlePaste(name, sender, player);
}
}
// ── internal ───────────────────────────────────────────────────────────────
private static void setPos(Player player, Location[] existing, Location loc, int index) {
String label = index == 0 ? "1" : "2";
send(player, LoParkour.PREFIX + "Position " + label + " set to " + Locations.toString(loc, true));
Location[] updated = existing != null ? existing.clone() : new Location[]{null, null};
updated[index] = loc;
selections.put(player, updated);
if (updated[0] != null && updated[1] != null) {
ParticleUtil.box(BoundingBox.of(updated[0], updated[1]), player.getWorld(), Particle.END_ROD, player, 0.2);
}
}
private static void handleSave(String name, CommandSender sender, Player player, PlayerCommandHandler cooldowns) {
if (!cooldowns.cooldown(sender, "schematic-save", 2500)) return;
Location[] sel = selections.get(player);
if (sel == null || sel[0] == null || sel[1] == null) {
send(player, "<dark_red><bold>Schematics <reset><red>Selection incomplete. Set both positions first.");
return;
}
try {
int w = Math.abs(sel[1].getBlockX() - sel[0].getBlockX()) + 1;
int h = Math.abs(sel[1].getBlockY() - sel[0].getBlockY()) + 1;
int l = Math.abs(sel[1].getBlockZ() - sel[0].getBlockZ()) + 1;
LPSchematic schematic = new LPSchematicBuilder(name, player.getName(), 0.5, w, h, l)
.fromWorld(player.getWorld(), sel[0], sel[1])
.build();
LoParkour.getSchematicManager().saveSchematic(schematic);
send(player, "<dark_red><bold>Schematics <reset><gray>Saved as <red>" + name + "<gray>!");
} catch (Exception e) {
send(player, "<dark_red><bold>Schematics <reset><red>Failed: " + e.getMessage());
}
}
private static void handlePaste(String name, CommandSender sender, Player player) {
LPSchematic s = LoParkour.getSchematicManager().getSchematic(name);
if (s == null) {
send(sender, LoParkour.PREFIX + "Schematic '" + name + "' not found.");
return;
}
s.paste(player.getLocation(), player.getWorld());
send(sender, LoParkour.PREFIX + "Pasted " + name + ".");
}
private static void send(CommandSender sender, String msg) {
sender.sendMessage(ColorUtil.color(msg));
}
}