diff --git a/leaf-server/minecraft-patches/features/0140-Spawner-Configurations.patch b/leaf-server/minecraft-patches/features/0140-Spawner-Configurations.patch new file mode 100644 index 00000000..e28ecea2 --- /dev/null +++ b/leaf-server/minecraft-patches/features/0140-Spawner-Configurations.patch @@ -0,0 +1,148 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Taiyou06 +Date: Sun, 9 Mar 2025 00:36:26 +0100 +Subject: [PATCH] Spawner Configurations + + +diff --git a/net/minecraft/world/level/BaseSpawner.java b/net/minecraft/world/level/BaseSpawner.java +index 8c6f8cb08b247dcf497822ae991aa3afbcb784f1..9df9f4308c717db523d3c58e4d8dab7437984d02 100644 +--- a/net/minecraft/world/level/BaseSpawner.java ++++ b/net/minecraft/world/level/BaseSpawner.java +@@ -53,6 +53,12 @@ public abstract class BaseSpawner { + + public boolean isNearPlayer(Level level, BlockPos pos) { + if (level.purpurConfig.spawnerDeactivateByRedstone && level.hasNeighborSignal(pos)) return false; // Purpur - Redstone deactivates spawners ++ // Leaf start - Spawner Configurations ++ // Skip player proximity check if disabled in config ++ if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && !org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.checkForNearbyPlayers) { ++ return true; // Always act as if players are nearby ++ } ++ // Leaf end - Spawner Configurations + return level.hasNearbyAlivePlayerThatAffectsSpawningForSpawner(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, this.requiredPlayerRange); // Paper - Affects Spawning API // Leaf - Optimize nearby alive players for spawning + } + +@@ -75,6 +81,20 @@ public abstract class BaseSpawner { + } + } + ++ // Leaf start - Spawner Configurations ++ private int maxAllowedLight(EntityType entityType) { ++ if (entityType.getCategory().isFriendly()) { ++ return 15; // No light restriction for passive mobs ++ } else if (entityType == EntityType.SPIDER || entityType == EntityType.CAVE_SPIDER) { ++ return 7; // Spiders can spawn in light level 7 or lower ++ } else if (entityType == EntityType.ENDERMAN) { ++ return 7; // Endermen can spawn in light level 7 or lower ++ } ++ ++ return 0; // Complete darkness for other hostile mobs ++ } ++ // Leaf end - Spawner Configurations ++ + public void serverTick(ServerLevel serverLevel, BlockPos pos) { + if (spawnCount <= 0 || maxNearbyEntities <= 0) return; // Paper - Ignore impossible spawn tick + // Paper start - Configurable mob spawner tick rate +@@ -82,6 +102,15 @@ public abstract class BaseSpawner { + tickDelay = serverLevel.paperConfig().tickRates.mobSpawner; + if (tickDelay == -1) { return; } // If disabled + // Paper end - Configurable mob spawner tick rate ++ ++ // Leaf start - Spawner Configurations ++ // Apply custom min/max spawn delays if enabled ++ if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled) { ++ this.minSpawnDelay = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.minSpawnDelay; ++ this.maxSpawnDelay = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.maxSpawnDelay; ++ } ++ // Leaf end - Spawner Configurations ++ + if (this.isNearPlayer(serverLevel, pos)) { + if (this.spawnDelay < -tickDelay) { // Paper - Configurable mob spawner tick rate + this.delay(serverLevel, pos); +@@ -107,18 +136,43 @@ public abstract class BaseSpawner { + double d = size >= 1 ? list.getDouble(0) : pos.getX() + (random.nextDouble() - random.nextDouble()) * this.spawnRange + 0.5; + double d1 = size >= 2 ? list.getDouble(1) : pos.getY() + random.nextInt(3) - 1; + double d2 = size >= 3 ? list.getDouble(2) : pos.getZ() + (random.nextDouble() - random.nextDouble()) * this.spawnRange + 0.5; +- if (serverLevel.noCollision(optional.get().getSpawnAABB(d, d1, d2))) { ++ ++ // Leaf start - Spawner Configurations ++ // Skip collision check if block checks are disabled ++ boolean skipBlockChecks = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && ++ !org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.spawnerBlockChecks; ++ if (skipBlockChecks || serverLevel.noCollision(optional.get().getSpawnAABB(d, d1, d2))) { + BlockPos blockPos = BlockPos.containing(d, d1, d2); ++ ++ // Add light level check if enabled ++ if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && ++ org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.lightLevelCheck) { ++ int lightLevel = serverLevel.getMaxLocalRawBrightness(blockPos); ++ if (lightLevel > maxAllowedLight(optional.get())) { ++ continue; ++ } ++ } ++ ++ // Add water check if enabled ++ if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled && ++ org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.waterPreventSpawnCheck && ++ serverLevel.getBlockState(blockPos).getFluidState().is(net.minecraft.tags.FluidTags.WATER)) { ++ continue; ++ } ++ ++ // Handle spawn rules checks ++ boolean skipSpawnRules = false; ++ // Leaf end - Spawner Configurations + if (nextSpawnData.getCustomSpawnRules().isPresent()) { + if (!optional.get().getCategory().isFriendly() && serverLevel.getDifficulty() == Difficulty.PEACEFUL) { + continue; + } + + SpawnData.CustomSpawnRules customSpawnRules = nextSpawnData.getCustomSpawnRules().get(); +- if (!customSpawnRules.isValidPosition(blockPos, serverLevel)) { ++ if (!skipBlockChecks && !customSpawnRules.isValidPosition(blockPos, serverLevel)) { // Leaf - Spawner Configurations + continue; + } +- } else if (!SpawnPlacements.checkSpawnRules(optional.get(), serverLevel, EntitySpawnReason.SPAWNER, blockPos, serverLevel.getRandom())) { ++ } else if (!skipBlockChecks && !SpawnPlacements.checkSpawnRules(optional.get(), serverLevel, EntitySpawnReason.SPAWNER, blockPos, serverLevel.getRandom())) { // Leaf - Spawner Configurations + continue; + } + +@@ -146,6 +200,7 @@ public abstract class BaseSpawner { + return; + } + ++ if (!org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled || org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.spawnerMaxNearbyCheck) { // Leaf - Spawner Configurations - Skip max nearby entity check if disabled + int size1 = serverLevel.getEntities( + EntityTypeTest.forExactClass(entity.getClass()), + new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1).inflate(this.spawnRange), +@@ -156,12 +211,16 @@ public abstract class BaseSpawner { + this.delay(serverLevel, pos); + return; + } ++ } // Leaf - Spawner Configurations + + entity.preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; preserve entity motion from tag + entity.moveTo(entity.getX(), entity.getY(), entity.getZ(), random.nextFloat() * 360.0F, 0.0F); + if (entity instanceof Mob mob) { +- if (nextSpawnData.getCustomSpawnRules().isEmpty() && !mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER) +- || !mob.checkSpawnObstruction(serverLevel)) { ++ // Leaf start - Spawner Configurations ++ // Skip spawn rule and obstruction checks if block checks are disabled ++ if (!skipBlockChecks && (nextSpawnData.getCustomSpawnRules().isEmpty() && !mob.checkSpawnRules(serverLevel, EntitySpawnReason.SPAWNER) ++ || !mob.checkSpawnObstruction(serverLevel))) { ++ // Leaf end - Spawner Configurations + continue; + } + +@@ -249,6 +308,13 @@ public abstract class BaseSpawner { + this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData()); + } + ++ // Leaf start - Spawner Configurations ++ if (org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.enabled) { ++ this.minSpawnDelay = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.minSpawnDelay; ++ this.maxSpawnDelay = org.dreeam.leaf.config.modules.gameplay.SpawnerSettings.maxSpawnDelay; ++ this.spawnCount = tag.getShort("SpawnCount"); ++ } else ++ // Leaf end - Spawner Configurations + // Paper start - use ints if set + if (tag.contains("Paper.MinSpawnDelay", net.minecraft.nbt.Tag.TAG_ANY_NUMERIC)) { + this.minSpawnDelay = tag.getInt("Paper.MinSpawnDelay"); diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java new file mode 100644 index 00000000..31e81d18 --- /dev/null +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/gameplay/SpawnerSettings.java @@ -0,0 +1,87 @@ +package org.dreeam.leaf.config.modules.gameplay; + +import org.dreeam.leaf.config.ConfigModules; +import org.dreeam.leaf.config.EnumConfigCategory; + +public class SpawnerSettings extends ConfigModules { + + public String getBasePath() { + return EnumConfigCategory.GAMEPLAY.getBaseKeyName() + ".spawner-settings"; + } + + // Global toggle + public static boolean enabled = false; + + // Default values for spawner settings + public static boolean lightLevelCheck = false; + public static boolean spawnerMaxNearbyCheck = true; + public static boolean checkForNearbyPlayers = true; + public static boolean spawnerBlockChecks = false; + public static boolean waterPreventSpawnCheck = false; + + public static int minSpawnDelay = 200; + public static int maxSpawnDelay = 800; + + @Override + public void onLoaded() { + config.addCommentRegionBased(getBasePath(), + "This section contains settings for mob spawner blocks.", + "此部分包含刷怪笼生物生成的设置."); + + // Global toggle + enabled = config.getBoolean(getBasePath() + ".enabled", enabled, + config.pickStringRegionBased( + "Enable custom spawner settings. Set to true to enable all features below.", + "启用自定义刷怪笼设置. 设为 true 以启用以下所有功能." + )); + + // Checks section + config.addCommentRegionBased(getBasePath() + ".checks", + "Various checks that can be enabled or disabled for spawner blocks.", + "可以为刷怪笼启用或禁用的各种检查."); + + lightLevelCheck = config.getBoolean(getBasePath() + ".checks.light-level-check", lightLevelCheck, + config.pickStringRegionBased( + "Check if there is the required light level to spawn the mob", + "检查是否有所需的光照等级来生成怪物" + )); + + spawnerMaxNearbyCheck = config.getBoolean(getBasePath() + ".checks.spawner-max-nearby-check", spawnerMaxNearbyCheck, + config.pickStringRegionBased( + "Check if there are the max amount of nearby mobs to spawn the mob", + "检查附近是否已达到最大怪物数量限制" + )); + + checkForNearbyPlayers = config.getBoolean(getBasePath() + ".checks.check-for-nearby-players", checkForNearbyPlayers, + config.pickStringRegionBased( + "Check if any players are in a radius to spawn the mob", + "检查是否有玩家在生成怪物的半径范围内" + )); + + spawnerBlockChecks = config.getBoolean(getBasePath() + ".checks.spawner-block-checks", spawnerBlockChecks, + config.pickStringRegionBased( + "Check if there are blocks blocking the spawner to spawn the mob", + "检查是否有方块阻挡刷怪笼生成怪物" + )); + + waterPreventSpawnCheck = config.getBoolean(getBasePath() + ".checks.water-prevent-spawn-check", waterPreventSpawnCheck, + config.pickStringRegionBased( + "Checks if there is water around that prevents spawning", + "检查周围是否有水阻止生成" + )); + + // Delay settings + + minSpawnDelay = config.getInt(getBasePath() + ".min-spawn-delay", minSpawnDelay, + config.pickStringRegionBased( + "Minimum delay (in ticks) between spawner spawns. Higher values slow down spawners.", + "刷怪笼生成怪物之间的最小延迟 (以刻为单位). 较高的值会减缓刷怪笼的速度." + )); + + maxSpawnDelay = config.getInt(getBasePath() + ".max-spawn-delay", maxSpawnDelay, + config.pickStringRegionBased( + "Maximum delay (in ticks) between spawner spawns. Higher values slow down spawners.", + "刷怪笼生成怪物之间的最大延迟 (以刻为单位). 较高的值会减缓刷怪笼的速度." + )); + } +}