Compare commits

...
Author SHA1 Message Date
Martin Sulikowski 68a6ce2350 Fix. 2026-02-15 20:26:47 +01:00
Martin Sulikowski 5aa8d0a07c Fix. 2026-02-15 20:25:00 +01:00
Martin Sulikowski ed0faea066 fix: block namespaced commands during combat and add regression test 2026-02-15 20:22:01 +01:00
6 changed files with 156 additions and 32 deletions

No files matched your search

@@ -7,6 +7,7 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT_JUPITER_API}")
testImplementation("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT_JUPITER_PARAMS}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT_JUPITER_ENGINE}")
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.0.2")
}
tasks.test {
+1
View File
@@ -4,6 +4,7 @@ import org.gradle.kotlin.dsl.shadowJar
plugins {
`eternalcombat-java`
`eternalcombat-java-unit-test`
`eternalcombat-repositories`
id("net.minecrell.plugin-yml.bukkit")
@@ -11,6 +11,7 @@ import com.eternalcode.combat.crystalpvp.EndCrystalListener;
import com.eternalcode.combat.fight.controller.FightBypassAdminController;
import com.eternalcode.combat.fight.controller.FightBypassCreativeController;
import com.eternalcode.combat.fight.controller.FightBypassPermissionController;
import com.eternalcode.combat.fight.controller.FightCommandController;
import com.eternalcode.combat.fight.controller.FightInventoryController;
import com.eternalcode.combat.fight.drop.DropKeepInventoryService;
import com.eternalcode.combat.fight.FightManager;
@@ -180,6 +181,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
new FightBypassPermissionController(server, pluginConfig),
new FightBypassCreativeController(server, pluginConfig),
new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server),
new FightCommandController(this.fightManager, noticeService, pluginConfig),
new FightPearlController(pluginConfig.pearl, noticeService, this.fightManager, this.fightPearlService),
new UpdaterNotificationController(updaterService, pluginConfig, this.audienceProvider, miniMessage),
new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server),
@@ -1,7 +1,6 @@
package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.WhitelistBlacklistMode;
import com.eternalcode.combat.config.implementation.BlockPlacementSettings;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.FightUntagEvent;
@@ -16,15 +15,11 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityToggleGlideEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import java.util.List;
import java.util.UUID;
import org.bukkit.util.StringUtil;
public class FightActionBlockerController implements Listener {
@@ -186,31 +181,4 @@ public class FightActionBlockerController implements Listener {
}
}
@EventHandler
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
UUID playerUniqueId = player.getUniqueId();
if (!this.fightManager.isInCombat(playerUniqueId)) {
return;
}
String command = event.getMessage().substring(1);
boolean isAnyMatch = this.config.commands.restrictedCommands.stream()
.anyMatch(restrictedCommand -> StringUtil.startsWithIgnoreCase(command, restrictedCommand));
WhitelistBlacklistMode mode = this.config.commands.commandRestrictionMode;
boolean shouldCancel = mode.shouldBlock(isAnyMatch);
if (shouldCancel) {
event.setCancelled(true);
this.noticeService.create()
.player(playerUniqueId)
.notice(this.config.messagesSettings.commandDisabledDuringCombat)
.send();
}
}
}
@@ -0,0 +1,114 @@
package com.eternalcode.combat.fight.controller;
import com.eternalcode.combat.WhitelistBlacklistMode;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.notification.NoticeService;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
public class FightCommandController implements Listener {
private static final char SLASH = '/';
private static final char NAMESPACE_SEPARATOR = ':';
private static final char SPACE = ' ';
private static final String EMPTY = "";
private static final String SINGLE_SPACE = " ";
private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\s+");
private final FightManager fightManager;
private final NoticeService noticeService;
private final PluginConfig config;
public FightCommandController(FightManager fightManager, NoticeService noticeService, PluginConfig config) {
this.fightManager = fightManager;
this.noticeService = noticeService;
this.config = config;
}
static String normalizeCommand(String command) {
if (command == null || command.isBlank()) {
return EMPTY;
}
String normalized = command.strip();
if (normalized.charAt(0) == SLASH) {
normalized = normalized.substring(1).stripLeading();
}
if (normalized.isEmpty()) {
return EMPTY;
}
int colonIndex = normalized.indexOf(NAMESPACE_SEPARATOR);
int firstSpaceIndex = normalized.indexOf(SPACE);
if (colonIndex >= 0 && (firstSpaceIndex < 0 || colonIndex < firstSpaceIndex)) {
normalized = normalized.substring(colonIndex + 1);
}
return WHITESPACE_PATTERN.matcher(normalized).replaceAll(SINGLE_SPACE);
}
static boolean matches(String command, List<String> restrictedCommands) {
if (command.isEmpty()) {
return false;
}
for (String restrictedCommand : restrictedCommands) {
String normalizedRestricted = normalizeCommand(restrictedCommand);
if (normalizedRestricted.isEmpty()) {
continue;
}
boolean exactMatch = command.equalsIgnoreCase(normalizedRestricted);
boolean prefixMatch = command.regionMatches(true, 0, normalizedRestricted, 0, normalizedRestricted.length())
&& command.length() > normalizedRestricted.length()
&& command.charAt(normalizedRestricted.length()) == SPACE;
if (exactMatch || prefixMatch) {
return true;
}
}
return false;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
Player player = event.getPlayer();
UUID uniqueId = player.getUniqueId();
if (!this.fightManager.isInCombat(uniqueId)) {
return;
}
String command = normalizeCommand(event.getMessage());
if (command.isEmpty()) {
return;
}
boolean matched = matches(command, this.config.commands.restrictedCommands);
WhitelistBlacklistMode mode = this.config.commands.commandRestrictionMode;
if (!mode.shouldBlock(matched)) {
return;
}
event.setCancelled(true);
this.noticeService.create()
.player(uniqueId)
.notice(this.config.messagesSettings.commandDisabledDuringCombat)
.send();
}
}
@@ -0,0 +1,38 @@
package com.eternalcode.combat.fight.controller;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import org.junit.jupiter.api.Test;
class FightCommandControllerTest {
@Test
void normalizeCommand_stripsSlashAndNamespace_collapsesWhitespace() {
assertEquals("tp Steve", FightCommandController.normalizeCommand("/minecraft:tp Steve"));
assertEquals("home set 1", FightCommandController.normalizeCommand("/home set 1"));
assertEquals("spawn", FightCommandController.normalizeCommand("/spawn"));
assertEquals("", FightCommandController.normalizeCommand("/ "));
assertEquals("", FightCommandController.normalizeCommand(null));
}
@Test
void matches_exactAndPrefixMatch_caseInsensitive() {
List<String> restricted = List.of("home set", "tp", " ", "/minecraft:msg admin");
assertTrue(FightCommandController.matches("home set", restricted));
assertTrue(FightCommandController.matches("home set 1", restricted));
assertTrue(FightCommandController.matches("TP", restricted));
assertTrue(FightCommandController.matches("tp Steve", restricted));
assertTrue(FightCommandController.matches("msg admin", restricted));
assertTrue(FightCommandController.matches("msg admin hello", restricted));
assertFalse(FightCommandController.matches("home setup", restricted));
assertFalse(FightCommandController.matches("tpper", restricted));
assertFalse(FightCommandController.matches("", restricted));
}
}