diff --git a/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 1222d1d5d..01b3509db 100644 --- a/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -40,8 +40,8 @@ public class AkarinGlobalConfig { config.options().header(HEADER); config.options().copyDefaults(true); - version = getInt("config-version", 2); - set("config-version", 2); + version = getInt("config-version", 3); + set("config-version", 3); readConfig(AkarinGlobalConfig.class, null); } @@ -178,7 +178,11 @@ public class AkarinGlobalConfig { public static double blockbreakAnimationVisibleDistance = 1024; private static void blockbreakAnimationVisibleDistance() { - blockbreakAnimationVisibleDistance = Math.sqrt(getDouble("alternative.block-break-animation-visible-distance", 32.00)); + double def = 32.00; + if (version == 2) + def = getDouble("alternative.block-break-animation-visible-distance", def); + + blockbreakAnimationVisibleDistance = Math.sqrt(getDouble("core.block-break-animation-visible-distance", def)); } public static boolean enableAsyncLighting = true; @@ -205,4 +209,9 @@ public class AkarinGlobalConfig { private static void ignoreRayTraceForSeatableBlocks() { ignoreRayTraceForSeatableBlocks = getBoolean("alternative.ignore-ray-trace-for-seatable-blocks", ignoreRayTraceForSeatableBlocks); } + + public static boolean improvedMobSpawnMechanics = false; + private static void improvedMobSpawnMechanics() { + improvedMobSpawnMechanics = getBoolean("core.improved-mob-spawn-mechanics.enable", improvedMobSpawnMechanics); + } } \ No newline at end of file diff --git a/src/main/java/io/akarin/server/core/AkarinAsyncScheduler.java b/src/main/java/io/akarin/server/core/AkarinAsyncScheduler.java index 3b22d3a36..253eb85e3 100644 --- a/src/main/java/io/akarin/server/core/AkarinAsyncScheduler.java +++ b/src/main/java/io/akarin/server/core/AkarinAsyncScheduler.java @@ -15,7 +15,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.NetworkManager; import net.minecraft.server.PacketPlayOutPlayerInfo; import net.minecraft.server.PacketPlayOutUpdateTime; -import net.minecraft.server.World; import net.minecraft.server.WorldServer; public class AkarinAsyncScheduler extends Thread { diff --git a/src/main/java/io/akarin/server/core/AkarinCreatureSpanwner.java b/src/main/java/io/akarin/server/core/AkarinCreatureSpanwner.java new file mode 100644 index 000000000..447c82c0c --- /dev/null +++ b/src/main/java/io/akarin/server/core/AkarinCreatureSpanwner.java @@ -0,0 +1,252 @@ +package io.akarin.server.core; + +import java.util.EnumMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; + +import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent; +import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent; +import com.destroystokyo.paper.exception.ServerInternalException; +import com.koloboke.collect.set.hash.HashObjSets; + +import io.akarin.server.misc.ChunkCoordOrdinalInt3Tuple; +import net.minecraft.server.BiomeBase; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkCoordIntPair; +import net.minecraft.server.EntityHuman; +import net.minecraft.server.EntityInsentient; +import net.minecraft.server.EntityPositionTypes; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.EnumCreatureType; +import net.minecraft.server.GroupDataEntity; +import net.minecraft.server.MCUtil; +import net.minecraft.server.MathHelper; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PlayerChunk; +import net.minecraft.server.SpawnerCreature; +import net.minecraft.server.WorldServer; + +/* + * Reference on spawning mechanics by Colin Godsey + * https://github.com/yesdog/Paper/blob/0de3dd84b7e6688feb42af4fe6b4f323ce7e3013/Spigot-Server-Patches/0433-alternate-mob-spawning-mechanic.patch + */ +public class AkarinCreatureSpanwner { + private static int getSpawnRange(WorldServer world, EntityHuman player) { + byte mobSpawnRange = world.spigotConfig.mobSpawnRange; + + mobSpawnRange = (mobSpawnRange > world.spigotConfig.viewDistance) ? (byte) world.spigotConfig.viewDistance : mobSpawnRange; + mobSpawnRange = (mobSpawnRange > 8) ? 8 : mobSpawnRange; + + if (PlayerNaturallySpawnCreaturesEvent.getHandlerList().getRegisteredListeners().length > 0) { + PlayerNaturallySpawnCreaturesEvent event = new PlayerNaturallySpawnCreaturesEvent((Player) player.getBukkitEntity(), mobSpawnRange); + new RuntimeException("Warning, one or more plugins is listening PlayerNaturallySpawnCreaturesEvent which is running asynchronously, this will may cause safe issue!").printStackTrace(); + synchronized (PlayerNaturallySpawnCreaturesEvent.class) { + Bukkit.getPluginManager().callEvent(event); + } + + return event.isCancelled() ? 0 : event.getSpawnRadius(); + } + + return mobSpawnRange; + } + + private static int getCreatureLimit(WorldServer world, EnumCreatureType type) { + switch (type) { + case MONSTER: + return world.getWorld().getMonsterSpawnLimit(); + case CREATURE: + return world.getWorld().getAnimalSpawnLimit(); + case WATER_CREATURE: + return world.getWorld().getWaterAnimalSpawnLimit(); + case AMBIENT: + return world.getWorld().getAmbientSpawnLimit(); + } + return type.spawnLimit(); + } + + @Nullable + private static EntityInsentient createMob(WorldServer world, EnumCreatureType type, BlockPosition pos, BiomeBase.BiomeMeta biomeMeta) { + if (!world.isBiomeMetaValidAt(type, biomeMeta, pos)) return null; + + EntityTypes entityType = biomeMeta.entityType(); + org.bukkit.entity.EntityType bType = EntityTypes.clsToTypeMap.get(entityType.entityClass()); + if (bType != null) { + if (PreCreatureSpawnEvent.getHandlerList().getRegisteredListeners().length > 0) { + PreCreatureSpawnEvent event = new PreCreatureSpawnEvent( + MCUtil.toLocation(world, pos), + bType, SpawnReason.NATURAL + ); + new RuntimeException("Warning, one or more plugins is listening PlayerNaturallySpawnCreaturesEvent which is running asynchronously, this will may cause safe issue!").printStackTrace(); + synchronized (PreCreatureSpawnEvent.class) { + Bukkit.getPluginManager().callEvent(event); + } + + if (!event.isCancelled() || event.shouldAbortSpawn()) + return null; + } + } + + EntityInsentient entity = null; + + try { + entity = entityType.create(world); + } catch (Exception exception) { + MinecraftServer.LOGGER.warn("Failed to create mob", exception); + ServerInternalException.reportInternalException(exception); + } + + return entity; + } + + private static void spawnMob0(WorldServer world, Set chunks, EnumCreatureType type, int amount) { + if (chunks.isEmpty()) return; + + final int maxPackIterations = 10; // X attempts per pack, 1 pack per chunk + Iterator iterator = chunks.iterator(); + BlockPosition worldSpawn = world.getSpawn(); + + int spawned = 0; + + while (spawned < amount && iterator.hasNext()) { + ChunkCoordIntPair chunkCoord = iterator.next(); + int packSize = world.random.nextInt(4) + 1; + BlockPosition packCenter = SpawnerCreature.getRandomPosition(world, chunkCoord.x, chunkCoord.z); + + if (world.getType(packCenter).isOccluding()) continue; + + int x = packCenter.getX(); + int y = packCenter.getY(); + int z = packCenter.getZ(); + BlockPosition.MutableBlockPosition blockPointer = new BlockPosition.MutableBlockPosition(); + BiomeBase.BiomeMeta biomeMeta = null; + GroupDataEntity group = null; + EntityPositionTypes.Surface surfaceType = null; + int iter = 0; + int packSpawned = 0; + + while (packSpawned < packSize && iter < maxPackIterations) { + iter++; + + // random walk + x += world.random.nextInt(12) - 6; + y += world.random.nextInt(2) - 1; + z += world.random.nextInt(12) - 6; + blockPointer.setValues(x, y, z); + + if (worldSpawn.distanceSquared(x + 0.5, y, z + 0.5) < (24 * 24)) continue; + + if (biomeMeta == null) { + biomeMeta = world.getBiomeMetaAt(type, blockPointer); + + if (biomeMeta == null) break; + + int packRange = 1 + biomeMeta.getMaxPackSize() - biomeMeta.getMinPackSize(); + packSize = biomeMeta.getMinPackSize() + world.random.nextInt(packRange); + surfaceType = EntityPositionTypes.a(biomeMeta.entityType()); + } + + EntityInsentient entity = createMob(world, type, blockPointer, biomeMeta); + + if (entity == null) continue; + + entity.setPositionRotation(x + 0.5, y, z + 0.5, world.random.nextFloat() * 360.0F, 0.0F); + + if (entity.canSpawnHere() && surfaceType != null + && SpawnerCreature.isValidSpawnSurface(surfaceType, world, blockPointer, biomeMeta.entityType()) + && entity.isNotColliding(world) && !world.isPlayerNearby(x + 0.5, y, z + 0.5, 24)) { + group = entity.prepare(world.getDamageScaler(new BlockPosition(entity)), group, null); + + if (entity.isNotColliding(world) && world.addEntity(entity, SpawnReason.NATURAL)) + packSpawned++; + + if (packSpawned >= entity.maxPackSize()) break; + if ((packSpawned + spawned) >= amount) break; + } else { + entity.die(); + } + } + + spawned += packSpawned; + } + } + + public static void spawnMobs(WorldServer world, boolean spawnMonsters, boolean spawnPassives, boolean spawnRare) { + if(!spawnMonsters && !spawnPassives) return; + + int hashOrdinal = world.random.nextInt(); + + Set rangeChunks = HashObjSets.newUpdatableSet(); + Map> creatureChunks = new EnumMap<>(EnumCreatureType.class); + int[] typeNumSpawn = new int[EnumCreatureType.values().length]; + + for (EnumCreatureType type : EnumCreatureType.values()) { + if (type.passive() && !spawnPassives) continue; + if (!type.passive() && !spawnMonsters) continue; + if (type.rare() && !spawnRare) continue; + if (getCreatureLimit(world, type) <= 0) continue; + + creatureChunks.put(type, HashObjSets.newUpdatableSet()); + } + + if (creatureChunks.isEmpty()) return; + + for (EntityHuman player : world.players) { + if (!player.affectsSpawning || player.isSpectator()) continue; + + int spawnRange = getSpawnRange(world, player); + if (spawnRange <= 0) continue; + + int playerChunkX = MathHelper.floor(player.locX / 16.0); + int playerChunkZ = MathHelper.floor(player.locZ / 16.0); + + rangeChunks.clear(); + + for (int dX = -spawnRange; dX <= spawnRange; ++dX) { + for (int dZ = -spawnRange; dZ <= spawnRange; ++dZ) { + ChunkCoordIntPair chunkCoord = new ChunkCoordOrdinalInt3Tuple(dX + playerChunkX, dZ + playerChunkZ, hashOrdinal); + + if (!world.getWorldBorder().isInBounds(chunkCoord)) continue; + + PlayerChunk pChunk = world.getPlayerChunkMap().getChunk(chunkCoord.x, chunkCoord.z); + + if (pChunk == null || !pChunk.isDone() || pChunk.chunk == null) continue; + + rangeChunks.add(pChunk.chunk); + } + } + + for (EnumCreatureType type : creatureChunks.keySet()) { + int limit = getCreatureLimit(world, type); + int creatureTotal = 0; + + for (Chunk chunk : rangeChunks) + creatureTotal += chunk.creatureCounts[type.ordinal()]; + + // if our local count is above the limit, dont qualify our chunks + if (creatureTotal >= limit) continue; + + Set chunks = creatureChunks.get(type); + for (Chunk chunk : rangeChunks) + chunks.add(chunk.getPos()); + + // expect number is rather meaningless, just a ceil + int expect = limit - creatureTotal; + typeNumSpawn[type.ordinal()] = Math.max(typeNumSpawn[type.ordinal()], expect); + } + } + + for (EnumCreatureType type : creatureChunks.keySet()) { + Set chunks = creatureChunks.get(type); + + if (!chunks.isEmpty()) + spawnMob0(world, chunks, type, typeNumSpawn[type.ordinal()]); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/akarin/server/misc/ChunkCoordOrdinalInt3Tuple.java b/src/main/java/io/akarin/server/misc/ChunkCoordOrdinalInt3Tuple.java new file mode 100644 index 000000000..de10f5bcf --- /dev/null +++ b/src/main/java/io/akarin/server/misc/ChunkCoordOrdinalInt3Tuple.java @@ -0,0 +1,47 @@ +package io.akarin.server.misc; + +import net.minecraft.server.ChunkCoordIntPair; + +import com.google.common.hash.Hashing; +import com.google.common.hash.HashFunction; + +/* + * Reference on spawning mechanics by Colin Godsey + * https://github.com/yesdog/Paper/blob/0de3dd84b7e6688feb42af4fe6b4f323ce7e3013/Spigot-Server-Patches/0433-alternate-mob-spawning-mechanic.patch + */ +public class ChunkCoordOrdinalInt3Tuple extends ChunkCoordIntPair { + public static final HashFunction hashFunc = Hashing.murmur3_32("akarin".hashCode()); + + public final int ordinal; + public final int cachedHashCode; + + public ChunkCoordOrdinalInt3Tuple(int x, int z, int ord) { + super(x, z); + + this.ordinal = ord; + + cachedHashCode = hashFunc.newHasher() + .putInt(ordinal) + .putInt(x) + .putInt(z) + .hash().asInt(); + } + + @Override + public int hashCode() { + return cachedHashCode; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } else if (!(object instanceof ChunkCoordOrdinalInt3Tuple)) { + return false; + } else { + ChunkCoordOrdinalInt3Tuple pair = (ChunkCoordOrdinalInt3Tuple) object; + + return this.x == pair.x && this.z == pair.z && this.ordinal == pair.ordinal; + } + } +} \ No newline at end of file diff --git a/src/main/java/net/minecraft/server/BiomeBase.java b/src/main/java/net/minecraft/server/BiomeBase.java index 3496d4236..8d80e748d 100644 --- a/src/main/java/net/minecraft/server/BiomeBase.java +++ b/src/main/java/net/minecraft/server/BiomeBase.java @@ -587,9 +587,9 @@ public abstract class BiomeBase { public static class BiomeMeta extends WeightedRandom.WeightedRandomChoice { - public EntityTypes b; - public int c; - public int d; + public EntityTypes b; public EntityTypes entityType() { return b; } // Akarin + public int c; public int getMinPackSize() { return c; } // Akarin - OBFHELPER + public int d; public int getMaxPackSize() { return d; } // Akarin - OBFHELPER public BiomeMeta(EntityTypes entitytypes, int i, int j, int k) { super(i); diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java index 67ce9e43b..d832c9520 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -75,6 +75,7 @@ public class Chunk implements IChunkAccess { private int D; private final AtomicInteger E; private final ChunkCoordIntPair F; + public final int[] creatureCounts = new int[EnumCreatureType.values().length]; // Akarin // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking private volatile int neighbors = 0x1 << 12; // Akarin - volatile @@ -766,6 +767,12 @@ public class Chunk implements IChunkAccess { itemCounts[k]++; } else if (entity instanceof IInventory) { inventoryEntityCounts[k]++; + // Akarin start + } else if (entity instanceof IAnimal) { + for (EnumCreatureType type : EnumCreatureType.values()) + if (type.matches(entity)) + creatureCounts[type.ordinal()]++; + // Akarin end } // Paper end } @@ -800,6 +807,14 @@ public class Chunk implements IChunkAccess { itemCounts[i]--; } else if (entity instanceof IInventory) { inventoryEntityCounts[i]--; + // Akarin start + } else if (entity instanceof IAnimal) { + for (EnumCreatureType type : EnumCreatureType.values()) + if (type.matches(entity)) { + int typeCount = creatureCounts[type.ordinal()]; + creatureCounts[type.ordinal()] = typeCount > 1 ? --typeCount : 0; + } + // Akarin end } entityCounts.decrement(entity.getMinecraftKeyString()); // Paper end diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java index f98e9e237..9b6e96464 100644 --- a/src/main/java/net/minecraft/server/EntityInsentient.java +++ b/src/main/java/net/minecraft/server/EntityInsentient.java @@ -754,6 +754,7 @@ public abstract class EntityInsentient extends EntityLiving { return f + f3; } + public boolean canSpawnHere() { return a((GeneratorAccess) this.world, false); } // Akarin public boolean a(GeneratorAccess generatoraccess, boolean flag) { IBlockData iblockdata = generatoraccess.getType((new BlockPosition(this)).down()); @@ -764,10 +765,12 @@ public abstract class EntityInsentient extends EntityLiving { return this.a((IWorldReader) this.world); } + public boolean isNotColliding(IWorldReader iworldreader) { return a(iworldreader); } // Akarin public boolean a(IWorldReader iworldreader) { return !iworldreader.containsLiquid(this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()) && iworldreader.a_(this, this.getBoundingBox()); } + public int maxPackSize() { return dg(); } // Akarin public int dg() { return 4; } diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java index 24ca35119..55d6adca1 100644 --- a/src/main/java/net/minecraft/server/EntityTypes.java +++ b/src/main/java/net/minecraft/server/EntityTypes.java @@ -1,5 +1,6 @@ package net.minecraft.server; +import com.koloboke.collect.map.hash.HashObjObjMaps; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.types.Type; @@ -125,8 +126,8 @@ public class EntityTypes { EntityTypes entitytypes = entitytypes_a.a(s); // Paper start - if (clsToKeyMap == null ) clsToKeyMap = new java.util.HashMap<>(); - if (clsToTypeMap == null ) clsToTypeMap = new java.util.HashMap<>(); + if (clsToKeyMap == null ) clsToKeyMap = HashObjObjMaps.newUpdatableMap(); // Akarin + if (clsToTypeMap == null ) clsToTypeMap = HashObjObjMaps.newUpdatableMap(); // Akarin MinecraftKey key = new MinecraftKey(s); Class entityClass = entitytypes_a.getEntityClass(); @@ -249,6 +250,7 @@ public class EntityTypes { return this.aV; } + public Class entityClass() { return this.c(); } // Akarin public Class c() { return this.aS; } diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java index 42f6a6a93..24e9ffade 100644 --- a/src/main/java/net/minecraft/server/EnumCreatureType.java +++ b/src/main/java/net/minecraft/server/EnumCreatureType.java @@ -22,14 +22,17 @@ public enum EnumCreatureType { return this.e; } + public int spawnLimit() { return this.b(); } // Akarin public int b() { return this.f; } + public boolean passive() { return c(); } // Akarin public boolean c() { return this.g; } + public boolean rare() { return d(); } // Akarin public boolean d() { return this.h; } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java index ac5d15809..6a80d640f 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -302,6 +302,7 @@ public class PlayerChunk { return false; } + public boolean isDone() { return e(); } // Paper - OBFHELPER public boolean e() { return this.done; } diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java index 297c53d15..1eeb8db1b 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -1,6 +1,10 @@ package net.minecraft.server; import com.google.common.collect.Sets; + +import io.akarin.server.core.AkarinCreatureSpanwner; +import io.akarin.server.core.AkarinGlobalConfig; + import java.util.Iterator; import java.util.List; import java.util.Random; @@ -25,6 +29,13 @@ public final class SpawnerCreature { public SpawnerCreature() {} public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) { + // Akarin start + if (AkarinGlobalConfig.improvedMobSpawnMechanics) { + int before = worldserver.entityList.size(); + AkarinCreatureSpanwner.spawnMobs(worldserver, flag, flag1, flag2); + return worldserver.entityList.size() - before; + } + // Akarin end if (!flag && !flag1) { return 0; } else { @@ -253,7 +264,7 @@ public final class SpawnerCreature { } } - private static BlockPosition getRandomPosition(World world, int i, int j) { + public static BlockPosition getRandomPosition(World world, int i, int j) { // Akarin - public Chunk chunk = world.getChunkAt(i, j); int k = i * 16 + world.random.nextInt(16); int l = j * 16 + world.random.nextInt(16); @@ -267,6 +278,7 @@ public final class SpawnerCreature { return iblockdata.k() ? false : (iblockdata.isPowerSource() ? false : (!fluid.e() ? false : !iblockdata.a(TagsBlock.RAILS))); } + public static boolean isValidSpawnSurface(EntityPositionTypes.Surface entitypositiontypes_surface, IWorldReader iworldreader, BlockPosition blockposition, @Nullable EntityTypes entitytypes) { return a(entitypositiontypes_surface, iworldreader, blockposition, entitytypes); } // Akarin public static boolean a(EntityPositionTypes.Surface entitypositiontypes_surface, IWorldReader iworldreader, BlockPosition blockposition, @Nullable EntityTypes entitytypes) { if (entitytypes != null && iworldreader.getWorldBorder().a(blockposition)) { IBlockData iblockdata = iworldreader.getType(blockposition); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 9ca142861..a44d64b92 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -341,6 +341,7 @@ public class WorldServer extends World implements IAsyncTaskHandler { return this.P; } + @Nullable public BiomeBase.BiomeMeta getBiomeMetaAt(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { return a(enumcreaturetype, blockposition); } // Akarin @Nullable public BiomeBase.BiomeMeta a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { List list = this.getChunkProvider().a(enumcreaturetype, blockposition); @@ -348,6 +349,7 @@ public class WorldServer extends World implements IAsyncTaskHandler { return list.isEmpty() ? null : (BiomeBase.BiomeMeta) WeightedRandom.a(this.random, list); } + public boolean isBiomeMetaValidAt(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) { return this.a(enumcreaturetype, biomebase_biomemeta, blockposition); } // Akarin public boolean a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) { List list = this.getChunkProvider().a(enumcreaturetype, blockposition); diff --git a/work/Paper b/work/Paper index 87a726351..2cb75d6cc 160000 --- a/work/Paper +++ b/work/Paper @@ -1 +1 @@ -Subproject commit 87a726351b1496a4bfea36ec5e54a286790caac2 +Subproject commit 2cb75d6ccee0a91d72d150d8ad62a05028a08d93