This commit is contained in:
Beaness 2022-08-06 23:38:31 +02:00
parent 762263ebe8
commit 40f1bec4cb
5 changed files with 224 additions and 427 deletions

View File

@ -1,111 +0,0 @@
package com.elevatemc.spigot.visuals;
import net.minecraft.server.*;
import java.util.List;
/*
* This is a custom entity tracker made for the cannoning entities tnt and sand.
* The goal behind this is to reduce packets and logic without hiding entities.
* It may not completely replicate the original behavior, but it should make up
* for that with it's advantages.
* Source: IonSpigot
*/
public class CannonTrackerEntry extends EntityTrackerEntry {
private boolean movingX;
private boolean movingY;
private boolean movingZ;
private double updateX;
private double updateY;
private double updateZ;
public CannonTrackerEntry(Entity entity, int i, int j, boolean flag) {
super(entity, i, j, flag);
this.movingX = entity.motX != 0.0;
this.movingY = true;
this.movingZ = entity.motZ != 0.0;
this.updateX = entity.locX;
this.updateY = entity.locY;
this.updateZ = entity.locZ;
}
@Override
public void track(List<EntityHuman> list) {
boolean motionX = this.tracker.motX != 0.0;
boolean motionY = this.tracker.motY != 0.0;
boolean motionZ = this.tracker.motZ != 0.0;
// This tracked entities motion has changed or an explosion has occurred, update it!
if (!this.tracker.ai && motionX == movingX && motionY == movingY && motionZ == movingZ) {
return;
}
// This entity has moved 4 blocks since the last update, search for players
if (this.tracker.e(updateX, updateY, updateZ) > 16.0D) {
this.scanPlayers(list);
this.updateX = this.tracker.locX;
this.updateY = this.tracker.locY;
this.updateZ = this.tracker.locZ;
}
// Update nearby players, only resynchronise when motion is updated
if (motionX || motionY || motionZ) {
this.broadcastUpdate();
}
// Keep what of which axis the entity is moving on
this.tracker.ai = false;
this.movingX = motionX;
this.movingY = motionY;
this.movingZ = motionZ;
}
private void broadcastUpdate() {
DataWatcher datawatcher = this.tracker.getDataWatcher();
if (datawatcher.a()) {
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
}
// Only update location on movement
if (this.tracker.lastX != this.tracker.locX || this.tracker.lastY != this.tracker.locY || this.tracker.lastZ != this.tracker.locZ) {
this.broadcast(new PacketPlayOutEntityTeleport(this.tracker));
}
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker));
}
@Override
public void updatePlayer(EntityPlayer entityplayer) {
// Check configurable distance as a cube then visible distance.
if (this.c(entityplayer) && this.tracker.h(entityplayer) < 4096.0D) {
if (this.trackedPlayers.contains(entityplayer) || (!this.e(entityplayer) && !this.tracker.attachedToPlayer)) {
return;
}
entityplayer.removeQueue.remove(this.tracker.getId());
this.trackedPlayerMap.put(entityplayer, true); // Paper
Packet packet = this.c(); // IonSpigot
if (packet == null) return; // IonSpigot - If it's null don't update the client!
entityplayer.playerConnection.sendPacket(packet);
if (this.tracker.getCustomNameVisible()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true));
}
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ));
if (this.tracker.vehicle != null) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle));
}
} else if (this.trackedPlayers.contains(entityplayer)) {
this.trackedPlayers.remove(entityplayer);
entityplayer.d(this.tracker);
}
}
}

View File

