diff --git a/leaf-server/minecraft-patches/features/0201-optimize-mob-despawn.patch b/leaf-server/minecraft-patches/features/0201-optimize-mob-despawn.patch index 6fc2dacc..3d6e0560 100644 --- a/leaf-server/minecraft-patches/features/0201-optimize-mob-despawn.patch +++ b/leaf-server/minecraft-patches/features/0201-optimize-mob-despawn.patch @@ -5,125 +5,48 @@ Subject: [PATCH] optimize mob despawn diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 7fb763e89e43698bfb2b9fcf6296705384e8624a..91752678f887559132921cff61697478e5f44e3d 100644 +index 97408ce24313ff26347ff434ab6460e9971c3598..af190cb959478cde3fa9011df85032dbab60ce56 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -797,13 +797,19 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -797,6 +797,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR + -+ if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled && tickRateManager.runsNormally()) { despawnMap.tick(this, this.entityTickList); } // Leaf - optimize despawn ++ if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled) despawnMap.prepare(this); // Leaf - optimize despawn this.entityTickList .forEach( entity -> { - entity.activatedPriorityReset = false; // Pufferfish - DAB - if (!entity.isRemoved()) { - if (!tickRateManager.isEntityFrozen(entity)) { -- entity.checkDespawn(); -+ // Leaf start - optimize despawn -+ if (!org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled) { -+ entity.checkDespawn(); -+ } -+ // Leaf end - optimize despawn - if (true) { // Paper - rewrite chunk system - Entity vehicle = entity.getVehicle(); - if (vehicle != null) { -@@ -945,6 +951,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -832,6 +834,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + } + } + ); ++ if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled) despawnMap.reset(); // Leaf - optimize despawn + this.tickBlockEntities(); + } + // Paper - rewrite chunk system +@@ -945,6 +948,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking -+ public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(paperConfig()); // Leaf - optimize despawn ++ public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(); // Leaf - optimize despawn public void tickChunk(LevelChunk chunk, int randomTickSpeed) { final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting ChunkPos pos = chunk.getPos(); -diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 129248da7e1bfa5edc1c1a43c98a400f697e735f..b17dca64375039bea3dc3f26b5965865a0a9200a 100644 ---- a/net/minecraft/world/entity/Entity.java -+++ b/net/minecraft/world/entity/Entity.java -@@ -5132,6 +5132,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - public void checkDespawn() { - } - -+ // Leaf start - optimize despawn -+ public void leafCheckDespawn() { -+ } -+ // Leaf end - optimize despawn -+ - public Vec3[] getQuadLeashHolderOffsets() { - return Leashable.createQuadLeashOffsets(this, 0.0, 0.5, 0.5, 0.0); - } diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 867353500482247bbec79f407246902c79a3d14a..7caf9ea3792089dcf890c2af0ac17ee1d5c85c16 100644 +index 867353500482247bbec79f407246902c79a3d14a..173ec6919cef2aa90c40d3bf33d927a2db7b4922 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java -@@ -751,6 +751,22 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab - } - } - -+ // Leaf start - optimize despawn -+ @Override -+ public void leafCheckDespawn() { -+ if (isRemoved()) { -+ return; -+ } -+ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { -+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { -+ ((ServerLevel) level()).despawnMap.checkDespawn(this); -+ } else { -+ this.noActionTime = 0; -+ } -+ } -+ // Leaf end - optimize despawn -+ - @Override - protected final void serverAiStep() { - this.noActionTime++; -diff --git a/net/minecraft/world/entity/boss/wither/WitherBoss.java b/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 0613d80561f50e32dc4d1c471521f001659d017d..021c609b689e5a08e60c54f360c4c5dbb9de987f 100644 ---- a/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -695,6 +695,21 @@ public class WitherBoss extends Monster implements RangedAttackMob { - } - } - -+ -+ // Leaf start - optimize despawn -+ @Override -+ public void leafCheckDespawn() { -+ if (isRemoved()) { -+ return; -+ } -+ if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { -+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } else { -+ this.noActionTime = 0; -+ } -+ } -+ // Leaf end - optimize despawn -+ - @Override - public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity) { - return false; -diff --git a/net/minecraft/world/entity/projectile/ShulkerBullet.java b/net/minecraft/world/entity/projectile/ShulkerBullet.java -index 00154ba80175bcb07b3378f19514fec1700c94e9..46d30c3273d31dbda06275ad098c6c2b9b2eed14 100644 ---- a/net/minecraft/world/entity/projectile/ShulkerBullet.java -+++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java -@@ -197,6 +197,16 @@ public class ShulkerBullet extends Projectile { - } - } - -+ -+ // Leaf start - optimize despawn -+ @Override -+ public void leafCheckDespawn() { -+ if (!isRemoved() && this.level().getDifficulty() == Difficulty.PEACEFUL) { -+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } -+ } -+ // Leaf end - optimize despawn -+ - @Override - protected double getDefaultGravity() { - return 0.04; +@@ -722,6 +722,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.shouldDespawnInPeaceful()) { + this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause + } else if (!this.isPersistenceRequired() && !this.requiresCustomPersistence()) { ++ // Leaf start - optimize despawn ++ if (org.dreeam.leaf.config.modules.opt.OptimizeDespawn.enabled) { ++ ((ServerLevel) level()).despawnMap.checkDespawn(this); ++ return; ++ } ++ // Leaf end - optimize despawn + Entity nearestPlayer = this.level().findNearbyPlayer(this, -1.0, EntitySelector.PLAYER_AFFECTS_SPAWNING); // Paper - Affects Spawning API + if (nearestPlayer != null) { + // Paper start - Configurable despawn distances diff --git a/leaf-server/minecraft-patches/features/0211-SparklyPaper-Parallel-world-ticking.patch b/leaf-server/minecraft-patches/features/0211-SparklyPaper-Parallel-world-ticking.patch index 62548aac..3e4b94a1 100644 --- a/leaf-server/minecraft-patches/features/0211-SparklyPaper-Parallel-world-ticking.patch +++ b/leaf-server/minecraft-patches/features/0211-SparklyPaper-Parallel-world-ticking.patch @@ -424,7 +424,7 @@ index 8f41326fda8c5f9f6926038508be6c6529b051bc..46e171ca454253c32e22c0c18587e9a7 } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 5b7b05ab3224e3dbc0957589e8d9bb20cd2e0158..c181fc4f895d0ea1e7b61919cc5e6fe90d6a62ae 100644 +index af190cb959478cde3fa9011df85032dbab60ce56..bc9cf4aa9b1e71e03100a10c45ef9fc23ee5ae9f 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -180,7 +180,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -610,7 +610,7 @@ index 5b7b05ab3224e3dbc0957589e8d9bb20cd2e0158..c181fc4f895d0ea1e7b61919cc5e6fe9 int _int = this.getGameRules().getInt(GameRules.RULE_PLAYERS_SLEEPING_PERCENTAGE); if (this.purpurConfig.playersSkipNight && this.sleepStatus.areEnoughSleeping(_int) && this.sleepStatus.areEnoughDeepSleeping(_int, this.players)) { // Purpur - Config for skipping night // Paper start - create time skip event - move up calculations -@@ -1311,9 +1444,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1308,9 +1441,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe fluidState.tick(this, pos, blockState); } // Paper start - rewrite chunk system @@ -625,7 +625,7 @@ index 5b7b05ab3224e3dbc0957589e8d9bb20cd2e0158..c181fc4f895d0ea1e7b61919cc5e6fe9 // Paper end - rewrite chunk system } -@@ -1324,9 +1460,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1321,9 +1457,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe blockState.tick(this, pos, this.random); } // Paper start - rewrite chunk system @@ -640,7 +640,7 @@ index 5b7b05ab3224e3dbc0957589e8d9bb20cd2e0158..c181fc4f895d0ea1e7b61919cc5e6fe9 // Paper end - rewrite chunk system } -@@ -1591,6 +1730,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1588,6 +1727,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe } private void addPlayer(ServerPlayer player) { @@ -649,7 +649,7 @@ index 5b7b05ab3224e3dbc0957589e8d9bb20cd2e0158..c181fc4f895d0ea1e7b61919cc5e6fe9 Entity entity = this.getEntity(player.getUUID()); if (entity != null) { LOGGER.warn("Force-added player with duplicate UUID {}", player.getUUID()); -@@ -1603,7 +1744,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1600,7 +1741,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // CraftBukkit start private boolean addEntity(Entity entity, @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { @@ -719,7 +719,7 @@ index 567187499c29fbd159b472278665a824dbb1cfd6..4b359856e2981cfb52f43bab8c088e0e // Paper end - Inventory close reason this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId)); diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java -index ff3798fd384679fd8324d4d47e71095024209fe2..8b72f2612c6968c4602a4cb72f3d1160d9679e36 100644 +index 9dbc73b7d5406c7e82b8f1dd2d60d144d24d5220..283115b85ba910f7b155354dd536146bb57e378e 100644 --- a/net/minecraft/server/players/PlayerList.java +++ b/net/minecraft/server/players/PlayerList.java @@ -251,6 +251,8 @@ public abstract class PlayerList { diff --git a/leaf-server/minecraft-patches/features/0216-Use-BFS-on-getSlopeDistance.patch b/leaf-server/minecraft-patches/features/0216-Use-BFS-on-getSlopeDistance.patch index cd937089..a04bafa2 100644 --- a/leaf-server/minecraft-patches/features/0216-Use-BFS-on-getSlopeDistance.patch +++ b/leaf-server/minecraft-patches/features/0216-Use-BFS-on-getSlopeDistance.patch @@ -9,10 +9,10 @@ Leaf: ~48ms (-36%) This should help drastically on the farms that use actively changing fluids. diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index f20d7591e473a37a0f5b97c60c2eb9974980b41e..4df5bf806b84d03b2aed13fcf6ab2c00a6698d86 100644 +index bbe2a420193d15874a4dd384ddb3e24ee2d62420..2ded2c36adc079b9bd3ab4857fb4043f92f52378 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1443,6 +1443,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1440,6 +1440,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe this.emptyTime = 0; } diff --git a/leaf-server/minecraft-patches/features/0222-Micro-optimizations-for-random-tick.patch b/leaf-server/minecraft-patches/features/0222-Micro-optimizations-for-random-tick.patch index 644d08dd..6bfb7452 100644 --- a/leaf-server/minecraft-patches/features/0222-Micro-optimizations-for-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0222-Micro-optimizations-for-random-tick.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Micro optimizations for random tick diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 4df5bf806b84d03b2aed13fcf6ab2c00a6698d86..ab509260d536dfb5cb0268c20abf445498602fff 100644 +index 2ded2c36adc079b9bd3ab4857fb4043f92f52378..65f6cd4508c41f14930f768575fe26dc80c9a2fa 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1041,7 +1041,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1038,7 +1038,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper start - optimise random ticking private void optimiseRandomTick(final LevelChunk chunk, final int tickSpeed) { final LevelChunkSection[] sections = chunk.getSections(); @@ -17,7 +17,7 @@ index 4df5bf806b84d03b2aed13fcf6ab2c00a6698d86..ab509260d536dfb5cb0268c20abf4454 final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Leaf - Faster random generator - upcasting final boolean doubleTickFluids = !ca.spottedleaf.moonrise.common.PlatformHooks.get().configFixMC224294(); -@@ -1050,41 +1050,41 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1047,41 +1047,41 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe final int offsetZ = cpos.z << 4; for (int sectionIndex = 0, sectionsLen = sections.length; sectionIndex < sectionsLen; sectionIndex++) { diff --git a/leaf-server/minecraft-patches/features/0230-Async-target-finding.patch b/leaf-server/minecraft-patches/features/0230-Async-target-finding.patch index 884e510e..ab550e0f 100644 --- a/leaf-server/minecraft-patches/features/0230-Async-target-finding.patch +++ b/leaf-server/minecraft-patches/features/0230-Async-target-finding.patch @@ -134,7 +134,7 @@ index 9b8d119116b0c3a51d3fe2ff7efb33cc39627cc4..436e73086678e4afbf94f1b7bca9b0c7 // Gale start - Pufferfish - SIMD support diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index ab509260d536dfb5cb0268c20abf445498602fff..da0ce01e3359686c7360367c3af9c79ad8e51aa4 100644 +index 68bba7703ffcbeb1bf18d7dfdba9dbb21fe9d4b5..cc88326fc1017605c65b32a7a124cc8734281a86 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java @@ -175,7 +175,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe @@ -195,7 +195,7 @@ index 6d9d8f85bf6936eee76c3d790dd676aa309a1d46..b19d2066b921d27f03b2c0af06fbf76f @Override diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java -index 7caf9ea3792089dcf890c2af0ac17ee1d5c85c16..3882bc7a8d1684b91876eb7c6799949071e72626 100644 +index 173ec6919cef2aa90c40d3bf33d927a2db7b4922..e99fd3cd5ca6863facd189bf9cd4a3d5c102424a 100644 --- a/net/minecraft/world/entity/Mob.java +++ b/net/minecraft/world/entity/Mob.java @@ -138,6 +138,12 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab @@ -223,7 +223,7 @@ index 7caf9ea3792089dcf890c2af0ac17ee1d5c85c16..3882bc7a8d1684b91876eb7c67999490 } // Paper end -@@ -792,6 +803,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab +@@ -782,6 +793,11 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking this.goalSelector.tick(); } diff --git a/leaf-server/minecraft-patches/features/0257-optimize-mob-spawning.patch b/leaf-server/minecraft-patches/features/0257-optimize-mob-spawning.patch index 431763b7..42539235 100644 --- a/leaf-server/minecraft-patches/features/0257-optimize-mob-spawning.patch +++ b/leaf-server/minecraft-patches/features/0257-optimize-mob-spawning.patch @@ -14,7 +14,7 @@ Generally faster than the non-async approach iterate over all entities, get their chunk, and increment the count diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java -index 5c369b3d94e369c3f240821ad90b9d96223f24ca..9803c395fce103cb7bc746f43a017ff9ed99728c 100644 +index 5913c41711e8226550aa65828380e31f295eddb2..ed8f53f4394d98f0b56bfb6d9e8b24d8636d55d2 100644 --- a/net/minecraft/server/level/ChunkMap.java +++ b/net/minecraft/server/level/ChunkMap.java @@ -278,6 +278,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -192,13 +192,13 @@ index 46e171ca454253c32e22c0c18587e9a7ba19f331..43156ecde8bb86c77f3b13c17b3330ea } } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index eb5e1e67db2ee1bdbedfa244088fcb7a9356bae3..5e1f29af864df93856eabccde05a98f5acbdd505 100644 +index ccb0913370e862e936f2a23289d4d49ce8773759..3621e23f98847801b230f181712d1686f1617918 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1108,6 +1108,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1105,6 +1105,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe private int currentIceAndSnowTick = 0; protected void resetIceAndSnowTick() { this.currentIceAndSnowTick = this.simpleRandom.nextInt(16); } // Gale - Airplane - optimize random calls in chunk ticking - public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(paperConfig()); // Leaf - optimize despawn + public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(); // Leaf - optimize despawn + public final org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning public void tickChunk(LevelChunk chunk, int randomTickSpeed) { final net.minecraft.world.level.levelgen.BitRandomSource simpleRandom = this.simpleRandom; // Paper - optimise random ticking // Leaf - Faster random generator - upcasting diff --git a/leaf-server/minecraft-patches/features/0264-optimize-random-tick.patch b/leaf-server/minecraft-patches/features/0264-optimize-random-tick.patch index 77ee558d..c99cf68a 100644 --- a/leaf-server/minecraft-patches/features/0264-optimize-random-tick.patch +++ b/leaf-server/minecraft-patches/features/0264-optimize-random-tick.patch @@ -24,12 +24,12 @@ index 43156ecde8bb86c77f3b13c17b3330eae95efcc3..ba0f66b5089e252e5d532cb4b94c6246 this.level.tickCustomSpawners(this.spawnEnemies, this.spawnFriendlies); } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 5e1f29af864df93856eabccde05a98f5acbdd505..964c97a394122083a2f3c77b8ca529592c826080 100644 +index 3621e23f98847801b230f181712d1686f1617918..23ff9783e6544af21c14aab7713f0255e4c04b1d 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1109,6 +1109,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1106,6 +1106,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(paperConfig()); // Leaf - optimize despawn + public final org.dreeam.leaf.world.DespawnMap despawnMap = new org.dreeam.leaf.world.DespawnMap(); // Leaf - optimize despawn public final org.dreeam.leaf.world.NatureSpawnChunkMap natureSpawnChunkMap = new org.dreeam.leaf.world.NatureSpawnChunkMap(); // Leaf - optimize mob spawning + public final org.dreeam.leaf.world.RandomTickSystem randomTickSystem = new org.dreeam.leaf.world.RandomTickSystem(); // Leaf - optimize random tick public void tickChunk(LevelChunk chunk, int randomTickSpeed) { diff --git a/leaf-server/minecraft-patches/features/0271-Paw-optimization.patch b/leaf-server/minecraft-patches/features/0271-Paw-optimization.patch index 6524478f..49888dad 100644 --- a/leaf-server/minecraft-patches/features/0271-Paw-optimization.patch +++ b/leaf-server/minecraft-patches/features/0271-Paw-optimization.patch @@ -100,10 +100,10 @@ index 4535858701b2bb232b9d2feb2af6551526232ddc..e65c62dbe4c1560ae153e4c4344e9194 - // Paper end - detailed watchdog information } diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 0709922df9b1dcef1010de725bf132d194e29aad..e39c3b7cea5de9a2e6d5e2256617c969d41a55c8 100644 +index 23ff9783e6544af21c14aab7713f0255e4c04b1d..5eb8ee3ef0273b58acbf31768e16fb707436aa12 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -1512,13 +1512,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1509,13 +1509,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe // Paper end - log detailed entity tick information public void tickNonPassenger(Entity entity) { @@ -117,7 +117,7 @@ index 0709922df9b1dcef1010de725bf132d194e29aad..e39c3b7cea5de9a2e6d5e2256617c969 entity.setOldPosAndRot(); entity.tickCount++; entity.totalEntityAge++; // Paper - age-like counter for all entities -@@ -1531,13 +1525,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -1528,13 +1522,6 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe for (Entity entity1 : entity.getPassengers()) { this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 } diff --git a/leaf-server/paper-patches/features/0059-optimize-despawn.patch b/leaf-server/paper-patches/features/0059-optimize-despawn.patch index 17c237b1..1be60e74 100644 --- a/leaf-server/paper-patches/features/0059-optimize-despawn.patch +++ b/leaf-server/paper-patches/features/0059-optimize-despawn.patch @@ -17,18 +17,3 @@ index ed687b0ab589fd2ddb8bf77f42ba42cf8b1c2ea7..5d075f51baeaf775a458f459b762bf62 public DespawnRange.Shape despawnRangeShape = DespawnRange.Shape.ELLIPSOID; @MergeMap public Reference2IntMap ticksPerSpawn = Util.make(new Reference2IntOpenHashMap<>(NaturalSpawner.SPAWNING_CATEGORIES.length), map -> Arrays.stream(NaturalSpawner.SPAWNING_CATEGORIES).forEach(mobCategory -> map.put(mobCategory, -1))); -diff --git a/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java b/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java -index 7779edcbbce36d7da177a92807dac73fbe24c9fa..093638ded4e35a33f34ef3d0e0fc2175efc5efaf 100644 ---- a/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java -+++ b/src/main/java/io/papermc/paper/configuration/type/DespawnRange.java -@@ -20,8 +20,8 @@ public final class DespawnRange { - - public static final TypeSerializer SERIALIZER = new Serializer(); - -- private final IntOr.Default horizontalLimit; -- private final IntOr.Default verticalLimit; -+ public final IntOr.Default horizontalLimit; // Leaf - optimize despawn -+ public final IntOr.Default verticalLimit; // Leaf - optimize despawn - private final boolean wasDefinedViaLongSyntax; - - // cached values diff --git a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeDespawn.java b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeDespawn.java index 18e4d14d..76530c19 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeDespawn.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeDespawn.java @@ -1,6 +1,5 @@ package org.dreeam.leaf.config.modules.opt; -import net.minecraft.world.entity.MobCategory; import org.dreeam.leaf.config.ConfigModules; import org.dreeam.leaf.config.EnumConfigCategory; import org.dreeam.leaf.config.annotations.Experimental; diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnMap.java b/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnMap.java index edb60a0f..ff90af01 100644 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnMap.java +++ b/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnMap.java @@ -1,354 +1,81 @@ package org.dreeam.leaf.world; -import gg.pufferfish.pufferfish.simd.SIMDDetection; import io.papermc.paper.configuration.WorldConfiguration; -import it.unimi.dsi.fastutil.doubles.DoubleArrays; -import it.unimi.dsi.fastutil.longs.LongArrays; +import io.papermc.paper.configuration.type.DespawnRange; +import it.unimi.dsi.fastutil.objects.ObjectArrays; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; +import net.minecraft.util.Mth; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.entity.EntityTickList; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; import org.bukkit.event.entity.EntityRemoveEvent; -import org.dreeam.leaf.LeafBootstrap; -import java.util.Map; -import java.util.OptionalInt; +import java.util.List; public final class DespawnMap { - private static final ServerPlayer[] EMPTY_PLAYERS = {}; - private static final double[] EMPTY_DOUBLES = {}; - private static final long[] EMPTY_LONGS = {}; - private static final int[] EMPTY_INTS = {}; - static final boolean FMA = LeafBootstrap.enableFMA; - private static final boolean SIMD = SIMDDetection.isEnabled(); - private static final int LEAF_THRESHOLD = SIMD ? DespawnVectorAPI.DOUBLE_VECTOR_LENGTH : 4; - private static final int INITIAL_CAP = 8; - static final long LEAF = -1L; - static final long AXIS_X = 0L; - static final long AXIS_Y = 1L; - static final long LEFT_MASK = 0xfffffffcL; - static final long RIGHT_MASK = 0x3fffffff00000000L; - static final long AXIS_MASK = 0b11L; + private static final ServerPlayer[] EMPTY_PLAYER_ARRAY = {}; - /// Stack for tree construction - private final Stack stack = new Stack(INITIAL_CAP); - /// Stack for tree traversal - private int[] search = EMPTY_INTS; + private ServerPlayer[] players = EMPTY_PLAYER_ARRAY; + private double[] pos = {}; - private int nodeLen = 0; - private int bucketLen = 0; + public void prepare(ServerLevel world) { + this.players = world.players().toArray(EMPTY_PLAYER_ARRAY); - /// Node coordinate for each internal node - private double[] nsl = EMPTY_DOUBLES; - /// Offsets(32) Lengths(32) for each player list of leaf nodes - private long[] nbl = EMPTY_LONGS; - /// Left(30) Right(30) Axis(2) for each internal node - private long[] nll = EMPTY_LONGS; - /// Nested player X coordinates of leaf nodes - private double[] bxl = EMPTY_DOUBLES; - /// Nested player Y coordinates of leaf nodes - private double[] byl = EMPTY_DOUBLES; - /// Nested player Z coordinates of leaf nodes - private double[] bzl = EMPTY_DOUBLES; - - private final double[] hard; - private final double[] sort; - - public DespawnMap(WorldConfiguration worldConfiguration) { - MobCategory[] caps = MobCategory.values(); - hard = new double[caps.length]; - sort = new double[caps.length]; - for (int i = 0; i < caps.length; i++) { - sort[i] = caps[i].getNoDespawnDistance(); - hard[i] = caps[i].getDespawnDistance(); - } - for (Map.Entry e : worldConfiguration.entities.spawning.despawnRanges.entrySet()) { - OptionalInt a = e.getValue().soft().verticalLimit.value(); - OptionalInt b = e.getValue().soft().horizontalLimit.value(); - OptionalInt c = e.getValue().hard().verticalLimit.value(); - OptionalInt d = e.getValue().hard().horizontalLimit.value(); - if (a.isPresent() && b.isPresent() && a.getAsInt() == b.getAsInt()) { - sort[e.getKey().ordinal()] = a.getAsInt(); - } - if (c.isPresent() && d.isPresent() && c.getAsInt() == d.getAsInt()) { - hard[e.getKey().ordinal()] = c.getAsInt(); - } - } - for (int i = 0; i < caps.length; i++) { - if (sort[i] > 0.0) { - sort[i] = sort[i] * sort[i]; - } - if (hard[i] > 0.0) { - hard[i] = hard[i] * hard[i]; - } - } - } - - private void build(double[] coordX, double[] coordY, double[] coordZ) { - final double[][] map = {coordX, coordY, coordZ}; - final int[] data = new int[coordX.length]; - for (int i = 0; i < coordX.length; i++) { - data[i] = i; - } - stack.push(new Node(-1, false, 0, coordX.length, 0)); - while (!stack.isEmpty()) { - grow(); - - final Node n = stack.pop(); - final int depth = n.depth; - final int offset = n.offset; - final int len = n.length; - final int curr = nodeLen++; - if (len <= LEAF_THRESHOLD) { - nbl[curr] = (long) bucketLen << 32 | (long) len; - growBucket(len); - for (int i = 0; i < len; i++) { - int p = data[offset + i]; - bxl[bucketLen + i] = coordX[p]; - byl[bucketLen + i] = coordY[p]; - bzl[bucketLen + i] = coordZ[p]; - } - bucketLen += len; - nll[curr] = LEAF; - } else { - final int axis = depth % 3; - final int median = len / 2; - quickSelect(data, offset, offset + len - 1, offset + median, map[axis]); - final int pivot = data[offset + median]; - nsl[curr] = axis == AXIS_X ? coordX[pivot] : axis == AXIS_Y ? coordY[pivot] : coordZ[pivot]; - nll[curr] = LEFT_MASK | RIGHT_MASK | (long) axis; - stack.push(new Node(curr, true, offset, median, depth + 1)); - stack.push(new Node(curr, false, offset + median + 1, len - median - 1, depth + 1)); - } - if (n.parent >= 0) { - if (n.left) { - nll[n.parent] &= AXIS_MASK | RIGHT_MASK; - nll[n.parent] |= (long) curr << 2; - } else { - nll[n.parent] &= AXIS_MASK | LEFT_MASK; - nll[n.parent] |= (long) curr << 32; - } - } - } - } - - private void insertionSort(int[] indices, int left, int right, double[] coord) { - for (int i = left + 1; i <= right; i++) { - int key = indices[i]; - double val = coord[key]; - int j = i - 1; - - while (j >= left && coord[indices[j]] > val) { - indices[j + 1] = indices[j]; - j--; - } - indices[j + 1] = key; - } - } - - private void quickSelect(int[] indices, int left, int right, int k, double[] coord) { - while (left < right) { - if (right - left < 8) { - insertionSort(indices, left, right, coord); - return; - } - int mid = left + (right - left) / 2; - int a = indices[left], b = indices[mid], c = indices[right]; - double va = coord[a], vb = coord[b], vc = coord[c]; - int pivotIdx = (va < vb) - ? (vb < vc ? mid : (va < vc ? right : left)) - : (va < vc ? left : (vb < vc ? right : mid)); - swap(indices, pivotIdx, left); - double pivot = coord[indices[left]]; - - int i = left; - int j = right + 1; - - while (true) { - while (++i <= right && coord[indices[i]] < pivot) ; - while (--j > left && coord[indices[j]] > pivot) ; - if (i >= j) break; - swap(indices, i, j); - } - - swap(indices, left, j); - int p = j; - if (p == k) { - return; - } else if (k < p) { - right = p - 1; - } else { - left = p + 1; - } - } - } - - private void swap(int[] arr, int i, int j) { - int temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - - private void reset() { - nodeLen = 0; - bucketLen = 0; - } - - private void grow() { - int capacity = nodeLen + 1; - if (capacity < nsl.length) { - return; - } - capacity += capacity >> 1; - if (capacity < INITIAL_CAP) { - capacity = INITIAL_CAP; - } - nsl = DoubleArrays.forceCapacity(nsl, capacity, nodeLen); - nll = LongArrays.forceCapacity(nll, capacity, nodeLen); - nbl = LongArrays.forceCapacity(nbl, capacity, nodeLen); - } - - private void growBucket(int capacity) { - capacity = bucketLen + capacity; - if (capacity < bxl.length) { - return; - } - capacity += capacity >> 1; - if (capacity < INITIAL_CAP) { - capacity = INITIAL_CAP; - } - bxl = DoubleArrays.forceCapacity(bxl, capacity, bucketLen); - byl = DoubleArrays.forceCapacity(byl, capacity, bucketLen); - bzl = DoubleArrays.forceCapacity(bzl, capacity, bucketLen); - } - - private record Node(int parent, boolean left, int offset, int length, int depth) { - } - - private double nearest(final double tx, final double ty, final double tz, double dist) { - if (nodeLen == 0) { - return Double.POSITIVE_INFINITY; - } - if (search.length < Math.max(64, nodeLen * 4)) { - search = new int[Math.max(64, nodeLen * 4)]; - } - if (SIMD) { - return DespawnVectorAPI.nearest(search, nsl, nll, nbl, bxl, byl, bzl, tx, ty, tz, dist); - } - final int[] stack = this.search; - final double[] nsl = this.nsl; - final long[] nll = this.nll; - final double[] bxl = this.bxl; - final double[] byl = this.byl; - final double[] bzl = this.bzl; - final long[] nbl = this.nbl; - int i = 0; - stack[i++] = 0; - while (i != 0) { - final int idx = stack[--i]; - final long data = nll[idx]; - if (data != LEAF) { - final long axis = data & AXIS_MASK; - final double delta = (axis == AXIS_X ? tx : axis == AXIS_Y ? ty : tz) - nsl[idx]; - final boolean negative = (Double.doubleToRawLongBits(delta) & 0x8000_0000_0000_0000L) == 0x8000_0000_0000_0000L; - final long sMask = negative ? -1L : 0L; - final boolean leftValid = (data & LEFT_MASK) != LEFT_MASK; - final boolean rightValid = (data & RIGHT_MASK) != RIGHT_MASK; - final long node = sMask & (data & LEFT_MASK) >>> 2 | ~sMask & data >>> 32; - final long other = sMask & data >>> 32 | ~sMask & (data & LEFT_MASK) >>> 2; - if ((negative & leftValid) | (!negative & rightValid)) { - stack[i++] = (int) node; - } - if ((!negative & leftValid) | (negative & rightValid) && delta * delta < dist) { - stack[i++] = (int) other; - } - } else { - final long bucket = nbl[idx]; - int start = (int) (bucket >>> 32); - final int end = start + (int) (bucket & 0xffffffffL); - for (; start < end; start++) { - final double dx = bxl[start] - tx; - final double dy = byl[start] - ty; - final double dz = bzl[start] - tz; - final double d2 = FMA ? Math.fma(dz, dz, Math.fma(dy, dy, dx * dx)) : dx * dx + dy * dy + dz * dz; - dist = Math.min(dist, d2); - } - } - } - return dist; - } - - private static final class Stack { - - private Node[] a; - private int i; - - private Stack(int capacity) { - a = new Node[capacity]; - i = 0; - } - - private boolean isEmpty() { - return i == 0; - } - - private void push(Node value) { - if (i == a.length) { - grow(); - } - a[i++] = value; - } - - private Node pop() { - return a[--i]; - } - - private void grow() { - Node[] b = new Node[a.length << 1]; - System.arraycopy(a, 0, b, 0, i); - a = b; - } - } - - public void tick(ServerLevel world, EntityTickList entityTickList) { - final ServerPlayer[] playerArr = world.players().toArray(EMPTY_PLAYERS); - final ServerPlayer[] list = new ServerPlayer[playerArr.length]; + List playerList = world.players(); + ServerPlayer[] list = new ServerPlayer[playerList.size()]; int newSize = 0; - for (ServerPlayer player1 : playerArr) { + for (ServerPlayer player1 : playerList) { if (EntitySelector.PLAYER_AFFECTS_SPAWNING.test(player1)) { list[newSize++] = player1; } } - double[] pxl = new double[newSize]; - double[] pyl = new double[newSize]; - double[] pzl = new double[newSize]; - for (int i = 0; i < newSize; i++) { - pxl[i] = list[i].getX(); - pyl[i] = list[i].getY(); - pzl[i] = list[i].getZ(); + list = ObjectArrays.trim(list, newSize); + this.players = list; + this.pos = new double[this.players.length * 3]; + for (int i = 0; i < players.length; i++) { + this.pos[i * 3] = this.players[i].getX(); + this.pos[i * 3 + 1] = this.players[i].getY(); + this.pos[i * 3 + 2] = this.players[i].getZ(); } - build(pxl, pyl, pzl); - entityTickList.forEach(Entity::leafCheckDespawn); - reset(); + } + + public void reset() { + this.players = EMPTY_PLAYER_ARRAY; } public void checkDespawn(Mob mob) { - final double x = mob.getX(); - final double y = mob.getY(); - final double z = mob.getZ(); - final int i = mob.getType().getCategory().ordinal(); - final double dist = nearest(x, y, z, hard[i]); - if (dist == Double.POSITIVE_INFINITY) { + double x = mob.getX(); + double y = mob.getY(); + double z = mob.getZ(); + double distance = Double.MAX_VALUE; + Player nearestPlayer = null; + for (int i = 0, playersSize = players.length; i < playersSize; i++) { + final double dx = this.pos[i * 3] - x; + final double dy = this.pos[i * 3 + 1] - y; + final double dz = this.pos[i * 3 + 2] - z; + double d1 = dx * dx + dy * dy + dz * dz; + if (d1 < distance) { + distance = d1; + nearestPlayer = players[i]; + } + } + if (nearestPlayer == null) { return; } - - if (dist >= hard[i] && mob.removeWhenFarAway(dist)) { + Level world = mob.level(); + final WorldConfiguration.Entities.Spawning.DespawnRangePair despawnRangePair = world.paperConfig().entities.spawning.despawnRanges.get(mob.getType().getCategory()); + final DespawnRange.Shape shape = world.paperConfig().entities.spawning.despawnRangeShape; + final double dy = Math.abs(nearestPlayer.getY() - y); + final double dySqr = Mth.square(dy); + final double dxSqr = Mth.square(nearestPlayer.getX() - x); + final double dzSqr = Mth.square(nearestPlayer.getZ() - z); + final double distanceSquared = dxSqr + dzSqr + dySqr; + if (despawnRangePair.hard().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy) && mob.removeWhenFarAway(distanceSquared)) { mob.discard(EntityRemoveEvent.Cause.DESPAWN); - } else if (dist > sort[i]) { - if (mob.getNoActionTime() > 600 && mob.random.nextInt(800) == 0 && mob.removeWhenFarAway(dist)) { + } else if (despawnRangePair.soft().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy)) { + if (mob.getNoActionTime() > 600 && mob.random.nextInt(800) == 0 && mob.removeWhenFarAway(distanceSquared)) { mob.discard(EntityRemoveEvent.Cause.DESPAWN); } } else { diff --git a/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnVectorAPI.java b/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnVectorAPI.java deleted file mode 100644 index bcf99fae..00000000 --- a/leaf-server/src/main/java/org/dreeam/leaf/world/DespawnVectorAPI.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.dreeam.leaf.world; -import jdk.incubator.vector.*; - -import static org.dreeam.leaf.world.DespawnMap.*; - -public final class DespawnVectorAPI { - - private DespawnVectorAPI() { - } - - private static final VectorSpecies DOUBLE_SPECIES = DoubleVector.SPECIES_PREFERRED; - static final int DOUBLE_VECTOR_LENGTH = DOUBLE_SPECIES.length(); - - static double nearest(final int[] stack, - final double[] nsl, - final long[] nll, - final long[] nbl, - final double[] bxl, final double[] byl, final double[] bzl, - final double tx, final double ty, final double tz, - double dist) { - final DoubleVector vtx = DoubleVector.broadcast(DOUBLE_SPECIES, tx); - final DoubleVector vty = DoubleVector.broadcast(DOUBLE_SPECIES, ty); - final DoubleVector vtz = DoubleVector.broadcast(DOUBLE_SPECIES, tz); - int i = 0; - stack[i++] = 0; - while (i != 0) { - final int idx = stack[--i]; - final long data = nll[idx]; - if (data != LEAF) { - final long axis = data & AXIS_MASK; - final double delta = (axis == AXIS_X ? tx : axis == AXIS_Y ? ty : tz) - nsl[idx]; - final boolean negative = (Double.doubleToRawLongBits(delta) & 0x8000_0000_0000_0000L) == 0x8000_0000_0000_0000L; - final long sMask = negative ? -1L : 0L; - final boolean leftValid = (data & LEFT_MASK) != LEFT_MASK; - final boolean rightValid = (data & RIGHT_MASK) != RIGHT_MASK; - final long node = sMask & (data & LEFT_MASK) >>> 2 | ~sMask & data >>> 32; - final long other = sMask & data >>> 32 | ~sMask & (data & LEFT_MASK) >>> 2; - if ((negative & leftValid) | (!negative & rightValid)) { - stack[i++] = (int) node; - } - if ((!negative & leftValid) | (negative & rightValid) && delta * delta < dist) { - stack[i++] = (int) other; - } - } else { - final long bucket = nbl[idx]; - final int start = (int) (bucket >>> 32); - final int bucketSize = (int) (bucket & 0xffffffffL); - if (DOUBLE_VECTOR_LENGTH == bucketSize) { - final DoubleVector vdx = DoubleVector.fromArray(DOUBLE_SPECIES, bxl, start).sub(vtx); - final DoubleVector vdy = DoubleVector.fromArray(DOUBLE_SPECIES, byl, start).sub(vty); - final DoubleVector vdz = DoubleVector.fromArray(DOUBLE_SPECIES, bzl, start).sub(vtz); - final DoubleVector vDist = FMA ? - vdz.fma(vdz, vdy.fma(vdy, vdx.mul(vdx))) : - vdx.mul(vdx).add(vdy.mul(vdy)).add(vdz.mul(vdz)); - dist = Math.min(dist, vDist.reduceLanes(VectorOperators.MIN)); - } else if (DOUBLE_VECTOR_LENGTH > 4 && bucketSize >= 4) { - final VectorMask mask = DOUBLE_SPECIES.indexInRange(0, bucketSize); - final DoubleVector vdx = DoubleVector.fromArray(DOUBLE_SPECIES, bxl, start, mask).sub(vtx); - final DoubleVector vdy = DoubleVector.fromArray(DOUBLE_SPECIES, byl, start, mask).sub(vty); - final DoubleVector vdz = DoubleVector.fromArray(DOUBLE_SPECIES, bzl, start, mask).sub(vtz); - final DoubleVector vDist = FMA ? - vdz.fma(vdz, vdy.fma(vdy, vdx.mul(vdx))) : - vdx.mul(vdx).add(vdy.mul(vdy)).add(vdz.mul(vdz)); - dist = Math.min(dist, vDist.reduceLanes(VectorOperators.MIN)); - } else { - final int end = start + bucketSize; - for (int j = start; j < end; j++) { - final double dx = bxl[j] - tx; - final double dy = byl[j] - ty; - final double dz = bzl[j] - tz; - final double d2 = FMA ? Math.fma(dz, dz, Math.fma(dy, dy, dx * dx)) : dx * dx + dy * dy + dz * dz; - dist = Math.min(dist, d2); - } - } - } - } - return dist; - } -}