From 0cf1ddec5ca4bbfe5316d9998d2224351b6d5454 Mon Sep 17 00:00:00 2001 From: NanaChiyo0721 Date: Fri, 17 Jul 2026 21:04:02 +0800 Subject: [PATCH] Partially rewrite kaiiju entity throttler --- shiroha-server/build.gradle.kts.patch | 3 +- ...iiju-Entity-tick-and-removal-limiter.patch | 12 ++ .../kaiijumc/kaiiju/KaiijuEntityLimits.java | 109 ++++++------------ .../kaiiju/KaiijuEntityThrottler.java | 3 +- 4 files changed, 52 insertions(+), 75 deletions(-) diff --git a/shiroha-server/build.gradle.kts.patch b/shiroha-server/build.gradle.kts.patch index d9339ed..b17d628 100644 --- a/shiroha-server/build.gradle.kts.patch +++ b/shiroha-server/build.gradle.kts.patch @@ -64,7 +64,7 @@ } } val log4jPlugins = sourceSets.create("log4jPlugins") { -@@ -134,7 +_,15 @@ +@@ -134,7 +_,14 @@ } dependencies { @@ -76,7 +76,6 @@ + implementation("com.github.luben:zstd-jni:1.5.4-1") + implementation("net.openhft:zero-allocation-hashing:0.16") + implementation("net.objecthunter:exp4j:0.4.8") -+ implementation("io.github.classgraph:classgraph:4.8.158") + // Shiroha end implementation("ca.spottedleaf:leafpile:1.0.0") implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ diff --git a/shiroha-server/minecraft-patches/features/0026-Kaiiju-Entity-tick-and-removal-limiter.patch b/shiroha-server/minecraft-patches/features/0026-Kaiiju-Entity-tick-and-removal-limiter.patch index 80a0dfe..37542fa 100644 --- a/shiroha-server/minecraft-patches/features/0026-Kaiiju-Entity-tick-and-removal-limiter.patch +++ b/shiroha-server/minecraft-patches/features/0026-Kaiiju-Entity-tick-and-removal-limiter.patch @@ -53,3 +53,15 @@ index ed4024b744f7913cfd423617936f9affe7c2c46e..d348fa43e6703dbe461eabbab29699ed } finally { foliaProfiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); } // Folia - profiler if (this.paperConfig().unsupportedSettings.ticking.blockEntities) { // Paper - option to disable ticking profiler.popPush("blockEntities"); +diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java +index 6efaa78faca15c7a48bdffb24480d1bac29f3ba8..31619890f1bd4f458b6f9a31fac8cefaafe09e8e 100644 +--- a/net/minecraft/world/entity/EntityType.java ++++ b/net/minecraft/world/entity/EntityType.java +@@ -73,6 +73,7 @@ public class EntityType implements EntityTypeTest, + private final float spawnDimensionsScale; + private final FeatureFlagSet requiredFeatures; + private final boolean allowedInPeaceful; ++ public volatile dev.kaiijumc.kaiiju.KaiijuEntityLimits.EntityLimit entityLimit; + + public static Identifier getKey(final EntityType type) { + return BuiltInRegistries.ENTITY_TYPE.getKey(type); diff --git a/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityLimits.java b/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityLimits.java index fa04eee..079bdd0 100644 --- a/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityLimits.java +++ b/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityLimits.java @@ -19,20 +19,19 @@ package dev.kaiijumc.kaiiju; import com.google.common.base.Throwables; import com.mojang.logging.LogUtils; -import io.github.classgraph.ClassGraph; -import io.github.classgraph.ClassInfo; -import io.github.classgraph.ScanResult; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.world.entity.Entity; +import net.minecraft.core.Holder; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.world.entity.EntityType; import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; +import org.jspecify.annotations.NonNull; import org.slf4j.Logger; import java.io.File; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.Optional; import java.util.logging.Level; @SuppressWarnings("unused") @@ -44,26 +43,11 @@ public class KaiijuEntityLimits { "Per region entity limits for Kaiiju.\n" + "If there are more of particular entity type in a region than limit, entity ticking will be throttled.\n" + "Example: for Wither limit 100 & 300 Withers in a region -> 100 Withers tick every tick & every Wither ticks every 3 ticks.\n" - + "Available entities: GlowSquid, Ambient, Bat, Animal, Bee, Cat, Chicken, Cod, Cow, Dolphin, Fish, FishSchool, Fox, Golem, IronGolem, " - + "MushroomCow, Ocelot, Panda, Parrot, Perchable, Pig, PolarBear, PufferFish, Rabbit, Salmon, Sheep, Snowman, Squid, TropicalFish, Turtle, " - + "WaterAnimal, Wolf, Allay, Axolotl, Camel, Frog, Tadpole, Goat, Horse, HorseAbstract, HorseChestedAbstract, HorseDonkey, HorseMule, " - + "HorseSkeleton, HorseZombie, Llama, LlamaTrader, Sniffer, EnderCrystal, EnderDragon, Wither, ArmorStand, Hanging, ItemFrame, Leash, " - + "Painting, GlowItemFrame, FallingBlock, Item, TNTPrimed, Blaze, CaveSpider, Creeper, Drowned, Enderman, Endermite, Evoker, Ghast, " - + "GiantZombie, Guardian, GuardianElder, IllagerAbstract, IllagerIllusioner, IllagerWizard, MagmaCube, Monster, MonsterPatrolling, Phantom, " - + "ZombifiedPiglin, Pillager, Ravager, Shulker, Silverfish, Skeleton, SkeletonAbstract, SkeletonStray, SkeletonWither, Slime, Spider, Strider, Vex, " - + "Vindicator, Witch, Zoglin, Zombie, ZombieHusk, ZombieVillager, Hoglin, Piglin, PiglinAbstract, PiglinBrute, Warden, Villager, " - + "VillagerTrader, Arrow, DragonFireball, Egg, EnderPearl, EnderSignal, EvokerFangs, Fireball, FireballFireball, Fireworks, FishingHook, " - + "LargeFireball, LlamaSpit, Potion, Projectile, ProjectileThrowable, ShulkerBullet, SmallFireball, Snowball, SpectralArrow, ThrownExpBottle, " - + "ThrownTrident, TippedArrow, WitherSkull, Raider, ChestBoat, Boat, MinecartAbstract, MinecartChest, MinecartCommandBlock, MinecartContainer, " - + "MinecartFurnace, MinecartHopper, MinecartMobSpawner, MinecartRideable, MinecartTNT\n"; + + "Entity names are named under the registry of minecraft's entity type"; protected static final File ENTITY_LIMITS_FILE = new File(CONFIG_FOLDER, "kaiiju_entity_limits.yml"); public static YamlConfiguration entityLimitsConfig; public static boolean enabled = false; - protected static Map, EntityLimit> entityLimits; - - static final String ENTITY_PREFIX = "Entity"; - public static void init() { init(true); } @@ -84,8 +68,9 @@ public class KaiijuEntityLimits { entityLimitsConfig.options().header(HEADER); entityLimitsConfig.options().copyDefaults(true); entityLimitsConfig.set("enabled", enabled); - entityLimitsConfig.set("Axolotl.limit", 1000); - entityLimitsConfig.set("Axolotl.removal", 2000); + entityLimitsConfig.set("axolotl.limit", 1000); + entityLimitsConfig.set("axolotl.removal", 2000); + try { entityLimitsConfig.save(ENTITY_LIMITS_FILE); } catch (IOException ex) { @@ -96,62 +81,42 @@ public class KaiijuEntityLimits { enabled = entityLimitsConfig.getBoolean("enabled"); - entityLimits = new Object2ObjectOpenHashMap<>(); - try (ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages("net.minecraft.world.entity").scan()) { - Map entityClasses = new HashMap<>(); - for (ClassInfo classInfo : scanResult.getAllClasses()) { - Class entityClass = Class.forName(classInfo.getName()); - if (Entity.class.isAssignableFrom(entityClass)) { - String entityName = extractEntityName(entityClass.getSimpleName()); - entityClasses.put(entityName, classInfo); - } - } - - for (String key : entityLimitsConfig.getKeys(false)) { - if (key.equals("enabled")) { - continue; - } - - if (!entityClasses.containsKey(key)) { - LOGGER.error("Unknown entity '" + key + "' in kaiiju-entity-limits.yml, skipping"); - continue; - } - int limit = entityLimitsConfig.getInt(key + ".limit"); - int removal = entityLimitsConfig.getInt(key + ".removal"); - - if (limit < 1) { - LOGGER.error(key + " has a limit less than the minimum of 1, ignoring"); - continue; - } - if (removal <= limit && removal != -1) { - LOGGER.error(key + " has a removal limit that is less than or equal to its limit, setting removal to limit * 10"); - removal = limit * 10; - } - - entityLimits.put((Class) Class.forName(entityClasses.get(key).getName()), new EntityLimit(limit, removal)); - } - } catch (ClassNotFoundException e) { - e.printStackTrace(); + if (!enabled) { + return; } - } - public static EntityLimit getEntityLimit(Entity entity) { - return entityLimits.get(entity.getClass()); - } + for (String key : entityLimitsConfig.getKeys(false)) { + if (key.equals("enabled")) { + continue; + } - private static String extractEntityName(String input) { - int prefixLength = ENTITY_PREFIX.length(); + final Optional>> lookup = BuiltInRegistries.ENTITY_TYPE.get(Identifier.fromNamespaceAndPath(Identifier.DEFAULT_NAMESPACE, key)); + if (lookup.isEmpty()) { + LOGGER.error("Unknown entity '{}' in kaiiju-entity-limits.yml, skipping", key); + continue; + } - if (input.length() <= prefixLength || !input.startsWith(ENTITY_PREFIX)) { - return input; - } else { - return input.substring(prefixLength); + final EntityType value = lookup.get().value(); + + int limit = entityLimitsConfig.getInt(key + ".limit"); + int removal = entityLimitsConfig.getInt(key + ".removal"); + + if (limit < 1) { + LOGGER.error("{} has a limit less than the minimum of 1, ignoring", key); + continue; + } + if (removal <= limit && removal != -1) { + LOGGER.error("{} has a removal limit that is less than or equal to its limit, setting removal to limit * 10", key); + removal = limit * 10; + } + + value.entityLimit = new EntityLimit(limit, removal); } } public record EntityLimit(int limit, int removal) { @Override - public String toString() { + public @NonNull String toString() { return "EntityLimit{limit=" + limit + ", removal=" + removal + "}"; } } diff --git a/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityThrottler.java b/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityThrottler.java index 330c206..08cea33 100644 --- a/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityThrottler.java +++ b/shiroha-server/src/main/java/dev/kaiijumc/kaiiju/KaiijuEntityThrottler.java @@ -45,7 +45,8 @@ public class KaiijuEntityThrottler { public EntityThrottlerReturn tickLimiterShouldSkip(Entity entity) { EntityThrottlerReturn retVal = new EntityThrottlerReturn(); if (entity.isRemoved()) return retVal; - KaiijuEntityLimits.EntityLimit entityLimit = KaiijuEntityLimits.getEntityLimit(entity); + + KaiijuEntityLimits.EntityLimit entityLimit = entity.getType().entityLimit; if (entityLimit != null) { TickInfo tickInfo = entityLimitTickInfoMap.computeIfAbsent(entityLimit, el -> {