@ -247,75 +247,54 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
if (!this.chunkCoordIntPairQueue.isEmpty()) { if (!this.chunkCoordIntPairQueue.isEmpty()) {
ArrayList<Chunk> chunkList = Lists.newArrayList(); ArrayList arraylist = Lists.newArrayList();
Iterator<ChunkCoordIntPair> chunksToLoad = this.chunkCoordIntPairQueue.iterator(); Iterator iterator1 = this.chunkCoordIntPairQueue.iterator();
ArrayList<TileEntity> tileEntities = Lists.newArrayList(); ArrayList arraylist1 = Lists.newArrayList();
Chunk chunk = null; Chunk chunk;
while (chunksToLoad.hasNext() && chunkList.size() < this.world.spigotConfig.maxBulkChunk) { // Spigot while (iterator1.hasNext() && arraylist.size() < this.world.spigotConfig.maxBulkChunk) { // Spigot
ChunkCoordIntPair chunkcoordintpair = chunksToLoad.next(); ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator1.next();
if (chunkcoordintpair != null) { if (chunkcoordintpair != null) {
if (this.world.isLoaded(chunkcoordintpair.x << 4, 0, chunkcoordintpair.z << 4)) {// [Nacho-0024] Do not create new BlockPosition when loading chunk if (this.world.isLoaded(new BlockPosition(chunkcoordintpair.x << 4, 0, chunkcoordintpair.z << 4))) {
chunk = this.world.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z); chunk = this.world.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z);
if (chunk.isReady()) { if (chunk.isReady()) {
chunkList.add(chunk); arraylist.add(chunk);
tileEntities.addAll(chunk.tileEntities.values()); // CraftBukkit - Get tile entities directly from the chunk instead of the world arraylist1.addAll(chunk.tileEntities.values()); // CraftBukkit - Get tile entities directly from the chunk instead of the world
chunksToLoad.remove(); iterator1.remove();
} }
} }
} else { } else {
chunksToLoad.remove(); iterator1.remove();
} }
} }
if (!chunkList.isEmpty()) { if (!arraylist.isEmpty()) {
if (chunkList.size() == 1) { if (arraylist.size() == 1) {
this.playerConnection.sendPacket(new PacketPlayOutMapChunk(chunkList.get(0), true, '\uffff')); this.playerConnection.sendPacket(new PacketPlayOutMapChunk((Chunk) arraylist.get(0), true, '\uffff'));
} else { } else {
this.playerConnection.sendPacket(new PacketPlayOutMapChunkBulk(chunkList)); this.playerConnection.sendPacket(new PacketPlayOutMapChunkBulk(arraylist));
} }
Iterator<TileEntity> tileEntitiesIterator = tileEntities.iterator(); Iterator iterator2 = arraylist1.iterator();
while (tileEntitiesIterator.hasNext()) { while (iterator2.hasNext()) {
TileEntity tileentity = tileEntitiesIterator.next(); TileEntity tileentity = (TileEntity) iterator2.next();
this.a(tileentity); this.a(tileentity);
} }
// //Nacho - if there are a lot of entities, we end up scanning the WHOLE list of entities multiple times iterator2 = arraylist.iterator();
// // Which isn't the best if we have 100 players doing that
// So instead of updating all entities by chunk, we update all entities at once with a hashset of chunks
// This means we don't have to pass over the list x chunks
// o(chunk * entityList) => o(entitylist)
// Iterator<Chunk> chunkIterator = chunkList.iterator(); while (iterator2.hasNext()) {
// while (chunkIterator.hasNext()) chunk = (Chunk) iterator2.next();
// { this.u().getTracker().a(this, chunk);
// chunk = (Chunk) chunkIterator.next();
// this.u().getTracker().a(this, chunk);
// }
// Nacho - end
LongOpenHashSet chunkPosSet = new LongOpenHashSet(chunkList.size());
for (Chunk newChunk : chunkList)
chunkPosSet.add(this.chunkToLong(newChunk.locX, newChunk.locZ));
Iterator<EntityTrackerEntry> trackerEntryIterator = this.u().getTracker().getEntityTrackerEntries();
while (trackerEntryIterator.hasNext())
{
EntityTrackerEntry entitytrackerentry = trackerEntryIterator.next();
if (entitytrackerentry.tracker != this && chunkPosSet.contains(this.chunkToLong(entitytrackerentry.tracker.ae, entitytrackerentry.tracker.ag)))
{
entitytrackerentry.updatePlayer(this);
}
} }
} }
} }
Entity entity = this.C(); Entity entity = this.C();
if (entity != this) { if (entity != this) {

View File

@ -1,34 +1,21 @@
package net.minecraft.server; package net.minecraft.server;
import com.elevatemc.spigot.config.eSpigotConfig;
import com.elevatemc.spigot.visuals.CannonTrackerEntry;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import com.elevatemc.spigot.util.SynchronizedIntHashMap;
import com.elevatemc.spigot.util.task.Task;
import com.elevatemc.spigot.util.task.impl.ThreadTaskExecutor;
public class EntityTracker { public class EntityTracker {
private static final Logger LOGGER = LogManager.getLogger(); private static final Logger a = LogManager.getLogger();
private static final ThreadTaskExecutor TASK_EXECUTOR = new ThreadTaskExecutor("EntityTracker"); private final WorldServer world;
private Set<EntityTrackerEntry> c = Sets.newConcurrentHashSet();
static { public IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
TASK_EXECUTOR.start();
}
private WorldServer world;
private Set<EntityTrackerEntry> c = Sets.newConcurrentHashSet(); // eSpigot - ConcurrentHashSet
public IntHashMap<EntityTrackerEntry> trackedEntities = new SynchronizedIntHashMap<>(); // eSpigot - Thread safe implementation of IntHashMap (SynchronizedIntHashMap)
private int e; private int e;
public EntityTracker(WorldServer worldserver) { public EntityTracker(WorldServer worldserver) {
@ -39,6 +26,16 @@ public class EntityTracker {
public void track(Entity entity) { public void track(Entity entity) {
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
this.addEntity(entity, 512, 2); this.addEntity(entity, 512, 2);
EntityPlayer entityplayer = (EntityPlayer) entity;
Iterator iterator = this.c.iterator();
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
if (entitytrackerentry.tracker != entityplayer) {
entitytrackerentry.updatePlayerFor(entityplayer);
}
}
} else if (entity instanceof EntityFishingHook) { } else if (entity instanceof EntityFishingHook) {
this.addEntity(entity, 64, 5, true); this.addEntity(entity, 64, 5, true);
} else if (entity instanceof EntityArrow) { } else if (entity instanceof EntityArrow) {
@ -98,204 +95,161 @@ public class EntityTracker {
} }
public void addEntity(Entity entity, int i, final int j, boolean flag) { public void addEntity(Entity entity, int i, final int j, boolean flag) {
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
if (i > this.e) { if (i > this.e) {
i = this.e; i = this.e;
} }
if (this.trackedEntities.b(entity.getId())) { // IntHashMap already contains entity id try {
throw new IllegalStateException("Entity is already tracked!"); if (this.trackedEntities.b(entity.getId())) {
} throw new IllegalStateException("Entity is already tracked!");
final int finalI = i; // CraftBukkit - fix decompile error
EntityTrackerEntry entitytrackerentry = createTracker(entity, i, j, flag); // IonSpigot
// We want to add them to these collections directly to ensure compatibility with plugins like Citizens
this.c.add(entitytrackerentry); // Add entity to entity tracker entries set
this.trackedEntities.a(entity.getId(), entitytrackerentry); // Add entity to trackedEntities collection
TASK_EXECUTOR.submit(executor -> {
try {
entitytrackerentry.scanPlayers(this.world.players); // Update all players in world,
// essentially the only method that would
// be heavy in this method is this method (EntityTrackerEntry#scanPlayers)
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
crashreportsystemdetails.a("Tracking range", finalI + " blocks");
crashreportsystemdetails.a("Update interval", new Callable() {
public String a() throws Exception {
String s = "Once per " + finalI + " ticks"; // CraftBukkit
if (finalI == Integer.MAX_VALUE) { // CraftBukkit
s = "Maximum (" + s + ")";
}
return s;
}
public Object call() throws Exception {
return this.a();
}
});
entity.appendEntityCrashDetails(crashreportsystemdetails);
CrashReportSystemDetails crashreportsystemdetails1 = crashreport.a("Entity That Is Already Tracked");
this.trackedEntities.get(entity.getId()).tracker.appendEntityCrashDetails(crashreportsystemdetails1);
try {
throw new ReportedException(crashreport);
} catch (ReportedException reportedexception) {
EntityTracker.LOGGER.error("\"Silently\" catching entity tracking error.", reportedexception);
}
} }
});
}
// IonSpigot start EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, j, flag);
private EntityTrackerEntry createTracker(Entity entity, int i, int j, boolean flag) {
if (entity.isCannoningEntity && eSpigotConfig.cannonTracker) { this.c.add(entitytrackerentry);
return new CannonTrackerEntry(entity, i, j, flag); this.trackedEntities.a(entity.getId(), entitytrackerentry);
} else { entitytrackerentry.scanPlayers(this.world.players);
return new EntityTrackerEntry(entity, i, j, flag); } catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
crashreportsystemdetails.a("Tracking range", (Object) (i + " blocks"));
final int finalI = i; // CraftBukkit - fix decompile error
crashreportsystemdetails.a("Update interval", new Callable() {
public String a() throws Exception {
String s = "Once per " + finalI + " ticks"; // CraftBukkit
if (finalI == Integer.MAX_VALUE) { // CraftBukkit
s = "Maximum (" + s + ")";
}
return s;
}
public Object call() throws Exception {
return this.a();
}
});
entity.appendEntityCrashDetails(crashreportsystemdetails);
CrashReportSystemDetails crashreportsystemdetails1 = crashreport.a("Entity That Is Already Tracked");
((EntityTrackerEntry) this.trackedEntities.get(entity.getId())).tracker.appendEntityCrashDetails(crashreportsystemdetails1);
try {
throw new ReportedException(crashreport);
} catch (ReportedException reportedexception) {
EntityTracker.a.error("\"Silently\" catching entity tracking error.", reportedexception);
}
} }
} }
// IonSpigot end
public void untrackEntity(Entity entity) { public void untrackEntity(Entity entity) {
//org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
boolean mainThread = Thread.currentThread() == MinecraftServer.getServer().primaryThread;
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
EntityPlayer entityplayer = (EntityPlayer) entity; EntityPlayer entityplayer = (EntityPlayer) entity;
// The best way to go around reducing load on this method is by fully doing the iteration asynchronously Iterator iterator = this.c.iterator();
// We honestly don't need to handle this asynchronously if the size of the entity tracker entry set
// Iterate through all entity tracker entries
if (this.c.size() < 20 || !mainThread) while (iterator.hasNext()) {
for (EntityTrackerEntry entitytrackerentry : this.c) EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(entityplayer); // Untrack entity
else { entitytrackerentry.a(entityplayer);
TASK_EXECUTOR.submit(executor ->
{for(EntityTrackerEntry entityTrackerEntry : this.c){entityTrackerEntry.a(entityplayer);}});
} }
} }
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId()); EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) this.trackedEntities.d(entity.getId());
if (entitytrackerentry1 != null) { if (entitytrackerentry1 != null) {
this.c.remove(entitytrackerentry1); this.c.remove(entitytrackerentry1);
TASK_EXECUTOR.submit(executor -> entitytrackerentry1.a()); entitytrackerentry1.a();
} }
} }
private final Task updatePlayerTask = executor -> {
List<Entity> pending = Lists.newArrayList();
// Iterate through all entitytracker entries
for (EntityTrackerEntry entitytrackerentry : this.c) {
entitytrackerentry.track(this.world.players); // Ensure you're tracking them
if (entitytrackerentry.n && entitytrackerentry.tracker instanceof EntityPlayer) {
pending.add(entitytrackerentry.tracker);
}
}
for (Object o : pending) {
EntityPlayer entityplayer = (EntityPlayer) o;
for (EntityTrackerEntry entitytrackerentry1 : this.c) {
if (entitytrackerentry1.tracker != entityplayer) {
entitytrackerentry1.updatePlayer(entityplayer);
}
}
}
};
public void updatePlayers() { public void updatePlayers() {
if (Bukkit.isPrimaryThread()) ArrayList arraylist = Lists.newArrayList();
TASK_EXECUTOR.submit(updatePlayerTask); Iterator iterator = this.c.iterator();
else updatePlayerTask.onExecute(null); // It's already being ran async so no need to make sure it runs on a separate thread
}
// Force update while (iterator.hasNext()) {
public void a(EntityPlayer entityplayer) { EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
boolean mainThread = Thread.currentThread() == MinecraftServer.getServer().primaryThread;
Task task = executor -> { entitytrackerentry.track(this.world.players);
for (EntityTrackerEntry entitytrackerentry : this.c) { if (entitytrackerentry.n && entitytrackerentry.tracker instanceof EntityPlayer) {
if (entitytrackerentry.tracker == entityplayer) { arraylist.add(entitytrackerentry.tracker);
entitytrackerentry.scanPlayers(this.world.players); }
} else { }
entitytrackerentry.updatePlayer(entityplayer);
for (int i = 0; i < arraylist.size(); ++i) {
EntityPlayer entityplayer = (EntityPlayer) arraylist.get(i);
Iterator iterator1 = this.c.iterator();
while (iterator1.hasNext()) {
EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) iterator1.next();
if (entitytrackerentry1.tracker != entityplayer) {
entitytrackerentry1.updatePlayerFor(entityplayer);
} }
} }
}; }
if (!mainThread)
task.onExecute(null);
else TASK_EXECUTOR.submit(task);
} }
public void a(Entity entity, Packet<?> packet) { public void a(EntityPlayer entityplayer) {
// The packets being passed onto this method generally don't tend to make gameplay less smooth if it takes a 10L delay, so we can submit them to our TaskExecutor Iterator iterator = this.c.iterator();
Task task = executor -> {
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
if (entitytrackerentry != null) { while (iterator.hasNext()) {
entitytrackerentry.broadcast(packet); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
if (entitytrackerentry.tracker == entityplayer) {
entitytrackerentry.scanPlayers(this.world.players);
} else {
entitytrackerentry.updatePlayerFor(entityplayer);
} }
}; }
if (!Bukkit.isPrimaryThread())
task.onExecute(null);
else TASK_EXECUTOR.submit(task);
} }
public void sendPacketToEntity(Entity entity, Packet<?> packet) { /* Used for entity animations & entity status packets, same reasoning for async as EntityTracker#a(Entity, Packet) method */ public void a(Entity entity, Packet packet) {
Task task = executor -> { EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
if (entitytrackerentry != null) if (entitytrackerentry != null) {
entitytrackerentry.broadcastIncludingSelf(packet); entitytrackerentry.broadcast(packet);
}; }
}
public void sendPacketToEntity(Entity entity, Packet packet) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) this.trackedEntities.get(entity.getId());
if (entitytrackerentry != null) {
entitytrackerentry.broadcastIncludingSelf(packet);
}
if (!Bukkit.isPrimaryThread())
task.onExecute(null);
else TASK_EXECUTOR.submit(task);
} }
public void untrackPlayer(EntityPlayer entityplayer) { public void untrackPlayer(EntityPlayer entityplayer) {
Task task = executor -> { Iterator iterator = this.c.iterator();
for (EntityTrackerEntry entitytrackerentry : this.c)
entitytrackerentry.clear(entityplayer);
};
if (!Bukkit.isPrimaryThread()) while (iterator.hasNext()) {
task.onExecute(null); EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
else TASK_EXECUTOR.submit(task);
} entitytrackerentry.clear(entityplayer);
}
public Iterator<EntityTrackerEntry> getEntityTrackerEntries()
{
return this.c.iterator();
} }
public void a(EntityPlayer entityplayer, Chunk chunk) { public void a(EntityPlayer entityplayer, Chunk chunk) {
Task task = executor -> { Iterator iterator = this.c.iterator();
for (EntityTrackerEntry entitytrackerentry : this.c) {
if (entitytrackerentry.tracker != entityplayer while (iterator.hasNext()) {
&& entitytrackerentry.tracker.ae == chunk.locX EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
&& entitytrackerentry.tracker.ag == chunk.locZ) {
entitytrackerentry.updatePlayer(entityplayer); if (entitytrackerentry.tracker != entityplayer && entitytrackerentry.tracker.ae == chunk.locX && entitytrackerentry.tracker.ag == chunk.locZ) {
} entitytrackerentry.updatePlayerFor(entityplayer);
} }
}; }
if (!Bukkit.isPrimaryThread())
task.onExecute(null);
else TASK_EXECUTOR.submit(task);
} }
} }

