Compare commits

...
Author SHA1 Message Date
Martin Sulikowski 4da0952879 wip, not working lands api is weird 2025-09-23 23:43:46 +02:00
Martin Sulikowski f02df36752 Add support for lands war's. 2025-09-21 21:39:43 +02:00
13 changed files with 446 additions and 14 deletions

No files matched your search

@@ -1,6 +1,12 @@
package com.eternalcode.combat.region;
import org.bukkit.Location;
import org.bukkit.World;
public record Point(World world, double x, double z) {
public Location toLocation() {
return new Location(world, x, 64, z);
}
}
@@ -0,0 +1,11 @@
package com.eternalcode.combat.region.access;
import com.eternalcode.combat.region.Region;
import org.bukkit.entity.Player;
public interface RegionAccessController {
boolean canEnter(Player player, Region region);
}
+6 -2
View File
@@ -74,8 +74,12 @@ bukkit {
tasks {
runServer {
minecraftVersion("1.21.4")
downloadPlugins.url("https://github.com/retrooper/packetevents/releases/download/v2.8.0/packetevents-spigot-2.8.0.jar")
minecraftVersion("1.21.8")
downloadPlugins {
url("https://github.com/retrooper/packetevents/releases/download/v2.9.5/packetevents-spigot-2.9.5.jar")
modrinth("WorldGuard", "7.0.14")
modrinth("FastAsyncWorldEdit", "2.13.1")
}
}
}
@@ -45,6 +45,8 @@ import com.eternalcode.combat.fight.tagout.FightTagOutCommand;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.fight.knockback.KnockbackRegionController;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.region.access.RegionAccessConfig;
import com.eternalcode.combat.region.access.RegionEntryGuard;
import com.eternalcode.combat.updater.UpdaterNotificationController;
import com.eternalcode.combat.updater.UpdaterService;
import com.eternalcode.commons.adventure.AdventureLegacyColorPostProcessor;
@@ -102,6 +104,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
Scheduler scheduler = new BukkitSchedulerImpl(this);
PluginConfig pluginConfig = configService.create(PluginConfig.class, new File(dataFolder, "config.yml"));
RegionAccessConfig regionAccessConfig = configService.create(RegionAccessConfig.class, new File(dataFolder, "region-access.yml"));
this.fightManager = new FightManagerImpl(eventManager);
this.fightPearlService = new FightPearlServiceImpl(pluginConfig.pearl);
@@ -128,12 +131,15 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
server.getPluginManager(),
this.getLogger(),
this,
this.fightManager
this.fightManager,
regionAccessConfig
);
bridgeService.init(server);
this.regionProvider = bridgeService.getRegionProvider();
BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border);
RegionEntryGuard regionEntryGuard = bridgeService.getRegionEntryGuard();
BorderService borderService = new BorderServiceImpl(scheduler, server, regionProvider, eventManager, () -> pluginConfig.border, regionEntryGuard);
KnockbackService knockbackService = new KnockbackService(pluginConfig, scheduler, regionProvider);
this.liteCommands = LiteBukkitFactory.builder(FALLBACK_PREFIX, this, server)
@@ -172,7 +178,7 @@ public final class CombatPlugin extends JavaPlugin implements EternalCombatApi {
new FightActionBlockerController(this.fightManager, noticeService, pluginConfig, server),
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),
new KnockbackRegionController(noticeService, this.regionProvider, this.fightManager, knockbackService, server, regionEntryGuard),
new FightEffectController(pluginConfig.effect, this.fightEffectService, this.fightManager, this.getServer()),
new FightTagOutController(this.fightTagOutService),
new FightMessageController(this.fightManager, noticeService, pluginConfig, this.getServer()),
@@ -3,7 +3,9 @@ package com.eternalcode.combat.border;
import com.eternalcode.combat.border.event.BorderHideAsyncEvent;
import com.eternalcode.combat.border.event.BorderShowAsyncEvent;
import com.eternalcode.combat.event.EventManager;
import com.eternalcode.combat.region.Region;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.region.access.RegionEntryGuard;
import com.eternalcode.commons.scheduler.Scheduler;
import dev.rollczi.litecommands.shared.Lazy;
import java.util.ArrayList;
@@ -21,22 +23,36 @@ public class BorderServiceImpl implements BorderService {
private final Scheduler scheduler;
private final EventManager eventManager;
private final RegionProvider regionProvider;
private final RegionEntryGuard regionEntryGuard;
private final Supplier<BorderSettings> settings;
private final BorderTriggerIndex borderIndexes;
private final BorderActivePointsIndex activeBorderIndex = new BorderActivePointsIndex();
public BorderServiceImpl(Scheduler scheduler, Server server, RegionProvider provider, EventManager eventManager, Supplier<BorderSettings> settings) {
public BorderServiceImpl(Scheduler scheduler, Server server, RegionProvider provider, EventManager eventManager, Supplier<BorderSettings> settings, RegionEntryGuard regionEntryGuard) {
this.scheduler = scheduler;
this.eventManager = eventManager;
this.regionProvider = provider;
this.regionEntryGuard = regionEntryGuard;
this.settings = settings;
this.borderIndexes = BorderTriggerIndex.started(server, scheduler, provider, settings);
}
@Override
public void updateBorder(Player player, Location location) {
Optional<BorderResult> result = resolveBorder(location);
// Zawsze sprawdź uprawnienia gracza - nawet jeśli BorderTriggerIndex nie ma triggerów
Optional<Region> regionOptional = this.regionProvider.getRegion(location);
if (regionOptional.isPresent() && this.regionEntryGuard.canEnter(player, regionOptional.get())) {
// Gracz może wejść - usuń wszystkie aktywne bordery
if (this.activeBorderIndex.hasPoints(player.getWorld().getName(), player.getUniqueId())) {
this.clearBorder(player);
}
return;
}
Optional<BorderResult> result = resolveBorder(player, location);
String world = player.getWorld().getName();
if (result.isEmpty()) {
@@ -83,13 +99,15 @@ public class BorderServiceImpl implements BorderService {
return this.activeBorderIndex.getPoints(player.getWorld().getName(), player.getUniqueId());
}
private Optional<BorderResult> resolveBorder(Location location) {
private Optional<BorderResult> resolveBorder(Player player, Location location) {
List<BorderTrigger> triggered = borderIndexes.getTriggered(location);
if (triggered.isEmpty()) {
return Optional.empty();
}
// Uprawnienia już sprawdzone w updateBorder()
BorderLazyResult result = new BorderLazyResult();
for (BorderTrigger trigger : triggered) {
result.addLazyBorderPoints(new Lazy<>(() -> this.resolveBorderPoints(trigger, location)));
@@ -27,6 +27,7 @@ public class BorderTriggerController implements Listener {
this.server = server;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
void onMove(PlayerMoveEvent event) {
if (!border.get().isEnabled()) {
@@ -58,7 +59,9 @@ public class BorderTriggerController implements Listener {
return;
}
borderService.updateBorder(player, event.getTo());
Location targetLocation = event.getTo();
borderService.updateBorder(player, targetLocation);
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
@@ -72,6 +75,7 @@ public class BorderTriggerController implements Listener {
return;
}
// Sprawdź uprawnienia przed pokazaniem bordera
borderService.updateBorder(player, player.getLocation());
}
@@ -1,12 +1,17 @@
package com.eternalcode.combat.bridge;
import com.eternalcode.combat.region.lands.LandsRegionProvider;
import com.eternalcode.combat.bridge.placeholder.FightTagPlaceholder;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.region.CompositeRegionProvider;
import com.eternalcode.combat.region.bukkit.DefaultRegionProvider;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.region.access.DefaultRegionAccessController;
import com.eternalcode.combat.region.access.RegionAccessConfig;
import com.eternalcode.combat.region.access.RegionAccessController;
import com.eternalcode.combat.region.access.RegionEntryGuard;
import com.eternalcode.combat.region.access.lands.LandsRegionAccessAdapter;
import com.eternalcode.combat.region.bukkit.DefaultRegionProvider;
import com.eternalcode.combat.region.lands.LandsRegionProvider;
import com.eternalcode.combat.region.worldguard.WorldGuardRegionProvider;
import java.util.ArrayList;
import java.util.List;
@@ -23,21 +28,25 @@ public class BridgeService {
private final Logger logger;
private final Plugin plugin;
private final FightManager fightManager;
private final RegionAccessConfig regionAccessConfig;
private RegionProvider regionProvider;
private RegionEntryGuard regionEntryGuard;
public BridgeService(
PluginConfig config,
PluginManager pluginManager,
Logger logger,
Plugin plugin,
FightManager fightManager
FightManager fightManager,
RegionAccessConfig regionAccessConfig
) {
this.config = config;
this.pluginManager = pluginManager;
this.logger = logger;
this.plugin = plugin;
this.fightManager = fightManager;
this.regionAccessConfig = regionAccessConfig;
}
public void init(Server server) {
@@ -51,7 +60,7 @@ public class BridgeService {
this.initialize(
"WorldGuard",
() -> providers.add( new WorldGuardRegionProvider(this.config)),
() -> providers.add(new WorldGuardRegionProvider(this.config)),
() -> this.logger.warning("WorldGuard not found; skipping WorldGuardRegionProvider.")
);
@@ -72,6 +81,8 @@ public class BridgeService {
() -> new FightTagPlaceholder(this.fightManager, server, this.plugin).register(),
() -> this.logger.warning("PlaceholderAPI not found; skipping placeholders.")
);
this.initializeRegionAccessControllers();
}
private void initialize(String pluginName, BridgeInitializer initializer, Runnable onFailure) {
@@ -84,7 +95,43 @@ public class BridgeService {
}
}
private void initializeRegionAccessControllers() {
List<RegionAccessController> controllers = new ArrayList<>();
this.initialize(
"Lands",
() -> {
try {
RegionAccessController landsController =
new LandsRegionAccessAdapter(this.plugin, this.regionAccessConfig);
controllers.add(landsController);
this.logger.info("Registered Lands region access controller");
}
catch (Exception exception) {
this.logger.warning("Failed to register Lands region access controller: " + exception.getMessage());
}
},
() -> this.logger.info("Lands not found; skipping Lands region access controller.")
);
if (controllers.isEmpty()) {
controllers.add(new DefaultRegionAccessController(this.regionAccessConfig));
this.logger.info("No region access controllers available, using default controller");
}
this.regionEntryGuard = new RegionEntryGuard(controllers, this.regionAccessConfig);
this.logger.info("Initialized region access system with: " +
controllers.stream()
.map(c -> c.getClass().getSimpleName())
.collect(Collectors.joining(", "))
);
}
public RegionProvider getRegionProvider() {
return this.regionProvider;
}
public RegionEntryGuard getRegionEntryGuard() {
return this.regionEntryGuard;
}
}
@@ -5,6 +5,7 @@ import com.eternalcode.combat.fight.event.FightTagEvent;
import com.eternalcode.combat.notification.NoticeService;
import com.eternalcode.combat.region.Region;
import com.eternalcode.combat.region.RegionProvider;
import com.eternalcode.combat.region.access.RegionEntryGuard;
import java.time.Duration;
import java.util.Optional;
import org.bukkit.Location;
@@ -23,13 +24,20 @@ public class KnockbackRegionController implements Listener {
private final FightManager fightManager;
private final KnockbackService knockbackService;
private final Server server;
private final RegionEntryGuard regionEntryGuard;
public KnockbackRegionController(NoticeService noticeService, RegionProvider regionProvider, FightManager fightManager, KnockbackService knockbackService, Server server) {
public KnockbackRegionController(NoticeService noticeService, RegionProvider regionProvider, FightManager fightManager, KnockbackService knockbackService, Server server, RegionEntryGuard regionEntryGuard) {
this.noticeService = noticeService;
this.regionProvider = regionProvider;
this.fightManager = fightManager;
this.knockbackService = knockbackService;
this.server = server;
this.regionEntryGuard = regionEntryGuard;
}
private boolean shouldKnockback(Player player, Location targetLocation) {
Optional<Region> regionOptional = this.regionProvider.getRegion(targetLocation);
return regionOptional.filter(region -> this.regionEntryGuard.canEnter(player, region)).isPresent();
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
@@ -55,6 +63,10 @@ public class KnockbackRegionController implements Listener {
return;
}
if (!this.shouldKnockback(player, locationTo)) {
return;
}
Region region = regionOptional.get();
if (region.contains(locationFrom)) {
this.knockbackService.knockback(region, player);
@@ -81,6 +93,10 @@ public class KnockbackRegionController implements Listener {
Location targetLocation = event.getTo();
if (this.regionProvider.isInRegion(targetLocation)) {
if (!this.shouldKnockback(player, targetLocation)) {
return;
}
event.setCancelled(true);
this.noticeService.create()
.player(player.getUniqueId())
@@ -101,6 +117,10 @@ public class KnockbackRegionController implements Listener {
return;
}
if (!this.shouldKnockback(player, player.getLocation())) {
return;
}
Region region = regionOptional.get();
this.knockbackService.knockback(region, player);
this.knockbackService.forceKnockbackLater(player, region);
@@ -0,0 +1,19 @@
package com.eternalcode.combat.region.access;
import com.eternalcode.combat.region.Region;
import org.bukkit.entity.Player;
public class DefaultRegionAccessController implements RegionAccessController {
private final RegionAccessConfig config;
public DefaultRegionAccessController(RegionAccessConfig config) {
this.config = config;
}
@Override
public boolean canEnter(Player player, Region region) {
return config.defaultAllow;
}
}
@@ -0,0 +1,15 @@
package com.eternalcode.combat.region.access;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
public class RegionAccessConfig extends OkaeriConfig {
@Comment("Enable wartime region access bypass")
public boolean wartimeEnabled = true;
@Comment("Default behavior when no provider is available")
public boolean defaultAllow = false;
}
@@ -0,0 +1,25 @@
package com.eternalcode.combat.region.access;
import com.eternalcode.combat.region.Region;
import java.util.List;
import org.bukkit.entity.Player;
public class RegionEntryGuard {
private final List<RegionAccessController> controllers;
private final RegionAccessConfig config;
public RegionEntryGuard(List<RegionAccessController> controllers, RegionAccessConfig config) {
this.controllers = controllers;
this.config = config;
}
public boolean canEnter(Player player, Region region) {
if (controllers.isEmpty()) {
return config.defaultAllow;
}
return controllers.stream().allMatch(controller -> controller.canEnter(player, region));
}
}
@@ -0,0 +1,91 @@
package com.eternalcode.combat.region.access.lands;
import com.eternalcode.combat.region.Region;
import com.eternalcode.combat.region.access.RegionAccessConfig;
import com.eternalcode.combat.region.access.RegionAccessController;
import me.angeschossen.lands.api.LandsIntegration;
import me.angeschossen.lands.api.land.Land;
import me.angeschossen.lands.api.nation.Nation;
import me.angeschossen.lands.api.player.LandPlayer;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class LandsRegionAccessAdapter implements RegionAccessController {
private final LandsIntegration lands;
private final RegionAccessConfig config;
public LandsRegionAccessAdapter(Plugin plugin, RegionAccessConfig config) {
this.lands = LandsIntegration.of(plugin);
this.config = config;
}
@Override
public boolean canEnter(Player player, Region region) {
if (!config.wartimeEnabled) {
return false;
}
LandPlayer landPlayer = lands.getLandPlayer(player.getUniqueId());
if (landPlayer == null) {
return false;
}
Location regionLocation = getRegionLocation(region);
if (regionLocation == null) {
return false;
}
Land targetLand = lands.getLandByChunk(
regionLocation.getWorld(),
regionLocation.getBlockX() >> 4,
regionLocation.getBlockZ() >> 4
);
if (targetLand == null) {
return false;
}
boolean isOwner = landPlayer.getLands().contains(targetLand);
if (isOwner) {
return true;
}
boolean atWar = areNationsAtWar(landPlayer, targetLand);
boolean result = atWar;
return result;
}
private boolean areNationsAtWar(LandPlayer landPlayer, Land targetLand) {
try {
Nation playerNation = getPlayerNation(landPlayer);
Nation targetNation = targetLand.getNation();
return playerNation != null && targetNation != null &&
(playerNation.isEnemy(targetNation) || targetNation.isEnemy(playerNation));
}
catch (Exception e) {
return false;
}
}
private Nation getPlayerNation(LandPlayer landPlayer) {
return landPlayer.getLands().stream()
.filter(land -> land != null)
.map(Land::getNation)
.filter(nation -> nation != null)
.findFirst()
.orElse(null);
}
private Location getRegionLocation(Region region) {
try {
return region.getCenter().toLocation();
}
catch (Exception exception) {
return null;
}
}
}
@@ -0,0 +1,166 @@
package com.eternalcode.combat.region.access.lands;
import com.eternalcode.combat.region.Region;
import com.eternalcode.combat.region.Point;
import com.eternalcode.combat.region.access.RegionAccessConfig;
import me.angeschossen.lands.api.LandsIntegration;
import me.angeschossen.lands.api.land.Land;
import me.angeschossen.lands.api.nation.Nation;
import me.angeschossen.lands.api.player.LandPlayer;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class LandsRegionAccessAdapterTest {
@Mock
private Plugin plugin;
@Mock
private RegionAccessConfig config;
@Mock
private Logger logger;
@Mock
private LandsIntegration lands;
@Mock
private Player player;
@Mock
private Region region;
@Mock
private Point point;
@Mock
private World world;
@Mock
private Location location;
@Mock
private LandPlayer landPlayer;
@Mock
private Land targetLand;
@Mock
private Land playerLand;
@Mock
private Nation playerNation;
@Mock
private Nation targetNation;
private LandsRegionAccessAdapter adapter;
@BeforeEach
void setUp() {
when(config.wartimeEnabled).thenReturn(true);
when(config.bypassPermission).thenReturn("eternalcombat.bypass.region");
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
when(region.getCenter()).thenReturn(point);
when(point.toLocation()).thenReturn(location);
when(location.getWorld()).thenReturn(world);
when(location.getBlockX()).thenReturn(100);
when(location.getBlockZ()).thenReturn(200);
adapter = new LandsRegionAccessAdapter(lands, config, logger);
}
@Test
void canEnter_WhenWartimeDisabled_ShouldReturnFalse() {
when(config.wartimeEnabled).thenReturn(false);
boolean result = adapter.canEnter(player, region);
assertFalse(result);
}
@Test
void canEnter_WhenPlayerHasBypassPermission_ShouldReturnTrue() {
when(player.hasPermission("eternalcombat.bypass.region")).thenReturn(true);
boolean result = adapter.canEnter(player, region);
assertTrue(result);
}
@Test
void canEnter_WhenPlayerNotInAnyLand_ShouldReturnFalse() {
when(lands.getLandPlayer(any(UUID.class))).thenReturn(null);
boolean result = adapter.canEnter(player, region);
assertFalse(result);
}
@Test
void canEnter_WhenNoTargetLand_ShouldReturnTrue() {
when(lands.getLandPlayer(any(UUID.class))).thenReturn(landPlayer);
when(lands.getLandByChunk(any(World.class), anyInt(), anyInt())).thenReturn(null);
boolean result = adapter.canEnter(player, region);
assertTrue(result);
}
@Test
void canEnter_WhenPlayerInOwnLand_ShouldReturnTrue() {
when(lands.getLandPlayer(any(UUID.class))).thenReturn(landPlayer);
when(lands.getLandByChunk(any(World.class), anyInt(), anyInt())).thenReturn(targetLand);
when(landPlayer.getLands()).thenReturn(Set.of(targetLand));
boolean result = adapter.canEnter(player, region);
assertTrue(result);
}
@Test
void canEnter_WhenNationsAtWar_ShouldReturnTrue() {
when(lands.getLandPlayer(any(UUID.class))).thenReturn(landPlayer);
when(lands.getLandByChunk(any(World.class), anyInt(), anyInt())).thenReturn(targetLand);
when(landPlayer.getLands()).thenReturn(Set.of(playerLand));
when(playerLand.getNation()).thenReturn(playerNation);
when(targetLand.getNation()).thenReturn(targetNation);
when(playerNation.isEnemy(targetNation)).thenReturn(true);
boolean result = adapter.canEnter(player, region);
assertTrue(result);
}
@Test
void canEnter_WhenNationsNotAtWar_ShouldReturnFalse() {
when(lands.getLandPlayer(any(UUID.class))).thenReturn(landPlayer);
when(lands.getLandByChunk(any(World.class), anyInt(), anyInt())).thenReturn(targetLand);
when(landPlayer.getLands()).thenReturn(Set.of(playerLand));
when(playerLand.getNation()).thenReturn(playerNation);
when(targetLand.getNation()).thenReturn(targetNation);
when(playerNation.isEnemy(targetNation)).thenReturn(false);
when(targetNation.isEnemy(playerNation)).thenReturn(false);
boolean result = adapter.canEnter(player, region);
assertFalse(result);
}
}