chore: rewrite ARCHITECTURE, enable tests via MockBukkit, checkstyle for tests, cleanup bin/libs
This commit is contained in:
parent
a0c1ab8ca1
commit
e8d516020c
53 changed files with 1093 additions and 1410 deletions
|
|
@ -3,6 +3,7 @@ package dev.loki.lomines.command.admin.info;
|
|||
import dev.loki.lomines.LoMinesPlugin;
|
||||
import dev.loki.lomines.core.mine.model.Mine;
|
||||
import dev.loki.lomines.data.config.block.BlockKey;
|
||||
import dev.loki.lomines.data.config.model.MineConfig;
|
||||
import dev.loki.lomines.util.location.geo.Cuboid;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
|
|
@ -40,8 +41,16 @@ public class InfoCommand {
|
|||
sender.sendMessage(Component.text("§7Блоков: §f" + mine.getBlocks() + "§7/§f" + mine.getTotalVolume() +
|
||||
" §7(§f" + String.format("%.1f", mine.getPercentFilled()) + "%§7)"));
|
||||
|
||||
printRegions(sender, mine.getRegions());
|
||||
printBlocks(sender, config.blocks().weights());
|
||||
printResetAndTeleport(sender, config);
|
||||
|
||||
sender.sendMessage(Component.text("§6§lНаграды: §f" + config.rewards().entries().size() + " записей"));
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
}
|
||||
|
||||
private void printRegions(CommandSender sender, List<Cuboid> regions) {
|
||||
sender.sendMessage(Component.text("§6§lРегионы:"));
|
||||
List<Cuboid> regions = mine.getRegions();
|
||||
for (int i = 0; i < regions.size(); i++) {
|
||||
Cuboid r = regions.get(i);
|
||||
sender.sendMessage(Component.text("§7 #" + (i + 1) + ": §f" +
|
||||
|
|
@ -50,8 +59,10 @@ public class InfoCommand {
|
|||
" §7(§f" + r.getVolume() + " §7блоков)"));
|
||||
}
|
||||
sender.sendMessage(Component.text(""));
|
||||
}
|
||||
|
||||
private void printBlocks(CommandSender sender, Map<BlockKey, Double> weights) {
|
||||
sender.sendMessage(Component.text("§6§lБлоки:"));
|
||||
Map<BlockKey, Double> weights = config.blocks().weights();
|
||||
weights.entrySet().stream()
|
||||
.sorted((a, b) -> Double.compare(b.getValue(), a.getValue()))
|
||||
.limit(10)
|
||||
|
|
@ -60,7 +71,9 @@ public class InfoCommand {
|
|||
if (weights.size() > 10) {
|
||||
sender.sendMessage(Component.text("§7 ... и ещё " + (weights.size() - 10) + " блоков"));
|
||||
}
|
||||
}
|
||||
|
||||
private void printResetAndTeleport(CommandSender sender, MineConfig config) {
|
||||
sender.sendMessage(Component.text("§6§lНастройки сброса:"));
|
||||
sender.sendMessage(Component.text("§7 Интервал: §f" + config.reset().intervalDisplay()));
|
||||
sender.sendMessage(Component.text("§7 Триггер по %: §f" +
|
||||
|
|
@ -71,9 +84,6 @@ public class InfoCommand {
|
|||
(config.teleport().enabled() ? "§aустановлена" : "§7не задана")));
|
||||
sender.sendMessage(Component.text("§7 Точка спавна: §f" +
|
||||
(config.playerSpawn().enabled() ? "§aустановлена" : "§7используется точка телепорта")));
|
||||
|
||||
sender.sendMessage(Component.text("§6§lНаграды: §f" + config.rewards().entries().size() + " записей"));
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
}
|
||||
|
||||
private String formatBlockKey(BlockKey key) {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,20 @@ public final class RegionActionHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
Cuboid newRegion = resolveNewRegion(player, mine);
|
||||
if (newRegion == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Cuboid> currentRegions = new ArrayList<>(mine.getRegions());
|
||||
currentRegions.add(newRegion);
|
||||
|
||||
updateMineRegions(mine, new RegionConfig(currentRegions));
|
||||
|
||||
sendRegionAdded(player, newRegion, currentRegions.size());
|
||||
}
|
||||
|
||||
private Cuboid resolveNewRegion(Player player, Mine mine) {
|
||||
var wandManager = plugin.getGroupWandManager();
|
||||
var session = wandManager.getSession(player.getUniqueId());
|
||||
int slot = session.getActiveSlot();
|
||||
|
|
@ -40,7 +54,7 @@ public final class RegionActionHandler {
|
|||
var pos2 = session.getPos2(slot);
|
||||
if (pos1 == null || pos2 == null) {
|
||||
player.sendMessage(Component.text("§cВыделите регион палочкой! ЛКМ - 1-я точка, ПКМ - 2-я точка"));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
Cuboid newRegion = new Cuboid(pos1, pos2);
|
||||
String mineWorld = mine.getConfig().region().worldName();
|
||||
|
|
@ -48,21 +62,17 @@ public final class RegionActionHandler {
|
|||
|
||||
if (!mineWorld.equals(regionWorld)) {
|
||||
player.sendMessage(Component.text("§cРегион должен быть в мире §f" + mineWorld + "§c, а не §f" + regionWorld));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
session.clearCorners(slot);
|
||||
return newRegion;
|
||||
}
|
||||
|
||||
List<Cuboid> currentRegions = new ArrayList<>(mine.getRegions());
|
||||
currentRegions.add(newRegion);
|
||||
|
||||
var newRegionConfig = new RegionConfig(currentRegions);
|
||||
updateMineRegions(mine, newRegionConfig);
|
||||
|
||||
player.sendMessage(Component.text("§aДобавлен регион #" + currentRegions.size()));
|
||||
private void sendRegionAdded(Player player, Cuboid newRegion, int count) {
|
||||
player.sendMessage(Component.text("§aДобавлен регион #" + count));
|
||||
player.sendMessage(Component.text("§7От: §f" + newRegion.getMinX() + ", " + newRegion.getMinY() + ", " + newRegion.getMinZ()));
|
||||
player.sendMessage(Component.text("§7До: §f" + newRegion.getMaxX() + ", " + newRegion.getMaxY() + ", " + newRegion.getMaxZ()));
|
||||
player.sendMessage(Component.text("§7Объём: §f" + newRegion.getVolume() + " §7блоков"));
|
||||
|
||||
session.clearCorners(slot);
|
||||
}
|
||||
|
||||
public void removeRegion(CommandSender sender, String mineName, int index) {
|
||||
|
|
|
|||
|
|
@ -21,53 +21,59 @@ public class RegionCommands {
|
|||
|
||||
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||
switch (subcommand.toLowerCase()) {
|
||||
case "regions" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm regions <mine>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
String mineName = args[0];
|
||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||
if (mine == null) {
|
||||
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
List<Cuboid> regions = mine.getRegions();
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
sender.sendMessage(Component.text("§6§lРегионы шахты §f" + mine.getName()));
|
||||
sender.sendMessage(Component.text("§7Всего регионов: §f" + regions.size()));
|
||||
sender.sendMessage(Component.text(""));
|
||||
|
||||
for (int i = 0; i < regions.size(); i++) {
|
||||
Cuboid r = regions.get(i);
|
||||
sender.sendMessage(Component.text("§e#" + (i + 1) + " §7(§f" + r.getVolume() + " §7блоков)"));
|
||||
sender.sendMessage(Component.text(" §7Мир: §f" + r.getWorld().getName()));
|
||||
sender.sendMessage(Component.text(" §7От: §f" + r.getMinX() + ", " + r.getMinY() + ", " + r.getMinZ()));
|
||||
sender.sendMessage(Component.text(" §7До: §f" + r.getMaxX() + ", " + r.getMaxY() + ", " + r.getMaxZ()));
|
||||
}
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
}
|
||||
case "addregion" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm addregion <mine>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
actionHandler.addRegion(sender, args[0]);
|
||||
}
|
||||
case "removeregion" -> {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(Component.text("Usage: /lm removeregion <mine> <index>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int index = Integer.parseInt(args[1]);
|
||||
actionHandler.removeRegion(sender, args[0], index);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(Component.text("Индекс должен быть числом: " + args[1], NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
case "regions" -> handleRegions(sender, args);
|
||||
case "addregion" -> handleAddRegion(sender, args);
|
||||
case "removeregion" -> handleRemoveRegion(sender, args);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRegions(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm regions <mine>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
String mineName = args[0];
|
||||
Mine mine = plugin.getMines().find(mineName).orElse(null);
|
||||
if (mine == null) {
|
||||
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
List<Cuboid> regions = mine.getRegions();
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
sender.sendMessage(Component.text("§6§lРегионы шахты §f" + mine.getName()));
|
||||
sender.sendMessage(Component.text("§7Всего регионов: §f" + regions.size()));
|
||||
sender.sendMessage(Component.text(""));
|
||||
|
||||
for (int i = 0; i < regions.size(); i++) {
|
||||
Cuboid r = regions.get(i);
|
||||
sender.sendMessage(Component.text("§e#" + (i + 1) + " §7(§f" + r.getVolume() + " §7блоков)"));
|
||||
sender.sendMessage(Component.text(" §7Мир: §f" + r.getWorld().getName()));
|
||||
sender.sendMessage(Component.text(" §7От: §f" + r.getMinX() + ", " + r.getMinY() + ", " + r.getMinZ()));
|
||||
sender.sendMessage(Component.text(" §7До: §f" + r.getMaxX() + ", " + r.getMaxY() + ", " + r.getMaxZ()));
|
||||
}
|
||||
sender.sendMessage(Component.text("§8§m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));
|
||||
}
|
||||
|
||||
private void handleAddRegion(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm addregion <mine>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
actionHandler.addRegion(sender, args[0]);
|
||||
}
|
||||
|
||||
private void handleRemoveRegion(CommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(Component.text("Usage: /lm removeregion <mine> <index>", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int index = Integer.parseInt(args[1]);
|
||||
actionHandler.removeRegion(sender, args[0], index);
|
||||
} catch (NumberFormatException e) {
|
||||
sender.sendMessage(Component.text("Индекс должен быть числом: " + args[1], NamedTextColor.RED));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,52 +25,62 @@ public class AdminCommands {
|
|||
|
||||
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||
switch (subcommand.toLowerCase()) {
|
||||
case "create" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm create <name>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.create(sender, args[0]);
|
||||
}
|
||||
case "delete" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm delete <name>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.delete(sender, args[0]);
|
||||
}
|
||||
case "edit" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm edit <mine>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.edit(sender, args[0]);
|
||||
}
|
||||
case "reset" -> {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm reset <mine> [silent]"));
|
||||
return;
|
||||
}
|
||||
boolean silent = args.length > 1 && (args[1].equalsIgnoreCase("true") || args[1].equalsIgnoreCase("silent"));
|
||||
actionHandler.reset(sender, args[0], silent);
|
||||
}
|
||||
case "create" -> handleCreate(sender, args);
|
||||
case "delete" -> handleDelete(sender, args);
|
||||
case "edit" -> handleEdit(sender, args);
|
||||
case "reset" -> handleReset(sender, args);
|
||||
case "reload" -> actionHandler.reload(sender);
|
||||
case "list" -> {
|
||||
Collection<Mine> allMines = mines.getAll();
|
||||
if (allMines.isEmpty()) {
|
||||
sender.sendMessage(Component.text("No mines found"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Component.text("=== Mines (" + allMines.size() + ") ==="));
|
||||
for (Mine mine : allMines) {
|
||||
String status = formatMineStatus(mine);
|
||||
sender.sendMessage(Component.text("- " + mine.getName() + " " + status));
|
||||
}
|
||||
}
|
||||
case "list" -> handleList(sender);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCreate(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm create <name>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.create(sender, args[0]);
|
||||
}
|
||||
|
||||
private void handleDelete(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm delete <name>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.delete(sender, args[0]);
|
||||
}
|
||||
|
||||
private void handleEdit(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm edit <mine>"));
|
||||
return;
|
||||
}
|
||||
actionHandler.edit(sender, args[0]);
|
||||
}
|
||||
|
||||
private void handleReset(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
sender.sendMessage(Component.text("Usage: /lm reset <mine> [silent]"));
|
||||
return;
|
||||
}
|
||||
boolean silent = args.length > 1 && (args[1].equalsIgnoreCase("true") || args[1].equalsIgnoreCase("silent"));
|
||||
actionHandler.reset(sender, args[0], silent);
|
||||
}
|
||||
|
||||
private void handleList(CommandSender sender) {
|
||||
Collection<Mine> allMines = mines.getAll();
|
||||
if (allMines.isEmpty()) {
|
||||
sender.sendMessage(Component.text("No mines found"));
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Component.text("=== Mines (" + allMines.size() + ") ==="));
|
||||
for (Mine mine : allMines) {
|
||||
String status = formatMineStatus(mine);
|
||||
sender.sendMessage(Component.text("- " + mine.getName() + " " + status));
|
||||
}
|
||||
}
|
||||
|
||||
private String formatMineStatus(Mine mine) {
|
||||
int blocks = mine.getBlocks();
|
||||
int total = mine.getTotalVolume();
|
||||
|
|
|
|||
|
|
@ -23,27 +23,51 @@ public class CopyCommand {
|
|||
String fromMineName = args[0];
|
||||
String toMineName = args[1];
|
||||
|
||||
MinePair pair = resolveMines(sender, fromMineName, toMineName);
|
||||
if (pair == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var sourceConfig = pair.fromMine().getConfig();
|
||||
|
||||
MineConfig newTargetConfig = buildCopiedConfig(pair.toMine(), sourceConfig);
|
||||
|
||||
try {
|
||||
plugin.getMines().updateMineConfig(pair.toMine().getName(), newTargetConfig);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(Component.text("Ошибка сохранения: " + e.getMessage(), NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
sendCopySummary(sender, pair.fromMine(), pair.toMine(), sourceConfig);
|
||||
}
|
||||
|
||||
private record MinePair(Mine fromMine, Mine toMine) {
|
||||
}
|
||||
|
||||
private MinePair resolveMines(CommandSender sender, String fromMineName, String toMineName) {
|
||||
Mine fromMine = plugin.getMines().find(fromMineName).orElse(null);
|
||||
if (fromMine == null) {
|
||||
sender.sendMessage(Component.text("Исходная шахта не найдена: " + fromMineName, NamedTextColor.RED));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
Mine toMine = plugin.getMines().find(toMineName).orElse(null);
|
||||
if (toMine == null) {
|
||||
sender.sendMessage(Component.text("Целевая шахта не найдена: " + toMineName, NamedTextColor.RED));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (fromMine.getName().equalsIgnoreCase(toMine.getName())) {
|
||||
sender.sendMessage(Component.text("Нельзя копировать шахту саму в себя!", NamedTextColor.RED));
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
return new MinePair(fromMine, toMine);
|
||||
}
|
||||
|
||||
var sourceConfig = fromMine.getConfig();
|
||||
private MineConfig buildCopiedConfig(Mine toMine, MineConfig sourceConfig) {
|
||||
var targetConfig = toMine.getConfig();
|
||||
|
||||
MineConfig newTargetConfig = MineConfig.builder(toMine.getName())
|
||||
return MineConfig.builder(toMine.getName())
|
||||
.region(targetConfig.region())
|
||||
.blocks(sourceConfig.blocks())
|
||||
.reset(sourceConfig.reset())
|
||||
|
|
@ -53,14 +77,9 @@ public class CopyCommand {
|
|||
.worldGuard(sourceConfig.worldGuard())
|
||||
.playerSpawn(sourceConfig.playerSpawn())
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
plugin.getMines().updateMineConfig(toMine.getName(), newTargetConfig);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(Component.text("Ошибка сохранения: " + e.getMessage(), NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
private void sendCopySummary(CommandSender sender, Mine fromMine, Mine toMine, MineConfig sourceConfig) {
|
||||
sender.sendMessage(Component.text("§aКонфигурация скопирована из §f" + fromMine.getName() +
|
||||
" §aв §f" + toMine.getName()));
|
||||
sender.sendMessage(Component.text("§7Скопировано:"));
|
||||
|
|
|
|||
|
|
@ -40,18 +40,7 @@ public final class TeleportActionHandler {
|
|||
}
|
||||
|
||||
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();
|
||||
var updatedConfig = withTeleport(config, TeleportConfig.at(player.getLocation()));
|
||||
|
||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||
sender.sendMessage(MessageFormatter.success("Teleport location for mine '" + mineName + "' set to your current position"));
|
||||
|
|
@ -76,18 +65,7 @@ public final class TeleportActionHandler {
|
|||
}
|
||||
|
||||
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();
|
||||
var updatedConfig = withPlayerSpawn(config, PlayerSpawnConfig.at(player.getLocation()));
|
||||
|
||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||
sender.sendMessage(MessageFormatter.success("Safe spawn location for mine '" + mineName + "' set to your current position"));
|
||||
|
|
@ -108,16 +86,7 @@ public final class TeleportActionHandler {
|
|||
}
|
||||
|
||||
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();
|
||||
var updatedConfig = withPlayerSpawn(config, PlayerSpawnConfig.disabled());
|
||||
|
||||
plugin.getMines().getFileManager().saveConfig(updatedConfig);
|
||||
sender.sendMessage(MessageFormatter.success("Spawn location for mine '" + mineName + "' cleared"));
|
||||
|
|
@ -128,6 +97,32 @@ public final class TeleportActionHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private MineConfig withTeleport(MineConfig config, TeleportConfig teleport) {
|
||||
return MineConfig.builder(config.name())
|
||||
.region(config.region())
|
||||
.blocks(config.blocks())
|
||||
.reset(config.reset())
|
||||
.rewards(config.rewards())
|
||||
.teleport(teleport)
|
||||
.ui(config.ui())
|
||||
.worldGuard(config.worldGuard())
|
||||
.playerSpawn(config.playerSpawn())
|
||||
.build();
|
||||
}
|
||||
|
||||
private MineConfig withPlayerSpawn(MineConfig config, PlayerSpawnConfig spawn) {
|
||||
return MineConfig.builder(config.name())
|
||||
.region(config.region())
|
||||
.blocks(config.blocks())
|
||||
.reset(config.reset())
|
||||
.rewards(config.rewards())
|
||||
.teleport(config.teleport())
|
||||
.ui(config.ui())
|
||||
.worldGuard(config.worldGuard())
|
||||
.playerSpawn(spawn)
|
||||
.build();
|
||||
}
|
||||
|
||||
public String formatLocation(Location loc) {
|
||||
return String.format("%.1f, %.1f, %.1f in %s",
|
||||
loc.getX(), loc.getY(), loc.getZ(), loc.getWorld().getName());
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package dev.loki.lomines.command.admin.stats;
|
|||
import dev.loki.lomines.LoMinesPlugin;
|
||||
import dev.loki.lomines.core.mine.model.Mine;
|
||||
import dev.loki.lomines.core.mine.registry.Mines;
|
||||
import dev.loki.lomines.data.stats.model.Leaderboard;
|
||||
import dev.loki.lomines.data.stats.model.LeaderboardEntry;
|
||||
import dev.loki.lomines.util.ErrorHandler;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
|
@ -77,43 +76,46 @@ public final class LeaderboardRenderer {
|
|||
}
|
||||
|
||||
public static void renderTopCommand(CommandSender sender, String arg1, String arg2, Mines mines, LoMinesPlugin plugin) {
|
||||
String a = arg1 == null ? "" : arg1.trim();
|
||||
String b = arg2 == null ? "" : arg2.trim();
|
||||
int limit;
|
||||
String mineName;
|
||||
if (a.isEmpty()) {
|
||||
limit = clampLimit(10);
|
||||
mineName = null;
|
||||
} else if (b.isEmpty()) {
|
||||
Integer asLimit = tryParsePositiveInt(a);
|
||||
if (asLimit != null) {
|
||||
limit = clampLimit(asLimit);
|
||||
mineName = null;
|
||||
} else {
|
||||
limit = clampLimit(10);
|
||||
mineName = a;
|
||||
}
|
||||
} else {
|
||||
mineName = a;
|
||||
Integer limParsed = tryParsePositiveInt(b);
|
||||
limit = clampLimit(limParsed != null ? limParsed : 10);
|
||||
}
|
||||
Leaderboard lb = plugin.getStatsManager().getLeaderboard();
|
||||
if (mineName != null) {
|
||||
var found = mines.find(mineName);
|
||||
if (found.isEmpty()) {
|
||||
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
Mine m = found.get();
|
||||
String key = m.getName();
|
||||
List<LeaderboardEntry> rows = lb.getTopByMine(key, limit);
|
||||
sender.sendMessage(Component.text("=== Топ по шахте \u00ab" + key + "\u00bb ===", NamedTextColor.GOLD));
|
||||
sendTopRows(sender, rows);
|
||||
TopRequest req = parseTopRequest(arg1, arg2);
|
||||
if (req.mineName() != null) {
|
||||
renderMineTop(sender, req.mineName(), req.limit(), mines, plugin);
|
||||
return;
|
||||
}
|
||||
List<LeaderboardEntry> rows = lb.getTopTotal(limit);
|
||||
List<LeaderboardEntry> rows = plugin.getStatsManager().getLeaderboard().getTopTotal(req.limit());
|
||||
sender.sendMessage(Component.text("=== Общий топ ===", NamedTextColor.GOLD));
|
||||
sendTopRows(sender, rows);
|
||||
}
|
||||
|
||||
private record TopRequest(int limit, String mineName) {
|
||||
}
|
||||
|
||||
private static TopRequest parseTopRequest(String arg1, String arg2) {
|
||||
String a = arg1 == null ? "" : arg1.trim();
|
||||
String b = arg2 == null ? "" : arg2.trim();
|
||||
if (a.isEmpty()) {
|
||||
return new TopRequest(clampLimit(10), null);
|
||||
}
|
||||
if (b.isEmpty()) {
|
||||
Integer asLimit = tryParsePositiveInt(a);
|
||||
if (asLimit != null) {
|
||||
return new TopRequest(clampLimit(asLimit), null);
|
||||
}
|
||||
return new TopRequest(clampLimit(10), a);
|
||||
}
|
||||
Integer limParsed = tryParsePositiveInt(b);
|
||||
return new TopRequest(clampLimit(limParsed != null ? limParsed : 10), a);
|
||||
}
|
||||
|
||||
private static void renderMineTop(CommandSender sender, String mineName, int limit, Mines mines, LoMinesPlugin plugin) {
|
||||
var found = mines.find(mineName);
|
||||
if (found.isEmpty()) {
|
||||
sender.sendMessage(Component.text("Шахта не найдена: " + mineName, NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
Mine m = found.get();
|
||||
String key = m.getName();
|
||||
List<LeaderboardEntry> rows = plugin.getStatsManager().getLeaderboard().getTopByMine(key, limit);
|
||||
sender.sendMessage(Component.text("=== Топ по шахте \u00ab" + key + "\u00bb ===", NamedTextColor.GOLD));
|
||||
sendTopRows(sender, rows);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,36 +26,7 @@ public class StatsCommands {
|
|||
|
||||
public void handle(CommandSender sender, String subcommand, String[] args) {
|
||||
switch (subcommand.toLowerCase()) {
|
||||
case "stats" -> {
|
||||
if (isStatsDisabled(sender)) {
|
||||
return;
|
||||
}
|
||||
String targetName = args.length > 0 ? args[0] : "";
|
||||
UUID uuid = LeaderboardRenderer.resolvePlayerUuid(sender, targetName, errorHandler);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StatsManager sm = plugin.getStatsManager();
|
||||
PlayerStats ps = sm.getOrCreate(uuid);
|
||||
String displayName = LeaderboardRenderer.formatPlayerName(uuid);
|
||||
sender.sendMessage(Component.text("=== Статистика: " + displayName + " ===", NamedTextColor.GOLD));
|
||||
sender.sendMessage(Component.text("Всего блоков: " + ps.getTotalBlocks(), NamedTextColor.WHITE));
|
||||
int pos = sm.getLeaderboard().getPosition(uuid);
|
||||
if (pos > 0) {
|
||||
sender.sendMessage(Component.text("Место в общем топе: #" + pos, NamedTextColor.GRAY));
|
||||
}
|
||||
Map<String, Long> byMine = ps.getMineStatsSnapshot();
|
||||
if (byMine.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Component.text("По шахтам:", NamedTextColor.GRAY));
|
||||
byMine.entrySet().stream()
|
||||
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
|
||||
.limit(12)
|
||||
.forEach(e -> sender.sendMessage(Component.text(
|
||||
" " + e.getKey() + ": " + e.getValue(), NamedTextColor.DARK_AQUA)));
|
||||
}
|
||||
case "stats" -> handleStats(sender, args);
|
||||
case "top" -> {
|
||||
if (isStatsDisabled(sender)) {
|
||||
return;
|
||||
|
|
@ -68,6 +39,37 @@ public class StatsCommands {
|
|||
}
|
||||
}
|
||||
|
||||
private void handleStats(CommandSender sender, String[] args) {
|
||||
if (isStatsDisabled(sender)) {
|
||||
return;
|
||||
}
|
||||
String targetName = args.length > 0 ? args[0] : "";
|
||||
UUID uuid = LeaderboardRenderer.resolvePlayerUuid(sender, targetName, errorHandler);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
StatsManager sm = plugin.getStatsManager();
|
||||
PlayerStats ps = sm.getOrCreate(uuid);
|
||||
String displayName = LeaderboardRenderer.formatPlayerName(uuid);
|
||||
sender.sendMessage(Component.text("=== Статистика: " + displayName + " ===", NamedTextColor.GOLD));
|
||||
sender.sendMessage(Component.text("Всего блоков: " + ps.getTotalBlocks(), NamedTextColor.WHITE));
|
||||
int pos = sm.getLeaderboard().getPosition(uuid);
|
||||
if (pos > 0) {
|
||||
sender.sendMessage(Component.text("Место в общем топе: #" + pos, NamedTextColor.GRAY));
|
||||
}
|
||||
Map<String, Long> byMine = ps.getMineStatsSnapshot();
|
||||
if (byMine.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(Component.text("По шахтам:", NamedTextColor.GRAY));
|
||||
byMine.entrySet().stream()
|
||||
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
|
||||
.limit(12)
|
||||
.forEach(e -> sender.sendMessage(Component.text(
|
||||
" " + e.getKey() + ": " + e.getValue(), NamedTextColor.DARK_AQUA)));
|
||||
}
|
||||
|
||||
private boolean isStatsDisabled(CommandSender sender) {
|
||||
if (!plugin.getConfig().getBoolean("statistics-enabled", true)) {
|
||||
sender.sendMessage(Component.text("Статистика отключена в config.yml.", NamedTextColor.RED));
|
||||
|
|
|
|||
|
|
@ -35,19 +35,8 @@ public class TeleportCommand {
|
|||
return;
|
||||
}
|
||||
|
||||
var config = mine.getConfig();
|
||||
Location target;
|
||||
|
||||
if (config.teleport().enabled() && config.teleport().getLocation().isPresent()) {
|
||||
target = config.teleport().getLocation().get();
|
||||
} else if (!mine.getRegions().isEmpty()) {
|
||||
Cuboid region = mine.getRegions().get(0);
|
||||
double centerX = (region.getMinX() + region.getMaxX()) / 2.0 + 0.5;
|
||||
double centerZ = (region.getMinZ() + region.getMaxZ()) / 2.0 + 0.5;
|
||||
double centerY = region.getMaxY() + 1;
|
||||
target = new Location(region.getWorld(), centerX, centerY, centerZ);
|
||||
} else {
|
||||
player.sendMessage(Component.text("У шахты нет настроенной точки телепорта!", NamedTextColor.RED));
|
||||
Location target = resolveTarget(player, mine);
|
||||
if (target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -55,4 +44,20 @@ public class TeleportCommand {
|
|||
player.sendMessage(Component.text("§aТелепортировано к шахте §f" + mine.getName() +
|
||||
" §a(§f" + String.format("%.0f, %.0f, %.0f", target.getX(), target.getY(), target.getZ()) + "§a)"));
|
||||
}
|
||||
|
||||
private Location resolveTarget(Player player, Mine mine) {
|
||||
var config = mine.getConfig();
|
||||
if (config.teleport().enabled() && config.teleport().getLocation().isPresent()) {
|
||||
return config.teleport().getLocation().get();
|
||||
}
|
||||
if (!mine.getRegions().isEmpty()) {
|
||||
Cuboid region = mine.getRegions().get(0);
|
||||
double centerX = (region.getMinX() + region.getMaxX()) / 2.0 + 0.5;
|
||||
double centerZ = (region.getMinZ() + region.getMaxZ()) / 2.0 + 0.5;
|
||||
double centerY = region.getMaxY() + 1;
|
||||
return new Location(region.getWorld(), centerX, centerY, centerZ);
|
||||
}
|
||||
player.sendMessage(Component.text("У шахты нет настроенной точки телепорта!", NamedTextColor.RED));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
/**
|
||||
* Core components of the LoMines plugin.
|
||||
* <p>
|
||||
* This package contains the main plugin class and core system classes:
|
||||
* <ul>
|
||||
* <li>{@code LoMinesPlugin} - Main plugin class extending LoPlugin</li>
|
||||
* <li>{@code Mines} - Registry for managing all active mines</li>
|
||||
* <li>{@code Mine} - Representation of a single mine with its configuration and state</li>
|
||||
* </ul>
|
||||
*/
|
||||
package dev.loki.lomines.core.common;
|
||||
|
|
@ -44,6 +44,7 @@ public final class Mines {
|
|||
}
|
||||
|
||||
public void create(String name, Location corner1, Location corner2) throws IOException {
|
||||
name = name.toLowerCase().trim();
|
||||
if (repository.exists(name)) {
|
||||
throw new IllegalArgumentException("Mine already exists: " + name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,22 +42,23 @@ public final class BlockConfigLoader {
|
|||
yaml.getString("blocks.fill-mode", "CUBOID").toUpperCase()
|
||||
);
|
||||
|
||||
// Parse mask config if applicable
|
||||
BlockConfig.MaskConfig mask = null;
|
||||
if (fillMode == FillMode.MASK) {
|
||||
String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete");
|
||||
BlockKey marker = BlockKey.deserialize(markerStr);
|
||||
return new BlockConfig(weights, fillMode, parseMaskConfig(yaml, fillMode));
|
||||
}
|
||||
|
||||
Map<String, Boolean> positions = new HashMap<>();
|
||||
List<String> posList = yaml.getStringList("blocks.mask.positions");
|
||||
for (String pos : posList) {
|
||||
positions.put(pos, true);
|
||||
}
|
||||
private BlockConfig.MaskConfig parseMaskConfig(YamlConfiguration yaml, FillMode fillMode) {
|
||||
if (fillMode != FillMode.MASK) {
|
||||
return null;
|
||||
}
|
||||
String markerStr = yaml.getString("blocks.mask.marker", "pink_concrete");
|
||||
BlockKey marker = BlockKey.deserialize(markerStr);
|
||||
|
||||
mask = new BlockConfig.MaskConfig(marker, positions);
|
||||
Map<String, Boolean> positions = new HashMap<>();
|
||||
List<String> posList = yaml.getStringList("blocks.mask.positions");
|
||||
for (String pos : posList) {
|
||||
positions.put(pos, true);
|
||||
}
|
||||
|
||||
return new BlockConfig(weights, fillMode, mask);
|
||||
return new BlockConfig.MaskConfig(marker, positions);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,44 +22,14 @@ public final class WorldGuardConfigLoader {
|
|||
* Supports any WorldGuard flags. Default: block-break=allow.
|
||||
*/
|
||||
public WorldGuardConfig parse(YamlConfiguration yaml) {
|
||||
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
||||
if (wgSection == null) {
|
||||
// Check defaults
|
||||
boolean defaultEnabled = defaults.getBoolean("worldguard.enabled", false);
|
||||
if (!defaultEnabled) {
|
||||
return WorldGuardConfig.disabled();
|
||||
}
|
||||
}
|
||||
|
||||
boolean enabled = yaml.getBoolean("worldguard.enabled",
|
||||
defaults.getBoolean("worldguard.enabled", false));
|
||||
|
||||
if (!enabled) {
|
||||
if (!isEnabled(yaml)) {
|
||||
return WorldGuardConfig.disabled();
|
||||
}
|
||||
|
||||
List<String> owners = yaml.getStringList("worldguard.owners");
|
||||
if (owners.isEmpty()) {
|
||||
owners = defaults.getStringList("worldguard.owners");
|
||||
}
|
||||
|
||||
List<String> members = yaml.getStringList("worldguard.members");
|
||||
if (members.isEmpty()) {
|
||||
members = defaults.getStringList("worldguard.members");
|
||||
}
|
||||
|
||||
// Support any WorldGuard flags. Default: only block-break=allow
|
||||
List<String> flags = yaml.getStringList("worldguard.flags");
|
||||
if (flags.isEmpty()) {
|
||||
flags = defaults.getStringList("worldguard.flags");
|
||||
}
|
||||
if (flags.isEmpty()) {
|
||||
flags = List.of("block-break=allow");
|
||||
}
|
||||
|
||||
boolean protect = yaml.getBoolean("worldguard.protect-on-create",
|
||||
defaults.getBoolean("worldguard.protect-on-create", true));
|
||||
|
||||
List<String> owners = resolveOwners(yaml);
|
||||
List<String> members = resolveMembers(yaml);
|
||||
List<String> flags = resolveFlags(yaml);
|
||||
boolean protect = resolveProtect(yaml);
|
||||
String tmpl = yaml.getString("worldguard.region-template",
|
||||
defaults.getString("worldguard.region-template", "{mine_name}_{random_4}"));
|
||||
|
||||
|
|
@ -73,6 +43,50 @@ public final class WorldGuardConfigLoader {
|
|||
.build();
|
||||
}
|
||||
|
||||
private boolean isEnabled(YamlConfiguration yaml) {
|
||||
ConfigurationSection wgSection = yaml.getConfigurationSection("worldguard");
|
||||
if (wgSection == null) {
|
||||
boolean defaultEnabled = defaults.getBoolean("worldguard.enabled", false);
|
||||
if (!defaultEnabled) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return yaml.getBoolean("worldguard.enabled", defaults.getBoolean("worldguard.enabled", false));
|
||||
}
|
||||
|
||||
private List<String> resolveOwners(YamlConfiguration yaml) {
|
||||
List<String> owners = yaml.getStringList("worldguard.owners");
|
||||
if (owners.isEmpty()) {
|
||||
owners = defaults.getStringList("worldguard.owners");
|
||||
}
|
||||
return owners;
|
||||
}
|
||||
|
||||
private List<String> resolveMembers(YamlConfiguration yaml) {
|
||||
List<String> members = yaml.getStringList("worldguard.members");
|
||||
if (members.isEmpty()) {
|
||||
members = defaults.getStringList("worldguard.members");
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
private List<String> resolveFlags(YamlConfiguration yaml) {
|
||||
// Support any WorldGuard flags. Default: only block-break=allow
|
||||
List<String> flags = yaml.getStringList("worldguard.flags");
|
||||
if (flags.isEmpty()) {
|
||||
flags = defaults.getStringList("worldguard.flags");
|
||||
}
|
||||
if (flags.isEmpty()) {
|
||||
flags = List.of("block-break=allow");
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private boolean resolveProtect(YamlConfiguration yaml) {
|
||||
return yaml.getBoolean("worldguard.protect-on-create",
|
||||
defaults.getBoolean("worldguard.protect-on-create", true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves WorldGuard configuration to YAML.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -36,16 +36,31 @@ public final class RewardConfigLoader {
|
|||
}
|
||||
|
||||
private RewardEntry parseRewardEntry(Map<?, ?> map) {
|
||||
List<BlockKey> blocks = parseBlocks(map.get("blocks"));
|
||||
List<ItemReward> items = parseItems(map.get("items"));
|
||||
List<String> commands = parseCommands(map.get("commands"));
|
||||
Object preventDropsObj = map.get("prevent-drops");
|
||||
boolean preventDrops = preventDropsObj instanceof Boolean b && b;
|
||||
|
||||
Object chanceObj = map.get("chance");
|
||||
double chance = chanceObj instanceof Number n ? n.doubleValue() : 0.0;
|
||||
return new RewardEntry(blocks, chance, items, commands, preventDrops);
|
||||
}
|
||||
|
||||
private List<BlockKey> parseBlocks(Object rawBlocks) {
|
||||
List<BlockKey> blocks = new ArrayList<>();
|
||||
List<?> blockList = (List<?>) map.get("blocks");
|
||||
List<?> blockList = (List<?>) rawBlocks;
|
||||
if (blockList != null) {
|
||||
for (Object b : blockList) {
|
||||
blocks.add(BlockKey.deserialize(b.toString()));
|
||||
}
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
private List<ItemReward> parseItems(Object rawItems) {
|
||||
List<ItemReward> items = new ArrayList<>();
|
||||
List<?> itemList = (List<?>) map.get("items");
|
||||
List<?> itemList = (List<?>) rawItems;
|
||||
if (itemList != null) {
|
||||
for (Object i : itemList) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -53,9 +68,11 @@ public final class RewardConfigLoader {
|
|||
items.add(parseItemReward(itemMap));
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<String> parseCommands(Object commandsObj) {
|
||||
List<String> commands = new ArrayList<>();
|
||||
Object commandsObj = map.get("commands");
|
||||
if (commandsObj instanceof List<?> list) {
|
||||
for (Object obj : list) {
|
||||
if (obj != null) {
|
||||
|
|
@ -63,12 +80,7 @@ public final class RewardConfigLoader {
|
|||
}
|
||||
}
|
||||
}
|
||||
Object preventDropsObj = map.get("prevent-drops");
|
||||
boolean preventDrops = preventDropsObj instanceof Boolean b && b;
|
||||
|
||||
Object chanceObj = map.get("chance");
|
||||
double chance = chanceObj instanceof Number n ? n.doubleValue() : 0.0;
|
||||
return new RewardEntry(blocks, chance, items, commands, preventDrops);
|
||||
return commands;
|
||||
}
|
||||
|
||||
private ItemReward parseItemReward(Map<String, Object> map) {
|
||||
|
|
|
|||
|
|
@ -29,29 +29,30 @@ public final class RewardParser {
|
|||
}
|
||||
|
||||
for (int i = 0; i < rewardsList.size(); i++) {
|
||||
Object rewardObj = rewardsList.get(i);
|
||||
|
||||
if (!(rewardObj instanceof Map)) {
|
||||
throw new ConfigParseException(
|
||||
"Invalid reward at index " + i + ": expected map, got " +
|
||||
(rewardObj != null ? rewardObj.getClass().getSimpleName() : "null")
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> rewardMap = (Map<String, Object>) rewardObj;
|
||||
|
||||
try {
|
||||
Reward reward = entryParser.parseReward(rewardMap);
|
||||
rewards.add(reward);
|
||||
} catch (ConfigParseException e) {
|
||||
throw new ConfigParseException(
|
||||
"Error parsing reward at index " + i + ": " + e.getMessage(),
|
||||
e
|
||||
);
|
||||
}
|
||||
rewards.add(parseRewardAt(rewardsList.get(i), i));
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
private Reward parseRewardAt(Object rewardObj, int i) throws ConfigParseException {
|
||||
if (!(rewardObj instanceof Map)) {
|
||||
throw new ConfigParseException(
|
||||
"Invalid reward at index " + i + ": expected map, got " +
|
||||
(rewardObj != null ? rewardObj.getClass().getSimpleName() : "null")
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> rewardMap = (Map<String, Object>) rewardObj;
|
||||
|
||||
try {
|
||||
return entryParser.parseReward(rewardMap);
|
||||
} catch (ConfigParseException e) {
|
||||
throw new ConfigParseException(
|
||||
"Error parsing reward at index " + i + ": " + e.getMessage(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,34 +43,38 @@ public final class StatsPersistence {
|
|||
}
|
||||
|
||||
for (String uuidString : playersSection.getKeys(false)) {
|
||||
try {
|
||||
UUID playerId = UUID.fromString(uuidString);
|
||||
ConfigurationSection playerSection = playersSection.getConfigurationSection(uuidString);
|
||||
|
||||
if (playerSection == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PlayerStats playerStats = stats.computeIfAbsent(playerId, PlayerStats::new);
|
||||
|
||||
long totalBlocks = playerSection.getLong("total", 0);
|
||||
playerStats.setTotalBlocks(totalBlocks);
|
||||
|
||||
ConfigurationSection minesSection = playerSection.getConfigurationSection("mines");
|
||||
if (minesSection != null) {
|
||||
for (String mineName : minesSection.getKeys(false)) {
|
||||
long mineBlocks = minesSection.getLong(mineName, 0);
|
||||
playerStats.setMineBlocks(mineName, mineBlocks);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Invalid UUID in stats.yml: " + uuidString);
|
||||
}
|
||||
loadPlayer(stats, uuidString, playersSection);
|
||||
}
|
||||
|
||||
plugin.getLogger().info("Loaded statistics for " + stats.size() + " player(s)");
|
||||
}
|
||||
|
||||
private void loadPlayer(Map<UUID, PlayerStats> stats, String uuidString, ConfigurationSection playersSection) {
|
||||
try {
|
||||
UUID playerId = UUID.fromString(uuidString);
|
||||
ConfigurationSection playerSection = playersSection.getConfigurationSection(uuidString);
|
||||
|
||||
if (playerSection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerStats playerStats = stats.computeIfAbsent(playerId, PlayerStats::new);
|
||||
|
||||
long totalBlocks = playerSection.getLong("total", 0);
|
||||
playerStats.setTotalBlocks(totalBlocks);
|
||||
|
||||
ConfigurationSection minesSection = playerSection.getConfigurationSection("mines");
|
||||
if (minesSection != null) {
|
||||
for (String mineName : minesSection.getKeys(false)) {
|
||||
long mineBlocks = minesSection.getLong(mineName, 0);
|
||||
playerStats.setMineBlocks(mineName, mineBlocks);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
plugin.getLogger().warning("Invalid UUID in stats.yml: " + uuidString);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(Map<UUID, PlayerStats> stats) throws IOException {
|
||||
YamlConfiguration yaml = new YamlConfiguration();
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,20 @@ public final class GroupCreateGui {
|
|||
return;
|
||||
}
|
||||
|
||||
CreationResult result = createSlots(plugin, player, session, base);
|
||||
if (result.created() == 0) {
|
||||
player.sendMessage(Component.text("Нет готовых слотов (2 точки в каждом).", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
player.sendMessage(Component.text(
|
||||
"Создано шахт: " + result.created() + (result.skipped() > 0 ? " (пропущено: " + result.skipped() + ")" : ""),
|
||||
NamedTextColor.GREEN));
|
||||
}
|
||||
|
||||
private record CreationResult(int created, int skipped) {
|
||||
}
|
||||
|
||||
private static CreationResult createSlots(LoMinesPlugin plugin, Player player, GroupWandSession session, String base) {
|
||||
Mines mines = plugin.getMines();
|
||||
int created = 0;
|
||||
int skipped = 0;
|
||||
|
|
@ -115,13 +129,6 @@ public final class GroupCreateGui {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (created == 0) {
|
||||
player.sendMessage(Component.text("Нет готовых слотов (2 точки в каждом).", NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
player.sendMessage(Component.text(
|
||||
"Создано шахт: " + created + (skipped > 0 ? " (пропущено: " + skipped + ")" : ""),
|
||||
NamedTextColor.GREEN));
|
||||
return new CreationResult(created, skipped);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,19 +30,32 @@ public final class BlockWeightEditor {
|
|||
Map<BlockKey, Double> weights = new java.util.HashMap<>(config.weights());
|
||||
double currentWeight = weights.getOrDefault(blockKey, 0.0) * 100.0;
|
||||
|
||||
double delta = 0;
|
||||
double newWeight = Math.max(0, Math.min(100,
|
||||
currentWeight + computeDelta(leftClick, shiftClick, rightClick)));
|
||||
|
||||
applyWeightChange(player, blockKey, weights, newWeight, rightClick, shiftClick);
|
||||
|
||||
BlocksGui.refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||
}
|
||||
|
||||
private static double computeDelta(boolean leftClick, boolean shiftClick, boolean rightClick) {
|
||||
if (leftClick && shiftClick) {
|
||||
delta = 1.0;
|
||||
} else if (rightClick && shiftClick) {
|
||||
delta = -1.0;
|
||||
} else if (leftClick) {
|
||||
delta = 5.0;
|
||||
} else if (rightClick) {
|
||||
delta = -5.0;
|
||||
return 1.0;
|
||||
}
|
||||
if (rightClick && shiftClick) {
|
||||
return -1.0;
|
||||
}
|
||||
if (leftClick) {
|
||||
return 5.0;
|
||||
}
|
||||
if (rightClick) {
|
||||
return -5.0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double newWeight = Math.max(0, Math.min(100, currentWeight + delta));
|
||||
|
||||
private static void applyWeightChange(Player player, BlockKey blockKey, Map<BlockKey, Double> weights,
|
||||
double newWeight, boolean rightClick, boolean shiftClick) {
|
||||
if (rightClick && shiftClick && newWeight <= 0.1) {
|
||||
weights.remove(blockKey);
|
||||
player.sendMessage(Component.text(
|
||||
|
|
@ -53,8 +66,6 @@ public final class BlockWeightEditor {
|
|||
"\u00a7a\u0412\u0435\u0441 \u00a7f" + blockKey.serialize() + " \u00a7a\u0438\u0437\u043c\u0435\u043d\u0435\u043d: \u00a7f"
|
||||
+ String.format("%.1f%%", newWeight)));
|
||||
}
|
||||
|
||||
BlocksGui.refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||
}
|
||||
|
||||
public static BlockKey getBlockAtSlot(Inventory inv, int slot, LoMinesPlugin plugin, String mineName) {
|
||||
|
|
|
|||
|
|
@ -87,14 +87,7 @@ public final class ResetGui {
|
|||
}
|
||||
|
||||
if (rawSlot == SLOT_MESSAGE) {
|
||||
if (leftClick) {
|
||||
player.sendMessage(Component.text("§eВведите сообщение в чат (или 'cancel' для отмены):"));
|
||||
player.closeInventory();
|
||||
} else if (rightClick) {
|
||||
player.sendMessage(Component.text("§cСообщение очищено."));
|
||||
refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||
}
|
||||
return true;
|
||||
return handleMessageSlot(plugin, player, mineName, leftClick, rightClick);
|
||||
}
|
||||
|
||||
if (rawSlot == SLOT_COMMANDS && leftClick) {
|
||||
|
|
@ -105,6 +98,18 @@ public final class ResetGui {
|
|||
return true;
|
||||
}
|
||||
|
||||
private static boolean handleMessageSlot(LoMinesPlugin plugin, Player player, String mineName,
|
||||
boolean leftClick, boolean rightClick) {
|
||||
if (leftClick) {
|
||||
player.sendMessage(Component.text("§eВведите сообщение в чат (или 'cancel' для отмены):"));
|
||||
player.closeInventory();
|
||||
} else if (rightClick) {
|
||||
player.sendMessage(Component.text("§cСообщение очищено."));
|
||||
refresh(player, player.getOpenInventory().getTopInventory(), mineName, plugin);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void adjustInterval(LoMinesPlugin plugin, Player player, String mineName,
|
||||
boolean leftClick, boolean rightClick, boolean shiftClick) {
|
||||
int delta = 0;
|
||||
|
|
|
|||
|
|
@ -81,11 +81,11 @@ public final class MineEditGui {
|
|||
if (rawSlot < 0 || rawSlot >= SIZE) {
|
||||
return false;
|
||||
}
|
||||
return handleNavigationSlot(plugin, player, mineName, rawSlot);
|
||||
}
|
||||
|
||||
private static boolean handleNavigationSlot(LoMinesPlugin plugin, Player player, String mineName, int rawSlot) {
|
||||
switch (rawSlot) {
|
||||
case SLOT_BACK -> {
|
||||
player.closeInventory();
|
||||
return true;
|
||||
}
|
||||
case SLOT_BLOCKS -> {
|
||||
BlocksGui.open(plugin, player, mineName);
|
||||
return true;
|
||||
|
|
@ -98,6 +98,22 @@ public final class MineEditGui {
|
|||
RewardsGui.open(plugin, player, mineName);
|
||||
return true;
|
||||
}
|
||||
case SLOT_DELETE -> {
|
||||
ConfirmDeleteGui.open(plugin, player, mineName);
|
||||
return true;
|
||||
}
|
||||
default -> {
|
||||
return handleInfoSlot(player, mineName, rawSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean handleInfoSlot(Player player, String mineName, int rawSlot) {
|
||||
switch (rawSlot) {
|
||||
case SLOT_BACK -> {
|
||||
player.closeInventory();
|
||||
return true;
|
||||
}
|
||||
case SLOT_TELEPORT -> {
|
||||
player.sendMessage(Component.text("Используйте команды /lm setteleport и /lm setspawn", NamedTextColor.YELLOW));
|
||||
return true;
|
||||
|
|
@ -111,10 +127,6 @@ public final class MineEditGui {
|
|||
player.closeInventory();
|
||||
return true;
|
||||
}
|
||||
case SLOT_DELETE -> {
|
||||
ConfirmDeleteGui.open(plugin, player, mineName);
|
||||
return true;
|
||||
}
|
||||
case SLOT_INFO, SLOT_REGIONS -> {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,30 +45,34 @@ public final class WorldGuardRegionService {
|
|||
}
|
||||
String regionName = wgConfig.generateRegionName(mineName);
|
||||
try {
|
||||
RegionManager regionManager = getRegionManager(config.worldName());
|
||||
if (regionManager == null) {
|
||||
return null;
|
||||
}
|
||||
if (regionManager.hasRegion(regionName)) {
|
||||
plugin.getLogger().info("WorldGuard region already exists: " + regionName);
|
||||
return regionName;
|
||||
}
|
||||
ProtectedCuboidRegion region = templateRenderer.createRegionFromCuboids(regionName, config);
|
||||
if (region == null) {
|
||||
plugin.getLogger().warning("Failed to create region for mine " + mineName + ": no valid regions");
|
||||
return null;
|
||||
}
|
||||
memberHandler.applyOwnersAndMembers(region, wgConfig);
|
||||
templateRenderer.applyFlags(region, wgConfig.flags());
|
||||
regionManager.addRegion(region);
|
||||
plugin.getLogger().info("Created WorldGuard region '" + regionName + "' for mine " + mineName);
|
||||
return regionName;
|
||||
return createRegionInManager(mineName, config, regionName, wgConfig);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().severe("Failed to create WorldGuard region for mine " + mineName + ": " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String createRegionInManager(String mineName, MineConfig config, String regionName, WorldGuardConfig wgConfig) {
|
||||
RegionManager regionManager = getRegionManager(config.worldName());
|
||||
if (regionManager == null) {
|
||||
return null;
|
||||
}
|
||||
if (regionManager.hasRegion(regionName)) {
|
||||
plugin.getLogger().info("WorldGuard region already exists: " + regionName);
|
||||
return regionName;
|
||||
}
|
||||
ProtectedCuboidRegion region = templateRenderer.createRegionFromCuboids(regionName, config);
|
||||
if (region == null) {
|
||||
plugin.getLogger().warning("Failed to create region for mine " + mineName + ": no valid regions");
|
||||
return null;
|
||||
}
|
||||
memberHandler.applyOwnersAndMembers(region, wgConfig);
|
||||
templateRenderer.applyFlags(region, wgConfig.flags());
|
||||
regionManager.addRegion(region);
|
||||
plugin.getLogger().info("Created WorldGuard region '" + regionName + "' for mine " + mineName);
|
||||
return regionName;
|
||||
}
|
||||
|
||||
public boolean updateRegion(String mineName, MineConfig config) {
|
||||
if (!worldGuardEnabled) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -48,38 +48,44 @@ public final class PlayerInteractListener implements Listener {
|
|||
|
||||
if (action == Action.LEFT_CLICK_BLOCK && event.hasBlock()) {
|
||||
event.setCancelled(true);
|
||||
Block block = event.getClickedBlock();
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
GroupWandSession session = plugin.getGroupWandManager().getSession(player.getUniqueId());
|
||||
int slot = session.getActiveSlot();
|
||||
session.setPos1(slot, block.getLocation());
|
||||
player.sendMessage(Component.text(
|
||||
"Угол 1 для шахты " + (slot + 1) + " установлен.", NamedTextColor.GREEN));
|
||||
|
||||
// Start showing particles when selection is made
|
||||
plugin.getWandParticleService().startShowingParticles(player);
|
||||
handleLeftClick(player, event.getClickedBlock());
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == Action.RIGHT_CLICK_BLOCK && !player.isSneaking()) {
|
||||
event.setCancelled(true);
|
||||
Block block = event.getClickedBlock();
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
GroupWandSession session = plugin.getGroupWandManager().getSession(player.getUniqueId());
|
||||
int slot = session.getActiveSlot();
|
||||
session.setPos2(slot, block.getLocation());
|
||||
player.sendMessage(Component.text(
|
||||
"Угол 2 для шахты " + (slot + 1) + " установлен.", NamedTextColor.GREEN));
|
||||
|
||||
// Start showing particles when selection is made
|
||||
plugin.getWandParticleService().startShowingParticles(player);
|
||||
handleRightClick(player, event.getClickedBlock());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleLeftClick(Player player, Block block) {
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
GroupWandSession session = plugin.getGroupWandManager().getSession(player.getUniqueId());
|
||||
int slot = session.getActiveSlot();
|
||||
session.setPos1(slot, block.getLocation());
|
||||
player.sendMessage(Component.text(
|
||||
"Угол 1 для шахты " + (slot + 1) + " установлен.", NamedTextColor.GREEN));
|
||||
|
||||
// Start showing particles when selection is made
|
||||
plugin.getWandParticleService().startShowingParticles(player);
|
||||
}
|
||||
|
||||
private void handleRightClick(Player player, Block block) {
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
GroupWandSession session = plugin.getGroupWandManager().getSession(player.getUniqueId());
|
||||
int slot = session.getActiveSlot();
|
||||
session.setPos2(slot, block.getLocation());
|
||||
player.sendMessage(Component.text(
|
||||
"Угол 2 для шахты " + (slot + 1) + " установлен.", NamedTextColor.GREEN));
|
||||
|
||||
// Start showing particles when selection is made
|
||||
plugin.getWandParticleService().startShowingParticles(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts/stops particle visualization when player switches items.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ public final class LocationParser {
|
|||
);
|
||||
}
|
||||
|
||||
return buildLocation(matcher, locationString);
|
||||
}
|
||||
|
||||
private static Location buildLocation(Matcher matcher, String locationString) throws ConfigParseException {
|
||||
try {
|
||||
String worldName = matcher.group(1);
|
||||
double x = Double.parseDouble(matcher.group(2));
|
||||
|
|
|
|||
|
|
@ -40,6 +40,10 @@ public final class SafeTeleportUtil {
|
|||
return destination;
|
||||
}
|
||||
|
||||
return findFallbackSafe(world, x, y, z, yaw, pitch, maxUpOffset);
|
||||
}
|
||||
|
||||
private static Location findFallbackSafe(World world, int x, int y, int z, float yaw, float pitch, int maxUpOffset) {
|
||||
Location result = findNearbySafe(world, x, y, z, yaw, pitch);
|
||||
if (result != null) {
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue