Add commands to set teleport and spawn locations from player position

New commands:
- /lm setteleport <mine> - sets teleport location to current player position
- /lm setspawn <mine> - sets safe spawn location for stuck players
- /lm clearspawn <mine> - removes spawn location, falls back to teleport

Features:
- All commands use player's current location (X, Y, Z, yaw, pitch, world)
- Auto-completion for mine names
- Separate permissions: lomines.admin.setteleport, lomines.admin.setspawn
- Permission lomines.admin includes all new permissions

Updates:
- AdminCommands.java - added three new command handlers
- Mines.java - added getFileManager() getter
- MineFileManager.java - added saveConfig() method
- LoMinesTabCompleter.java - added tab completion for new commands
- plugin.yml - added new permissions
- MineEditGui.java - shows spawn status and command hints in teleport item

Usage example:
1. Stand where you want players to teleport
2. Type: /lm setteleport mymine
3. Stand where stuck players should spawn
4. Type: /lm setspawn mymine
This commit is contained in:
loki5512344 2026-05-31 23:11:20 +02:00
parent ed294bb78a
commit cfaf638abf
6 changed files with 172 additions and 5 deletions

View file

@ -3,6 +3,9 @@ package dev.loki.lomines.command;
import dev.loki.lomines.LoMinesPlugin; import dev.loki.lomines.LoMinesPlugin;
import dev.loki.lomines.core.Mine; import dev.loki.lomines.core.Mine;
import dev.loki.lomines.core.Mines; import dev.loki.lomines.core.Mines;
import dev.loki.lomines.data.config.MineConfig;
import dev.loki.lomines.data.config.spawn.PlayerSpawnConfig;
import dev.loki.lomines.data.config.teleport.TeleportConfig;
import dev.loki.lomines.gui.MineEditGui; import dev.loki.lomines.gui.MineEditGui;
import dev.loki.lomines.util.ErrorHandler; import dev.loki.lomines.util.ErrorHandler;
import dev.loki.lomines.util.MessageFormatter; import dev.loki.lomines.util.MessageFormatter;
@ -11,6 +14,7 @@ import dev.lolib.commands.annotation.Command;
import dev.lolib.commands.annotation.Subcommand; import dev.lolib.commands.annotation.Subcommand;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -152,6 +156,133 @@ public class AdminCommands {
} }
} }
/**
* Sets the teleport location for players when mine resets.
* Usage: /lm setteleport <mine>
*/
@Subcommand(value = "setteleport", permission = "lomines.admin.setteleport")
public void setTeleport(CommandSender sender, @Arg("mine") String mineName) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
return;
}
try {
Mine mine = mines.find(mineName).orElse(null);
if (mine == null) {
errorHandler.handleNotFound(sender, "Mine", mineName);
return;
}
// Get current player location and update config
var config = mine.getConfig();
var newTeleport = TeleportConfig.at(player.getLocation());
var updatedConfig = MineConfig.builder(mineName)
.region(config.region())
.blocks(config.blocks())
.reset(config.reset())
.rewards(config.rewards())
.teleport(newTeleport)
.ui(config.ui())
.worldGuard(config.worldGuard())
.playerSpawn(config.playerSpawn())
.build();
plugin.getMines().getFileManager().saveConfig(updatedConfig);
sender.sendMessage(MessageFormatter.success("Teleport location for mine '" + mineName + "' set to your current position"));
sender.sendMessage(Component.text("Location: " + formatLocation(player.getLocation()), NamedTextColor.GRAY));
} catch (Exception e) {
errorHandler.handleError(sender, "Failed to set teleport: " + e.getMessage(),
"Failed to set teleport for " + mineName, e);
}
}
/**
* Sets the safe spawn location for stuck players after mine reset.
* Usage: /lm setspawn <mine>
*/
@Subcommand(value = "setspawn", permission = "lomines.admin.setspawn")
public void setSpawn(CommandSender sender, @Arg("mine") String mineName) {
if (!(sender instanceof Player player)) {
sender.sendMessage(Component.text("This command can only be used by players!", NamedTextColor.RED));
return;
}
try {
Mine mine = mines.find(mineName).orElse(null);
if (mine == null) {
errorHandler.handleNotFound(sender, "Mine", mineName);
return;
}
// Get current player location and update config
var config = mine.getConfig();
var newSpawn = PlayerSpawnConfig.at(player.getLocation());
var updatedConfig = MineConfig.builder(mineName)
.region(config.region())
.blocks(config.blocks())
.reset(config.reset())
.rewards(config.rewards())
.teleport(config.teleport())
.ui(config.ui())
.worldGuard(config.worldGuard())
.playerSpawn(newSpawn)
.build();
plugin.getMines().getFileManager().saveConfig(updatedConfig);
sender.sendMessage(MessageFormatter.success("Safe spawn location for mine '" + mineName + "' set to your current position"));
sender.sendMessage(Component.text("Location: " + formatLocation(player.getLocation()), NamedTextColor.GRAY));
sender.sendMessage(Component.text("Players stuck in blocks will be teleported here after reset", NamedTextColor.YELLOW));
} catch (Exception e) {
errorHandler.handleError(sender, "Failed to set spawn: " + e.getMessage(),
"Failed to set spawn for " + mineName, e);
}
}
/**
* Clears the spawn location for stuck players.
* Usage: /lm clearspawn <mine>
*/
@Subcommand(value = "clearspawn", permission = "lomines.admin.setspawn")
public void clearSpawn(CommandSender sender, @Arg("mine") String mineName) {
try {
Mine mine = mines.find(mineName).orElse(null);
if (mine == null) {
errorHandler.handleNotFound(sender, "Mine", mineName);
return;
}
var config = mine.getConfig();
var updatedConfig = MineConfig.builder(mineName)
.region(config.region())
.blocks(config.blocks())
.reset(config.reset())
.rewards(config.rewards())
.teleport(config.teleport())
.ui(config.ui())
.worldGuard(config.worldGuard())
.playerSpawn(PlayerSpawnConfig.disabled())
.build();
plugin.getMines().getFileManager().saveConfig(updatedConfig);
sender.sendMessage(MessageFormatter.success("Spawn location for mine '" + mineName + "' cleared"));
sender.sendMessage(Component.text("Players will use teleport location instead", NamedTextColor.GRAY));
} catch (Exception e) {
errorHandler.handleError(sender, "Failed to clear spawn: " + e.getMessage(),
"Failed to clear spawn for " + mineName, e);
}
}
/**
* Formats location for display.
*/
private String formatLocation(Location loc) {
return String.format("%.1f, %.1f, %.1f in %s",
loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName());
}
/** /**
* Formats mine status information for display. * Formats mine status information for display.
*/ */

