From eccf850d118a5cfc0f7c05ddda5dd9edb98cdc58 Mon Sep 17 00:00:00 2001 From: itqop Date: Wed, 12 Nov 2025 12:12:58 +0300 Subject: [PATCH] fixes --- .claude/settings.local.json | 4 +- .../HubmcEssentials/api/HubGWClient.java | 48 +++--- .../command/custom/GotoCommand.java | 154 ++++++++++-------- .../command/deluxe/PotCommand.java | 48 +++--- .../command/deluxe/TimeCommand.java | 29 ++-- .../command/deluxe/TopCommand.java | 68 ++++---- .../command/general/ClearCommand.java | 26 ++- .../command/general/EcCommand.java | 27 +-- .../command/general/HatCommand.java | 29 ++-- .../command/premium/RepairAllCommand.java | 59 ++++--- .../command/vip/BackCommand.java | 93 ++++++----- .../command/vip/FeedCommand.java | 26 ++- .../command/vip/HealCommand.java | 32 ++-- .../command/vip/NearCommand.java | 42 +++-- .../command/vip/RepairCommand.java | 24 ++- .../command/vip/RtpCommand.java | 55 ++++--- .../HubmcEssentials/util/LocationUtil.java | 21 +++ 17 files changed, 459 insertions(+), 326 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 9d94b8a..67380a3 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,9 @@ "Bash(./gradlew tasks:*)", "Bash(./gradlew build:*)", "Bash(./gradlew compileJava:*)", - "Bash(./gradlew clean build:*)" + "Bash(./gradlew clean build:*)", + "Bash(.gradlew clean build)", + "Bash(gradlew.bat clean build)" ], "deny": [], "ask": [] diff --git a/src/main/java/org/itqop/HubmcEssentials/api/HubGWClient.java b/src/main/java/org/itqop/HubmcEssentials/api/HubGWClient.java index eb8e0c3..5f4a74d 100644 --- a/src/main/java/org/itqop/HubmcEssentials/api/HubGWClient.java +++ b/src/main/java/org/itqop/HubmcEssentials/api/HubGWClient.java @@ -12,6 +12,8 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; /** * HTTP client for HubGW API integration. @@ -22,9 +24,9 @@ public class HubGWClient { private static final Logger LOGGER = LogUtils.getLogger(); private static final Gson GSON = new GsonBuilder().create(); - // Circuit breaker to prevent log spam - private volatile long lastErrorLogTime = 0; - private volatile int consecutiveErrors = 0; + // Circuit breaker to prevent log spam (thread-safe with atomics) + private final AtomicLong lastErrorLogTime = new AtomicLong(0); + private final AtomicInteger consecutiveErrors = new AtomicInteger(0); private static final long ERROR_LOG_COOLDOWN_MS = 60_000; // 1 minute private static final int ERROR_THRESHOLD = 3; @@ -215,52 +217,54 @@ public class HubGWClient { } /** - * Log HTTP error with circuit breaker logic. + * Log HTTP error with circuit breaker logic (thread-safe). */ private void logHttpError(String endpoint, int statusCode, String responseBody) { - consecutiveErrors++; + int errorCount = consecutiveErrors.incrementAndGet(); long now = System.currentTimeMillis(); - if (consecutiveErrors == 1 || (now - lastErrorLogTime) > ERROR_LOG_COOLDOWN_MS) { + long lastLog = lastErrorLogTime.get(); + + if (errorCount == 1 || (now - lastLog) > ERROR_LOG_COOLDOWN_MS) { if (Config.enableDebugLogging) { LOGGER.warn("API request failed: {} returned {} - Response: {}", endpoint, statusCode, responseBody); } else { LOGGER.warn("API request failed: {} returned {}", endpoint, statusCode); } - lastErrorLogTime = now; + lastErrorLogTime.set(now); - if (consecutiveErrors > ERROR_THRESHOLD) { - LOGGER.warn("API has failed {} times consecutively. Further errors will be throttled.", consecutiveErrors); + if (errorCount > ERROR_THRESHOLD) { + LOGGER.warn("API has failed {} times consecutively. Further errors will be throttled.", errorCount); } } } /** - * Log API connection error with circuit breaker logic. + * Log API connection error with circuit breaker logic (thread-safe). */ private void logApiError(String message, Throwable ex) { - consecutiveErrors++; + int errorCount = consecutiveErrors.incrementAndGet(); long now = System.currentTimeMillis(); - if (consecutiveErrors == 1 || (now - lastErrorLogTime) > ERROR_LOG_COOLDOWN_MS) { - LOGGER.warn("{}: {} (consecutive errors: {})", message, ex.getMessage(), consecutiveErrors); - lastErrorLogTime = now; + long lastLog = lastErrorLogTime.get(); - if (consecutiveErrors > ERROR_THRESHOLD) { - LOGGER.warn("API has failed {} times consecutively. Further errors will be throttled.", consecutiveErrors); + if (errorCount == 1 || (now - lastLog) > ERROR_LOG_COOLDOWN_MS) { + LOGGER.warn("{}: {} (consecutive errors: {})", message, ex.getMessage(), errorCount); + lastErrorLogTime.set(now); + + if (errorCount > ERROR_THRESHOLD) { + LOGGER.warn("API has failed {} times consecutively. Further errors will be throttled.", errorCount); } } } /** - * Reset error counter after successful request. + * Reset error counter after successful request (thread-safe). */ private void resetErrorCounter() { - if (consecutiveErrors > 0) { - if (consecutiveErrors > ERROR_THRESHOLD) { - LOGGER.info("API connection restored after {} consecutive errors", consecutiveErrors); - } - consecutiveErrors = 0; + int previousErrors = consecutiveErrors.getAndSet(0); + if (previousErrors > ERROR_THRESHOLD) { + LOGGER.info("API connection restored after {} consecutive errors", previousErrors); } } } diff --git a/src/main/java/org/itqop/HubmcEssentials/command/custom/GotoCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/custom/GotoCommand.java index 0b37e9a..bc455d3 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/custom/GotoCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/custom/GotoCommand.java @@ -19,6 +19,8 @@ import org.itqop.HubmcEssentials.util.LocationUtil; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /goto command - Custom teleport command with cooldowns and logging. * @@ -82,55 +84,60 @@ public class GotoCommand { String cooldownType = "tp|" + target.getGameProfile().getName(); // Check cooldown - CooldownService.checkCooldown(playerUuid, cooldownType).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, cooldownType).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Save current location for /back - LocationStorage.saveLocation(player); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownGoto).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Store from location for teleport history - Vec3 fromPos = player.position(); - String fromWorld = LocationUtil.getWorldId(player.level()); + // NOW perform action - save location and teleport + LocationStorage.saveLocation(player); - // Teleport to target - boolean success = LocationUtil.teleportPlayer( - player, - target.serverLevel(), - target.getX(), - target.getY(), - target.getZ(), - target.getYRot(), - target.getXRot() - ); + // Store from location for teleport history + Vec3 fromPos = player.position(); + String fromWorld = LocationUtil.getWorldId(player.level()); - if (success) { - MessageUtil.sendSuccess(player, "Телепортация к игроку §6" + target.getGameProfile().getName()); - - // Log teleport history - TeleportHistoryRequest historyRequest = new TeleportHistoryRequest( - playerUuid, - fromWorld, - fromPos.x, fromPos.y, fromPos.z, - LocationUtil.getWorldId(target.level()), - target.getX(), target.getY(), target.getZ(), - "to_player", - target.getGameProfile().getName() + // Teleport to target + boolean teleportSuccess = LocationUtil.teleportPlayer( + player, + target.serverLevel(), + target.getX(), + target.getY(), + target.getZ(), + target.getYRot(), + target.getXRot() ); - TeleportService.logTeleport(historyRequest); - // Set cooldown - CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownGoto); - } else { - MessageUtil.sendError(player, "Ошибка телепортации"); - } + if (teleportSuccess) { + MessageUtil.sendSuccess(player, "Телепортация к игроку §6" + target.getGameProfile().getName()); + + // Log teleport history + TeleportHistoryRequest historyRequest = new TeleportHistoryRequest( + playerUuid, + fromWorld, + fromPos.x, fromPos.y, fromPos.z, + LocationUtil.getWorldId(target.level()), + target.getX(), target.getY(), target.getZ(), + "to_player", + target.getGameProfile().getName() + ); + TeleportService.logTeleport(historyRequest); + } else { + MessageUtil.sendError(player, "Ошибка телепортации"); + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; @@ -227,54 +234,59 @@ public class GotoCommand { String cooldownType = "tp|coords"; // Check cooldown - CooldownService.checkCooldown(playerUuid, cooldownType).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, cooldownType).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Save current location for /back - LocationStorage.saveLocation(player); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownGoto).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Store from location for teleport history - Vec3 fromPos = player.position(); - String fromWorld = LocationUtil.getWorldId(player.level()); + // NOW perform action - save location and teleport + LocationStorage.saveLocation(player); - // Teleport to coordinates - boolean success = LocationUtil.teleportPlayer( - player, - player.serverLevel(), - location.x, - location.y, - location.z - ); + // Store from location for teleport history + Vec3 fromPos = player.position(); + String fromWorld = LocationUtil.getWorldId(player.level()); - if (success) { - MessageUtil.sendSuccess(player, "Телепортация на координаты: " + - MessageUtil.formatCoords(location.x, location.y, location.z)); - - // Log teleport history - TeleportHistoryRequest historyRequest = new TeleportHistoryRequest( - playerUuid, - fromWorld, - fromPos.x, fromPos.y, fromPos.z, - LocationUtil.getWorldId(player.level()), - location.x, location.y, location.z, - "to_coords", - MessageUtil.formatCoords(location.x, location.y, location.z) + // Teleport to coordinates + boolean teleportSuccess = LocationUtil.teleportPlayer( + player, + player.serverLevel(), + location.x, + location.y, + location.z ); - TeleportService.logTeleport(historyRequest); - // Set cooldown - CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownGoto); - } else { - MessageUtil.sendError(player, "Ошибка телепортации"); - } + if (teleportSuccess) { + MessageUtil.sendSuccess(player, "Телепортация на координаты: " + + MessageUtil.formatCoords(location.x, location.y, location.z)); + + // Log teleport history + TeleportHistoryRequest historyRequest = new TeleportHistoryRequest( + playerUuid, + fromWorld, + fromPos.x, fromPos.y, fromPos.z, + LocationUtil.getWorldId(player.level()), + location.x, location.y, location.z, + "to_coords", + MessageUtil.formatCoords(location.x, location.y, location.z) + ); + TeleportService.logTeleport(historyRequest); + } else { + MessageUtil.sendError(player, "Ошибка телепортации"); + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/PotCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/PotCommand.java index 6ee8199..e5f82f0 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/PotCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/PotCommand.java @@ -20,6 +20,7 @@ import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; import java.util.Optional; +import java.util.concurrent.CompletableFuture; /** * /pot command - Apply potion effects to player. @@ -82,38 +83,43 @@ public class PotCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Apply effect - MobEffect effect = effectHolder.get().value(); - int durationTicks = durationSeconds * 20; // Convert seconds to ticks - MobEffectInstance effectInstance = new MobEffectInstance( - effectHolder.get(), - durationTicks, - amplifier, - false, // ambient - true, // visible particles - true // show icon - ); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownPot).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - player.addEffect(effectInstance); + // NOW perform action - apply effect + MobEffect effect = effectHolder.get().value(); + int durationTicks = durationSeconds * 20; // Convert seconds to ticks + MobEffectInstance effectInstance = new MobEffectInstance( + effectHolder.get(), + durationTicks, + amplifier, + false, // ambient + true, // visible particles + true // show icon + ); - // Get effect display name - String effectDisplayName = effect.getDisplayName().getString(); - MessageUtil.sendSuccess(player, "Эффект применен: §6" + effectDisplayName + - "§a (Уровень " + (amplifier + 1) + ", " + durationSeconds + " сек.)"); + player.addEffect(effectInstance); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownPot); + // Get effect display name + String effectDisplayName = effect.getDisplayName().getString(); + MessageUtil.sendSuccess(player, "Эффект применен: §6" + effectDisplayName + + "§a (Уровень " + (amplifier + 1) + ", " + durationSeconds + " сек.)"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TimeCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TimeCommand.java index d87e0a3..fc7e7ac 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TimeCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TimeCommand.java @@ -13,6 +13,8 @@ import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * Time control commands - Set world time. * Commands: /day, /night, /morning, /evening @@ -66,27 +68,32 @@ public class TimeCommand { String cooldownType = "time|" + timeType; // Check cooldown - CooldownService.checkCooldown(playerUuid, cooldownType).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, cooldownType).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Set world time - ServerLevel level = player.serverLevel(); - level.setDayTime(timeValue); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownTime).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Send success message - String timeDisplayName = getTimeDisplayName(timeType); - MessageUtil.sendSuccess(player, "Время установлено: §6" + timeDisplayName); + // NOW perform action - set world time + ServerLevel level = player.serverLevel(); + level.setDayTime(timeValue); - // Set cooldown - CooldownService.createCooldown(playerUuid, cooldownType, Config.cooldownTime); + // Send success message + String timeDisplayName = getTimeDisplayName(timeType); + MessageUtil.sendSuccess(player, "Время установлено: §6" + timeDisplayName); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TopCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TopCommand.java index bc41a3c..5b481d8 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TopCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/deluxe/TopCommand.java @@ -17,6 +17,8 @@ import org.itqop.HubmcEssentials.util.LocationUtil; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /top command - Teleport to the highest block above player. * Permission: hubmc.cmd.top @@ -48,50 +50,56 @@ public class TopCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - MessageUtil.sendInfo(player, "Поиск самого высокого блока..."); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownTop).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Find highest block - ServerLevel level = player.serverLevel(); - BlockPos currentPos = player.blockPosition(); - int highestY = findHighestBlock(level, currentPos.getX(), currentPos.getZ()); + // NOW perform action - find highest block and teleport + MessageUtil.sendInfo(player, "Поиск самого высокого блока..."); - if (highestY == -1) { - MessageUtil.sendError(player, "Не удалось найти безопасную локацию"); - return; - } + // Find highest block + ServerLevel level = player.serverLevel(); + BlockPos currentPos = player.blockPosition(); + int highestY = findHighestBlock(level, currentPos.getX(), currentPos.getZ()); - // Save current location for /back - LocationStorage.saveLocation(player); + if (highestY == -1) { + MessageUtil.sendError(player, "Не удалось найти безопасную локацию"); + return; + } - // Teleport player - boolean success = LocationUtil.teleportPlayer( - player, - level, - currentPos.getX() + 0.5, - highestY + 1, - currentPos.getZ() + 0.5 - ); + // Save current location for /back + LocationStorage.saveLocation(player); - if (success) { - MessageUtil.sendSuccess(player, "Телепортация на самый высокий блок: " + - MessageUtil.formatCoords(currentPos.getX(), highestY + 1, currentPos.getZ())); + // Teleport player + boolean teleportSuccess = LocationUtil.teleportPlayer( + player, + level, + currentPos.getX() + 0.5, + highestY + 1, + currentPos.getZ() + 0.5 + ); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownTop); - } else { - MessageUtil.sendError(player, "Ошибка телепортации"); - } + if (teleportSuccess) { + MessageUtil.sendSuccess(player, "Телепортация на самый высокий блок: " + + MessageUtil.formatCoords(currentPos.getX(), highestY + 1, currentPos.getZ())); + } else { + MessageUtil.sendError(player, "Ошибка телепортации"); + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/general/ClearCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/general/ClearCommand.java index 1d0e15f..cbef7fa 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/general/ClearCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/general/ClearCommand.java @@ -8,6 +8,8 @@ import net.minecraft.server.level.ServerPlayer; import org.itqop.HubmcEssentials.Config; import org.itqop.HubmcEssentials.api.dto.cooldown.CooldownCheckResponse; import org.itqop.HubmcEssentials.api.service.CooldownService; + +import java.util.concurrent.CompletableFuture; import org.itqop.HubmcEssentials.permission.PermissionManager; import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; @@ -24,7 +26,7 @@ public class ClearCommand { public static void register(CommandDispatcher dispatcher) { dispatcher.register(Commands.literal("clear") - .requires(source -> source.isPlayer()) + .requires(CommandSourceStack::isPlayer) .executes(ClearCommand::execute)); } @@ -43,23 +45,29 @@ public class ClearCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Clear inventory - player.getInventory().clearContent(); - MessageUtil.sendSuccess(player, "Инвентарь очищен"); + // Set cooldown FIRST to prevent race condition + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownClear) + .thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownClear); + // NOW perform action (cooldown already set) + player.getInventory().clearContent(); + MessageUtil.sendSuccess(player, "Инвентарь очищен"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/general/EcCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/general/EcCommand.java index 7311a39..3a6850b 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/general/EcCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/general/EcCommand.java @@ -16,6 +16,8 @@ import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /ec command - Open player's ender chest. * Permission: hubmc.cmd.ec @@ -46,25 +48,30 @@ public class EcCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Open ender chest - player.openMenu(new SimpleMenuProvider( - (id, playerInventory, p) -> ChestMenu.threeRows(id, playerInventory, player.getEnderChestInventory()), - Component.literal("Эндер-сундук") - )); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownEc).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownEc); + // NOW perform action - open ender chest + player.openMenu(new SimpleMenuProvider( + (id, playerInventory, p) -> ChestMenu.threeRows(id, playerInventory, player.getEnderChestInventory()), + Component.literal("Эндер-сундук") + )); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/general/HatCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/general/HatCommand.java index 3dcb75a..08ed8d8 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/general/HatCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/general/HatCommand.java @@ -15,6 +15,8 @@ import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /hat command - Wear held item as a hat. * Permission: hubmc.cmd.hat @@ -52,28 +54,33 @@ public class HatCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Get current helmet - ItemStack currentHelmet = player.getItemBySlot(EquipmentSlot.HEAD); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownHat).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Swap hand item with helmet - player.setItemSlot(EquipmentSlot.HEAD, handItem.copy()); - player.setItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND, currentHelmet); + // NOW perform action - swap hand item with helmet + ItemStack currentHelmet = player.getItemBySlot(EquipmentSlot.HEAD); - MessageUtil.sendSuccess(player, "Предмет надет на голову"); + // Swap hand item with helmet + player.setItemSlot(EquipmentSlot.HEAD, handItem.copy()); + player.setItemInHand(net.minecraft.world.InteractionHand.MAIN_HAND, currentHelmet); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownHat); + MessageUtil.sendSuccess(player, "Предмет надет на голову"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/premium/RepairAllCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/premium/RepairAllCommand.java index 353fb31..664b37f 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/premium/RepairAllCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/premium/RepairAllCommand.java @@ -13,6 +13,8 @@ import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /repair all command - Repair all items in inventory and armor. * Permission: hubmc.cmd.repair.all @@ -46,46 +48,51 @@ public class RepairAllCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Repair all items - int repairedCount = 0; - - // Repair inventory items - for (int i = 0; i < player.getInventory().getContainerSize(); i++) { - ItemStack item = player.getInventory().getItem(i); - if (!item.isEmpty() && item.isDamageableItem() && item.isDamaged()) { - item.setDamageValue(0); - repairedCount++; + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRepairAll).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; } - } - // Repair armor - for (ItemStack armorItem : player.getArmorSlots()) { - if (!armorItem.isEmpty() && armorItem.isDamageableItem() && armorItem.isDamaged()) { - armorItem.setDamageValue(0); - repairedCount++; + // NOW perform action - repair all items + int repairedCount = 0; + + // Repair inventory items + for (int i = 0; i < player.getInventory().getContainerSize(); i++) { + ItemStack item = player.getInventory().getItem(i); + if (!item.isEmpty() && item.isDamageableItem() && item.isDamaged()) { + item.setDamageValue(0); + repairedCount++; + } } - } - if (repairedCount == 0) { - MessageUtil.sendInfo(player, "Нет поврежденных предметов"); - return; - } + // Repair armor + for (ItemStack armorItem : player.getArmorSlots()) { + if (!armorItem.isEmpty() && armorItem.isDamageableItem() && armorItem.isDamaged()) { + armorItem.setDamageValue(0); + repairedCount++; + } + } - MessageUtil.sendSuccess(player, "Отремонтировано предметов: " + repairedCount); + if (repairedCount == 0) { + MessageUtil.sendInfo(player, "Нет поврежденных предметов"); + return; + } - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRepairAll); + MessageUtil.sendSuccess(player, "Отремонтировано предметов: " + repairedCount); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/BackCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/BackCommand.java index c036b55..8831c5b 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/BackCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/BackCommand.java @@ -18,6 +18,8 @@ import org.itqop.HubmcEssentials.util.LocationUtil; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /back command - Teleport to last location. * Permission: hubmc.cmd.back @@ -55,64 +57,69 @@ public class BackCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Get last location - LocationStorage.LastLocation lastLoc = LocationStorage.getLastLocation(player); - if (lastLoc == null) { - MessageUtil.sendError(player, "Нет сохраненной позиции для возврата"); - return; - } + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownBack).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Parse world dimension - ResourceLocation worldLocation = ResourceLocation.tryParse(lastLoc.getWorldId()); - if (worldLocation == null) { - MessageUtil.sendError(player, "Неверный мир"); - return; - } + // NOW perform action - get last location and teleport + LocationStorage.LastLocation lastLoc = LocationStorage.getLastLocation(player); + if (lastLoc == null) { + MessageUtil.sendError(player, "Нет сохраненной позиции для возврата"); + return; + } - ResourceKey worldKey = ResourceKey.create( - Registries.DIMENSION, - worldLocation - ); + // Parse world dimension + ResourceLocation worldLocation = ResourceLocation.tryParse(lastLoc.getWorldId()); + if (worldLocation == null) { + MessageUtil.sendError(player, "Неверный мир"); + return; + } - ServerLevel targetLevel = context.getSource().getServer().getLevel(worldKey); - if (targetLevel == null) { - MessageUtil.sendError(player, "Мир не найден"); - return; - } + ResourceKey worldKey = ResourceKey.create( + Registries.DIMENSION, + worldLocation + ); - // Save current location before teleporting - LocationStorage.saveLocation(player); + ServerLevel targetLevel = context.getSource().getServer().getLevel(worldKey); + if (targetLevel == null) { + MessageUtil.sendError(player, "Мир не найден"); + return; + } - // Teleport player - boolean success = LocationUtil.teleportPlayer( - player, - targetLevel, - lastLoc.getX(), - lastLoc.getY(), - lastLoc.getZ(), - lastLoc.getYaw(), - lastLoc.getPitch() - ); + // Save current location before teleporting + LocationStorage.saveLocation(player); - if (success) { - MessageUtil.sendSuccess(player, "Телепортация на последнюю позицию"); - } else { - MessageUtil.sendError(player, "Ошибка телепортации"); - } + // Teleport player + boolean teleportSuccess = LocationUtil.teleportPlayer( + player, + targetLevel, + lastLoc.getX(), + lastLoc.getY(), + lastLoc.getZ(), + lastLoc.getYaw(), + lastLoc.getPitch() + ); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownBack); + if (teleportSuccess) { + MessageUtil.sendSuccess(player, "Телепортация на последнюю позицию"); + } else { + MessageUtil.sendError(player, "Ошибка телепортации"); + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/FeedCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/FeedCommand.java index ab7594c..cdbba03 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/FeedCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/FeedCommand.java @@ -7,6 +7,8 @@ import net.minecraft.commands.Commands; import net.minecraft.server.level.ServerPlayer; import org.itqop.HubmcEssentials.Config; import org.itqop.HubmcEssentials.api.service.CooldownService; + +import java.util.concurrent.CompletableFuture; import org.itqop.HubmcEssentials.permission.PermissionManager; import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; @@ -43,25 +45,31 @@ public class FeedCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Feed player - player.getFoodData().setFoodLevel(20); - player.getFoodData().setSaturation(20.0f); + // Set cooldown FIRST to prevent race condition + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownFeed) + .thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - MessageUtil.sendSuccess(player, "Вы сыты"); + // NOW perform action (cooldown already set) + player.getFoodData().setFoodLevel(20); + player.getFoodData().setSaturation(20.0f); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownFeed); + MessageUtil.sendSuccess(player, "Вы сыты"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/HealCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/HealCommand.java index 73c64ff..18a2841 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/HealCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/HealCommand.java @@ -6,6 +6,8 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.server.level.ServerPlayer; import org.itqop.HubmcEssentials.Config; + +import java.util.concurrent.CompletableFuture; import org.itqop.HubmcEssentials.api.service.CooldownService; import org.itqop.HubmcEssentials.permission.PermissionManager; import org.itqop.HubmcEssentials.permission.PermissionNodes; @@ -43,29 +45,35 @@ public class HealCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Heal player - player.setHealth(player.getMaxHealth()); - player.getFoodData().setFoodLevel(20); - player.getFoodData().setSaturation(20.0f); + // Set cooldown FIRST to prevent race condition + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownHeal) + .thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Remove negative effects (optional) - player.removeAllEffects(); + // NOW perform action (cooldown already set) + player.setHealth(player.getMaxHealth()); + player.getFoodData().setFoodLevel(20); + player.getFoodData().setSaturation(20.0f); - MessageUtil.sendSuccess(player, "Вы полностью исцелены"); + // Remove negative effects (optional) + player.removeAllEffects(); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownHeal); + MessageUtil.sendSuccess(player, "Вы полностью исцелены"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/NearCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/NearCommand.java index cc87a31..86e133b 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/NearCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/NearCommand.java @@ -14,6 +14,7 @@ import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; import java.util.List; +import java.util.concurrent.CompletableFuture; /** * /near [radius] command - Show nearby players. @@ -52,34 +53,39 @@ public class NearCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Find nearby players - List nearbyPlayers = PlayerUtil.getPlayersNear(player, radius); - - if (nearbyPlayers.isEmpty()) { - MessageUtil.sendInfo(player, "Поблизости нет игроков (радиус: " + radius + " блоков)"); - } else { - MessageUtil.sendInfo(player, "§eИгроки поблизости (радиус: " + radius + " блоков):"); - - for (ServerPlayer nearby : nearbyPlayers) { - double distance = player.distanceTo(nearby); - String distanceStr = MessageUtil.formatDistance(distance); - MessageUtil.sendInfo(player, " §7- §f" + nearby.getName().getString() + " §7(" + distanceStr + ")"); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownNear).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; } - } - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownNear); + // NOW perform action - find nearby players + List nearbyPlayers = PlayerUtil.getPlayersNear(player, radius); + + if (nearbyPlayers.isEmpty()) { + MessageUtil.sendInfo(player, "Поблизости нет игроков (радиус: " + radius + " блоков)"); + } else { + MessageUtil.sendInfo(player, "§eИгроки поблизости (радиус: " + radius + " блоков):"); + + for (ServerPlayer nearby : nearbyPlayers) { + double distance = player.distanceTo(nearby); + String distanceStr = MessageUtil.formatDistance(distance); + MessageUtil.sendInfo(player, " §7- §f" + nearby.getName().getString() + " §7(" + distanceStr + ")"); + } + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/RepairCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/RepairCommand.java index dbfd0cf..04278c9 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/RepairCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/RepairCommand.java @@ -13,6 +13,8 @@ import org.itqop.HubmcEssentials.permission.PermissionNodes; import org.itqop.HubmcEssentials.util.MessageUtil; import org.itqop.HubmcEssentials.util.PlayerUtil; +import java.util.concurrent.CompletableFuture; + /** * /repair command - Repair item in hand. * Permission: hubmc.cmd.repair @@ -63,24 +65,28 @@ public class RepairCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } - // Repair item - handItem.setDamageValue(0); + // Set cooldown FIRST + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRepair).thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - MessageUtil.sendSuccess(player, "Предмет отремонтирован"); - - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRepair); + // NOW perform action - repair item + handItem.setDamageValue(0); + MessageUtil.sendSuccess(player, "Предмет отремонтирован"); + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/command/vip/RtpCommand.java b/src/main/java/org/itqop/HubmcEssentials/command/vip/RtpCommand.java index 34c8ae8..d416393 100644 --- a/src/main/java/org/itqop/HubmcEssentials/command/vip/RtpCommand.java +++ b/src/main/java/org/itqop/HubmcEssentials/command/vip/RtpCommand.java @@ -7,6 +7,8 @@ import net.minecraft.commands.Commands; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import org.itqop.HubmcEssentials.Config; + +import java.util.concurrent.CompletableFuture; import org.itqop.HubmcEssentials.api.service.CooldownService; import org.itqop.HubmcEssentials.permission.PermissionManager; import org.itqop.HubmcEssentials.permission.PermissionNodes; @@ -53,51 +55,58 @@ public class RtpCommand { String playerUuid = PlayerUtil.getUUIDString(player); // Check cooldown - CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenAccept(cooldownResponse -> { + CooldownService.checkCooldown(playerUuid, COOLDOWN_TYPE).thenCompose(cooldownResponse -> { if (cooldownResponse == null) { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); - return; + return CompletableFuture.completedFuture(null); } if (cooldownResponse.isActive()) { MessageUtil.sendCooldownMessage(player, cooldownResponse.getRemainingSeconds()); - return; + return CompletableFuture.completedFuture(null); } MessageUtil.sendInfo(player, "Поиск безопасной локации..."); - // Find random safe location + // Find random safe location (synchronous, fast operation) ServerLevel level = player.serverLevel(); Optional randomLoc = findSafeRandomLocation(level); if (randomLoc.isEmpty()) { MessageUtil.sendError(player, "Не удалось найти безопасную локацию"); - return; + return CompletableFuture.completedFuture(null); } RandomLocation loc = randomLoc.get(); - // Save current location for /back - LocationStorage.saveLocation(player); + // Set cooldown FIRST to prevent race condition (only if location found) + return CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRtp) + .thenAccept(success -> { + if (!success) { + MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); + return; + } - // Teleport player - boolean success = LocationUtil.teleportPlayer( - player, - level, - loc.x, - loc.y, - loc.z - ); + // NOW perform teleport (cooldown already set) + // Save current location for /back + LocationStorage.saveLocation(player); - if (success) { - MessageUtil.sendSuccess(player, "Телепортация на случайную локацию: " + - MessageUtil.formatCoords(loc.x, loc.y, loc.z)); - } else { - MessageUtil.sendError(player, "Ошибка телепортации"); - } + // Teleport player + boolean tpSuccess = LocationUtil.teleportPlayer( + player, + level, + loc.x, + loc.y, + loc.z + ); - // Set cooldown - CooldownService.createCooldown(playerUuid, COOLDOWN_TYPE, Config.cooldownRtp); + if (tpSuccess) { + MessageUtil.sendSuccess(player, "Телепортация на случайную локацию: " + + MessageUtil.formatCoords(loc.x, loc.y, loc.z)); + } else { + MessageUtil.sendError(player, "Ошибка телепортации"); + } + }); }).exceptionally(ex -> { MessageUtil.sendError(player, MessageUtil.API_UNAVAILABLE); return null; diff --git a/src/main/java/org/itqop/HubmcEssentials/util/LocationUtil.java b/src/main/java/org/itqop/HubmcEssentials/util/LocationUtil.java index b36bb1d..a5dee57 100644 --- a/src/main/java/org/itqop/HubmcEssentials/util/LocationUtil.java +++ b/src/main/java/org/itqop/HubmcEssentials/util/LocationUtil.java @@ -155,6 +155,27 @@ public final class LocationUtil { if (feet.is(Blocks.FIRE) || head.is(Blocks.FIRE)) { return false; } + if (ground.is(Blocks.MAGMA_BLOCK)) { + return false; + } + if (ground.is(Blocks.CAMPFIRE) || ground.is(Blocks.SOUL_CAMPFIRE)) { + return false; + } + if (feet.is(Blocks.CACTUS) || head.is(Blocks.CACTUS) || ground.is(Blocks.CACTUS)) { + return false; + } + if (feet.is(Blocks.SWEET_BERRY_BUSH) || ground.is(Blocks.SWEET_BERRY_BUSH)) { + return false; + } + if (feet.is(Blocks.WITHER_ROSE)) { + return false; + } + if (feet.is(Blocks.POWDER_SNOW) || head.is(Blocks.POWDER_SNOW)) { + return false; + } + if (feet.is(Blocks.POINTED_DRIPSTONE) || head.is(Blocks.POINTED_DRIPSTONE)) { + return false; + } return true; }