diff --git a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 4abecb100..27d4480a9 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -262,4 +262,9 @@ public class AkarinGlobalConfig { private static void parallelMode() { parallelMode = getInt("core.parallel-mode", 1); } + + public static boolean noChunksPastWorldBorder = true; + private static void noChunksPastWorldBorder() { + noChunksPastWorldBorder = getBoolean("core.no-chunks-past-world-border", noChunksPastWorldBorder); + } } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerChunk.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerChunk.java new file mode 100644 index 000000000..4e9a8315f --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerChunk.java @@ -0,0 +1,85 @@ +package io.akarin.server.mixin.core; + +import java.util.Iterator; +import java.util.List; + +import org.apache.logging.log4j.Logger; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import io.akarin.server.core.AkarinGlobalConfig; +import net.minecraft.server.Chunk; +import net.minecraft.server.ChunkCoordIntPair; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.PacketPlayOutMapChunk; +import net.minecraft.server.PlayerChunk; +import net.minecraft.server.PlayerChunkMap; + +@Mixin(value = PlayerChunk.class, remap = false) +public abstract class MixinPlayerChunk { + @Shadow @Final private static Logger a; + @Shadow @Final private ChunkCoordIntPair location; + @Shadow @Final private PlayerChunkMap playerChunkMap; + @Shadow @Final public List c; + @Shadow public Chunk chunk; + @Shadow private int dirtyCount; + @Shadow private int h; + @Shadow private long i; + @Shadow private boolean done; + + @Shadow public abstract void sendChunk(EntityPlayer entityplayer); + + @Overwrite + public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument + if (this.c.contains(entityplayer)) { + a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z)); + return; + } + if (AkarinGlobalConfig.noChunksPastWorldBorder && !playerChunkMap.getWorld().getWorldBorder().isChunkInBounds(location.x, location.z)) { + return; + } + if (this.c.isEmpty()) { + this.i = this.playerChunkMap.getWorld().getTime(); + } + + this.c.add(entityplayer); + + if (this.done) { + this.sendChunk(entityplayer); + } + } + + @Overwrite + public boolean b() { + if (this.done) { + return true; + } + if (this.chunk == null) { + return false; + } + if (!this.chunk.isReady()) { + return false; + } + if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary + return false; + } + this.dirtyCount = 0; + this.h = 0; + this.done = true; + if (c.isEmpty()) return true; // Akarin - Fixes MC-120780 + PacketPlayOutMapChunk packetplayoutmapchunk = new PacketPlayOutMapChunk(this.chunk, '\uffff'); + Iterator iterator = this.c.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = iterator.next(); + + entityplayer.playerConnection.sendPacket(packetplayoutmapchunk); + this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); + } + + return true; + } +} diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinWorldBorder.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinWorldBorder.java new file mode 100644 index 000000000..13a54b165 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinWorldBorder.java @@ -0,0 +1,29 @@ +package io.akarin.server.mixin.core; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.server.BlockPosition; +import net.minecraft.server.WorldBorder; + +@Mixin(value = WorldBorder.class, remap = false) +public abstract class MixinWorldBorder { + @Shadow + public abstract boolean isInBounds(BlockPosition blockposition); + + @Overwrite + public boolean isBlockInBounds(int chunkX, int chunkZ) { + BlockPosition.MutableBlockPosition mutPos = new BlockPosition.MutableBlockPosition(); // Dionysus - avoid collisions with other threads + mutPos.setValues(chunkX, 64, chunkZ); + return isInBounds(mutPos); + } + + @Overwrite + public boolean isChunkInBounds(int chunkX, int chunkZ) { + BlockPosition.MutableBlockPosition mutPos = new BlockPosition.MutableBlockPosition(); // Dionysus - avoid collisions with other threads // threads + mutPos.setValues(((chunkX << 4) + 15), 64, (chunkZ << 4) + 15); + return isInBounds(mutPos); + } + +} diff --git a/sources/src/main/java/net/minecraft/server/PlayerChunk.java b/sources/src/main/java/net/minecraft/server/PlayerChunk.java deleted file mode 100644 index aa5e8722f..000000000 --- a/sources/src/main/java/net/minecraft/server/PlayerChunk.java +++ /dev/null @@ -1,314 +0,0 @@ -package net.minecraft.server; - -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - -import java.util.Iterator; -import java.util.List; -import javax.annotation.Nullable; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -// CraftBukkit Start -import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; -// CraftBukkit end - -/** - * Akarin Changes Note - * 1) Check whether players empty (performance, MC-120780) - */ -public class PlayerChunk { - - private static final Logger a = LogManager.getLogger(); - private final PlayerChunkMap playerChunkMap; - public final List c = Lists.newArrayList(); // CraftBukkit - public - private final ChunkCoordIntPair location; - private final short[] dirtyBlocks = new short[64]; - @Nullable - public Chunk chunk; // CraftBukkit - public - private int dirtyCount; - private int h; - private long i; - private boolean done; - - // CraftBukkit start - add fields - boolean chunkExists; // Paper - private boolean loadInProgress = false; - private Runnable loadedRunnable = new Runnable() { - @Override - public void run() { - loadInProgress = false; - PlayerChunk.this.chunk = PlayerChunk.this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(location.x, location.z); - markChunkUsed(); // Paper - delay chunk unloads - } - }; - // Paper start - delay chunk unloads - public final void markChunkUsed() { - if (chunk != null && chunk.scheduledForUnload != null) { - chunk.scheduledForUnload = null; - } - } - // Paper end - // CraftBukkit end - - public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { - this.playerChunkMap = playerchunkmap; - this.location = new ChunkCoordIntPair(i, j); - // CraftBukkit start - loadInProgress = true; - this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false); - this.chunkExists = this.chunk != null || ChunkIOExecutor.hasQueuedChunkLoad(playerChunkMap.getWorld(), i, j); // Paper - markChunkUsed(); // Paper - delay chunk unloads - // CraftBukkit end - } - - public ChunkCoordIntPair a() { - return this.location; - } - - public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument - if (this.c.contains(entityplayer)) { - PlayerChunk.a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z)); - } else { - if (this.c.isEmpty()) { - this.i = this.playerChunkMap.getWorld().getTime(); - } - - this.c.add(entityplayer); - // CraftBukkit start - use async chunk io - // if (this.done) { - // this.sendChunk(entityplayer); - // } - if (this.done) { - this.sendChunk(entityplayer); - } - // CraftBukkit end - - } - } - - public void b(EntityPlayer entityplayer) { - if (this.c.contains(entityplayer)) { - // CraftBukkit start - If we haven't loaded yet don't load the chunk just so we can clean it up - if (!this.done) { - this.c.remove(entityplayer); - - if (this.c.isEmpty()) { - ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.getWorld(), this.location.x, this.location.z, this.loadedRunnable); - this.playerChunkMap.b(this); - } - - return; - } - // CraftBukkit end - if (this.done) { - entityplayer.playerConnection.sendPacket(new PacketPlayOutUnloadChunk(this.location.x, this.location.z)); - } - - this.c.remove(entityplayer); - if (this.c.isEmpty()) { - this.playerChunkMap.b(this); - } - - } - } - - public boolean a(boolean flag) { - if (this.chunk != null) { - return true; - } else { - /* CraftBukkit start - if (flag) { - this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z); - } else { - this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(this.location.x, this.location.z); - } - */ - if (!loadInProgress) { - loadInProgress = true; - this.chunk = playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z, loadedRunnable, flag); - markChunkUsed(); // Paper - delay chunk unloads - } - // CraftBukkit end - - return this.chunk != null; - } - } - - public boolean b() { - if (this.done) { - return true; - } else if (this.chunk == null) { - return false; - } else if (!this.chunk.isReady()) { - return false; - } else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary - return false; // Paper - Anti-Xray - Wait and try again later - } else { - this.dirtyCount = 0; - this.h = 0; - this.done = true; - if (c.isEmpty()) return true; // Akarin - Fixes MC-120780 - PacketPlayOutMapChunk packetplayoutmapchunk = new PacketPlayOutMapChunk(this.chunk, '\uffff'); - Iterator iterator = this.c.iterator(); - - while (iterator.hasNext()) { - EntityPlayer entityplayer = (EntityPlayer) iterator.next(); - - entityplayer.playerConnection.sendPacket(packetplayoutmapchunk); - this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); - } - - return true; - } - } - - public void sendChunk(EntityPlayer entityplayer) { - if (this.done) { - this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary - entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, '\uffff')); - this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); - } - } - - public void c() { - long i = this.playerChunkMap.getWorld().getTime(); - - if (this.chunk != null) { - this.chunk.c(this.chunk.x() + i - this.i); - } - - this.i = i; - } - - public void a(int i, int j, int k) { - if (this.done) { - if (this.dirtyCount == 0) { - this.playerChunkMap.a(this); - } - - this.h |= 1 << (j >> 4); - if (this.dirtyCount < 64) { - short short0 = (short) (i << 12 | k << 8 | j); - - for (int l = 0; l < this.dirtyCount; ++l) { - if (this.dirtyBlocks[l] == short0) { - return; - } - } - - this.dirtyBlocks[this.dirtyCount++] = short0; - } - - } - } - - public void a(Packet packet) { - if (this.done) { - for (int i = 0; i < this.c.size(); ++i) { - this.c.get(i).playerConnection.sendPacket(packet); - } - - } - } - - public void d() { - if (this.done && this.chunk != null) { - if (this.dirtyCount != 0) { - int i; - int j; - int k; - - if (this.dirtyCount == 1) { - i = (this.dirtyBlocks[0] >> 12 & 15) + this.location.x * 16; - j = this.dirtyBlocks[0] & 255; - k = (this.dirtyBlocks[0] >> 8 & 15) + this.location.z * 16; - BlockPosition blockposition = new BlockPosition(i, j, k); - - this.a((new PacketPlayOutBlockChange(this.playerChunkMap.getWorld(), blockposition))); - if (this.playerChunkMap.getWorld().getType(blockposition).getBlock().isTileEntity()) { - this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition)); - } - } else if (this.dirtyCount == 64) { - // Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104 - //this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary - this.a((new PacketPlayOutMapChunk(this.chunk, this.h))); - } else { - this.a((new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk))); - - for (i = 0; i < this.dirtyCount; ++i) { - j = (this.dirtyBlocks[i] >> 12 & 15) + this.location.x * 16; - k = this.dirtyBlocks[i] & 255; - int l = (this.dirtyBlocks[i] >> 8 & 15) + this.location.z * 16; - BlockPosition blockposition1 = new BlockPosition(j, k, l); - - if (this.playerChunkMap.getWorld().getType(blockposition1).getBlock().isTileEntity()) { - this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition1)); - } - } - } - - this.dirtyCount = 0; - this.h = 0; - } - } - } - - private void a(@Nullable TileEntity tileentity) { - if (tileentity != null) { - PacketPlayOutTileEntityData packetplayouttileentitydata = tileentity.getUpdatePacket(); - - if (packetplayouttileentitydata != null) { - this.a(packetplayouttileentitydata); - } - } - - } - - public boolean d(EntityPlayer entityplayer) { - return this.c.contains(entityplayer); - } - - public boolean a(Predicate predicate) { - return Iterables.tryFind(this.c, predicate).isPresent(); - } - - public boolean a(double d0, Predicate predicate) { - int i = 0; - - for (int j = this.c.size(); i < j; ++i) { - EntityPlayer entityplayer = this.c.get(i); - - if (predicate.apply(entityplayer) && this.location.a(entityplayer) < d0 * d0) { - return true; - } - } - - return false; - } - - public boolean e() { - return this.done; - } - - @Nullable - public Chunk f() { - return this.chunk; - } - - public double g() { - double d0 = Double.MAX_VALUE; - Iterator iterator = this.c.iterator(); - - while (iterator.hasNext()) { - EntityPlayer entityplayer = (EntityPlayer) iterator.next(); - double d1 = this.location.a(entityplayer); - - if (d1 < d0) { - d0 = d1; - } - } - - return d0; - } -} diff --git a/sources/src/main/resources/mixins.akarin.core.json b/sources/src/main/resources/mixins.akarin.core.json index 090661739..5737d042d 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -28,6 +28,8 @@ "core.MixinMinecraftServer", "core.MixinChunkIOExecutor", "core.MixinPlayerConnectionUtils", + "core.MixinPlayerChunk", + "core.MixinWorldBorder", "nsc.OptimisticNetworkManager", "nsc.NonblockingServerConnection",