View file

@ -20,7 +20,8 @@ public class LoMinesTabCompleter implements TabCompleter {
// Static command lists for fast lookup // Static command lists for fast lookup
private static final List<String> ADMIN_COMMANDS = List.of( private static final List<String> ADMIN_COMMANDS = List.of(
"create", "delete", "reset", "reload", "list", "maskscan" "create", "delete", "reset", "reload", "list", "maskscan",
"edit", "setteleport", "setspawn", "clearspawn"
); );
private static final List<String> PLAYER_COMMANDS = List.of( private static final List<String> PLAYER_COMMANDS = List.of(
@ -29,7 +30,8 @@ public class LoMinesTabCompleter implements TabCompleter {
private static final List<String> ALL_COMMANDS = List.of( private static final List<String> ALL_COMMANDS = List.of(
"create", "delete", "reset", "reload", "list", "wand", "group", "create", "delete", "reset", "reload", "list", "wand", "group",
"stats", "top", "maskscan", "help" "stats", "top", "maskscan", "edit", "setteleport", "setspawn",
"clearspawn", "help"
); );
public LoMinesTabCompleter(LoMinesPlugin plugin) { public LoMinesTabCompleter(LoMinesPlugin plugin) {
@ -65,7 +67,7 @@ public class LoMinesTabCompleter implements TabCompleter {
String partial = args[1].toLowerCase(); String partial = args[1].toLowerCase();
switch (subcommand) { switch (subcommand) {
case "delete", "reset", "maskscan" -> case "delete", "reset", "maskscan", "edit", "setteleport", "setspawn", "clearspawn" ->
completions.addAll(getMineNames(partial)); completions.addAll(getMineNames(partial));
case "create" -> { case "create" -> {
if (partial.isEmpty()) { if (partial.isEmpty()) {
@ -106,8 +108,10 @@ public class LoMinesTabCompleter implements TabCompleter {
private boolean hasPermission(CommandSender sender, String command) { private boolean hasPermission(CommandSender sender, String command) {
return switch (command) { return switch (command) {
case "create", "delete", "reset", "reload", "list", "maskscan" -> case "create", "delete", "reset", "reload", "list", "maskscan", "edit" ->
sender.hasPermission("lomines.admin"); sender.hasPermission("lomines.admin");
case "setteleport" -> sender.hasPermission("lomines.admin.setteleport");
case "setspawn", "clearspawn" -> sender.hasPermission("lomines.admin.setspawn");
case "wand", "group" -> sender.hasPermission("lomines.admin.wand"); case "wand", "group" -> sender.hasPermission("lomines.admin.wand");
case "stats" -> sender.hasPermission("lomines.stats"); case "stats" -> sender.hasPermission("lomines.stats");
case "top" -> sender.hasPermission("lomines.stats"); case "top" -> sender.hasPermission("lomines.stats");

View file

@ -198,4 +198,11 @@ public final class Mines {
public WorldGuardRegionService getWorldGuardService() { public WorldGuardRegionService getWorldGuardService() {
return worldGuardService; return worldGuardService;
} }
/**
* Returns the file manager for saving mine configurations.
*/
public MineFileManager getFileManager() {
return fileManager;
}
} }

View file

@ -134,6 +134,16 @@ public record MineFileManager(Path minesFolder, ConfigLoader configLoader) {
} }
} }
/**
* Saves a mine configuration to file.
*
* @param config the configuration to save
* @throws ConfigLoader.ConfigLoadException if saving fails
*/
public void saveConfig(MineConfig config) throws ConfigLoader.ConfigLoadException {
configLoader.save(config);
}
/** /**
* Saves mask positions for mask fill mode. * Saves mask positions for mask fill mode.
*/ */

View file

@ -184,10 +184,17 @@ public final class MineEditGui {
if (meta != null) { if (meta != null) {
meta.displayName(Component.text("§aТелепорт", NamedTextColor.GREEN)); meta.displayName(Component.text("§aТелепорт", NamedTextColor.GREEN));
boolean enabled = config.teleport().enabled(); boolean enabled = config.teleport().enabled();
boolean spawnEnabled = config.playerSpawn().enabled();
meta.lore(List.of( meta.lore(List.of(
Component.text("Включено: " + (enabled ? "§aДа" : "§cНет"), NamedTextColor.GRAY), Component.text("Включено: " + (enabled ? "§aДа" : "§cНет"), NamedTextColor.GRAY),
Component.text("Точка спавна (застрявших): " + (spawnEnabled ? "§aДа" : "§7Нет"), NamedTextColor.GRAY),
Component.empty(), Component.empty(),
Component.text("§eНажмите для редактирования", NamedTextColor.YELLOW) Component.text("§eНажмите для редактирования", NamedTextColor.YELLOW),
Component.empty(),
Component.text("§7Команды:", NamedTextColor.DARK_GRAY),
Component.text("§7/lm setteleport <mine>", NamedTextColor.GRAY),
Component.text("§7/lm setspawn <mine>", NamedTextColor.GRAY),
Component.text("§7/lm clearspawn <mine>", NamedTextColor.GRAY)
)); ));
stack.setItemMeta(meta); stack.setItemMeta(meta);
} }

View file

@ -28,6 +28,8 @@ permissions:
lomines.admin.wand: true lomines.admin.wand: true
lomines.admin.maskscan: true lomines.admin.maskscan: true
lomines.admin.edit: true lomines.admin.edit: true
lomines.admin.setteleport: true
lomines.admin.setspawn: true
lomines.stats: lomines.stats:
description: View mining stats and leaderboards (/lm stats, /lm top) description: View mining stats and leaderboards (/lm stats, /lm top)
default: true default: true
@ -46,3 +48,9 @@ permissions:
lomines.use: lomines.use:
description: Basic LoMines usage description: Basic LoMines usage
default: true default: true
lomines.admin.setteleport:
description: Set teleport location for mine reset (/lm setteleport <mine>)
default: op
lomines.admin.setspawn:
description: Set safe spawn location for stuck players (/lm setspawn <mine>, /lm clearspawn <mine>)
default: op