Skip to content

Commit

Permalink
Fix wither spawning with disabled spawn-limits (#66)
Browse files Browse the repository at this point in the history
* Fix wither spawning with disabled spawn-limits

Resolves #60

Decouples wither-origin tracking and spawn limit listeners. The issue was that withers were tagged with their origin island in the creature spawn listeners for of SpawnEvents, which is not registered if spawn limits are disabled. I solved this by moving the wither tagging logic into a separate listener that is always active. Untagged Withers are despawned by the GriefEvents listener.

I also migrated the tagging to PDC to make the tagging consistent without relying on name tagging that is prone to errors.

* Cleanup SpawnEvents
  • Loading branch information
minoneer authored Dec 28, 2024
1 parent c817470 commit bd879b9
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 71 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dk.lockfuglsang.minecraft.po.I18nUtil;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Creature;
Expand All @@ -25,14 +26,15 @@
import org.bukkit.event.player.PlayerEggThrowEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerShearEntityEvent;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.projectiles.ProjectileSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import us.talabrek.ultimateskyblock.handler.WorldGuardHandler;
import us.talabrek.ultimateskyblock.island.IslandInfo;
import us.talabrek.ultimateskyblock.uSkyBlock;

import java.text.MessageFormat;
import java.text.ParseException;

/**
* Handling of mob-related events.
*/
Expand Down Expand Up @@ -171,39 +173,34 @@ public void onWitherSkullExplosion(EntityDamageByEntityEvent e) {
}
}

