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.
This commit is contained in:
2026-07-23 22:58:47 +02:00
parent 76ddfc95af
commit a024e42b80
10 changed files with 48 additions and 29 deletions
+4 -1
View File
@@ -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 {
@@ -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();
}
@@ -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);
}
}
@@ -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());
}
@@ -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());
}
}
@@ -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);
@@ -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(),
@@ -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(
@@ -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);
}
}
@@ -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,7 +80,6 @@ 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);
@@ -86,9 +89,6 @@ public class PixelBlock {
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()));
}
}
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<PixelBlockWorld.PixelData> getPixelData() {
return pixelData;
}