diff --git a/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java b/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java index 46d93bd7..4feda8d7 100644 --- a/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java +++ b/src/main/java/net/tfminecraft/simplefactions/SimpleFactions.java @@ -1093,7 +1093,8 @@ private void registerVehicleIntegration(boolean full) { vehicleRegistryClaimService, vehicleFeeStore, vehicleFeeConfirmations, this::saveVehicleFees, getLogger()), this); } else { - getServer().getPluginManager().registerEvents(new VehicleClaimsOff(), this); + getServer().getPluginManager().registerEvents( + net.tfminecraft.simplefactions.vehicles.owners.VehicleClaimGuards.create(), this); } // VehicleFramework loads its vehicles as it enables, so wait before reading owners. getServer().getScheduler().runTaskLater(this, this::recordVehicleOwners, 200L); diff --git a/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/LegacyVehicleClaimsOff.java b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/LegacyVehicleClaimsOff.java new file mode 100644 index 00000000..1888258d --- /dev/null +++ b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/LegacyVehicleClaimsOff.java @@ -0,0 +1,15 @@ +package net.tfminecraft.simplefactions.vehicles.owners; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +import net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent; + +/** Compatibility bridge, loaded only when the provider lacks the modern event. */ +public final class LegacyVehicleClaimsOff implements Listener { + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onClaim(VehicleOwnerClaimedEvent event) { + VehicleClaimGuards.refuse(event.getPlayer(), event); + } +} diff --git a/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimGuards.java b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimGuards.java new file mode 100644 index 00000000..5e4dfde2 --- /dev/null +++ b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimGuards.java @@ -0,0 +1,40 @@ +package net.tfminecraft.simplefactions.vehicles.owners; + +import org.bukkit.entity.Player; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Listener; + +import net.tfminecraft.simplefactions.utils.Permissions; + +/** Selects a claim guard without loading listener types absent from the installed provider. */ +public final class VehicleClaimGuards { + public static final String MESSAGE = "§cClaiming vehicles is off until VehicleFramework is updated."; + + private VehicleClaimGuards() {} + + public static Listener create() { + return create("net.tfminecraft.vehicleframework.events.VehicleOwnershipChangeEvent", + "net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent"); + } + + static Listener create(String modernEvent, String legacyEvent) { + if (present(modernEvent)) return new VehicleClaimsOff(); + if (present(legacyEvent)) return new LegacyVehicleClaimsOff(); + throw new IllegalStateException("VehicleFramework has no supported claim event"); + } + + private static boolean present(String eventClass) { + try { + Class.forName(eventClass); + return true; + } catch (ClassNotFoundException | LinkageError missing) { + return false; + } + } + + static void refuse(Player player, Cancellable event) { + if (player == null || Permissions.isAdmin(player)) return; + event.setCancelled(true); + player.sendMessage(MESSAGE); + } +} diff --git a/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOff.java b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOff.java index 91ceaed2..de031dd0 100644 --- a/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOff.java +++ b/src/main/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOff.java @@ -4,21 +4,21 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import net.tfminecraft.simplefactions.utils.Permissions; -import net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent; +import net.tfminecraft.vehicleframework.api.OwnershipReason; +import net.tfminecraft.vehicleframework.events.VehicleOwnershipChangeEvent; /** * Without VehicleFramework 2.13 in characters mode, SimpleFactions cannot record a - * character payer for a claim, so non-staff claims are refused. The claim event exists - * in every VehicleFramework version, so this listener loads on older ones too. + * character payer for a claim, so non-staff claims are refused. + * Load this listener only after probing the modern ownership event. */ public final class VehicleClaimsOff implements Listener { - public static final String MESSAGE = "§cClaiming vehicles is off until VehicleFramework is updated."; + public static final String MESSAGE = VehicleClaimGuards.MESSAGE; @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void onClaim(VehicleOwnerClaimedEvent event) { - if (event.getPlayer() == null || Permissions.isAdmin(event.getPlayer())) return; - event.setCancelled(true); - event.getPlayer().sendMessage(MESSAGE); + public void onClaim(VehicleOwnershipChangeEvent event) { + if (event.getReason() == OwnershipReason.CLAIM) { + VehicleClaimGuards.refuse(event.getPlayer(), event); + } } } diff --git a/src/test/java/net/tfminecraft/simplefactions/managers/PlayerManagerCoverageTest.java b/src/test/java/net/tfminecraft/simplefactions/managers/PlayerManagerCoverageTest.java index bf39b892..2c129d5e 100644 --- a/src/test/java/net/tfminecraft/simplefactions/managers/PlayerManagerCoverageTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/managers/PlayerManagerCoverageTest.java @@ -403,7 +403,6 @@ void materialDepositsConsumeOnlySupportedMaterialsAtAnOwnedBank(String state) { assertEquals(state.equals("deposit") ? 0 : 7, material.getAmount()); long credited = net.tfminecraft.denareconomy.api.Denars.balance(net.tfminecraft.denareconomy.api.Denars.bank(ref)); assertEquals(87.5, credited / 100d + material.getAmount() * coin.getValue()); - verify(money, never()).addMoneyToAccount(anyString(), anyDouble(), anyBoolean(), anyBoolean(), any()); } @ParameterizedTest diff --git a/src/test/java/net/tfminecraft/simplefactions/vehicles/berth/VehicleInteractionsCoverageTest.java b/src/test/java/net/tfminecraft/simplefactions/vehicles/berth/VehicleInteractionsCoverageTest.java index 989eb6d2..b89be37b 100644 --- a/src/test/java/net/tfminecraft/simplefactions/vehicles/berth/VehicleInteractionsCoverageTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/vehicles/berth/VehicleInteractionsCoverageTest.java @@ -86,7 +86,6 @@ import net.tfminecraft.vehicleframework.database.VehicleSnapshot; import net.tfminecraft.vehicleframework.enums.VehicleDeath; import net.tfminecraft.vehicleframework.enums.VehicleRemoveReason; -import net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent; import net.tfminecraft.vehicleframework.events.VehiclePreInteractEvent; import net.tfminecraft.vehicleframework.events.VehicleRemoveEvent; import net.tfminecraft.vehicleframework.events.VehicleSpawnEvent; diff --git a/src/test/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOffTest.java b/src/test/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOffTest.java index 9091c312..cf7e7008 100644 --- a/src/test/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOffTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/vehicles/owners/VehicleClaimsOffTest.java @@ -1,15 +1,14 @@ package net.tfminecraft.simplefactions.vehicles.owners; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import java.util.UUID; import org.bukkit.entity.Player; import org.junit.jupiter.api.Test; - +import net.tfminecraft.vehicleframework.api.OwnershipReason; import net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent; +import net.tfminecraft.vehicleframework.events.VehicleOwnershipChangeEvent; import net.tfminecraft.vehicleframework.vehicles.ActiveVehicle; class VehicleClaimsOffTest { @@ -19,18 +18,51 @@ void withoutCharacterOwnershipOnlyStaffMayClaim() { Player player = mock(Player.class); Player staff = mock(Player.class); when(staff.hasPermission("simplefactions.admin")).thenReturn(true); - ActiveVehicle vehicle = mock(ActiveVehicle.class); - - VehicleOwnerClaimedEvent claim = new VehicleOwnerClaimedEvent(player, vehicle, "none", "player_Steve"); + VehicleOwnershipChangeEvent claim = claim(player, OwnershipReason.CLAIM); guard.onClaim(claim); - VehicleOwnerClaimedEvent staffClaim = new VehicleOwnerClaimedEvent(staff, vehicle, "none", "player_Admin"); + VehicleOwnershipChangeEvent staffClaim = claim(staff, OwnershipReason.CLAIM); guard.onClaim(staffClaim); - VehicleOwnerClaimedEvent nobody = new VehicleOwnerClaimedEvent(null, vehicle, "none", "player_x"); - guard.onClaim(nobody); - + VehicleOwnershipChangeEvent other = claim(player, OwnershipReason.TAKEOVER); + guard.onClaim(other); assertTrue(claim.isCancelled()); verify(player).sendMessage(VehicleClaimsOff.MESSAGE); assertFalse(staffClaim.isCancelled()); - assertFalse(nobody.isCancelled()); + assertFalse(other.isCancelled()); + } + + @Test + void legacyBridgeKeepsTheOldStaffAndMissingPlayerPolicy() { + LegacyVehicleClaimsOff guard = new LegacyVehicleClaimsOff(); + Player player = mock(Player.class); + Player staff = mock(Player.class); + when(staff.hasPermission("simplefactions.admin")).thenReturn(true); + VehicleOwnerClaimedEvent claim = new VehicleOwnerClaimedEvent(player, null, "none", "player_A"); + VehicleOwnerClaimedEvent allowed = new VehicleOwnerClaimedEvent(staff, null, "none", "player_Staff"); + VehicleOwnerClaimedEvent absent = new VehicleOwnerClaimedEvent(null, null, "none", "player_X"); + guard.onClaim(claim); + guard.onClaim(allowed); + guard.onClaim(absent); + assertTrue(claim.isCancelled()); + verify(player).sendMessage(VehicleClaimsOff.MESSAGE); + assertFalse(allowed.isCancelled()); + assertFalse(absent.isCancelled()); + } + + @Test + void selectsModernBeforeLegacyAndRefusesAnUnknownProvider() { + String legacy = VehicleOwnerClaimedEvent.class.getName(); + assertInstanceOf(VehicleClaimsOff.class, VehicleClaimGuards.create()); + assertInstanceOf(LegacyVehicleClaimsOff.class, VehicleClaimGuards.create("missing.Modern", legacy)); + assertInstanceOf(LegacyVehicleClaimsOff.class, VehicleClaimGuards.create(BrokenEvent.class.getName(), legacy)); + assertThrows(IllegalStateException.class, () -> VehicleClaimGuards.create("missing.Modern", "missing.Legacy")); + } + + static final class BrokenEvent { + static { if (true) throw new NoClassDefFoundError("missing dependency"); } + } + + private static VehicleOwnershipChangeEvent claim(Player player, OwnershipReason reason) { + return new VehicleOwnershipChangeEvent(player, mock(ActiveVehicle.class), reason, UUID.randomUUID(), + null, false, null, null, "none", "player_A"); } } diff --git a/src/test/java/net/tfminecraft/simplefactions/vehicles/registry/VehicleRegistryClaimListenerTest.java b/src/test/java/net/tfminecraft/simplefactions/vehicles/registry/VehicleRegistryClaimListenerTest.java index 7bc0a437..5e1e289e 100644 --- a/src/test/java/net/tfminecraft/simplefactions/vehicles/registry/VehicleRegistryClaimListenerTest.java +++ b/src/test/java/net/tfminecraft/simplefactions/vehicles/registry/VehicleRegistryClaimListenerTest.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import net.tfminecraft.simplefactions.loaders.VehiclesConfigLoader; -import net.tfminecraft.vehicleframework.events.VehicleOwnerClaimedEvent; +import net.tfminecraft.vehicleframework.events.VehiclePreInteractEvent; class VehicleRegistryClaimListenerTest { private Path tempDir; @@ -52,8 +52,8 @@ void tearDown() throws IOException { @Test void handleClaimResult_cancelsOnSlotLimit() { Player player = mock(Player.class); - VehicleOwnerClaimedEvent event = - new VehicleOwnerClaimedEvent(player, null, "none", "player_Test"); + VehiclePreInteractEvent event = + new VehiclePreInteractEvent(player, null); VehicleRegistryClaimListener.handleClaimResult( event, @@ -68,8 +68,8 @@ void handleClaimResult_cancelsOnSlotLimit() { @Test void handleClaimResult_allowsRegistered() { Player player = mock(Player.class); - VehicleOwnerClaimedEvent event = - new VehicleOwnerClaimedEvent(player, null, "none", "player_Test"); + VehiclePreInteractEvent event = + new VehiclePreInteractEvent(player, null); VehicleRegistryClaimListener.handleClaimResult( event,