1414import org .bukkit .Material ;
1515import org .bukkit .block .BlockFace ;
1616import org .bukkit .entity .Entity ;
17+ import org .bukkit .entity .Item ;
1718import org .bukkit .entity .Player ;
1819import org .bukkit .event .EventHandler ;
1920import org .bukkit .event .EventPriority ;
2223import org .bukkit .event .entity .EntityDamageEvent .DamageCause ;
2324import org .bukkit .event .entity .EntityDismountEvent ;
2425import org .bukkit .event .entity .EntityMountEvent ;
26+ import org .bukkit .event .entity .PlayerDeathEvent ;
27+ import org .bukkit .event .player .PlayerDropItemEvent ;
2528import org .bukkit .event .player .PlayerJoinEvent ;
2629import org .bukkit .event .player .PlayerMoveEvent ;
2730import org .bukkit .event .player .PlayerQuitEvent ;
2831import org .bukkit .event .player .PlayerRespawnEvent ;
2932import org .bukkit .event .player .PlayerTeleportEvent ;
3033import org .bukkit .event .player .PlayerTeleportEvent .TeleportCause ;
3134import org .bukkit .event .vehicle .VehicleMoveEvent ;
35+ import org .bukkit .scheduler .BukkitRunnable ;
3236import org .bukkit .scheduler .BukkitTask ;
3337import org .bukkit .util .NumberConversions ;
3438import org .bukkit .util .RayTraceResult ;
3539import org .bukkit .util .Vector ;
3640
37- import world .bentobox .bentobox .BentoBox ;
3841import world .bentobox .bentobox .api .events .island .IslandProtectionRangeChangeEvent ;
3942import world .bentobox .bentobox .api .flags .Flag ;
4043import world .bentobox .bentobox .api .metadata .MetaDataValue ;
4144import world .bentobox .bentobox .api .user .User ;
45+ import world .bentobox .bentobox .database .objects .Island ;
4246import world .bentobox .bentobox .util .Util ;
4347import world .bentobox .border .Border ;
4448import world .bentobox .border .BorderType ;
@@ -111,7 +115,7 @@ public void onPlayerDamage(EntityDamageEvent e) {
111115 }
112116 Material type = p .getLocation ().getBlock ().getRelative (BlockFace .DOWN ).getType ();
113117 if (type == Material .AIR ) {
114- ((BorderShower ) show ).teleportPlayer ( p );
118+ ((BorderShower ) show ).teleportEntity ( addon , p );
115119 e .setCancelled (true );
116120 }
117121 }
@@ -204,8 +208,13 @@ public void onPlayerLeaveIsland(PlayerMoveEvent e) {
204208 Util .teleportAsync (p , from ).thenRun (() -> inTeleport .remove (p .getUniqueId ()));
205209 return ;
206210 }
207- // Backtrack
208- addon .getIslands ().getIslandAt (p .getLocation ()).ifPresent (i -> {
211+ // Backtrack - try to find island at current location, or fall back to the player's own island
212+ Optional <Island > optionalIsland = addon .getIslands ().getIslandAt (p .getLocation ());
213+ if (optionalIsland .isEmpty ()) {
214+ optionalIsland = Optional
215+ .ofNullable (addon .getIslands ().getIsland (p .getWorld (), User .getInstance (p )));
216+ }
217+ optionalIsland .ifPresent (i -> {
209218 Vector unitVector = i .getProtectionCenter ().toVector ().subtract (p .getLocation ().toVector ()).normalize ()
210219 .multiply (new Vector (1 ,0 ,1 ));
211220 if (unitVector .lengthSquared () <= 0D ) {
@@ -259,7 +268,10 @@ private boolean outsideCheck(Player player, Location from, Location to) {
259268 || !user .getMetaData (BorderShower .BORDER_STATE_META_DATA ).map (MetaDataValue ::asBoolean ).orElse (addon .getSettings ().isShowByDefault ())) {
260269 return false ;
261270 }
262- return addon .getIslands ().getIslandAt (to ).filter (i -> !i .onIsland (to )).isPresent ();
271+ Optional <Island > islandAt = addon .getIslands ().getIslandAt (to );
272+ // Player is outside if they are on an island but not within its protection zone,
273+ // or if they are not on any island at all (e.g., pushed out by piston)
274+ return islandAt .isEmpty () || islandAt .filter (i -> !i .onIsland (to )).isPresent ();
263275 }
264276
265277 /**
@@ -338,8 +350,8 @@ public void onVehicleMove(VehicleMoveEvent e) {
338350 // Remove head movement
339351 if (!e .getFrom ().toVector ().equals (e .getTo ().toVector ())) {
340352 e .getVehicle ().getPassengers ().stream ().filter (Player .class ::isInstance ).map (Player .class ::cast )
341- .filter (this ::isOn ).forEach (p -> addon .getIslands ().getIslandAt (p .getLocation ())
342- .ifPresent (i -> show .refreshView (User .getInstance (p ), i )));
353+ .filter (this ::isOn ).forEach (p -> addon .getIslands ().getIslandAt (p .getLocation ())
354+ .ifPresent (i -> show .refreshView (User .getInstance (p ), i )));
343355 }
344356 }
345357
@@ -357,4 +369,61 @@ public void onProtectionRangeChange(IslandProtectionRangeChangeEvent e) {
357369 }
358370 });
359371 }
372+
373+ /**
374+ * Bounces items back to inside the barrier if thrown by a player
375+ * @param event event
376+ */
377+ @ EventHandler (priority = EventPriority .NORMAL , ignoreCancelled = true )
378+ public void onItemDrop (PlayerDropItemEvent event ) {
379+ if (addon .getSettings ().isBounceBack ()
380+ && addon .inGameWorld (event .getPlayer ().getWorld ())
381+ && isOn (event .getPlayer ())
382+ ) {
383+ // Get this island
384+ addon .getIslands ().getIslandAt (event .getPlayer ().getLocation ()).ifPresent (is -> trackItem (event .getItemDrop (), is ));
385+ }
386+ }
387+
388+ /**
389+ * Bounces items back to inside the barrier if dropped when a player dies
390+ * @param event event
391+ */
392+ @ EventHandler (priority = EventPriority .NORMAL , ignoreCancelled = true )
393+ public void onPlayerDeath (PlayerDeathEvent event ) {
394+ if (addon .getSettings ().isBounceBack ()
395+ && addon .inGameWorld (event .getPlayer ().getWorld ())
396+ && isOn (event .getPlayer ())) {
397+ // Get this island
398+ addon .getIslands ().getIslandAt (event .getPlayer ().getLocation ()).ifPresent (is -> {
399+ event .getDrops ().forEach (item -> trackItem (event .getPlayer ().getWorld ().dropItemNaturally (event .getPlayer ().getLocation (), item ), is ));
400+ event .getDrops ().clear (); // We handled them
401+ });
402+ }
403+ }
404+
405+ private void trackItem (Item item , Island island ) {
406+ new BukkitRunnable () {
407+ int ticksActive = 0 ;
408+
409+ @ Override
410+ public void run () {
411+ // Stop tracking if the item is picked up, despawned, or 20 seconds have passed
412+ if (!item .isValid () || ticksActive > 400 ) {
413+ this .cancel ();
414+ return ;
415+ }
416+
417+ Location loc = item .getLocation ();
418+ // Check if the item is going outside the border
419+ if (!island .onIsland (loc )) {
420+ // Reverse the direction
421+ item .setVelocity (item .getVelocity ().multiply (-0.5 ));
422+ this .cancel ();
423+ }
424+ ticksActive ++;
425+ }
426+ }.runTaskTimer (addon .getPlugin (), 1L , 2L ); // Check every 2 ticks (0.1 seconds)
427+ }
428+
360429}
0 commit comments