Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
Expand All @@ -29,6 +31,7 @@
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventClass;
import org.mvplugins.multiverse.core.dynamiclistener.annotations.EventMethod;
import org.mvplugins.multiverse.core.utils.compatibility.EntityCompatibility;
import org.mvplugins.multiverse.core.world.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.world.WorldManager;

import java.util.Arrays;
Expand Down Expand Up @@ -102,14 +105,11 @@ public void onEvent(PreCreatureSpawnEvent event) {
return;
}

worldManager.getLoadedWorld(event.getSpawnLocation().getWorld())
.peek(world -> {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getType())) {
Logging.finest("Cancelling Pre Creature Spawn Event for: " + event.getType());
event.setCancelled(true);
event.setShouldAbortSpawn(true);
}
});
if (!shouldAllowSpawn(event.getSpawnLocation().getWorld(), event.getType())) {
Logging.finest("Cancelling Pre Creature Spawn Event for: " + event.getType());
event.setCancelled(true);
event.setShouldAbortSpawn(true);
}
}
};
}
Expand All @@ -130,13 +130,10 @@ void creatureSpawn(CreatureSpawnEvent event) {
return;
}

worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek(world -> {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntityType())) {
Logging.finest("Cancelling Creature Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
});
if (!shouldAllowSpawn(event.getEntity().getWorld(), event.getEntityType())) {
Logging.finest("Cancelling Creature Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
}

/**
Expand All @@ -149,13 +146,20 @@ void entitySpawn(EntitySpawnEvent event) {
return;
}

worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek(world -> {
if (!world.getEntitySpawnConfig().shouldAllowSpawn(event.getEntityType())) {
Logging.finest("Cancelling Entity Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
});
if (!shouldAllowSpawn(event.getEntity().getWorld(), event.getEntityType())) {
Logging.finest("Cancelling Entity Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
}

/**
* Checks the world's spawn config. Spawn events fire thousands of times per second, so this avoids the
* Option and lambda allocations of {@link WorldManager#getLoadedWorld(World)}.
* Worlds not managed by Multiverse always allow the spawn.
*/
private boolean shouldAllowSpawn(@Nullable World bukkitWorld, @NotNull EntityType entityType) {
LoadedMultiverseWorld world = worldManager.getLoadedWorldOrNull(bukkitWorld);
return world == null || world.getEntitySpawnConfig().shouldAllowSpawn(entityType);
}

@EventMethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,18 @@ public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable World world) {
return Option.of(world).flatMap(notNullWorld -> getLoadedWorld(notNullWorld.getName()));
}

/**
* Same as {@link #getLoadedWorld(World)}, but returns null instead of allocating an {@link Option}.
* Meant for hot paths such as entity spawn events.
*
* @param world The bukkit world that should be loaded.
* @return The multiverse world, or null if it is not a loaded multiverse world.
*/
@ApiStatus.Internal
public @Nullable LoadedMultiverseWorld getLoadedWorldOrNull(@Nullable World world) {
return world == null ? null : worldStore.getLoadedWorldOrNull(world.getName());
}

/**
* Get a multiverse world that is loaded.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,13 @@ Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable NamespacedKey worldKey) {

@NotNull
Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldKeyString) {
return Option.of(loadedMap.get(worldKeyString))
.filter(MultiverseWorld::isLoaded);
return Option.of(getLoadedWorldOrNull(worldKeyString));
}

@Nullable
LoadedMultiverseWorld getLoadedWorldOrNull(@Nullable String worldKeyString) {
LoadedMultiverseWorld world = loadedMap.get(worldKeyString);
return world != null && world.isLoaded() ? world : null;
}

boolean isLoadedWorld(@Nullable WorldKeyOrName keyOrName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public final class EntitySpawnConfig {
}

public @NotNull SpawnCategoryConfig getSpawnCategoryConfig(@NotNull SpawnCategory spawnCategory) {
// Plain get first, so the common case does not allocate the capturing lambda below.
SpawnCategoryConfig spawnCategoryConfig = spawnCategoriesConfig.get(spawnCategory);
if (spawnCategoryConfig != null) {
return spawnCategoryConfig;
}
return spawnCategoriesConfig.computeIfAbsent(spawnCategory,
computeSpawnCategory -> new SpawnCategoryConfig(
config,
Expand All @@ -40,9 +45,12 @@ public final class EntitySpawnConfig {
public boolean shouldAllowSpawn(@NotNull EntityType entityType) {
// Ensure it defaults to true if SpawnCategoryMapper fails to find a spawn category for the entity type,
// to avoid accidentally breaking mob spawns.
return SpawnCategoryMapper.getSpawnCategory(entityType)
.map(spawnCategory -> getSpawnCategoryConfig(spawnCategory).shouldAllowSpawn(entityType))
.getOrElse(true);
// This runs for every spawn event, so it avoids Option and lambda allocations on purpose.
SpawnCategory spawnCategory = SpawnCategoryMapper.getSpawnCategoryOrNull(entityType);
if (spawnCategory == null) {
return true;
}
return getSpawnCategoryConfig(spawnCategory).shouldAllowSpawn(entityType);
}

public boolean shouldAllowSpawn(@NotNull Entity entity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,18 @@ public static void buildSpawnCategoryMap() {
* @return The spawn category that the given entity type is in, or null if it cannot be determined
*/
static @NotNull Option<SpawnCategory> getSpawnCategory(@Nullable EntityType entityType) {
return Option.of(entityTypeToSpawnCategoryMap.get(entityType));
return Option.of(getSpawnCategoryOrNull(entityType));
}

/**
* Gets the spawn category that the given entity type is in, without wrapping it in an {@link Option}.
* Meant for hot paths such as entity spawn events.
*
* @param entityType The entity type
* @return The spawn category that the given entity type is in, or null if it cannot be determined
*/
static @Nullable SpawnCategory getSpawnCategoryOrNull(@Nullable EntityType entityType) {
return entityTypeToSpawnCategoryMap.get(entityType);
}

/**
Expand Down