private void handleWitherRampage(Cancellable e, Wither shooter, Location targetLocation) {
String islandName = getOwningIsland(shooter);
private void handleWitherRampage(Cancellable event, Wither shooter, Location targetLocation) {
String withersIsland = getOwningIsland(shooter);
String targetIsland = WorldGuardHandler.getIslandNameAt(targetLocation);
if (targetIsland == null || !targetIsland.equals(islandName)) {
e.setCancelled(true);
checkWitherLeash(shooter, islandName);
if (targetIsland == null || !targetIsland.equals(withersIsland)) {
event.setCancelled(true);
checkWitherLeash(shooter, withersIsland);
}
}

private void checkWitherLeash(Wither shooter, String islandName) {
private void checkWitherLeash(@NotNull Wither shooter, @Nullable String withersIsland) {
String currentIsland = WorldGuardHandler.getIslandNameAt(shooter.getLocation());
if (currentIsland == null || !currentIsland.equals(islandName)) {
if (currentIsland == null || !currentIsland.equals(withersIsland)) {
shooter.remove();
IslandInfo islandInfo = plugin.getIslandInfo(islandName);
IslandInfo islandInfo = plugin.getIslandInfo(withersIsland);
if (islandInfo != null) {
islandInfo.sendMessageToOnlineMembers(I18nUtil.tr("\u00a7cWither Despawned!\u00a7e It wandered too far from your island."));
}
}
}

private String getOwningIsland(Wither wither) {
if (wither.hasMetadata("fromIsland")) {
return wither.getMetadata("fromIsland").get(0).asString();
private @Nullable String getOwningIsland(@NotNull Wither wither) {
PersistentDataContainer container = wither.getPersistentDataContainer();
NamespacedKey key = new NamespacedKey(plugin, WitherTagListener.ENTITY_ORIGIN_METADATA);
if (container.has(key, PersistentDataType.STRING)) {
return container.get(key, PersistentDataType.STRING);
} else {
return null;
}
try {
Object[] parse = new MessageFormat(I18nUtil.marktr("{0}''s Wither")).parse(wither.getCustomName());
if (parse != null && parse.length == 1 && parse[0] instanceof String) {
return (String) parse[0];
}
} catch (ParseException e) {
// Ignore
}
return null;
}

@EventHandler
Expand All @@ -215,5 +212,4 @@ public void onEgg(PlayerEggThrowEvent e) {
e.setHatching(false);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package us.talabrek.ultimateskyblock.event;

import dk.lockfuglsang.minecraft.po.I18nUtil;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Ghast;
import org.bukkit.entity.Phantom;
import org.bukkit.entity.Player;
import org.bukkit.entity.WaterMob;
import org.bukkit.entity.Wither;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
Expand All @@ -19,17 +16,13 @@
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MonsterEggs;
import org.bukkit.material.SpawnEgg;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.inventory.meta.SpawnEggMeta;
import us.talabrek.ultimateskyblock.api.IslandInfo;
import us.talabrek.ultimateskyblock.handler.WorldGuardHandler;
import us.talabrek.ultimateskyblock.uSkyBlock;
import us.talabrek.ultimateskyblock.util.LocationUtil;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Collection;
import java.util.Set;

import static dk.lockfuglsang.minecraft.po.I18nUtil.tr;
Expand All @@ -38,15 +31,7 @@
* Responsible for controlling spawns on uSkyBlock islands.
*/
public class SpawnEvents implements Listener {
private static final Set<Action> RIGHT_CLICKS = new HashSet<>(Arrays.asList(Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK));
private static final Set<CreatureSpawnEvent.SpawnReason> PLAYER_INITIATED = new HashSet<>(Arrays.asList(
CreatureSpawnEvent.SpawnReason.BREEDING,
CreatureSpawnEvent.SpawnReason.BUILD_IRONGOLEM, CreatureSpawnEvent.SpawnReason.BUILD_SNOWMAN,
CreatureSpawnEvent.SpawnReason.BUILD_WITHER
));
private static final Set<CreatureSpawnEvent.SpawnReason> ADMIN_INITIATED = new HashSet<>(Arrays.asList(
CreatureSpawnEvent.SpawnReason.SPAWNER_EGG
));
private static final Set<Action> RIGHT_CLICKS = Set.of(Action.RIGHT_CLICK_AIR, Action.RIGHT_CLICK_BLOCK);

private final uSkyBlock plugin;

Expand All @@ -69,14 +54,14 @@ public void onSpawnEggEvent(PlayerInteractEvent event) {
return;
}
ItemStack item = event.getItem();
if (RIGHT_CLICKS.contains(event.getAction()) && item != null && isSpawnEgg(item)) {
if (RIGHT_CLICKS.contains(event.getAction()) && item != null && item.getItemMeta() instanceof SpawnEggMeta spawnEggMeta) {
if (!plugin.playerIsOnIsland(player)) {
event.setCancelled(true);
plugin.notifyPlayer(player, tr("\u00a7eYou can only use spawn-eggs on your own island."));
return;
}
SpawnEgg spawnEgg = (SpawnEgg) item.getData();
checkLimits(event, spawnEgg.getSpawnedType(), player.getLocation());

checkLimits(event, spawnEggMeta.getSpawnedType(), player.getLocation());
if (event.isCancelled()) {
plugin.notifyPlayer(player, tr("\u00a7cYou have reached your spawn-limit for your island."));
event.setUseItemInHand(Event.Result.DENY);
Expand All @@ -85,16 +70,12 @@ public void onSpawnEggEvent(PlayerInteractEvent event) {
}
}

private boolean isSpawnEgg(ItemStack item) {
return item.getType().name().endsWith("_SPAWN_EGG") && item.getData() instanceof MonsterEggs;
}

@EventHandler(ignoreCancelled = true)
public void onCreatureSpawn(CreatureSpawnEvent event) {
if (event == null || !plugin.getWorldManager().isSkyAssociatedWorld(event.getLocation().getWorld())) {
return; // Bail out, we don't care
}
if (!event.isCancelled() && ADMIN_INITIATED.contains(event.getSpawnReason())) {
if (event.getSpawnReason().equals(CreatureSpawnEvent.SpawnReason.SPAWNER_EGG)) {
return; // Allow it, the above method would have blocked it if it should be blocked.
}
checkLimits(event, event.getEntity().getType(), event.getLocation());
Expand All @@ -105,22 +86,15 @@ public void onCreatureSpawn(CreatureSpawnEvent event) {
event.setCancelled(true);
}
}
if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.BUILD_WITHER && event.getEntity() instanceof Wither) {
IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation());
if (islandInfo != null && islandInfo.getLeader() != null) {
event.getEntity().setCustomName(I18nUtil.tr("{0}''s Wither", islandInfo.getLeader()));
event.getEntity().setMetadata("fromIsland", new FixedMetadataValue(plugin, islandInfo.getName()));
}
}
}

private boolean isPrismarineRoof(Location loc) {
List<Material> prismarineBlocks = Arrays.asList(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE);
Collection<Material> prismarineBlocks = Set.of(Material.PRISMARINE, Material.PRISMARINE_BRICKS, Material.DARK_PRISMARINE);
return prismarineBlocks.contains(LocationUtil.findRoofBlock(loc).getType());
}

private boolean isDeepOceanBiome(Location loc) {
List<Biome> deepOceans = Arrays.asList(Biome.DEEP_OCEAN, Biome.DEEP_COLD_OCEAN, Biome.DEEP_FROZEN_OCEAN, Biome.DEEP_LUKEWARM_OCEAN);
Collection<Biome> deepOceans = Set.of(Biome.DEEP_OCEAN, Biome.DEEP_COLD_OCEAN, Biome.DEEP_FROZEN_OCEAN, Biome.DEEP_LUKEWARM_OCEAN);
return deepOceans.contains(loc.getWorld().getBiome(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()));
}

Expand All @@ -133,12 +107,12 @@ private void checkLimits(Cancellable event, EntityType entityType, Location loca
event.setCancelled(true); // Only allow spawning on active islands...
return;
}
if (entityType.getEntityClass().isAssignableFrom(Ghast.class) && location.getWorld().getEnvironment() != World.Environment.NETHER) {
if (entityType.equals(EntityType.GHAST) && location.getWorld().getEnvironment() != World.Environment.NETHER) {
// Disallow ghasts for now...
event.setCancelled(true);
return;
}
us.talabrek.ultimateskyblock.api.IslandInfo islandInfo = plugin.getIslandInfo(islandName);
IslandInfo islandInfo = plugin.getIslandInfo(islandName);
if (islandInfo == null) {
// Disallow spawns on inactive islands
event.setCancelled(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package us.talabrek.ultimateskyblock.event;

import dk.lockfuglsang.minecraft.po.I18nUtil;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Wither;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.persistence.PersistentDataType;
import us.talabrek.ultimateskyblock.api.IslandInfo;
import us.talabrek.ultimateskyblock.uSkyBlock;

public class WitherTagListener implements Listener {

static final String ENTITY_ORIGIN_METADATA = "from-island";
private final uSkyBlock plugin;

public WitherTagListener(uSkyBlock plugin) {
this.plugin = plugin;
}

@EventHandler(ignoreCancelled = true)
public void onCreatureSpawn(CreatureSpawnEvent event) {
if (event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.BUILD_WITHER
&& event.getEntity() instanceof Wither wither) {
IslandInfo islandInfo = plugin.getIslandInfo(event.getLocation());
if (islandInfo != null && islandInfo.getLeader() != null) {
wither.setCustomName(I18nUtil.tr("{0}''s Wither", islandInfo.getLeader()));
NamespacedKey key = new NamespacedKey(plugin, ENTITY_ORIGIN_METADATA);
wither.getPersistentDataContainer().set(key, PersistentDataType.STRING, islandInfo.getName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,7 @@
import us.talabrek.ultimateskyblock.command.admin.DebugCommand;
import us.talabrek.ultimateskyblock.command.admin.SetMaintenanceCommand;
import us.talabrek.ultimateskyblock.command.island.BiomeCommand;
import us.talabrek.ultimateskyblock.event.ExploitEvents;
import us.talabrek.ultimateskyblock.event.GriefEvents;
import us.talabrek.ultimateskyblock.event.InternalEvents;
import us.talabrek.ultimateskyblock.event.ItemDropEvents;
import us.talabrek.ultimateskyblock.event.MenuEvents;
import us.talabrek.ultimateskyblock.event.NetherTerraFormEvents;
import us.talabrek.ultimateskyblock.event.PlayerEvents;
import us.talabrek.ultimateskyblock.event.SpawnEvents;
import us.talabrek.ultimateskyblock.event.ToolMenuEvents;
import us.talabrek.ultimateskyblock.event.WorldGuardEvents;
import us.talabrek.ultimateskyblock.event.*;
import us.talabrek.ultimateskyblock.handler.AsyncWorldEditHandler;
import us.talabrek.ultimateskyblock.handler.ConfirmHandler;
import us.talabrek.ultimateskyblock.handler.CooldownHandler;
Expand Down Expand Up @@ -334,6 +325,7 @@ public void registerEvents() {
manager.registerEvents(new PlayerEvents(this), this);
manager.registerEvents(new MenuEvents(this), this);
manager.registerEvents(new ExploitEvents(this), this);
manager.registerEvents(new WitherTagListener(this), this);
if (getConfig().getBoolean("options.protection.enabled", true)) {
manager.registerEvents(new GriefEvents(this), this);
if (getConfig().getBoolean("options.protection.item-drops", true)) {
Expand Down

0 comments on commit bd879b9

Please sign in to comment.