From 214748ba37598d708dfcb8df57e7f5ea0a20ab1a Mon Sep 17 00:00:00 2001 From: William Blake Galbreath Date: Sun, 1 Mar 2020 20:07:54 -0600 Subject: [PATCH] Optimize Chunk Ticks --- .../co/aikar/timings/WorldTimingsHandler.java | 6 +- .../minecraft/server/ChunkMapDistance.java | 19 +- .../minecraft/server/ChunkProviderServer.java | 254 +++++++++--------- .../minecraft/server/EnumCreatureType.java | 26 +- .../net/minecraft/server/PlayerChunk.java | 3 + .../net/minecraft/server/PlayerChunkMap.java | 19 +- .../net/minecraft/server/WorldServer.java | 1 + 7 files changed, 183 insertions(+), 145 deletions(-) diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java index 51281cfa2..37702cd51 100644 --- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java +++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java @@ -60,7 +60,8 @@ public class WorldTimingsHandler { public final Timing miscMobSpawning; - public final Timing chunkInhibitedRangeCheck; + public final Timing bigRangeCheck; // Purpur + public final Timing smallRangeCheck; // Purpur public final Timing playerMobDistanceMapUpdate; public final Timing poiUnload; @@ -130,7 +131,8 @@ public class WorldTimingsHandler { countNaturalMobs = Timings.ofSafe(name + "Count natural mobs"); - chunkInhibitedRangeCheck = Timings.ofSafe(name + "Chunks - Inhibited Range Check"); + bigRangeCheck = Timings.ofSafe(name + "Chunks - Big Range Check"); // Purpur + smallRangeCheck = Timings.ofSafe(name + "Chunks - Small Range Check"); // Purpur miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc"); playerMobDistanceMapUpdate = Timings.ofSafe(name + "Per Player Mob Spawning - Distance Map Update"); diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java index 73d157076..1c72ec1c7 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java @@ -31,7 +31,7 @@ public abstract class ChunkMapDistance { private final Long2ObjectMap> c = new Long2ObjectOpenHashMap(); public final Long2ObjectOpenHashMap>> tickets = new Long2ObjectOpenHashMap(); private final ChunkMapDistance.a e = new ChunkMapDistance.a(); - private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); + private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); private ChunkMapDistance.b getPlayerDistanceChunkMap() { return f; } // Purpur - OBFHELPER private final ChunkMapDistance.c g = new ChunkMapDistance.c(33); private final java.util.Queue pendingChunkUpdates = new java.util.LinkedList<>(); // PAIL pendingChunkUpdates // Paper - use a queue private final ChunkTaskQueueSorter i; @@ -258,6 +258,20 @@ public abstract class ChunkMapDistance { return this.f.a.size(); } + // Purpur start + public void updatePlayerDistanceChunkMap() { + getPlayerDistanceChunkMap().update(); + } + + public int getPlayerDistanceChunkMapSize() { + return getPlayerDistanceChunkMap().getChunks().size(); + } + + public boolean hasPlayersNearby(long chunk) { + return getPlayerDistanceChunkMap().getChunks().containsKey(chunk); + } + // Purpur end + public boolean d(long i) { this.f.a(); return this.f.a.containsKey(i); @@ -425,7 +439,7 @@ public abstract class ChunkMapDistance { class b extends ChunkMap { - protected final Long2ByteMap a = new Long2ByteOpenHashMap(); + protected final Long2ByteMap a = new Long2ByteOpenHashMap(); protected Long2ByteMap getChunks() { return a; } // Purpur - OBFHELPER protected final int b; protected b(int i) { @@ -465,6 +479,7 @@ public abstract class ChunkMapDistance { return objectset != null && !objectset.isEmpty(); } + public void update() { a(); } // Purpur - OBFHELPER public void a() { this.b(Integer.MAX_VALUE); } diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 76c7f4a50..f01abf574 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -3,7 +3,7 @@ package net.minecraft.server; import com.google.common.annotations.VisibleForTesting; import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.util.Either; -import it.unimi.dsi.fastutil.objects.Object2IntMap; + import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -15,13 +15,10 @@ import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.Supplier; import javax.annotation.Nullable; -import com.destroystokyo.paper.exception.ServerInternalException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; public class ChunkProviderServer extends IChunkProvider { - private static final int b = (int) Math.pow(17.0D, 2.0D); + private static final int b = (int) Math.pow(17.0D, 2.0D); private static int chunksEligibleForSpawning() { return b; } // Purpur - OBFHELPER private static final List c = ChunkStatus.a(); static final List getPossibleChunkStatuses() { return ChunkProviderServer.c; } // Paper - OBFHELPER private final ChunkMapDistance chunkMapDistance; public final ChunkGenerator chunkGenerator; @@ -610,147 +607,152 @@ public class ChunkProviderServer extends IChunkProvider { } private void tickChunks() { - long i = this.world.getTime(); - long j = i - this.lastTickTime; - - this.lastTickTime = i; - WorldData worlddata = this.world.getWorldData(); - boolean flag = worlddata.getType() == WorldType.DEBUG_ALL_BLOCK_STATES; - boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !world.getPlayers().isEmpty(); // CraftBukkit - - if (!flag) { - this.world.getMethodProfiler().enter("pollingChunks"); - int k = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED); - BlockPosition blockposition = this.world.getSpawn(); - boolean flag2 = world.ticksPerAnimalSpawns != 0L && worlddata.getTime() % world.ticksPerAnimalSpawns == 0L; // CraftBukkit // PAIL: TODO monster ticks - - this.world.getMethodProfiler().enter("naturalSpawnCount"); - this.world.timings.countNaturalMobs.startTiming(); // Paper - timings - int l = this.chunkMapDistance.b(); - EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); - // Paper start - per player mob spawning + // Purpur start + long tickTime = world.getTime(); + long tickTimeDiff = tickTime - lastTickTime; + lastTickTime = tickTime; + + if (world.getWorldData().getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { + world.getMethodProfiler().enter("pollingChunks"); + + int randomTickSpeed = world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED); + BlockPosition worldSpawn = world.getSpawn(); + + boolean doMobSpawning = world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !world.getPlayers().isEmpty(); + boolean canSpawn = (world.ticksPerAnimalSpawns != 0L && tickTime % world.ticksPerAnimalSpawns == 0L) + || (world.ticksPerMonsterSpawns != 0L && tickTime % world.ticksPerMonsterSpawns == 0L); + + world.getMethodProfiler().enter("naturalSpawnCount"); + world.timings.countNaturalMobs.startTiming(); + + chunkMapDistance.updatePlayerDistanceChunkMap(); + int chunksCount = chunkMapDistance.getPlayerDistanceChunkMapSize(); + int[] worldMobCount; - if (this.playerChunkMap.playerMobDistanceMap != null) { + if (playerChunkMap.playerMobDistanceMap != null) { // update distance map - this.world.timings.playerMobDistanceMapUpdate.startTiming(); - this.playerChunkMap.playerMobDistanceMap.update(this.world.players, this.playerChunkMap.viewDistance); - this.world.timings.playerMobDistanceMapUpdate.stopTiming(); + world.timings.playerMobDistanceMapUpdate.startTiming(); + playerChunkMap.playerMobDistanceMap.update(world.players, playerChunkMap.viewDistance); + world.timings.playerMobDistanceMapUpdate.stopTiming(); // re-set mob counts - for (EntityPlayer player : this.world.players) { + for (EntityPlayer player : world.players) { Arrays.fill(player.mobCounts, 0); } - worldMobCount = this.world.countMobs(true); + worldMobCount = world.countMobs(true); } else { - worldMobCount = this.world.countMobs(false); + worldMobCount = world.countMobs(false); } - // Paper end - this.world.timings.countNaturalMobs.stopTiming(); // Paper - timings - this.world.getMethodProfiler().exit(); - //Paper start - call player naturally spawn event - int chunkRange = world.spigotConfig.mobSpawnRange; - chunkRange = (chunkRange > world.spigotConfig.viewDistance) ? (byte) world.spigotConfig.viewDistance : chunkRange; - chunkRange = Math.min(chunkRange, 8); - for (EntityPlayer entityPlayer : this.world.players) { - entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange); - entityPlayer.playerNaturallySpawnedEvent.callEvent(); - }; - // Paper end - this.playerChunkMap.f().forEach((playerchunk) -> { - Optional optional = ((Either) playerchunk.b().getNow(PlayerChunk.UNLOADED_CHUNK)).left(); + world.timings.countNaturalMobs.stopTiming(); + world.getMethodProfiler().exit(); // naturalSpawnCount - if (optional.isPresent()) { - Chunk chunk = (Chunk) optional.get(); - - this.world.getMethodProfiler().enter("broadcast"); - this.world.timings.broadcastChunkUpdates.startTiming(); // Paper - timings - playerchunk.a(chunk); - this.world.timings.broadcastChunkUpdates.stopTiming(); // Paper - timings - this.world.getMethodProfiler().exit(); - ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); - - this.world.timings.chunkInhibitedRangeCheck.startTiming(); - if (!this.playerChunkMap.isOutsideOfRange(chunkcoordintpair)) { - // Paper end - chunk.setInhabitedTime(chunk.getInhabitedTime() + j); - if (flag1 && (this.allowMonsters || this.allowAnimals) && this.world.getWorldBorder().isInBounds(chunk.getPos()) && !this.playerChunkMap.isOutsideOfRange(chunkcoordintpair, true)) { // Spigot - this.world.getMethodProfiler().enter("spawner"); - this.world.timings.mobSpawn.startTiming(); // Spigot - EnumCreatureType[] aenumcreaturetype1 = aenumcreaturetype; - int i1 = aenumcreaturetype.length; - - for (int j1 = 0; j1 < i1; ++j1) { - EnumCreatureType enumcreaturetype = aenumcreaturetype1[j1]; - - // CraftBukkit start - Use per-world spawn limits - int limit = enumcreaturetype.b(); - switch (enumcreaturetype) { - case MONSTER: - limit = world.getWorld().getMonsterSpawnLimit(); - break; - case CREATURE: - limit = world.getWorld().getAnimalSpawnLimit(); - break; - case WATER_CREATURE: - limit = world.getWorld().getWaterAnimalSpawnLimit(); - break; - case AMBIENT: - limit = world.getWorld().getAmbientSpawnLimit(); - break; - } - - if (limit == 0) { - continue; - } - // CraftBukkit end - - if (enumcreaturetype != EnumCreatureType.MISC && (!enumcreaturetype.c() || this.allowAnimals) && (enumcreaturetype.c() || this.allowMonsters) && (!enumcreaturetype.d() || flag2)) { - int k1 = limit * l / ChunkProviderServer.b; // CraftBukkit - use per-world limits - - // Paper start - only allow spawns upto the limit per chunk and update count afterwards - int currEntityCount = worldMobCount[enumcreaturetype.ordinal()]; - int difference = k1 - currEntityCount; - - if (this.world.paperConfig.perPlayerMobSpawns) { - int minDiff = Integer.MAX_VALUE; - for (EntityPlayer entityplayer : this.playerChunkMap.playerMobDistanceMap.getPlayersInRange(chunk.getPos())) { - minDiff = Math.min(limit - this.playerChunkMap.getMobCountNear(entityplayer, enumcreaturetype), minDiff); - } - difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; - } - - if (difference > 0) { - int spawnCount = SpawnerCreature.spawnMobs(enumcreaturetype, this.world, chunk, blockposition, difference, - this.world.paperConfig.perPlayerMobSpawns ? this.playerChunkMap::updatePlayerMobTypeMap : null); - worldMobCount[enumcreaturetype.ordinal()] += spawnCount; - // Paper end - } - } + byte chunkRange = (byte) Math.min(Math.min(world.spigotConfig.mobSpawnRange, world.spigotConfig.viewDistance), 8); + for (EntityPlayer player : world.players) { + player.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), chunkRange); + player.playerNaturallySpawnedEvent.callEvent(); + } + + EnumCreatureType[] creatureTypes = EnumCreatureType.values(); + EnumCreatureType.MONSTER.setSpigotCap(world.getWorld().getMonsterSpawnLimit()); + EnumCreatureType.CREATURE.setSpigotCap(world.getWorld().getAnimalSpawnLimit()); + EnumCreatureType.WATER_CREATURE.setSpigotCap(world.getWorld().getWaterAnimalSpawnLimit()); + EnumCreatureType.AMBIENT.setSpigotCap(world.getWorld().getAmbientSpawnLimit()); + + for (PlayerChunk playerchunk : playerChunkMap.getChunks()) { + Chunk chunk = playerchunk.getEntityTickingFuture().getNow(PlayerChunk.UNLOADED_CHUNK).left().orElse(null); + if (chunk == null) { + continue; + } + + world.getMethodProfiler().enter("broadcast"); + world.timings.broadcastChunkUpdates.startTiming(); + playerchunk.broadcast(chunk); + world.timings.broadcastChunkUpdates.stopTiming(); + world.getMethodProfiler().exit(); // broadcast + + ChunkCoordIntPair playerChunkPos = playerchunk.getPos(); + ChunkCoordIntPair chunkPos = chunk.getPos(); + + world.timings.bigRangeCheck.startTiming(); + boolean outsideBigRange = playerChunkMap.isOutsideOfRange(playerChunkPos); + world.timings.bigRangeCheck.stopTiming(); + if (outsideBigRange) { + continue; + } + + chunk.setInhabitedTime(chunk.getInhabitedTime() + tickTimeDiff); + + world.timings.smallRangeCheck.startTiming(); + boolean outsideSmallRange = playerChunkMap.isOutsideOfRange(playerChunkPos, true); + world.timings.smallRangeCheck.stopTiming(); + + if (!outsideSmallRange && doMobSpawning && (allowMonsters || allowAnimals) && world.getWorldBorder().isInBounds(chunkPos)) { + world.getMethodProfiler().enter("spawner"); + world.timings.mobSpawn.startTiming(); + for (EnumCreatureType creatureType : creatureTypes) { + if (creatureType == EnumCreatureType.MISC) { + continue; // do not spawn misc entities + } + + if (creatureType.isFriendly() && !allowAnimals) { + continue; // not allowed to spawn animals + } + + if (!creatureType.isFriendly() && !allowMonsters) { + continue; // not allowed to spawn monsters + } + + if (creatureType.isPersistent() && !canSpawn) { + continue; // skip this entity this tick + } + + int limit = creatureType.getSpigotCap(); + if (limit <= 0) { + continue; // entity is disabled + } + + int worldLimit = limit * chunksCount / chunksEligibleForSpawning(); + + int currEntityCount = worldMobCount[creatureType.ordinal()]; + int openSlots = worldLimit - currEntityCount; + + if (world.paperConfig.perPlayerMobSpawns) { + int minDiff = Integer.MAX_VALUE; + for (EntityPlayer player : playerChunkMap.playerMobDistanceMap.getPlayersInRange(chunkPos)) { + minDiff = Math.min(limit - playerChunkMap.getMobCountNear(player, creatureType), minDiff); } + openSlots = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; + } - this.world.timings.mobSpawn.stopTiming(); // Spigot - this.world.getMethodProfiler().exit(); + if (openSlots <= 0) { + continue; // mob cap reached } - this.world.timings.chunkTicks.startTiming(); // Spigot // Paper - this.world.a(chunk, k); - this.world.timings.chunkTicks.stopTiming(); // Spigot // Paper + worldMobCount[creatureType.ordinal()] += SpawnerCreature.spawnMobs(creatureType, world, chunk, worldSpawn, openSlots, world.paperConfig.perPlayerMobSpawns ? playerChunkMap::updatePlayerMobTypeMap : null); } - this.world.timings.chunkInhibitedRangeCheck.stopTiming(); // Paper + + world.timings.mobSpawn.stopTiming(); + world.getMethodProfiler().exit(); // spawner } - }); - this.world.getMethodProfiler().enter("customSpawners"); - if (flag1) { - try (co.aikar.timings.Timing ignored = this.world.timings.miscMobSpawning.startTiming()) { // Paper - timings - this.chunkGenerator.doMobSpawning(this.world, this.allowMonsters, this.allowAnimals); + + world.timings.chunkTicks.startTiming(); + world.tickChunk(chunk, randomTickSpeed); + world.timings.chunkTicks.stopTiming(); + } + + world.getMethodProfiler().enter("customSpawners"); + if (doMobSpawning) { + try (co.aikar.timings.Timing ignored = world.timings.miscMobSpawning.startTiming()) { // Paper - timings + chunkGenerator.doMobSpawning(world, allowMonsters, allowAnimals); } // Paper - timings } + world.getMethodProfiler().exit(); // customSpawners - this.world.getMethodProfiler().exit(); - this.world.getMethodProfiler().exit(); + world.getMethodProfiler().exit(); // pollingChunks } - this.playerChunkMap.g(); + playerChunkMap.tick(); + // Purpur end } @Override diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java index 3ed7fa324..d22d70bb4 100644 --- a/src/main/java/net/minecraft/server/EnumCreatureType.java +++ b/src/main/java/net/minecraft/server/EnumCreatureType.java @@ -6,11 +6,13 @@ import java.util.stream.Collectors; public enum EnumCreatureType { - MONSTER("monster", 70, false, false), CREATURE("creature", 10, true, true), AMBIENT("ambient", 15, true, false), WATER_CREATURE("water_creature", 15, true, false), MISC("misc", 15, true, false); + MONSTER("monster", 70, false, false), + CREATURE("creature", 10, true, true), + AMBIENT("ambient", 15, true, false), + WATER_CREATURE("water_creature", 15, true, false), + MISC("misc", 15, true, false); - private static final Map f = (Map) Arrays.stream(values()).collect(Collectors.toMap(EnumCreatureType::a, (enumcreaturetype) -> { - return enumcreaturetype; - })); + private static final Map f = Arrays.stream(values()).collect(Collectors.toMap(EnumCreatureType::a, (enumcreaturetype) -> enumcreaturetype)); // Purpur - decompile error private final int g; private final boolean h; private final boolean i; @@ -23,19 +25,35 @@ public enum EnumCreatureType { this.i = flag1; } + public String getName() { return a(); } // Purpur - OBFHELPER public String a() { return this.j; } + public int getSpawnCap() { return b(); } // Purpur - OBFHELPER public int b() { return this.g; } + public boolean isFriendly() { return c(); } // Purpur - OBFHELPER public boolean c() { return this.h; } + public boolean isPersistent() { return d(); } // Purpur - OBFHELPER public boolean d() { return this.i; } + + // Purpur start + private int spigotCap = -1; + + void setSpigotCap(int cap) { + this.spigotCap = cap; + } + + int getSpigotCap() { + return this.spigotCap; + } + // Purpur end } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java index b82ea26eb..1fc8f4b08 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -155,6 +155,7 @@ public class PlayerChunk { return this.tickingFuture; } + public CompletableFuture> getEntityTickingFuture() { return b(); } // Purpur - OBFHELPER public CompletableFuture> b() { return this.entityTickingFuture; } @@ -227,6 +228,7 @@ public class PlayerChunk { } } + public void broadcast(Chunk chunk) { a(chunk); } // Purpur - OBFHELPER public void a(Chunk chunk) { if (this.dirtyCount != 0 || this.u != 0 || this.t != 0) { World world = chunk.getWorld(); @@ -342,6 +344,7 @@ public class PlayerChunk { }); } + public ChunkCoordIntPair getPos() { return i(); } // Purpur - OBFHELPER public ChunkCoordIntPair i() { return this.location; } diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java index 57bea926a..ffcdc0b4a 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -1069,6 +1069,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { return this.chunkDistanceManager; } + protected Iterable getChunks() { return f(); } // Purpur - OBFHELPER protected Iterable f() { return Iterables.unmodifiableIterable(this.visibleChunks.values()); } @@ -1266,26 +1267,21 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { } boolean isOutsideOfRange(ChunkCoordIntPair chunkcoordintpair, boolean reducedRange) { - int chunkRange = world.spigotConfig.mobSpawnRange; - chunkRange = (chunkRange > world.spigotConfig.viewDistance) ? (byte) world.spigotConfig.viewDistance : chunkRange; - chunkRange = (chunkRange > 8) ? 8 : chunkRange; - - final int finalChunkRange = chunkRange; // Paper for lambda below - //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event + // Purpur - removed a bunch of crap not used anymore // Spigot end long i = chunkcoordintpair.pair(); - return !this.chunkDistanceManager.d(i) ? true : this.playerMap.a(i).noneMatch((entityplayer) -> { + return !this.chunkDistanceManager.hasPlayersNearby(i) || this.playerMap.a(i).noneMatch((entityplayer) -> { // Purpur // Paper start - - com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; + // com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event // Purpur - moved down double blockRange = 16384.0D; if (reducedRange) { - event = entityplayer.playerNaturallySpawnedEvent; + com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = entityplayer.playerNaturallySpawnedEvent; // Purpur if (event == null || event.isCancelled()) return false; - blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4)); + blockRange = (event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4); // Purpur - removed pointless cast } - return (!entityplayer.isSpectator() && a(chunkcoordintpair, (Entity) entityplayer) < blockRange); // Spigot + return !entityplayer.isSpectator() && a(chunkcoordintpair, entityplayer) < blockRange; // Spigot // Purpur - remove pointless cast // Paper end }); } @@ -1496,6 +1492,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { entity.tracker = null; // Paper - We're no longer tracked } + protected void tick() { g(); } // Purpur - OBFHELPER protected void g() { List list = Lists.newArrayList(); List list1 = this.world.getPlayers(); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java index 456c835c8..ccb6372ab 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -537,6 +537,7 @@ public class WorldServer extends World { private final com.destroystokyo.paper.util.math.ThreadUnsafeRandom randomTickRandom = new com.destroystokyo.paper.util.math.ThreadUnsafeRandom(); // Paper end + public void tickChunk(Chunk chunk, int randomTickSpeed) { a(chunk, randomTickSpeed); } // Purpur - OBFHELPER public void a(Chunk chunk, int i) { ChunkCoordIntPair chunkcoordintpair = chunk.getPos(); boolean flag = this.isRaining(); -- 2.24.0