From 6f08780b477530e6dee9c9a0d5101e2d0fe0b405 Mon Sep 17 00:00:00 2001 From: Gerwin Verkerk <43728830+GerwinVerkerk@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:03:29 +0000 Subject: [PATCH] fix: support Lands 8 team identities --- Hooks/Team-Lands/build.gradle.kts | 13 ++- .../maxlego08/koth/hook/teams/LandHook.java | 33 +++++-- .../koth/hook/teams/LandHookTest.java | 91 +++++++++++++++++++ 3 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 Hooks/Team-Lands/src/test/java/fr/maxlego08/koth/hook/teams/LandHookTest.java diff --git a/Hooks/Team-Lands/build.gradle.kts b/Hooks/Team-Lands/build.gradle.kts index 43d0f54..18a0660 100644 --- a/Hooks/Team-Lands/build.gradle.kts +++ b/Hooks/Team-Lands/build.gradle.kts @@ -1,3 +1,14 @@ dependencies { - compileOnly("com.github.angeschossen:LandsAPI:6.44.6") + // Latest public Lands 8 API artifact. + compileOnly("com.incredibleplugins:lands-api:8.0.0") + testImplementation(project(":API")) + testImplementation("io.papermc.paper:paper-api:26.2.build.62-beta") + testImplementation("com.incredibleplugins:lands-api:8.0.0") + testImplementation("org.junit.jupiter:junit-jupiter:5.13.4") + testRuntimeOnly("org.junit.platform:junit-platform-launcher:1.13.4") + testImplementation("org.mockito:mockito-core:5.18.0") +} + +tasks.test { + useJUnitPlatform() } diff --git a/Hooks/Team-Lands/src/main/java/fr/maxlego08/koth/hook/teams/LandHook.java b/Hooks/Team-Lands/src/main/java/fr/maxlego08/koth/hook/teams/LandHook.java index b700bc5..90e84b4 100644 --- a/Hooks/Team-Lands/src/main/java/fr/maxlego08/koth/hook/teams/LandHook.java +++ b/Hooks/Team-Lands/src/main/java/fr/maxlego08/koth/hook/teams/LandHook.java @@ -5,6 +5,8 @@ import me.angeschossen.lands.api.LandsIntegration; import me.angeschossen.lands.api.events.LandDeleteEvent; import me.angeschossen.lands.api.land.Land; +import me.angeschossen.lands.api.land.enums.LandType; +import me.angeschossen.lands.api.player.LandPlayer; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; @@ -18,10 +20,15 @@ public class LandHook implements KothTeam { private final KothPlugin plugin; + private final LandsIntegration lands; public LandHook(KothPlugin plugin) { - super(); + this(plugin, LandsIntegration.of(plugin)); + } + + LandHook(KothPlugin plugin, LandsIntegration lands) { this.plugin = plugin; + this.lands = lands; } @Override @@ -38,12 +45,25 @@ public List getOnlinePlayer(OfflinePlayer player) { return new ArrayList<>(optional.get().getOnlinePlayers()); } - return Collections.singletonList(player.getPlayer()); + Player onlinePlayer = player.getPlayer(); + return onlinePlayer == null ? Collections.emptyList() : Collections.singletonList(onlinePlayer); } private Optional getLandByPlayer(OfflinePlayer player) { - LandsIntegration api = LandsIntegration.of(this.plugin); - return api.getLandPlayer(player.getUniqueId()).getLands().stream().findFirst(); + LandPlayer landPlayer = this.lands.getLandPlayer(player.getUniqueId()); + if (landPlayer == null) return Optional.empty(); + + Land ownedLand = landPlayer.getOwningLand(); + if (isUsableLand(ownedLand)) return Optional.of(ownedLand); + + return landPlayer.getLands().stream() + .filter(this::isUsableLand) + .sorted((left, right) -> left.getULID().toString().compareTo(right.getULID().toString())) + .findFirst(); + } + + private boolean isUsableLand(Land land) { + return land != null && land.exists() && land.getLandType() == LandType.LAND && land.getULID() != null; } @Override @@ -56,12 +76,13 @@ public String getLeaderName(OfflinePlayer player) { @Override public String getTeamId(OfflinePlayer player) { Optional optional = getLandByPlayer(player); - return optional.map(land -> String.valueOf(land.getId())).orElseGet(() -> player.getUniqueId().toString()); + return optional.map(land -> land.getULID().toString()).orElseGet(() -> player.getUniqueId().toString()); } @EventHandler public void onDisband(LandDeleteEvent event) { - this.plugin.onTeamDisband(String.valueOf(event.getLand().getId())); + Land land = event.getLand(); + if (land.getULID() != null) this.plugin.onTeamDisband(land.getULID().toString()); } } diff --git a/Hooks/Team-Lands/src/test/java/fr/maxlego08/koth/hook/teams/LandHookTest.java b/Hooks/Team-Lands/src/test/java/fr/maxlego08/koth/hook/teams/LandHookTest.java new file mode 100644 index 0000000..15526d6 --- /dev/null +++ b/Hooks/Team-Lands/src/test/java/fr/maxlego08/koth/hook/teams/LandHookTest.java @@ -0,0 +1,91 @@ +package fr.maxlego08.koth.hook.teams; + +import fr.maxlego08.koth.api.KothPlugin; +import me.angeschossen.lands.api.LandsIntegration; +import me.angeschossen.lands.api.applicationframework.util.ULID; +import me.angeschossen.lands.api.events.LandDeleteEvent; +import me.angeschossen.lands.api.land.Land; +import me.angeschossen.lands.api.land.enums.LandType; +import me.angeschossen.lands.api.player.LandPlayer; +import org.bukkit.OfflinePlayer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class LandHookTest { + + private final UUID playerId = UUID.fromString("7e8eeb2c-ffce-44ae-8935-692898e84067"); + private KothPlugin plugin; + private LandsIntegration lands; + private OfflinePlayer player; + private LandPlayer landPlayer; + private LandHook hook; + + @BeforeEach + void setUp() { + plugin = mock(KothPlugin.class); + lands = mock(LandsIntegration.class); + player = mock(OfflinePlayer.class); + landPlayer = mock(LandPlayer.class); + when(player.getUniqueId()).thenReturn(playerId); + when(lands.getLandPlayer(playerId)).thenReturn(landPlayer); + hook = new LandHook(plugin, lands); + } + + @Test + void usesCurrentUlidForOwnedLand() { + Land ownedLand = usableLand("01JZKOTHOWNEDLAND0000000000"); + when(landPlayer.getOwningLand()).thenReturn(ownedLand); + + assertEquals("01JZKOTHOWNEDLAND0000000000", hook.getTeamId(player)); + } + + @Test + void choosesStableValidMembershipWhenPlayerOwnsNoLand() { + Land later = usableLand("01JZKOTHZZZZZZZZZZZZZZZZZ"); + Land earlier = usableLand("01JZKOTHAAAAAAAAAAAAAAAAA"); + Land deleted = usableLand("01JZKOTHDELETED0000000000"); + when(deleted.exists()).thenReturn(false); + doReturn(List.of(later, deleted, earlier)).when(landPlayer).getLands(); + + assertEquals("01JZKOTHAAAAAAAAAAAAAAAAA", hook.getTeamId(player)); + } + + @Test + void fallsBackToPlayerUuidWithoutUsableLand() { + Land adminLand = usableLand("01JZKOTHADMIN000000000000"); + when(adminLand.getLandType()).thenReturn(LandType.ADMIN); + doReturn(List.of(adminLand)).when(landPlayer).getLands(); + + assertEquals(playerId.toString(), hook.getTeamId(player)); + } + + @Test + void disbandUsesSameCurrentUlidIdentifier() { + Land land = usableLand("01JZKOTHDISBAND0000000000"); + LandDeleteEvent event = mock(LandDeleteEvent.class); + when(event.getLand()).thenReturn(land); + + hook.onDisband(event); + + verify(plugin).onTeamDisband("01JZKOTHDISBAND0000000000"); + } + + private Land usableLand(String id) { + Land land = mock(Land.class); + ULID ulid = mock(ULID.class); + when(ulid.toString()).thenReturn(id); + when(land.exists()).thenReturn(true); + when(land.getLandType()).thenReturn(LandType.LAND); + when(land.getULID()).thenReturn(ulid); + return land; + } +}