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/MixinBlockMinecartDetector.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinBlockMinecartDetector.java new file mode 100644 index 000000000..cf3f51481 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinBlockMinecartDetector.java @@ -0,0 +1,19 @@ +package io.akarin.server.mixin.core; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.server.BlockMinecartDetector; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.IBlockData; +import net.minecraft.server.World; + +@Mixin(value = BlockMinecartDetector.class, remap = false) +public abstract class MixinBlockMinecartDetector { + @Inject(at = @At("HEAD"), method = "e", cancellable = true) + private void e(World world, BlockPosition blockposition, IBlockData iblockdata, CallbackInfo ci) { + if (iblockdata.getBlock() != (Object)this) ci.cancel(); + } +} diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinEntityLiving.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinEntityLiving.java new file mode 100644 index 000000000..9290b335a --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinEntityLiving.java @@ -0,0 +1,50 @@ +package io.akarin.server.mixin.core; + +import javax.annotation.Nullable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.server.EntityLiving; +import net.minecraft.server.EntityPlayer; +import net.minecraft.server.MathHelper; +import net.minecraft.server.MobEffect; +import net.minecraft.server.MobEffectList; +import net.minecraft.server.MobEffects; + +@Mixin(value = EntityLiving.class, remap = false) +public abstract class MixinEntityLiving { + @Shadow public abstract boolean hasEffect(MobEffectList mobeffectlist); + @Shadow @Nullable public abstract MobEffect getEffect(MobEffectList mobeffectlist); + @Shadow protected abstract float ct(); + protected long lastJumpTime = 0L; // Dionysus - Backport ArrowDMG fix + + @Overwrite + protected void cu() { + // Dionysus start - Backport ArrowDMG fix + long time = System.nanoTime(); + boolean canCrit = true; + if ((Object)this instanceof EntityPlayer) { + canCrit = false; + if (time - lastJumpTime > (long)(0.250e9)) { + lastJumpTime = time; + canCrit = true; + } + } + // Dionysus end - Backport ArrowDMG fix + ((EntityLiving)(Object)this).motY = (double) ct(); + if (hasEffect(MobEffects.JUMP)) { + ((EntityLiving)(Object)this).motY += (double) ((float) (getEffect(MobEffects.JUMP).getAmplifier() + 1) * 0.1F); + } + + if (canCrit&&((EntityLiving)(Object)this).isSprinting()) { + float f = ((EntityLiving)(Object)this).yaw * 0.017453292F; + + ((EntityLiving)(Object)this).motX -= (double) (MathHelper.sin(f) * 0.2F); + ((EntityLiving)(Object)this).motZ += (double) (MathHelper.cos(f) * 0.2F); + } + + ((EntityLiving)(Object)this).impulse = true; + } +} 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..75f23436d --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerChunk.java @@ -0,0 +1,92 @@ +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; +import net.minecraft.server.WorldBorder; + +@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) { + WorldBorder worldborder = playerChunkMap.getWorld().getWorldBorder(); + int centerchunkx = ((int)worldborder.getCenterX()) >> 4; + int centerchunkz = ((int)worldborder.getCenterZ()) >> 4; + int sizechunks = ((int)worldborder.getSize()) >> 5; + ++sizechunks; + if(location.x=centerchunkx + sizechunks||location.z=centerchunkz + sizechunks) { + 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/MixinTileEntityLootable.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinTileEntityLootable.java new file mode 100644 index 000000000..1ea2c6b01 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinTileEntityLootable.java @@ -0,0 +1,17 @@ +package io.akarin.server.mixin.core; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.server.EntityHuman; +import net.minecraft.server.TileEntityLootable; + +@Mixin(value = TileEntityLootable.class, remap = false) +public abstract class MixinTileEntityLootable { + @Inject(at = @At("HEAD"), method = "b(Lnet/minecraft/server/EntityHuman;Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfo;)V", cancellable = true) + private void b(EntityHuman entityhuman, CallbackInfo ci) { + if (entityhuman == null) ci.cancel(); + } +} 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/io/akarin/server/mixin/optimization/MixinDataBits.java b/sources/src/main/java/io/akarin/server/mixin/optimization/MixinDataBits.java new file mode 100644 index 000000000..01b596858 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/optimization/MixinDataBits.java @@ -0,0 +1,47 @@ +package io.akarin.server.mixin.optimization; + +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 net.minecraft.server.DataBits; + +@Mixin(value = DataBits.class, remap = false) +public abstract class MixinDataBits { + @Shadow @Final private long[] a; + @Shadow @Final private int b; + @Shadow @Final private long c; + + @Overwrite + public void a(int i, int j) { + int k = i * this.b; + int l = k >> 6; + int i1 = (i + 1) * this.b - 1 >> 6; + int j1 = k ^ l << 6; + + this.a[l] = this.a[l] & ~(this.c << j1) | ((long) j & this.c) << j1; + if (l != i1) { + int k1 = 64 - j1; + int l1 = this.b - k1; + + this.a[i1] = this.a[i1] >>> l1 << l1 | ((long) j & this.c) >> k1; + } + + } + @Overwrite + public int a(int i) { + int j = i * this.b; + int k = j >> 6; + int l = (i + 1) * this.b - 1 >> 6; + int i1 = j ^ k << 6; + + if (k == l) { + return (int) (this.a[k] >>> i1 & this.c); + } else { + int j1 = 64 - i1; + + return (int) ((this.a[k] >>> i1 | this.a[l] << j1) & this.c); + } + } +} 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..82a34ca4d 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -28,6 +28,11 @@ "core.MixinMinecraftServer", "core.MixinChunkIOExecutor", "core.MixinPlayerConnectionUtils", + "core.MixinPlayerChunk", + "core.MixinWorldBorder", + "core.MixinEntityLiving", + "core.MixinTileEntityLootable", + "core.MixinBlockMinecartDetector", "nsc.OptimisticNetworkManager", "nsc.NonblockingServerConnection", @@ -44,6 +49,7 @@ "optimization.MixinContainerHorse", "optimization.MixinExplosion", "optimization.MixinMathHelper", - "optimization.MixinBlockStationary" + "optimization.MixinBlockStationary", + "optimization.MixinDataBits" ] }