diff --git a/sources/src/main/java/io/akarin/api/Akari.java b/sources/src/main/java/io/akarin/api/Akari.java index 5294190d8..b8c084fce 100644 --- a/sources/src/main/java/io/akarin/api/Akari.java +++ b/sources/src/main/java/io/akarin/api/Akari.java @@ -39,7 +39,7 @@ public abstract class Akari { /** * A common tick pool */ - public static final ExecutorCompletionService STAGE_TICK = new ExecutorCompletionService<>(Executors.newFixedThreadPool(1, Akari.STAGE_FACTORY)); + public static final ExecutorCompletionService STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY)); /* * The unsafe diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java index a7fab080f..eb6b624ad 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinMinecraftServer.java @@ -3,8 +3,6 @@ package io.akarin.server.mixin.core; import java.util.List; import java.util.Queue; import java.util.concurrent.FutureTask; -import java.util.concurrent.locks.ReentrantReadWriteLock; - import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; import org.bukkit.event.inventory.InventoryMoveItemEvent; diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java index 60435de04..3957b1857 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java @@ -8,6 +8,8 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; +import co.aikar.timings.Timing; +import co.aikar.timings.TimingsManager; import io.akarin.api.Akari; import io.akarin.server.core.AkarinGlobalConfig; @@ -33,6 +35,17 @@ public class MixinTimingHandler { @Shadow void addDiff(long diff) {} + + + public Timing startTiming() { + if (enabled && ++timingDepth == 1) { + start = System.nanoTime(); + parent = TimingsManager.CURRENT; + TimingsManager.CURRENT = this; + } + return this; + } + public void stopTiming(boolean sync) { if (enabled && --timingDepth == 0 && start != 0) { if (Akari.silentTiming) { // It must be off-main thread now diff --git a/sources/src/main/java/net/minecraft/server/EntityTracker.java b/sources/src/main/java/net/minecraft/server/EntityTracker.java new file mode 100644 index 000000000..168bb75f7 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/EntityTracker.java @@ -0,0 +1,326 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class EntityTracker { + + private static final Logger a = LogManager.getLogger(); + private final WorldServer world; + private final Set c = Sets.newConcurrentHashSet(); // Akarin - make concurrent + private final ReentrantReadWriteLock entriesLock = new ReentrantReadWriteLock(); // Akarin - add lock + public final IntHashMap trackedEntities = new IntHashMap(); + private int e; + + public EntityTracker(WorldServer worldserver) { + this.world = worldserver; + this.e = PlayerChunkMap.getFurthestViewableBlock(worldserver.spigotConfig.viewDistance); // Spigot + } + + public static long a(double d0) { + return MathHelper.d(d0 * 4096.0D); + } + + public void track(Entity entity) { + if (entity instanceof EntityPlayer) { + this.addEntity(entity, 512, 2); + EntityPlayer entityplayer = (EntityPlayer) entity; + entriesLock.writeLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + if (entitytrackerentry.b() != entityplayer) { + entitytrackerentry.updatePlayer(entityplayer); + } + } + entriesLock.writeLock().unlock(); // Akarin + } else if (entity instanceof EntityFishingHook) { + this.addEntity(entity, 64, 5, true); + } else if (entity instanceof EntityArrow) { + this.addEntity(entity, 64, 20, false); + } else if (entity instanceof EntitySmallFireball) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntityFireball) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntitySnowball) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityLlamaSpit) { + this.addEntity(entity, 64, 10, false); + } else if (entity instanceof EntityEnderPearl) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityEnderSignal) { + this.addEntity(entity, 64, 4, true); + } else if (entity instanceof EntityEgg) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityPotion) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityThrownExpBottle) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityFireworks) { + this.addEntity(entity, 64, 10, true); + } else if (entity instanceof EntityItem) { + this.addEntity(entity, 64, 20, true); + } else if (entity instanceof EntityMinecartAbstract) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityBoat) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntitySquid) { + this.addEntity(entity, 64, 3, true); + } else if (entity instanceof EntityWither) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof EntityShulkerBullet) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityBat) { + this.addEntity(entity, 80, 3, false); + } else if (entity instanceof EntityEnderDragon) { + this.addEntity(entity, 160, 3, true); + } else if (entity instanceof IAnimal) { + this.addEntity(entity, 80, 3, true); + } else if (entity instanceof EntityTNTPrimed) { + this.addEntity(entity, 160, 10, true); + } else if (entity instanceof EntityFallingBlock) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityHanging) { + this.addEntity(entity, 160, Integer.MAX_VALUE, false); + } else if (entity instanceof EntityArmorStand) { + this.addEntity(entity, 160, 3, true); + } else if (entity instanceof EntityExperienceOrb) { + this.addEntity(entity, 160, 20, true); + } else if (entity instanceof EntityAreaEffectCloud) { + this.addEntity(entity, 160, 10, true); // CraftBukkit + } else if (entity instanceof EntityEnderCrystal) { + this.addEntity(entity, 256, Integer.MAX_VALUE, false); + } else if (entity instanceof EntityEvokerFangs) { + this.addEntity(entity, 160, 2, false); + } + + } + + public void addEntity(Entity entity, int i, int j) { + this.addEntity(entity, i, j, false); + } + + 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 + try { + if (this.trackedEntities.b(entity.getId())) { + throw new IllegalStateException("Entity is already tracked!"); + } + + EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag); + + entriesLock.writeLock().lock(); // Akarin + this.c.add(entitytrackerentry); + entriesLock.writeLock().unlock(); // Akarin + + this.trackedEntities.a(entity.getId(), entitytrackerentry); + entitytrackerentry.scanPlayers(this.world.players); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track"); + + crashreportsystemdetails.a("Tracking range", i + " blocks"); + final int finalI = i; // CraftBukkit - fix decompile error + crashreportsystemdetails.a("Update interval", new CrashReportCallable() { + public String a() throws Exception { + String s = "Once per " + finalI + " ticks"; // CraftBukkit + + if (finalI == Integer.MAX_VALUE) { // CraftBukkit + s = "Maximum (" + s + ")"; + } + + return s; + } + + @Override + public Object call() throws Exception { + return this.a(); + } + }); + entity.appendEntityCrashDetails(crashreportsystemdetails); + this.trackedEntities.get(entity.getId()).b().appendEntityCrashDetails(crashreport.a("Entity That Is Already Tracked")); + + try { + throw new ReportedException(crashreport); + } catch (ReportedException reportedexception) { + EntityTracker.a.error("\"Silently\" catching entity tracking error.", reportedexception); + } + } + + } + + public void untrackEntity(Entity entity) { + org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot + entriesLock.writeLock().lock(); // Akarin + if (entity instanceof EntityPlayer) { + EntityPlayer entityplayer = (EntityPlayer) entity; + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.a(entityplayer); + } + } + + EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId()); + + if (entitytrackerentry1 != null) { + this.c.remove(entitytrackerentry1); + entitytrackerentry1.a(); + } + entriesLock.writeLock().unlock(); // Akarin + } + + public void updatePlayers() { + ArrayList arraylist = Lists.newArrayList(); + entriesLock.writeLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + world.timings.tracker1.startTiming(); // Spigot + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.track(this.world.players); + if (entitytrackerentry.b) { + Entity entity = entitytrackerentry.b(); + + if (entity instanceof EntityPlayer) { + arraylist.add(entity); + } + } + } + entriesLock.writeLock().unlock(); // Akarin + world.timings.tracker1.stopTiming(); // Spigot + + world.timings.tracker2.startTiming(); // Spigot + 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.b() != entityplayer) { + entitytrackerentry1.updatePlayer(entityplayer); + } + } + } + world.timings.tracker2.stopTiming(); // Spigot + + } + + public void a(EntityPlayer entityplayer) { + entriesLock.writeLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + if (entitytrackerentry.b() == entityplayer) { + entitytrackerentry.scanPlayers(this.world.players); + } else { + entitytrackerentry.updatePlayer(entityplayer); + } + } + entriesLock.writeLock().unlock(); // Akarin + } + + public void a(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcast(packet); + } + + } + + public void sendPacketToEntity(Entity entity, Packet packet) { + EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId()); + + if (entitytrackerentry != null) { + entitytrackerentry.broadcastIncludingSelf(packet); + } + + } + + public void untrackPlayer(EntityPlayer entityplayer) { + entriesLock.writeLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.clear(entityplayer); + } + entriesLock.writeLock().unlock(); // Akarin + } + + public void a(EntityPlayer entityplayer, Chunk chunk) { + ArrayList arraylist = Lists.newArrayList(); + ArrayList arraylist1 = Lists.newArrayList(); + entriesLock.readLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + Entity entity = entitytrackerentry.b(); + + if (entity != entityplayer && entity.ab == chunk.locX && entity.ad == chunk.locZ) { + entitytrackerentry.updatePlayer(entityplayer); + if (entity instanceof EntityInsentient && ((EntityInsentient) entity).getLeashHolder() != null) { + arraylist.add(entity); + } + + if (!entity.bF().isEmpty()) { + arraylist1.add(entity); + } + } + } + entriesLock.readLock().unlock(); // Akarin + + Entity entity1; + + if (!arraylist.isEmpty()) { + iterator = arraylist.iterator(); + + while (iterator.hasNext()) { + entity1 = (Entity) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(entity1, ((EntityInsentient) entity1).getLeashHolder())); + } + } + + if (!arraylist1.isEmpty()) { + iterator = arraylist1.iterator(); + + while (iterator.hasNext()) { + entity1 = (Entity) iterator.next(); + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(entity1)); + } + } + + } + + public void a(int i) { + this.e = (i - 1) * 16; + entriesLock.readLock().lock(); // Akarin + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next(); + + entitytrackerentry.a(this.e); + } + entriesLock.readLock().unlock(); // Akarin + } +}