View File

@ -1,15 +1,14 @@
package net.minecraft.server; package net.minecraft.server;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerVelocityEvent;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import com.elevatemc.spigot.config.eSpigotConfig;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start // CraftBukkit start
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerVelocityEvent;
// CraftBukkit end // CraftBukkit end
public class EntityTrackerEntry { public class EntityTrackerEntry {
@ -41,7 +40,7 @@ public class EntityTrackerEntry {
// PaperSpigot start // PaperSpigot start
// Replace trackedPlayers Set with a Map. The value is true until the player receives // Replace trackedPlayers Set with a Map. The value is true until the player receives
// their first update (which is forced to have absolute coordinates), false afterward. // their first update (which is forced to have absolute coordinates), false afterward.
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new ConcurrentHashMap<>(); // eSpigot - ConcurrentHashMap public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new ConcurrentHashMap<>();
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet(); public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
// PaperSpigot end // PaperSpigot end
@ -60,7 +59,7 @@ public class EntityTrackerEntry {
} }
public boolean equals(Object object) { public boolean equals(Object object) {
return object instanceof EntityTrackerEntry && ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId(); return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false;
} }
public int hashCode() { public int hashCode() {
@ -83,16 +82,17 @@ public class EntityTrackerEntry {
this.broadcast(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle)); this.broadcast(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle));
} }
if (this.tracker instanceof EntityItemFrame && this.m % 20 == 0) { // Paper if (this.tracker instanceof EntityItemFrame /*&& this.m % 10 == 0*/) { // CraftBukkit - Moved below, should always enter this block
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
ItemStack itemstack = entityitemframe.getItem(); ItemStack itemstack = entityitemframe.getItem();
if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // Paper - moved back up if (this.m % 10 == 0 && itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.m % 10 logic here so item frames do not enter the other blocks
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world); WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world);
// CraftBukkit Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
for (EntityPlayer trackedPlayer : this.trackedPlayers) { while (iterator.hasNext()) {
EntityPlayer entityplayer = trackedPlayer; EntityHuman entityhuman = (EntityHuman) iterator.next();
EntityPlayer entityplayer = (EntityPlayer) entityhuman;
worldmap.a(entityplayer, itemstack); worldmap.a(entityplayer, itemstack);
Packet packet = Items.FILLED_MAP.c(itemstack, this.tracker.world, entityplayer); Packet packet = Items.FILLED_MAP.c(itemstack, this.tracker.world, entityplayer);
@ -112,16 +112,17 @@ public class EntityTrackerEntry {
if (this.tracker.vehicle == null) { if (this.tracker.vehicle == null) {
++this.v; ++this.v;
i = MathHelper.floor(this.tracker.locX * 32.0D); i = MathHelper.floor((this.tracker.locX) * 32.0D);
j = MathHelper.floor(this.tracker.locY * 32.0D); j = MathHelper.floor(this.tracker.locY * 32.0D);
int k = MathHelper.floor(this.tracker.locZ * 32.0D); int k = MathHelper.floor((this.tracker.locZ) * 32.0D);
int l = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); int l = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
int i1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); int i1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
//System.out.println(String.format("%s, %s, %s", this.tracker.locX, this.tracker.locY, this.tracker.locZ));
int j1 = i - this.xLoc; int j1 = i - this.xLoc;
int k1 = j - this.yLoc; int k1 = j - this.yLoc;
int l1 = k - this.zLoc; int l1 = k - this.zLoc;
Object object = null; Object object = null;
boolean flag = Math.abs(j1) >= 4 || Math.abs(k1) >= 4 || Math.abs(l1) >= 4 || this.m % 60 == 0; boolean flag = Math.abs(j1) >= 2 || Math.abs(k1) >= 2 || Math.abs(l1) >= 2;
boolean flag1 = Math.abs(l - this.yRot) >= 4 || Math.abs(i1 - this.xRot) >= 4; boolean flag1 = Math.abs(l - this.yRot) >= 4 || Math.abs(i1 - this.xRot) >= 4;
if (this.m > 0 || this.tracker instanceof EntityArrow) { // PaperSpigot - Moved up if (this.m > 0 || this.tracker instanceof EntityArrow) { // PaperSpigot - Moved up
@ -138,26 +139,28 @@ public class EntityTrackerEntry {
} }
// CraftBukkit end // CraftBukkit end
if (j1 >= -128 && j1 < 128 && k1 >= -128 && k1 < 128 && l1 >= -128 && l1 < 128 && this.v <= 100 && !this.x && this.y == this.tracker.onGround) { // Kohi - greatly reduce forced teleport interval - 400 -> 100
if ((!flag || !flag1) && !(this.tracker instanceof EntityArrow)) { if (!(j1 == 0 && k1 == 0 && l1 == 0 && !flag1)) {
if (flag) { if (j1 >= -128 && j1 < 128 && k1 >= -128 && k1 < 128 && l1 >= -128 && l1 < 128 && this.v <= 5 && !this.x && this.y == this.tracker.onGround) {
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, this.tracker.onGround); if ((!flag || !flag1) && !(this.tracker instanceof EntityArrow)) {
} else if (flag1) { if (flag) {
object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) l, (byte) i1, this.tracker.onGround); object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, this.tracker.onGround);
} else if (flag1) {
object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) l, (byte) i1, this.tracker.onGround);
}
} else {
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, (byte) l, (byte) i1, this.tracker.onGround);
} }
} else { } else {
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), (byte) j1, (byte) k1, (byte) l1, (byte) l, (byte) i1, this.tracker.onGround); this.y = this.tracker.onGround;
this.v = 0;
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
if (this.tracker instanceof EntityPlayer) {
this.scanPlayers(new ArrayList<>(this.trackedPlayers));
}
// CraftBukkit end
object = new PacketPlayOutEntityTeleport(this.tracker.getId(), i, j, k, (byte) l, (byte) i1, this.tracker.onGround);
} }
} else {
this.y = this.tracker.onGround;
this.v = 0;
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
if (this.tracker instanceof EntityPlayer) {
// SportPaper - Fix invisibility on teleport
this.scanPlayers(new ArrayList<EntityHuman>(this.tracker.world.players));
}
// CraftBukkit end
object = new PacketPlayOutEntityTeleport(this.tracker.getId(), i, j, k, (byte) l, (byte) i1, this.tracker.onGround);
} }
} }
@ -261,11 +264,7 @@ public class EntityTrackerEntry {
} }
if (!cancelled) { if (!cancelled) {
if (this.tracker instanceof EntityPlayer) { this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker));
} else if (this.tracker instanceof EntityArrow || this.tracker instanceof EntityProjectile) {
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker));
}
} }
// CraftBukkit end // CraftBukkit end
this.tracker.velocityChanged = false; this.tracker.velocityChanged = false;
@ -277,51 +276,20 @@ public class EntityTrackerEntry {
DataWatcher datawatcher = this.tracker.getDataWatcher(); DataWatcher datawatcher = this.tracker.getDataWatcher();
if (datawatcher.a()) { if (datawatcher.a()) {
if (eSpigotConfig.obfuscatePlayerHealth && this.tracker instanceof EntityHuman) { this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
List<DataWatcher.WatchableObject> changedMetadata = datawatcher.c();
Iterator<DataWatcher.WatchableObject> iter = changedMetadata.iterator();
boolean found = false;
while (iter.hasNext()) {
DataWatcher.WatchableObject watchable = iter.next();
if (watchable.a() == 6) {
iter.remove();
found = true;
}
}
if (found) {
changedMetadata.add(new DataWatcher.WatchableObject(3, 6, 1.0F));
}
PacketPlayOutEntityMetadata modifiedPacket = new PacketPlayOutEntityMetadata(this.tracker.getId(), changedMetadata);
// Beanes - Broadcast the modified metadata packet to everyone and send the correct packet to the player
this.broadcast(modifiedPacket);
if (this.tracker instanceof EntityPlayer)
((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
} else {
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
}
} }
if (this.tracker instanceof EntityLiving) { if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Set<AttributeInstance> set = attributemapserver.getAttributes(); Set set = attributemapserver.getAttributes();
if (!set.isEmpty()) { if (!set.isEmpty()) {
// CraftBukkit start - Send scaled max health // CraftBukkit start - Send scaled max health
if (this.tracker instanceof EntityPlayer) { if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false); ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false);
((EntityPlayer) this.tracker).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // MineHQ
} }
// CraftBukkit end // CraftBukkit end
// MineHQ start this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
// this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); // CraftBukkit
if (this.tracker.passenger instanceof EntityPlayer) {
((EntityPlayer) this.tracker.passenger).playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
}
// MineHQ end
} }
set.clear(); set.clear();
@ -330,25 +298,35 @@ public class EntityTrackerEntry {
} }
public void broadcast(Packet packet) { public void broadcast(Packet packet) {
for (EntityPlayer entityplayer : this.trackedPlayers) Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
entityplayer.playerConnection.sendPacket(packet); entityplayer.playerConnection.sendPacket(packet);
}
} }
public void broadcastIncludingSelf(Packet packet) { public void broadcastIncludingSelf(Packet packet) {
this.broadcast(packet); this.broadcast(packet);
if (this.tracker instanceof EntityPlayer) if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet); ((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
}
} }
public void a() { public void a() {
Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
for (EntityPlayer entityplayer : this.trackedPlayers) {
entityplayer.d(this.tracker); entityplayer.d(this.tracker);
} }
} }
// Untrack entity
public void a(EntityPlayer entityplayer) { public void a(EntityPlayer entityplayer) {
if (this.trackedPlayers.contains(entityplayer)) { if (this.trackedPlayers.contains(entityplayer)) {
entityplayer.d(this.tracker); entityplayer.d(this.tracker);
@ -357,7 +335,8 @@ public class EntityTrackerEntry {
} }
public void updatePlayer(EntityPlayer entityplayer) { public void updatePlayerFor(EntityPlayer entityplayer) {
org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
if (entityplayer != this.tracker) { if (entityplayer != this.tracker) {
if (this.c(entityplayer)) { if (this.c(entityplayer)) {
if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) { if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) {
@ -385,8 +364,6 @@ public class EntityTrackerEntry {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateEntityNBT(this.tracker.getId(), nbttagcompound)); entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateEntityNBT(this.tracker.getId(), nbttagcompound));
} }
// MineHQ start
/*
if (this.tracker instanceof EntityLiving) { if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Collection collection = attributemapserver.c(); Collection collection = attributemapserver.c();
@ -401,8 +378,6 @@ public class EntityTrackerEntry {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection)); entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection));
} }
} }
*/
// MineHQ end
this.j = this.tracker.motX; this.j = this.tracker.motX;
this.k = this.tracker.motY; this.k = this.tracker.motY;
@ -438,22 +413,17 @@ public class EntityTrackerEntry {
} }
// CraftBukkit start - Fix for nonsensical head yaw // CraftBukkit start - Fix for nonsensical head yaw
if(this.tracker instanceof EntityLiving) { // SportPaper - avoid processing entities that can't change head rotation this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
// SportPaper start
// This was originally introduced by CraftBukkit, though the implementation is wrong since it's broadcasting
// the packet again in a method that is already called for each player. This would create a very serious performance issue
// with high player and entity counts (each sendPacket call involves waking up the event loop and flushing the network stream).
// this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
// SportPaper end
}
// CraftBukkit end // CraftBukkit end
if (this.tracker instanceof EntityLiving) { if (this.tracker instanceof EntityLiving) {
EntityLiving entityliving = (EntityLiving) this.tracker; EntityLiving entityliving = (EntityLiving) this.tracker;
Iterator iterator = entityliving.getEffects().iterator();
while (iterator.hasNext()) {
MobEffect mobeffect = (MobEffect) iterator.next();
for (MobEffect mobeffect : entityliving.getEffects()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect)); entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect));
} }
} }
@ -475,17 +445,18 @@ public class EntityTrackerEntry {
return d0 >= (double) (-this.b) && d0 <= (double) this.b && d1 >= (double) (-this.b) && d1 <= (double) this.b && this.tracker.a(entityplayer); return d0 >= (double) (-this.b) && d0 <= (double) this.b && d1 >= (double) (-this.b) && d1 <= (double) this.b && this.tracker.a(entityplayer);
} }
protected boolean e(EntityPlayer entityplayer) { // IonSpigot - private -> protected private boolean e(EntityPlayer entityplayer) {
return entityplayer.u().getPlayerChunkMap().a(entityplayer, this.tracker.ae, this.tracker.ag); return entityplayer.u().getPlayerChunkMap().a(entityplayer, this.tracker.ae, this.tracker.ag);
} }
public void scanPlayers(List<EntityHuman> list) { public void scanPlayers(List<EntityHuman> list) {
for (EntityHuman entityHuman : list) { for (int i = 0; i < list.size(); ++i) {
this.updatePlayer((EntityPlayer) entityHuman); this.updatePlayerFor((EntityPlayer) list.get(i));
} }
} }
protected Packet c() { private Packet c() {
if (this.tracker.dead) { if (this.tracker.dead) {
// CraftBukkit start - Remove useless error spam, just return // CraftBukkit start - Remove useless error spam, just return
// EntityTrackerEntry.p.warn("Fetching addPacket for removed entity"); // EntityTrackerEntry.p.warn("Fetching addPacket for removed entity");
@ -596,7 +567,11 @@ public class EntityTrackerEntry {
} }
public void clear(EntityPlayer entityplayer) { public void clear(EntityPlayer entityplayer) {
if (this.trackedPlayers.remove(entityplayer)) // eSpigot - Directly remove org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot
if (this.trackedPlayers.contains(entityplayer)) {
this.trackedPlayers.remove(entityplayer);
entityplayer.d(this.tracker); entityplayer.d(this.tracker);
}
} }
} }

View File

@ -1027,9 +1027,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
if (!eSpigotConfig.showHiddenPlayersInTab) if (!eSpigotConfig.showHiddenPlayersInTab)
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other)); getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); EntityTrackerEntry entry = (EntityTrackerEntry) tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) { if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
entry.updatePlayer(getHandle()); entry.updatePlayerFor(getHandle());
} }
} }