mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-03 05:26:50 +01:00
36f34f01c0
Upstream has released updates that appears to apply and compile correctly. This update has not been tested by PaperMC and as with ANY update, please do your own testing Bukkit Changes: da9ef3c5 #496: Add methods to get/set ItemStacks in EquipmentSlots 3abebc9f #492: Let Tameable extend Animals rather than Entity 941111a0 #495: Expose ItemStack and hand used in PlayerShearEntityEvent 4fe19cae #494: InventoryView - Add missing Brewing FUEL_TIME CraftBukkit Changes:933e9094
#664: Add methods to get/set ItemStacks in EquipmentSlots18722312
#662: Expose ItemStack and hand used in PlayerShearEntityEvent
208 lines
12 KiB
Diff
208 lines
12 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
|
Date: Tue, 5 May 2020 19:49:23 -0700
|
|
Subject: [PATCH] Optimize entity list iteration requiring entities be in
|
|
loaded chunks
|
|
|
|
We retain a list of loaded entities specifically for this usage
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
|
|
index 750fb07756f7e40b21f8ab0925f2e842aae50f7b..69bfece7d43bc9e0c4cf7f71bffec9a44c2b9a67 100644
|
|
--- a/src/main/java/net/minecraft/server/Chunk.java
|
|
+++ b/src/main/java/net/minecraft/server/Chunk.java
|
|
@@ -801,6 +801,7 @@ public class Chunk implements IChunkAccess {
|
|
this.setNeighbourLoaded(0, 0, this);
|
|
this.loadedTicketLevel = true;
|
|
// Paper end - neighbour cache
|
|
+ ((WorldServer)this.world).onChunkLoad(this); // Paper - optimise entity list iteration
|
|
org.bukkit.Server server = this.world.getServer();
|
|
((WorldServer)this.world).getChunkProvider().addLoadedChunk(this); // Paper
|
|
if (server != null) {
|
|
@@ -859,6 +860,7 @@ public class Chunk implements IChunkAccess {
|
|
this.loadedTicketLevel = false;
|
|
this.resetNeighbours();
|
|
// Paper end
|
|
+ ((WorldServer)this.world).onChunkUnload(this); // Paper - optimise entity list iteration
|
|
}
|
|
// CraftBukkit end
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
|
|
index b3785775ecd8e3c13e7829f641f2c1b5fd0d9d47..d9b3aa285a7b187f692088bab320f1de2160300b 100644
|
|
--- a/src/main/java/net/minecraft/server/WorldServer.java
|
|
+++ b/src/main/java/net/minecraft/server/WorldServer.java
|
|
@@ -189,6 +189,25 @@ public class WorldServer extends World {
|
|
}
|
|
// Paper end - rewrite ticklistserver
|
|
|
|
+ // Paper start - Optimize entity list iteration requiring entities be in loaded chunks
|
|
+ public final com.destroystokyo.paper.util.maplist.EntityList loadedEntities = new com.destroystokyo.paper.util.maplist.EntityList();
|
|
+ void onChunkLoad(final Chunk chunk) {
|
|
+ final com.destroystokyo.paper.util.maplist.EntityList list = chunk.entities;
|
|
+ final Entity[] entities = list.getRawData();
|
|
+ for (int i = 0, size = list.size(); i < size; ++i) {
|
|
+ this.loadedEntities.add(entities[i]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ void onChunkUnload(final Chunk chunk) {
|
|
+ final com.destroystokyo.paper.util.maplist.EntityList list = chunk.entities;
|
|
+ final Entity[] entities = list.getRawData();
|
|
+ for (int i = 0, size = list.size(); i < size; ++i) {
|
|
+ this.loadedEntities.remove(entities[i]);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Optimize entity list iteration requiring entities be in loaded chunks
|
|
+
|
|
// Add env and gen to constructor
|
|
public WorldServer(MinecraftServer minecraftserver, Executor executor, WorldNBTStorage worldnbtstorage, WorldData worlddata, DimensionManager dimensionmanager, GameProfilerFiller gameprofilerfiller, WorldLoadListener worldloadlistener, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) {
|
|
super(worlddata, dimensionmanager, (world, worldprovider) -> {
|
|
@@ -489,14 +508,13 @@ public class WorldServer extends World {
|
|
|
|
gameprofilerfiller.exitEnter("regular");
|
|
this.tickingEntities = true;
|
|
- ObjectIterator objectiterator = this.entitiesById.int2ObjectEntrySet().iterator();
|
|
+ Iterator<Entity> entityiterator = this.loadedEntities.iterator(); // Paper - use loaded entity list - change var name to compile fail on usage change, we need to hook remove() calls here
|
|
|
|
org.spigotmc.ActivationRange.activateEntities(this); // Spigot
|
|
timings.entityTick.startTiming(); // Spigot
|
|
TimingHistory.entityTicks += this.globalEntityList.size(); // Paper
|
|
- while (objectiterator.hasNext()) {
|
|
- Entry<Entity> entry = (Entry) objectiterator.next();
|
|
- Entity entity1 = (Entity) entry.getValue();
|
|
+ while (entityiterator.hasNext()) { // Paper - use loaded entity list
|
|
+ Entity entity1 = entityiterator.next(); // Paper - use loaded entity list
|
|
Entity entity2 = entity1.getVehicle();
|
|
|
|
/* CraftBukkit start - We prevent spawning in general, so this butchering is not needed
|
|
@@ -532,7 +550,7 @@ public class WorldServer extends World {
|
|
gameprofilerfiller.enter("remove");
|
|
if (entity1.dead) {
|
|
this.removeEntityFromChunk(entity1);
|
|
- objectiterator.remove();
|
|
+ entityiterator.remove(); this.entitiesById.remove(entity1.getId()); // Paper - use loaded entity list
|
|
this.unregisterEntity(entity1);
|
|
}
|
|
|
|
@@ -1445,6 +1463,7 @@ public class WorldServer extends World {
|
|
if (entity instanceof EntityInsentient) {
|
|
this.navigators.remove(((EntityInsentient) entity).getNavigation());
|
|
}
|
|
+ this.loadedEntities.remove(entity); // Paper - loaded entity list
|
|
new com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid
|
|
entity.valid = false; // CraftBukkit
|
|
}
|
|
@@ -1514,6 +1533,11 @@ public class WorldServer extends World {
|
|
}
|
|
// Paper end
|
|
entity.shouldBeRemoved = false; // Paper - shouldn't be removed after being re-added
|
|
+ // Paper start - loaded entity list
|
|
+ if (this.isChunkLoaded(net.minecraft.server.MCUtil.getChunkCoordinate(entity.locX()), net.minecraft.server.MCUtil.getChunkCoordinate(entity.locZ()))) {
|
|
+ this.loadedEntities.add(entity);
|
|
+ }
|
|
+ // Paper end - loaded entity list
|
|
new com.destroystokyo.paper.event.entity.EntityAddToWorldEvent(entity.getBukkitEntity()).callEvent(); // Paper - fire while valid
|
|
}
|
|
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
index ac257d50dea8f42a515f19bbae12ab5680e26bb4..995f706678fa0f4e88078d5a15c62dcac25f5fca 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
|
@@ -1110,16 +1110,16 @@ public class CraftWorld implements World {
|
|
|
|
@Override
|
|
public List<Entity> getEntities() {
|
|
- List<Entity> list = new ArrayList<Entity>();
|
|
+ List<Entity> list = new ArrayList<Entity>(world.loadedEntities.size()); // Paper - optimize this call
|
|
|
|
- for (Object o : world.entitiesById.values()) {
|
|
+ for (Object o : world.loadedEntities) { // Paper - optimize this call
|
|
if (o instanceof net.minecraft.server.Entity) {
|
|
net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o;
|
|
if (mcEnt.shouldBeRemoved) continue; // Paper
|
|
Entity bukkitEntity = mcEnt.getBukkitEntity();
|
|
|
|
// Assuming that bukkitEntity isn't null
|
|
- if (bukkitEntity != null && bukkitEntity.isValid()) {
|
|
+ if (bukkitEntity != null && CraftEntity.canBeSeenByPlugins(bukkitEntity)) { // Paper - optimize this call
|
|
list.add(bukkitEntity);
|
|
}
|
|
}
|
|
@@ -1130,16 +1130,16 @@ public class CraftWorld implements World {
|
|
|
|
@Override
|
|
public List<LivingEntity> getLivingEntities() {
|
|
- List<LivingEntity> list = new ArrayList<LivingEntity>();
|
|
+ List<LivingEntity> list = new ArrayList<LivingEntity>(world.loadedEntities.size()); // Paper - optimize this call
|
|
|
|
- for (Object o : world.entitiesById.values()) {
|
|
+ for (Object o : world.loadedEntities) { // Paper - optimize this call
|
|
if (o instanceof net.minecraft.server.Entity) {
|
|
net.minecraft.server.Entity mcEnt = (net.minecraft.server.Entity) o;
|
|
if (mcEnt.shouldBeRemoved) continue; // Paper
|
|
Entity bukkitEntity = mcEnt.getBukkitEntity();
|
|
|
|
// Assuming that bukkitEntity isn't null
|
|
- if (bukkitEntity != null && bukkitEntity instanceof LivingEntity && bukkitEntity.isValid()) {
|
|
+ if (bukkitEntity != null && bukkitEntity instanceof LivingEntity && CraftEntity.canBeSeenByPlugins(bukkitEntity)) { // Paper - optimize this call
|
|
list.add((LivingEntity) bukkitEntity);
|
|
}
|
|
}
|
|
@@ -1160,7 +1160,7 @@ public class CraftWorld implements World {
|
|
public <T extends Entity> Collection<T> getEntitiesByClass(Class<T> clazz) {
|
|
Collection<T> list = new ArrayList<T>();
|
|
|
|
- for (Object entity: world.entitiesById.values()) {
|
|
+ for (Object entity: world.loadedEntities) { // Paper - optimize this call
|
|
if (entity instanceof net.minecraft.server.Entity) {
|
|
if (((net.minecraft.server.Entity) entity).shouldBeRemoved) continue; // Paper
|
|
Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity();
|
|
@@ -1171,7 +1171,7 @@ public class CraftWorld implements World {
|
|
|
|
Class<?> bukkitClass = bukkitEntity.getClass();
|
|
|
|
- if (clazz.isAssignableFrom(bukkitClass) && bukkitEntity.isValid()) {
|
|
+ if (clazz.isAssignableFrom(bukkitClass) && CraftEntity.canBeSeenByPlugins(bukkitEntity)) { // Paper - optimize this call
|
|
list.add((T) bukkitEntity);
|
|
}
|
|
}
|
|
@@ -1184,7 +1184,7 @@ public class CraftWorld implements World {
|
|
public Collection<Entity> getEntitiesByClasses(Class<?>... classes) {
|
|
Collection<Entity> list = new ArrayList<Entity>();
|
|
|
|
- for (Object entity: world.entitiesById.values()) {
|
|
+ for (Object entity: world.loadedEntities) { // Paper - optimize this call
|
|
if (entity instanceof net.minecraft.server.Entity) {
|
|
if (((net.minecraft.server.Entity) entity).shouldBeRemoved) continue; // Paper
|
|
Entity bukkitEntity = ((net.minecraft.server.Entity) entity).getBukkitEntity();
|
|
@@ -1197,7 +1197,7 @@ public class CraftWorld implements World {
|
|
|
|
for (Class<?> clazz : classes) {
|
|
if (clazz.isAssignableFrom(bukkitClass)) {
|
|
- if (bukkitEntity.isValid()) {
|
|
+ if (CraftEntity.canBeSeenByPlugins(bukkitEntity)) { // Paper - optimize this call
|
|
list.add(bukkitEntity);
|
|
}
|
|
break;
|
|
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
index ff60568ce4320e6ebfa50489a564538b0f57da82..f3771121617ad6ad256562dc72a50f241f530929 100644
|
|
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
|
|
@@ -180,6 +180,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
|
|
this.entity = entity;
|
|
}
|
|
|
|
+ // Paper start
|
|
+ // note: this does not check isChunkLoaded, use Entity#isValid to do that
|
|
+ public static boolean canBeSeenByPlugins(org.bukkit.entity.Entity entity) {
|
|
+ Entity handle = ((CraftEntity)entity).getHandle();
|
|
+ // TODO
|
|
+ // isAlive is a dumb choice, given living entities aren't alive (but are in the world) if health < 0
|
|
+ // this needs to be brought up to spigot to fix though, we are NOT breaking api implementation, especially
|
|
+ // if no-one's complained.
|
|
+ return !handle.shouldBeRemoved && handle.isAlive() && handle.valid;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
@Override
|
|
public Chunk getChunk() {
|
|
net.minecraft.server.Chunk currentChunk = entity.getCurrentChunk();
|