mirror of https://github.com/YatopiaMC/Yatopia.git
464 lines
24 KiB
Diff
464 lines
24 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Paul Sauve <paul@technove.co>
|
|
Date: Fri, 12 Feb 2021 16:29:41 -0600
|
|
Subject: [PATCH] Multithreaded entity tracking
|
|
|
|
Adds a configuration option to run the tracker on multiple threads.
|
|
This will generally be faster than the single threaded approach,
|
|
unless you don't have a lot of entities/players.
|
|
|
|
Airplane
|
|
Copyright (C) 2020 Technove LLC
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
diff --git a/src/main/java/com/tuinity/tuinity/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/com/tuinity/tuinity/util/maplist/IteratorSafeOrderedReferenceSet.java
|
|
index be408aebbccbda46e8aa82ef337574137cfa0096..739839314fd8a88b5fca8b9678e1df07a166c35d 100644
|
|
--- a/src/main/java/com/tuinity/tuinity/util/maplist/IteratorSafeOrderedReferenceSet.java
|
|
+++ b/src/main/java/com/tuinity/tuinity/util/maplist/IteratorSafeOrderedReferenceSet.java
|
|
@@ -16,11 +16,11 @@ public final class IteratorSafeOrderedReferenceSet<E> {
|
|
|
|
/* list impl */
|
|
protected E[] listElements;
|
|
- protected int listSize;
|
|
+ protected int listSize; public int getListSize() { return this.listSize; } // Airplane - getter
|
|
|
|
protected final double maxFragFactor;
|
|
|
|
- protected int iteratorCount;
|
|
+ public int iteratorCount; // Airplane - public for debug
|
|
|
|
private final boolean threadRestricted;
|
|
|
|
diff --git a/src/main/java/gg/airplane/AirplaneConfig.java b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
index 7ec84ef1d1cbb1fabf4c590a2f2c1da3cc181010..b9118cc08ac38e0813d0677700d3d7dcf9b74159 100644
|
|
--- a/src/main/java/gg/airplane/AirplaneConfig.java
|
|
+++ b/src/main/java/gg/airplane/AirplaneConfig.java
|
|
@@ -102,4 +102,17 @@ public class AirplaneConfig {
|
|
}
|
|
|
|
|
|
+ public static boolean multithreadedEntityTracker = false;
|
|
+ public static boolean entityTrackerAsyncPackets = false;
|
|
+
|
|
+ private static void entityTracker() {
|
|
+ config.setComment("tracker", "Options to improve the performance of the entity tracker");
|
|
+
|
|
+ multithreadedEntityTracker = config.getBoolean("tracker.multithreaded", multithreadedEntityTracker,
|
|
+ "This enables the multithreading of the tracker.");
|
|
+ entityTrackerAsyncPackets = config.getBoolean("tracker.unsafe-async-packets", entityTrackerAsyncPackets,
|
|
+ "This option can break plugins that assume packets from the",
|
|
+ "entity tracker will be sent sync.");
|
|
+ }
|
|
+
|
|
}
|
|
diff --git a/src/main/java/gg/airplane/commands/AirplaneCommands.java b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
index 66b20250a26d005427601b1cdee43bdd9eba70cc..f84e26b2d8ab9a9b2d9e92e18002483127121135 100644
|
|
--- a/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
+++ b/src/main/java/gg/airplane/commands/AirplaneCommands.java
|
|
@@ -2,11 +2,13 @@ package gg.airplane.commands;
|
|
|
|
import gg.airplane.AirplaneCommand;
|
|
import gg.airplane.flare.FlareCommand;
|
|
+import gg.airplane.structs.TrackQueue;
|
|
import net.minecraft.server.MinecraftServer;
|
|
|
|
public class AirplaneCommands {
|
|
public static void init() {
|
|
MinecraftServer.getServer().server.getCommandMap().register("airplane", "Airplane", new AirplaneCommand());
|
|
MinecraftServer.getServer().server.getCommandMap().register("flare", "Airplane", new FlareCommand());
|
|
+ TrackQueue.TrackQueueDebugCommand.register();
|
|
}
|
|
}
|
|
diff --git a/src/main/java/gg/airplane/structs/TrackQueue.java b/src/main/java/gg/airplane/structs/TrackQueue.java
|
|
new file mode 100644
|
|
index 0000000000000000000000000000000000000000..4419fbe94041f4b8a0ea848880798289d063b8e2
|
|
--- /dev/null
|
|
+++ b/src/main/java/gg/airplane/structs/TrackQueue.java
|
|
@@ -0,0 +1,109 @@
|
|
+package gg.airplane.structs;
|
|
+
|
|
+import com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet;
|
|
+import net.minecraft.server.level.WorldServer;
|
|
+import net.minecraft.world.level.chunk.Chunk;
|
|
+import net.minecraft.server.MinecraftServer;
|
|
+import org.apache.logging.log4j.Level;
|
|
+import org.bukkit.command.Command;
|
|
+import org.bukkit.command.CommandSender;
|
|
+
|
|
+import java.util.concurrent.ConcurrentLinkedQueue;
|
|
+import java.util.concurrent.ForkJoinPool;
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
+
|
|
+/**
|
|
+ * Helper class to handle processing a track queue.
|
|
+ */
|
|
+public class TrackQueue {
|
|
+
|
|
+ public static class TrackQueueDebugCommand extends Command {
|
|
+ protected TrackQueueDebugCommand() {
|
|
+ super("trackqueuedebug");
|
|
+ }
|
|
+
|
|
+ public static void register() {
|
|
+ MinecraftServer.getServer().server.getCommandMap().register("trackqueuedebug", "Airplane", new TrackQueueDebugCommand());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
|
+ if (!sender.isOp()) {
|
|
+ return false;
|
|
+ }
|
|
+ for (WorldServer world : MinecraftServer.getServer().getWorlds()) {
|
|
+ IteratorSafeOrderedReferenceSet<Chunk> chunks = world.getChunkProvider().entityTickingChunks;
|
|
+ sender.sendMessage(world.getWorld().getName() + ": " + chunks.size() + " / " + chunks.getListSize() + " (iterators: " + chunks.iteratorCount + ")");
|
|
+ }
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private final IteratorSafeOrderedReferenceSet<Chunk> chunks;
|
|
+ private final ForkJoinPool pool = new ForkJoinPool(Math.max(4, Runtime.getRuntime().availableProcessors() >> 2));
|
|
+ private final AtomicInteger taskIndex = new AtomicInteger();
|
|
+
|
|
+ private final ConcurrentLinkedQueue<Runnable> mainThreadTasks;
|
|
+
|
|
+ public TrackQueue(IteratorSafeOrderedReferenceSet<Chunk> chunks, ConcurrentLinkedQueue<Runnable> mainThreadTasks) {
|
|
+ this.chunks = chunks;
|
|
+ this.mainThreadTasks = mainThreadTasks;
|
|
+ }
|
|
+
|
|
+ public void start() {
|
|
+ int iterator = this.chunks.createRawIterator();
|
|
+ if (iterator == -1) {
|
|
+ return;
|
|
+ }
|
|
+ try {
|
|
+ this.taskIndex.set(iterator);
|
|
+
|
|
+ for (int i = 0; i < this.pool.getParallelism(); i++) {
|
|
+ this.pool.execute(this::run);
|
|
+ }
|
|
+
|
|
+ while (this.taskIndex.get() < this.chunks.getListSize()) {
|
|
+ this.runMainThreadTasks();
|
|
+ this.handleTask(); // assist
|
|
+ }
|
|
+ this.runMainThreadTasks(); // finish tasks
|
|
+ } finally {
|
|
+ this.chunks.finishRawIterator();
|
|
+ }
|
|
+ }
|
|
+
|
|
+ private void runMainThreadTasks() {
|
|
+ Runnable task;
|
|
+ while ((task = this.mainThreadTasks.poll()) != null) {
|
|
+ try {
|
|
+ task.run();
|
|
+ } catch (Throwable t) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Tasks failed while ticking track queue", t);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ }
|
|
+
|
|
+ private void run() {
|
|
+ while (handleTask());
|
|
+ }
|
|
+
|
|
+ private boolean handleTask() {
|
|
+ int index;
|
|
+ while ((index = this.taskIndex.getAndIncrement()) < this.chunks.getListSize()) {
|
|
+ Chunk chunk = this.chunks.rawGet(index);
|
|
+ if (chunk != null) {
|
|
+ try {
|
|
+ chunk.entityTracker.run();
|
|
+ } catch (Throwable t) {
|
|
+ MinecraftServer.LOGGER.log(Level.WARN, "Ticking tracker failed", t);
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+}
|
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
|
|
index 207a9c3928aad7c6e89a120b54d87e003ebd232c..424cd048f905cd0ed3f7a4545a26ffde8da1f91f 100644
|
|
--- a/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkProviderServer.java
|
|
@@ -388,7 +388,7 @@ public class ChunkProviderServer extends IChunkProvider {
|
|
}
|
|
|
|
final com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<Chunk> tickingChunks = new com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
|
|
- final com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<Chunk> entityTickingChunks = new com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true);
|
|
+ public final com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<Chunk> entityTickingChunks = new com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet<>(4096, 0.75f, 4096, 0.15, true); // Airplane - public for debug
|
|
// Tuinity end
|
|
|
|
public ChunkProviderServer(WorldServer worldserver, Convertable.ConversionSession convertable_conversionsession, DataFixer datafixer, DefinedStructureManager definedstructuremanager, Executor executor, ChunkGenerator chunkgenerator, int i, boolean flag, WorldLoadListener worldloadlistener, Supplier<WorldPersistentData> supplier) {
|
|
diff --git a/src/main/java/net/minecraft/server/level/EntityPlayer.java b/src/main/java/net/minecraft/server/level/EntityPlayer.java
|
|
index 62b95dcba8606330fbb3239e74c5eaf8baa3c51d..fd6dbbf619b828c4ef3d00f8dc30d3893007db7b 100644
|
|
--- a/src/main/java/net/minecraft/server/level/EntityPlayer.java
|
|
+++ b/src/main/java/net/minecraft/server/level/EntityPlayer.java
|
|
@@ -182,7 +182,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|
public NetworkManager networkManager; // Paper
|
|
public final MinecraftServer server;
|
|
public final PlayerInteractManager playerInteractManager;
|
|
- public final Deque<Integer> removeQueue = new ArrayDeque<>(); // Paper
|
|
+ public final Deque<Integer> removeQueue = new java.util.concurrent.ConcurrentLinkedDeque<>(); // Paper // Airplane concurrent deque
|
|
private final AdvancementDataPlayer advancementDataPlayer;
|
|
private final ServerStatisticManager serverStatisticManager;
|
|
private float lastHealthScored = Float.MIN_VALUE;
|
|
diff --git a/src/main/java/net/minecraft/server/level/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/level/EntityTrackerEntry.java
|
|
index 67ca28463f5add7c18f7f16b918c3f36f8feeeda..37e64e24ca3a90370cdf12e5ff9cd1fceede796b 100644
|
|
--- a/src/main/java/net/minecraft/server/level/EntityTrackerEntry.java
|
|
+++ b/src/main/java/net/minecraft/server/level/EntityTrackerEntry.java
|
|
@@ -75,6 +75,10 @@ public class EntityTrackerEntry {
|
|
* Requested in https://github.com/PaperMC/Paper/issues/1537 to allow intercepting packets
|
|
*/
|
|
public void sendPlayerPacket(EntityPlayer player, Packet packet) {
|
|
+ if (!gg.airplane.AirplaneConfig.entityTrackerAsyncPackets && !org.bukkit.Bukkit.isPrimaryThread()) {
|
|
+ this.b.chunkProvider.playerChunkMap.trackerEnsureMain(() -> sendPlayerPacket(player, packet));
|
|
+ return;
|
|
+ }
|
|
player.playerConnection.sendPacket(packet);
|
|
}
|
|
|
|
@@ -103,7 +107,7 @@ public class EntityTrackerEntry {
|
|
|
|
public final void tick() { this.a(); } // Paper - OBFHELPER
|
|
public void a() {
|
|
- com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Tracker update"); // Tuinity
|
|
+ //com.tuinity.tuinity.util.TickThread.softEnsureTickThread("Tracker update"); // Tuinity // Airplane - allow multithreaded
|
|
List<Entity> list = this.tracker.passengers; // Paper - do not copy list
|
|
|
|
if (!list.equals(this.p)) {
|
|
@@ -116,6 +120,8 @@ public class EntityTrackerEntry {
|
|
ItemStack itemstack = entityitemframe.getItem();
|
|
|
|
if (this.tickCounter % 10 == 0 && itemstack.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks
|
|
+ // Airplane start - process maps on main
|
|
+ this.b.chunkProvider.playerChunkMap.trackerEnsureMain(() -> {
|
|
WorldMap worldmap = ItemWorldMap.getSavedMap(itemstack, this.b);
|
|
Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
|
|
|
|
@@ -129,6 +135,8 @@ public class EntityTrackerEntry {
|
|
entityplayer.playerConnection.sendPacket(packet);
|
|
}
|
|
}
|
|
+ });
|
|
+ // Airplane end
|
|
}
|
|
|
|
this.c();
|
|
@@ -263,18 +271,25 @@ public class EntityTrackerEntry {
|
|
// CraftBukkit start - Create PlayerVelocity event
|
|
boolean cancelled = false;
|
|
|
|
- if (this.tracker instanceof EntityPlayer) {
|
|
+ if (this.tracker instanceof EntityPlayer && PlayerVelocityEvent.getHandlerList().getRegisteredListeners().length > 0) { // Airplane - ensure there's listeners
|
|
+ // Airplane start - run on main thread
|
|
+ this.b.chunkProvider.playerChunkMap.trackerEnsureMain(() -> {
|
|
Player player = (Player) this.tracker.getBukkitEntity();
|
|
org.bukkit.util.Vector velocity = player.getVelocity();
|
|
|
|
PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone());
|
|
this.tracker.world.getServer().getPluginManager().callEvent(event);
|
|
|
|
- if (event.isCancelled()) {
|
|
- cancelled = true;
|
|
- } else if (!velocity.equals(event.getVelocity())) {
|
|
+ if (!event.isCancelled() && !velocity.equals(event.getVelocity())) {
|
|
player.setVelocity(event.getVelocity());
|
|
}
|
|
+ if (!event.isCancelled()) {
|
|
+ this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker)); // duplicate from !cancelled below
|
|
+ }
|
|
+
|
|
+ });
|
|
+ cancelled = true; // don't broadcast until the event has finished
|
|
+ // Airplane end
|
|
}
|
|
|
|
if (!cancelled) {
|
|
@@ -358,7 +373,9 @@ public class EntityTrackerEntry {
|
|
if (!list.isEmpty()) {
|
|
consumer.accept(new PacketPlayOutEntityEquipment(this.tracker.getId(), list));
|
|
}
|
|
+ this.b.chunkProvider.playerChunkMap.trackerEnsureMain(() -> { // Airplane
|
|
((EntityLiving) this.tracker).updateEquipment(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending
|
|
+ }); // Airplane
|
|
}
|
|
|
|
// CraftBukkit start - Fix for nonsensical head yaw
|
|
@@ -436,6 +453,10 @@ public class EntityTrackerEntry {
|
|
// Paper end
|
|
|
|
private void broadcastIncludingSelf(Packet<?> packet) {
|
|
+ if (!gg.airplane.AirplaneConfig.entityTrackerAsyncPackets && !org.bukkit.Bukkit.isPrimaryThread()) {
|
|
+ this.b.chunkProvider.playerChunkMap.trackerEnsureMain(() -> broadcastIncludingSelf(packet));
|
|
+ return;
|
|
+ }
|
|
this.f.accept(packet);
|
|
if (this.tracker instanceof EntityPlayer) {
|
|
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
|
|
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
|
|
index b28995ecfd7f45e6b6197be96c418aa0d05d3383..bb5ebacf99238223b84f3663af3ab9c6c60332eb 100644
|
|
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
|
|
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
|
|
@@ -763,6 +763,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
return this.updatingChunks.getVisibleAsync(i);
|
|
// Tuinity end - Don't copy
|
|
}
|
|
+ // Airplane start - since neither map can be updated during tracker tick, it's safe to allow direct retrieval here
|
|
+ private PlayerChunk trackerGetVisibleChunk(long i) {
|
|
+ return this.hasPendingVisibleUpdate ? this.pendingVisibleChunks.get(i) : ((ProtectedVisibleChunksMap) this.visibleChunks).safeGet(i);
|
|
+ }
|
|
+ // Airplane end
|
|
|
|
protected final IntSupplier getPrioritySupplier(long i) { return c(i); } // Paper - OBFHELPER
|
|
protected IntSupplier c(long i) {
|
|
@@ -2158,10 +2163,30 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
|
|
entity.tracker = null; // Paper - We're no longer tracked
|
|
}
|
|
|
|
+ // Airplane start - tools to ensure main thread
|
|
+ private final java.util.concurrent.ConcurrentLinkedQueue<Runnable> trackerMainQueue = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
|
+ private gg.airplane.structs.TrackQueue trackQueue;
|
|
+
|
|
+ void trackerEnsureMain(Runnable runnable) {
|
|
+ if (this.world.serverThread == Thread.currentThread()) {
|
|
+ runnable.run();
|
|
+ } else {
|
|
+ this.trackerMainQueue.add(runnable);
|
|
+ }
|
|
+ }
|
|
+ // Airplane end
|
|
+
|
|
// Paper start - optimised tracker
|
|
private final void processTrackQueue() {
|
|
this.world.timings.tracker1.startTiming();
|
|
try {
|
|
+ // Airplane start - multithreaded tracker
|
|
+ if (this.trackQueue == null) this.trackQueue = new gg.airplane.structs.TrackQueue(this.world.getChunkProvider().entityTickingChunks, trackerMainQueue);
|
|
+ if (gg.airplane.AirplaneConfig.multithreadedEntityTracker) {
|
|
+ this.trackQueue.start();
|
|
+ return;
|
|
+ }
|
|
+ // Airplane end
|
|
com.tuinity.tuinity.util.maplist.IteratorSafeOrderedReferenceSet.Iterator<Chunk> iterator = this.world.getChunkProvider().entityTickingChunks.iterator();
|
|
try {
|
|
while (iterator.hasNext()) {
|
|
@@ -2427,7 +2452,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
public class EntityTracker {
|
|
|
|
final EntityTrackerEntry trackerEntry; // Paper - private -> package private
|
|
- private final Entity tracker;
|
|
+ public final Entity tracker; // Airplane - public for chunk
|
|
private final int trackingDistance;
|
|
private SectionPosition e;
|
|
// Paper start
|
|
@@ -2446,7 +2471,9 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
// Paper start - use distance map to optimise tracker
|
|
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> lastTrackerCandidates;
|
|
|
|
- final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newTrackerCandidates) {
|
|
+ public synchronized final void tickEntry() { this.trackerEntry.tick(); } // Airplane - move entry tick into sync block
|
|
+
|
|
+ public synchronized final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> newTrackerCandidates) { // Airplane
|
|
com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<EntityPlayer> oldTrackerCandidates = this.lastTrackerCandidates;
|
|
this.lastTrackerCandidates = newTrackerCandidates;
|
|
|
|
@@ -2487,7 +2514,13 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
return this.tracker.getId();
|
|
}
|
|
|
|
- public void broadcast(Packet<?> packet) {
|
|
+ public synchronized void broadcast(Packet<?> packet) { // Airplane - synchronized for tracked player
|
|
+ // Airplane start
|
|
+ if (!gg.airplane.AirplaneConfig.entityTrackerAsyncPackets && !org.bukkit.Bukkit.isPrimaryThread()) {
|
|
+ trackerEnsureMain(() -> broadcast(packet));
|
|
+ return;
|
|
+ }
|
|
+ // Airplane end
|
|
Iterator iterator = this.trackedPlayers.iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -2499,6 +2532,12 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
}
|
|
|
|
public void broadcastIncludingSelf(Packet<?> packet) {
|
|
+ // Airplane start
|
|
+ if (!gg.airplane.AirplaneConfig.entityTrackerAsyncPackets && !org.bukkit.Bukkit.isPrimaryThread()) {
|
|
+ trackerEnsureMain(() -> broadcastIncludingSelf(packet));
|
|
+ return;
|
|
+ }
|
|
+ // Airplane end
|
|
this.broadcast(packet);
|
|
if (this.tracker instanceof EntityPlayer) {
|
|
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
|
|
@@ -2525,8 +2564,8 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
|
|
}
|
|
|
|
- public void updatePlayer(EntityPlayer entityplayer) {
|
|
- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
|
+ public synchronized void updatePlayer(EntityPlayer entityplayer) { // Airplane - sync for access to map
|
|
+ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // Airplane - allow sync for tracker
|
|
if (entityplayer != this.tracker) {
|
|
// Paper start - remove allocation of Vec3D here
|
|
//Vec3D vec3d = entityplayer.getPositionVector().d(this.tracker.getPositionVector()); // MC-155077, SPIGOT-5113
|
|
@@ -2542,7 +2581,7 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
|
|
|
|
if (!flag1) {
|
|
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(this.tracker.chunkX, this.tracker.chunkZ);
|
|
- PlayerChunk playerchunk = PlayerChunkMap.this.getVisibleChunk(chunkcoordintpair.pair());
|
|
+ PlayerChunk playerchunk = PlayerChunkMap.this.trackerGetVisibleChunk(chunkcoordintpair.pair()); // Airplane
|
|
|
|
if (playerchunk != null && playerchunk.getSendingChunk() != null && PlayerChunkMap.this.playerChunkManager.isChunkSent(entityplayer, MathHelper.floor(this.tracker.locX()) >> 4, MathHelper.floor(this.tracker.locZ()) >> 4)) { // Paper - no-tick view distance // Tuinity - don't broadcast in chunks the player hasn't received
|
|
flag1 = PlayerChunkMap.b(chunkcoordintpair, entityplayer, false) <= PlayerChunkMap.this.viewDistance;
|
|
diff --git a/src/main/java/net/minecraft/world/level/chunk/Chunk.java b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
|
|
index 8f5809756b4fb358f1207c1d61c5cbe6df3fff00..2e8eb0bb8fb4f7ce6b92fe01a81327da30e614ae 100644
|
|
--- a/src/main/java/net/minecraft/world/level/chunk/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
|
|
@@ -111,6 +111,26 @@ public class Chunk implements IChunkAccess {
|
|
}
|
|
// Airplane end
|
|
|
|
+ // Airplane start - entity tracker runnable
|
|
+ // prevents needing to allocate new lambda in processTrackQueue
|
|
+ public final Runnable entityTracker = new Runnable() {
|
|
+ @Override
|
|
+ public void run() {
|
|
+ Entity[] entities = Chunk.this.entities.getRawData();
|
|
+ for (int i = 0, len = Chunk.this.entities.size(); i < len; ++i) {
|
|
+ Entity entity = entities[i];
|
|
+ if (entity != null) {
|
|
+ PlayerChunkMap.EntityTracker tracker = ((WorldServer) Chunk.this.getWorld()).getChunkProvider().playerChunkMap.trackedEntities.get(entity.getId());
|
|
+ if (tracker != null) {
|
|
+ tracker.updatePlayers(tracker.tracker.getPlayersInTrackRange());
|
|
+ tracker.tickEntry();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ };
|
|
+ // Airplane end
|
|
+
|
|
public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeStorage biomestorage) {
|
|
this(world, chunkcoordintpair, biomestorage, ChunkConverter.a, TickListEmpty.b(), TickListEmpty.b(), 0L, (ChunkSection[]) null, (Consumer) null);
|
|
}
|