From a024e42b801ba8f3fcdb7e711499bef04d43fdf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20M=C3=BCller?= Date: Thu, 23 Jul 2026 22:58:47 +0200 Subject: [PATCH] fixed NPE and shutdown issues, cleaned up listeners and build config Blocks destroyed before ever being entered no longer NPE on the null entry location (new getReturnLocation fallback), destroy no longer mutates the stored block location, placing an item whose block UUID already exists cancels the event with a message instead of eating the item, and pending task chains are flushed on shutdown. Failed block initialization now propagates instead of leaving half-built blocks registered. Listener null checks replaced requireNonNull, listener and method name typos fixed, and the test-server copy path in build.gradle is now a gradle property instead of a hardcoded home directory. --- build.gradle | 5 ++- .../eu/mhsl/minecraft/pixelblocks/Main.java | 4 +- .../listeners/DiscoverRecipesListener.java | 2 +- .../listeners/ExitPixelWorldListener.java | 7 ++- .../FallOutOfPixelBlockListener.java | 3 +- .../listeners/PlacePixelBlockListener.java | 6 +++ .../listeners/PlacePixelListener.java | 2 +- ...r.java => PreventInventoriesListener.java} | 2 +- .../QuitWhileInPixelBlockListener.java | 3 +- .../pixelblocks/pixelblock/PixelBlock.java | 43 +++++++++++-------- 10 files changed, 48 insertions(+), 29 deletions(-) rename src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/{PreventInventorysListener.java => PreventInventoriesListener.java} (95%) diff --git a/build.gradle b/build.gradle index ee57e5e..ebcb5de 100644 --- a/build.gradle +++ b/build.gradle @@ -45,8 +45,11 @@ processResources { } } +// Zielverzeichnis über testServerPluginsDir in gradle.properties oder -PtestServerPluginsDir=... setzen tasks.register('copyJarToTestServer', Exec) { - commandLine 'cp', 'build/libs/PixelBlocks-1.0-SNAPSHOT-all.jar', '/home/lars/Documents/Minecraft/Server/pixelblocks/plugins/PixelBlocks-1.0-SNAPSHOT-all.jar' + def pluginsDir = providers.gradleProperty('testServerPluginsDir').getOrNull() + onlyIf { pluginsDir != null } + commandLine 'cp', "build/libs/PixelBlocks-${version}-all.jar", "${pluginsDir}/PixelBlocks-${version}-all.jar" } shadowJar { diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java index 798a62b..f864ddf 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/Main.java @@ -16,6 +16,7 @@ import java.io.File; import java.util.List; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.TimeUnit; import java.util.logging.Logger; public final class Main extends JavaPlugin { @@ -61,7 +62,7 @@ public final class Main extends JavaPlugin { new FallOutOfPixelBlockListener(), new BreakPixelListener(), new PlacePixelBlockListener(), - new PreventInventorysListener(), + new PreventInventoriesListener(), new ExitPixelWorldListener(), new PreventIllegalBlocksListener(), new BreakPixelBlockListener(), @@ -91,6 +92,7 @@ public final class Main extends JavaPlugin { @Override public void onDisable() { Bukkit.getOnlinePlayers().forEach(QuitWhileInPixelBlockListener::kickPlayerOutOfWorld); + taskFactory.shutdown(5, TimeUnit.SECONDS); database.close(); } diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java index b46ffc2..71b12c0 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/DiscoverRecipesListener.java @@ -20,7 +20,7 @@ public class DiscoverRecipesListener implements Listener { if(!(event.getWhoClicked() instanceof Player player)) return; if(!List.of(Material.HEART_OF_THE_SEA, Material.END_CRYSTAL).contains(clickedItem.getType())) return; if(player.hasDiscoveredRecipe(PixelBlockItem.recipeKey)) return; - Main.logger().log(Level.INFO, String.format("%s unlocked tne PixelBlock recipe!", player.getName())); + Main.logger().log(Level.INFO, String.format("%s unlocked the PixelBlock recipe!", player.getName())); player.discoverRecipe(PixelBlockItem.recipeKey); } } diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java index a46b27c..59d4999 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/ExitPixelWorldListener.java @@ -1,5 +1,6 @@ package eu.mhsl.minecraft.pixelblocks.listeners; +import eu.mhsl.minecraft.pixelblocks.Main; import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlock; import eu.mhsl.minecraft.pixelblocks.pixelblock.PixelBlockWorld; import org.bukkit.World; @@ -8,7 +9,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.event.player.PlayerPortalEvent; -import java.util.Objects; public class ExitPixelWorldListener implements Listener { @EventHandler @@ -18,7 +18,10 @@ public class ExitPixelWorldListener implements Listener { event.setCancelled(true); PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(pixelBlockWorld); - Objects.requireNonNull(pixelBlock); + if(pixelBlock == null) { + Main.logger().warning("Player used a portal in an unknown pixel world: " + pixelBlockWorld.getName()); + return; + } pixelBlock.exitBlock(event.getPlayer()); } diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java index 3a4a660..0315117 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/FallOutOfPixelBlockListener.java @@ -7,7 +7,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; -import java.util.Objects; public class FallOutOfPixelBlockListener implements Listener { @EventHandler @@ -17,7 +16,7 @@ public class FallOutOfPixelBlockListener implements Listener { if(!PixelBlockWorld.isPixelWorld(player.getWorld())) return; PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(player.getWorld()); - Objects.requireNonNull(pixelBlock); + if(pixelBlock == null) return; player.teleport(pixelBlock.getPixelWorld().getSpawnLocation()); } } diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java index ddaef3e..67e3943 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelBlockListener.java @@ -29,6 +29,12 @@ public class PlacePixelBlockListener implements Listener { return; } + if(PixelBlock.exists(info.id())) { + event.getPlayer().sendMessage(Component.text("Dieser Pixelblock existiert bereits in der Welt!", NamedTextColor.RED)); + event.setCancelled(true); + return; + } + Location newBlockLocation = event.getBlock().getLocation(); playerWorld.getBlockAt(newBlockLocation).setType(Material.AIR); diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java index ba5d54a..ebf47a2 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PlacePixelListener.java @@ -17,7 +17,7 @@ public class PlacePixelListener implements Listener { } @EventHandler - public void onBuketEmpty(PlayerBucketEmptyEvent event) { + public void onBucketEmpty(PlayerBucketEmptyEvent event) { EventCanceling.shouldCancelInPixelBlock( event, event.getBlock().getWorld(), diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventoriesListener.java similarity index 95% rename from src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java rename to src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventoriesListener.java index ea2339e..4a09a9c 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventorysListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/PreventInventoriesListener.java @@ -10,7 +10,7 @@ import org.bukkit.inventory.CraftingInventory; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.PlayerInventory; -public class PreventInventorysListener implements Listener { +public class PreventInventoriesListener implements Listener { @EventHandler public void onInventoryOpen(InventoryOpenEvent event) { EventCanceling.shouldCancelInPixelBlock( diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java index ca03ed2..2d4b7ff 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/listeners/QuitWhileInPixelBlockListener.java @@ -8,7 +8,6 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; -import java.util.Objects; public class QuitWhileInPixelBlockListener implements Listener { @EventHandler @@ -20,7 +19,7 @@ public class QuitWhileInPixelBlockListener implements Listener { World pixelBlockWorld = player.getLocation().getWorld(); if(!PixelBlockWorld.isPixelWorld(pixelBlockWorld)) return; PixelBlock pixelBlock = PixelBlock.getPixelBlockFromBlockWorld(pixelBlockWorld); - Objects.requireNonNull(pixelBlock); + if(pixelBlock == null) return; pixelBlock.exitBlock(player); } } diff --git a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java index 95a25ae..47e114f 100644 --- a/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java +++ b/src/main/java/eu/mhsl/minecraft/pixelblocks/pixelblock/PixelBlock.java @@ -52,6 +52,10 @@ public class PixelBlock { } } + public static boolean exists(@NotNull UUID blockUUID) { + return Main.pixelBlocks.stream().anyMatch(pixelBlock -> pixelBlock.blockUUID.equals(blockUUID)); + } + public static @Nullable PixelBlock getPixelBlockFromPlacedLocation(@NotNull Location placedLocation) { Location searchLocation = placedLocation.clone().toBlockLocation(); searchLocation.setPitch(0); @@ -76,19 +80,15 @@ public class PixelBlock { this.facingDirection = direction; this.lastEntryLocation = lastEntryLocation; - try { - this.pixelWorld = new PixelBlockWorld(this); - this.pixelData = this.pixelWorld.getPixels(this.facingDirection); - this.pixels = new Pixels(this); - this.placeholder = new PixelBlockPlaceholder(this); - this.hitbox = new PixelBlockHitbox(this); + this.pixelWorld = new PixelBlockWorld(this); + this.pixelData = this.pixelWorld.getPixels(this.facingDirection); + this.pixels = new Pixels(this); + this.placeholder = new PixelBlockPlaceholder(this); + this.hitbox = new PixelBlockHitbox(this); - this.getBlockTaskChain().sync(() -> this.isAccessible = true).execute(); + this.getBlockTaskChain().sync(() -> this.isAccessible = true).execute(); - Main.logger().info(String.format("Loaded existing pixelblock '%s'", this.blockUUID)); - } catch(Exception e) { - Main.logger().info(String.format("Failed initializing existing pixelblock '%s': %s", this.blockUUID, e.getMessage())); - } + Main.logger().info(String.format("Loaded existing pixelblock '%s'", this.blockUUID)); } public static PixelBlock createPixelBlock(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction) { @@ -96,8 +96,8 @@ public class PixelBlock { } private PixelBlock(UUID blockUUID, UUID ownerUUID, Location pixelBlockLocation, Direction direction) { - if(Main.pixelBlocks.stream().anyMatch(pixelBlock -> pixelBlock.getBlockUUID().equals(blockUUID))) - throw new IllegalStateException(String.format("PixelBlock '%s' ist bereits in der Welt vorhanden!", blockUUID)); + if(exists(blockUUID)) + throw new IllegalStateException(String.format("PixelBlock '%s' already exists in the world!", blockUUID)); this.blockUUID = blockUUID; this.ownerUUID = ownerUUID; @@ -149,7 +149,7 @@ public class PixelBlock { public void exitBlock(@NotNull Player player) { this.getBlockTaskChain() - .sync(() -> player.teleport(this.lastEntryLocation != null ? this.lastEntryLocation : this.pixelBlockLocation)) + .sync(() -> player.teleport(this.getReturnLocation())) .sync(() -> this.pixelData = this.pixelWorld.getPixels(this.facingDirection)) .current(() -> Main.logger().info(String.format("%s exited PixelBlock", player.getName()))) .delay(1) @@ -181,9 +181,10 @@ public class PixelBlock { return; } + Location returnLocation = this.getReturnLocation(); this.pixelWorld.getPlayersInWorld().forEach(p -> { p.sendMessage(Component.text("Der Pixelblock wurde von einem anderen Spieler abgebaut!", NamedTextColor.RED)); - p.teleport(this.lastEntryLocation); + p.teleport(returnLocation); }); Main.logger().info(String.format("Destroying PixelBlock '%s' at %s", this.blockUUID, pixelBlockLocation)); @@ -191,14 +192,14 @@ public class PixelBlock { this.pixelWorld.getEntitiesInWorld().stream() .filter(entity -> entity instanceof Item) - .forEach(entity -> entity.teleport(this.lastEntryLocation)); + .forEach(entity -> entity.teleport(returnLocation)); this.getBlockTaskChain() .sync(() -> { this.removeEntities(); World world = this.pixelBlockLocation.getWorld(); - world.playSound(this.pixelBlockLocation, Sound.BLOCK_COPPER_BULB_BREAK, 1.0F, 30); - world.dropItem(this.pixelBlockLocation.add(new Vector(0.5, 0.5, 0.5)), PixelBlockItem.getBlockAsItem(this)); + world.playSound(this.pixelBlockLocation, Sound.BLOCK_COPPER_BULB_BREAK, 1.0F, 2.0F); + world.dropItem(this.getPixelBlockLocation().add(new Vector(0.5, 0.5, 0.5)), PixelBlockItem.getBlockAsItem(this)); }) .execute(); @@ -238,6 +239,12 @@ public class PixelBlock { return this.lastEntryLocation != null; } + private @NotNull Location getReturnLocation() { + return this.hasLastEntryLocation() + ? this.lastEntryLocation.clone() + : this.getPixelBlockLocation().add(0.5, 0, 0.5); + } + public List getPixelData() { return pixelData; }