Запомни код
package ru.dragonsfixer;

import com.google.inject.Inject;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.slf4j.Logger;
import org.yaml.snakeyaml.Yaml;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

@Plugin(
        id = "dragonsfixer",
        name = "DragonsFixer",
        version = "1.2",
        description = "Антиспам плагин для Velocity",
        authors = {"boboliii"}
)
public class DragonsFixer {
    private final ProxyServer server;
    private final Path dataDirectory;
    private final Map<UUID, Long> cooldowns = new ConcurrentHashMap<>();
    private final Map<UUID, Long> blockedUntil = new ConcurrentHashMap<>();
    private final Map<UUID, Integer> warningCounts = new ConcurrentHashMap<>();

    private int cooldownTime = 500;
    private Component spamMessage = Component.text("DragonsGrief » Не спамь!")
            .color(NamedTextColor.RED);
    private int maxWarnings = 15;
    private long blockTime = 3500L;
    private int kickAfterWarnings = 20;
    private Component kickMessage = Component.text("DragonsGrief » Вы были кикнуты за спам командами")
            .color(NamedTextColor.RED);

    @Inject
    private Logger logger;

    @Inject
    public DragonsFixer(ProxyServer server, @DataDirectory Path dataDirectory) {
        this.server = server;
        this.dataDirectory = dataDirectory;
    }

    @Subscribe
    public void onProxyInitialization(ProxyInitializeEvent event) {
        try {
            initializeConfiguration();
        } catch (Exception e) {
            logger.error("Ошибка инициализации конфигурации", e);
            setDefaultConfig();
        }
    }

    private void initializeConfiguration() throws IOException {
        Files.createDirectories(dataDirectory);
        Path configPath = dataDirectory.resolve("config.yml");

        if (Files.notExists(configPath)) {
            Optional.ofNullable(getClass().getResourceAsStream("/config.yml"))
                    .ifPresentOrElse(input -> {
                        try {
                            Files.copy(input, configPath);
                            input.close();
                        } catch (IOException e) {
                            logger.error("Не удалось скопировать конфигурацию", e);
                        }
                    }, () -> logger.warn("Дефолтный конфиг не найден"));
        }

        loadConfiguration(configPath);
    }

    private void loadConfiguration(Path configPath) throws IOException {
        try (var input = Files.newInputStream(configPath)) {
            Yaml yaml = new Yaml();
            Map<String, Object> config = yaml.load(input);

            cooldownTime = getIntValue(config, "cooldown", 500);
            spamMessage = parseMessage(config, "spam-message", "§6DragonsGrief §7» §c§lНе спамь!");
            maxWarnings = getIntValue(config, "max-warnings", 15);
            blockTime = getLongValue(config, "block-time", 3500L);
            kickAfterWarnings = getIntValue(config, "kick-after-warnings", 20);
            kickMessage = parseMessage(config, "kick-message", "§6DragonsGrief §7» §cВы были кикнуты за превышение количества предупреждений.");
        }
    }

    private void setDefaultConfig() {
        logger.warn("Установлена стандартная конфигурация");
    }

    @Subscribe
    public void onCommand(CommandExecuteEvent event) {
        var commandSource = event.getCommandSource();
        if (commandSource instanceof Player player) {
            UUID playerId = player.getUniqueId();

            if (isBlockedOrInCooldown(playerId)) {
                event.setResult(CommandResult.denied());
                player.sendMessage(spamMessage);
                return;
            }

            // Увеличиваем счетчик предупреждений и проверяем условия кика
            int warnings = warningCounts.merge(playerId, 1, Integer::sum);

            if (warnings >= kickAfterWarnings) {
                // Кикаем игрока и очищаем его данные
                player.disconnect(kickMessage);
                cleanupPlayerData(playerId);
                event.setResult(CommandResult.denied());
                return;
            }

            if (warnings >= maxWarnings) {
                blockPlayer(playerId);
                event.setResult(CommandResult.denied());
                player.sendMessage(spamMessage);
                return;
            }

            cooldowns.put(playerId, System.currentTimeMillis() + cooldownTime);
        }
    }

    private boolean isBlockedOrInCooldown(UUID playerId) {
        long currentTime = System.currentTimeMillis();
        return Optional.ofNullable(cooldowns.get(playerId))
                .map(cooldownEnd -> currentTime < cooldownEnd)
                .orElse(false) ||
                Optional.ofNullable(blockedUntil.get(playerId))
                        .map(blockEnd -> currentTime < blockEnd)
                        .orElse(false);
    }

    private void blockPlayer(UUID playerId) {
        long blockEndTime = System.currentTimeMillis() + blockTime;
        blockedUntil.put(playerId, blockEndTime);

        server.getScheduler().buildTask(this, () -> {
            blockedUntil.remove(playerId);
            warningCounts.put(playerId, maxWarnings - 1);
        }).delay(Duration.ofMillis(blockTime)).schedule();
    }

    private void cleanupPlayerData(UUID playerId) {
        warningCounts.remove(playerId);
        blockedUntil.remove(playerId);
        cooldowns.remove(playerId);
    }

    private int getIntValue(Map<String, Object> config, String key, int defaultValue) {
        return Optional.ofNullable(config.get(key))
                .map(value -> value instanceof Number num ? num.intValue() : defaultValue)
                .orElse(defaultValue);
    }

    private long getLongValue(Map<String, Object> config, String key, long defaultValue) {
        return Optional.ofNullable(config.get(key))
                .map(value -> value instanceof Number num ? num.longValue() : defaultValue)
                .orElse(defaultValue);
    }

    private Component parseMessage(Map<String, Object> config, String key, String defaultMessage) {
        String message = Optional.ofNullable(config.get(key))
                .map(Object::toString)
                .orElse(defaultMessage);
        return LegacyComponentSerializer.legacyAmpersand().deserialize(message);
    }
}

config.yml
# Время кулдауна в миллисекундах на все команды
# 1000 = 1 секунда
# 10000 = 10 секунд
cooldown: 500

# Максимальное количество предупреждений, после которого игрок будет заблокирован от ввода команд
max-warnings: 15

# Время блокировки команд в миллисекундах
# 1500 = 1.5 секунды
# 5000 = 5 секунд
block-time: 3500

# Максимальное количество предупреждений, после которого игрок будет кикнут
kick-after-warnings: 20

spam-message: "§6DragonsGrief §7» §c§lНе спамь!"

# Сообщение при кике за превышение предупреждений
kick-message: "&cВы были кикнуты за спам командами!"


Проблема в том что игрок пишет 40 комманд за 1 секунду и ему отправляется 40 предупреждений но НЕ КИКАЕТ!. Улучши код и сделай чтобы игрока кикало