├── .gitmodules ├── scripts ├── inst.sh ├── upstream.sh └── build.sh ├── sources └── src │ └── main │ ├── java │ ├── io │ │ └── akarin │ │ │ ├── api │ │ │ └── internal │ │ │ │ ├── mixin │ │ │ │ ├── IMixinWorldServer.java │ │ │ │ └── IMixinRealTimeTicking.java │ │ │ │ ├── utils │ │ │ │ ├── thread │ │ │ │ │ ├── OpenExecutionException.java │ │ │ │ │ └── SuspendableExecutorCompletionService.java │ │ │ │ ├── random │ │ │ │ │ ├── StatefulRandomness.java │ │ │ │ │ ├── LightRandom.java │ │ │ │ │ └── RandomnessSource.java │ │ │ │ └── ReentrantSpinningLock.java │ │ │ │ ├── LocalAddress.java │ │ │ │ └── Akari.java │ │ │ └── server │ │ │ ├── mixin │ │ │ ├── optimization │ │ │ │ ├── MixinTileEntityEnchantTable.java │ │ │ │ ├── MixinContainerHorse.java │ │ │ │ ├── WeakBigTree.java │ │ │ │ ├── MixinBlockStationary.java │ │ │ │ ├── MixinDataBits.java │ │ │ │ ├── MixinPersistentCollection.java │ │ │ │ ├── MixinMathHelper.java │ │ │ │ ├── MixinBlockChest.java │ │ │ │ ├── MixinEntityTameableAnimal.java │ │ │ │ ├── MixinEntity.java │ │ │ │ ├── MixinEntityHorseAbstract.java │ │ │ │ ├── MixinEntityMushroomCow.java │ │ │ │ └── WeakEnchantmentManager.java │ │ │ ├── core │ │ │ │ ├── MixinChunkSection.java │ │ │ │ ├── MixinTileEntityLootable.java │ │ │ │ ├── MixinBlockMinecartDetector.java │ │ │ │ ├── MixinAsyncCatcher.java │ │ │ │ ├── MixinWorldManager.java │ │ │ │ ├── MixinChunkIOExecutor.java │ │ │ │ ├── MixinWorldBorder.java │ │ │ │ ├── MixinWorldServer.java │ │ │ │ ├── MixinWorld.java │ │ │ │ ├── MixinPlayerConnectionUtils.java │ │ │ │ ├── MixinMCUtil.java │ │ │ │ ├── MixinCraftServer.java │ │ │ │ ├── MixinEntityLiving.java │ │ │ │ ├── MixinCommandKick.java │ │ │ │ ├── MixinCommandBanIp.java │ │ │ │ ├── MixinFileIOThread.java │ │ │ │ ├── MixinCommandBan.java │ │ │ │ └── MixinPlayerChunk.java │ │ │ ├── bootstrap │ │ │ │ ├── DummyEula.java │ │ │ │ ├── MixinRestartCommand.java │ │ │ │ ├── Bootstrap.java │ │ │ │ ├── MetricsBootstrap.java │ │ │ │ ├── MixinMetrics.java │ │ │ │ └── ParallelRegistry.java │ │ │ ├── realtime │ │ │ │ ├── MixinMinecraftServer.java │ │ │ │ ├── MixinWorld.java │ │ │ │ ├── MixinEntityZombieVillager.java │ │ │ │ ├── MixinEntityInsentient.java │ │ │ │ ├── MixinTileEntityBrewingStand.java │ │ │ │ ├── MixinPlayerInteractManager.java │ │ │ │ ├── MixinEntityPlayer.java │ │ │ │ ├── MixinEntityAgeable.java │ │ │ │ ├── MixinEntityItem.java │ │ │ │ ├── MixinEntity.java │ │ │ │ ├── MixinWorldServer.java │ │ │ │ ├── MixinEntityExperienceOrb.java │ │ │ │ ├── MixinPlayerConnection.java │ │ │ │ ├── MixinEntityHuman.java │ │ │ │ └── MixinTileEntityFurnace.java │ │ │ ├── cps │ │ │ │ ├── MixinCraftWorld.java │ │ │ │ └── MixinChunkProviderServer.java │ │ │ └── nsc │ │ │ │ └── OptimisticNetworkManager.java │ │ │ └── core │ │ │ └── AkarinSlackScheduler.java │ ├── net │ │ └── minecraft │ │ │ └── server │ │ │ ├── FileIOThread.java │ │ │ ├── IntCache.java │ │ │ ├── RegistryID.java │ │ │ ├── WorldManager.java │ │ │ ├── ItemEnderEye.java │ │ │ └── MethodProfiler.java │ └── co │ │ └── aikar │ │ └── timings │ │ ├── TimedChunkGenerator.java │ │ ├── WorldTimingsHandler.java │ │ └── MinecraftTimings.java │ └── resources │ ├── mixins.akarin.optimization.pandawire.json │ ├── mixins.akarin.optimization.chunk.json │ ├── mixins.akarin.optimization.realtime.json │ ├── configurations │ └── bukkit.yml │ └── mixins.akarin.core.json ├── Jenkinsfile ├── .gitignore ├── LICENSE.md ├── licenses └── MIT.md ├── .circleci └── config.yml ├── removed └── com │ └── destroystokyo │ └── paper │ └── antixray │ ├── DataBitsReader.java │ ├── PacketPlayOutMapChunkInfo.java │ └── DataBitsWriter.java └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "work/Paper"] 2 | path = work/Paper 3 | url = https://github.com/Akarin-project/Paper.git 4 | -------------------------------------------------------------------------------- /scripts/inst.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ( 4 | set -e 5 | basedir="$pwd" 6 | 7 | (chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || ( 8 | echo "Failed to build Akarin" 9 | exit 1 10 | ) || exit 1 11 | 12 | ) 13 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/mixin/IMixinWorldServer.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.mixin; 2 | 3 | import java.util.Random; 4 | 5 | public interface IMixinWorldServer { 6 | public Object lock(); 7 | public Random rand(); 8 | } -------------------------------------------------------------------------------- /scripts/upstream.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ( 4 | set -e 5 | basedir="$(cd "$1" && pwd -P)" 6 | 7 | (git submodule update --init --remote && git add . && git commit -m 'Upstream Paper') || ( 8 | echo "Failed to upstream" 9 | exit 1 10 | ) || exit 1 11 | 12 | ) -------------------------------------------------------------------------------- /sources/src/main/resources/mixins.akarin.optimization.pandawire.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.10", 4 | "package": "io.akarin.server.mixin", 5 | "target": "@env(DEFAULT)", 6 | "compatibilityLevel": "JAVA_8", 7 | "server": [ 8 | "optimization.PandaRedstoneWire", 9 | ] 10 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/thread/OpenExecutionException.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.utils.thread; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | 5 | public class OpenExecutionException extends ExecutionException { 6 | private static final long serialVersionUID = 7830266012832686185L; 7 | } 8 | -------------------------------------------------------------------------------- /sources/src/main/resources/mixins.akarin.optimization.chunk.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.10", 4 | "package": "io.akarin.server.mixin", 5 | "target": "@env(DEFAULT)", 6 | "compatibilityLevel": "JAVA_8", 7 | "server": [ 8 | "cps.MixinCraftWorld", 9 | "cps.MixinChunkProviderServer", 10 | ] 11 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinTileEntityEnchantTable.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import net.minecraft.server.TileEntityEnchantTable; 6 | 7 | @Mixin(value = TileEntityEnchantTable.class, remap = false) 8 | public abstract class MixinTileEntityEnchantTable { 9 | @Overwrite 10 | public void e() {} // No tickable 11 | } 12 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | stages { 4 | stage('Init Submodules') { 5 | steps { 6 | sh 'git submodule update --init --recursive' 7 | } 8 | } 9 | 10 | stage('Build') { 11 | steps { 12 | sh 'update-alternatives --set java /usr/lib/jvm/zulu8/jre/bin/java' 13 | sh 'chmod +x scripts/inst.sh' 14 | sh './scripts/inst.sh --setup --remote' 15 | } 16 | } 17 | 18 | stage('Archive') { 19 | steps { 20 | archiveArtifacts(artifacts: '*.jar', fingerprint: true) 21 | } 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinChunkSection.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | 7 | import io.akarin.server.core.AkarinGlobalConfig; 8 | import net.minecraft.server.ChunkSection; 9 | 10 | @Mixin(value = ChunkSection.class, remap = false) 11 | public abstract class MixinChunkSection { 12 | @Shadow private int nonEmptyBlockCount; 13 | 14 | @Overwrite // OBFHELPER: isEmpty 15 | public boolean a() { 16 | return AkarinGlobalConfig.sendLightOnlyChunkSection ? false : nonEmptyBlockCount == 0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/LocalAddress.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal; 2 | 3 | import java.net.InetAddress; 4 | 5 | import javax.annotation.concurrent.Immutable; 6 | 7 | @Immutable 8 | public class LocalAddress { 9 | private final InetAddress host; 10 | private final int port; 11 | 12 | public static LocalAddress create(InetAddress localHost, int localPort) { 13 | return new LocalAddress(localHost, localPort); 14 | } 15 | 16 | public LocalAddress(InetAddress localHost, int localPort) { 17 | host = localHost; 18 | port = localPort; 19 | } 20 | 21 | public InetAddress host() { 22 | return host; 23 | } 24 | 25 | public int port() { 26 | return port; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/random/StatefulRandomness.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.utils.random; 2 | 3 | /** 4 | * A simple interface for RandomnessSources that have the additional property of a state that can be re-set. 5 | * Created by Tommy Ettinger on 9/15/2015. 6 | */ 7 | public interface StatefulRandomness extends RandomnessSource { 8 | /** 9 | * Get the current internal state of the StatefulRandomness as a long. 10 | * @return the current internal state of this object. 11 | */ 12 | long getState(); 13 | 14 | /** 15 | * Set the current internal state of this StatefulRandomness with a long. 16 | * 17 | * @param state a 64-bit long. You should avoid passing 0, even though some implementations can handle that. 18 | */ 19 | void setState(long state); 20 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse stuff 2 | .classpath 3 | .project 4 | .settings/ 5 | .factorypath 6 | .externalToolBuilders/ 7 | 8 | # netbeans 9 | nbproject/ 10 | nbactions.xml 11 | 12 | # we use maven! 13 | build.xml 14 | 15 | # maven 16 | target/ 17 | dependency-reduced-pom.xml 18 | 19 | # vim 20 | .*.sw[a-p] 21 | 22 | # various other potential build files 23 | build/ 24 | bin/ 25 | dist/ 26 | manifest.mf 27 | 28 | work/1.* 29 | work/Minecraft 30 | work/BuildData 31 | work/Bukkit 32 | work/CraftBukkit 33 | work/Paperclip 34 | work/Spigot 35 | work/Spigot-Server 36 | work/Spigot-API 37 | work/*.jar 38 | work/test-server 39 | 40 | # Mac filesystem dust 41 | .DS_Store/ 42 | .DS_Store 43 | 44 | # intellij 45 | *.iml 46 | *.ipr 47 | *.iws 48 | .idea/ 49 | out/ 50 | 51 | # Linux temp files 52 | *~ 53 | 54 | # other stuff 55 | run/ 56 | 57 | akarin-*.jar -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinTileEntityLootable.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import net.minecraft.server.EntityHuman; 9 | import net.minecraft.server.TileEntityLootable; 10 | 11 | @Mixin(value = TileEntityLootable.class, remap = false) 12 | public abstract class MixinTileEntityLootable { 13 | @Inject(at = @At("HEAD"), method = "b(Lnet/minecraft/server/EntityHuman;Lorg/spongepowered/asm/mixin/injection/callback/CallbackInfo;)V", cancellable = true) 14 | private void b(EntityHuman entityhuman, CallbackInfo ci) { 15 | if (entityhuman == null) ci.cancel(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinBlockMinecartDetector.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import net.minecraft.server.BlockMinecartDetector; 9 | import net.minecraft.server.BlockPosition; 10 | import net.minecraft.server.IBlockData; 11 | import net.minecraft.server.World; 12 | 13 | @Mixin(value = BlockMinecartDetector.class, remap = false) 14 | public abstract class MixinBlockMinecartDetector { 15 | @Inject(at = @At("HEAD"), method = "e", cancellable = true) 16 | private void e(World world, BlockPosition blockposition, IBlockData iblockdata, CallbackInfo ci) { 17 | if (iblockdata.getBlock() != (Object)this) ci.cancel(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/DummyEula.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import java.io.File; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import net.minecraft.server.EULA; 8 | 9 | @Mixin(value = EULA.class, remap = false) 10 | public abstract class DummyEula { 11 | /** 12 | * Read then check the EULA file formerly 13 | * @param file 14 | * @return true 15 | */ 16 | @Overwrite 17 | public boolean a(File file) { 18 | return true; 19 | } 20 | 21 | /** 22 | * Check whether the EULA has been accepted formerly 23 | * @return true 24 | */ 25 | @Overwrite 26 | public boolean a() { 27 | return true; 28 | } 29 | 30 | /** 31 | * Generate an EULA file formerly 32 | */ 33 | @Overwrite 34 | public void b() {} 35 | } 36 | -------------------------------------------------------------------------------- /sources/src/main/resources/mixins.akarin.optimization.realtime.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.10", 4 | "package": "io.akarin.server.mixin", 5 | "target": "@env(DEFAULT)", 6 | "compatibilityLevel": "JAVA_8", 7 | "server": [ 8 | "realtime.MixinWorld", 9 | "realtime.MixinEntity", 10 | "realtime.MixinEntityItem", 11 | "realtime.MixinWorldServer", 12 | "realtime.MixinEntityHuman", 13 | "realtime.MixinEntityPlayer", 14 | "realtime.MixinEntityAgeable", 15 | "realtime.MixinMinecraftServer", 16 | "realtime.MixinEntityInsentient", 17 | "realtime.MixinPlayerConnection", 18 | "realtime.MixinTileEntityFurnace", 19 | "realtime.MixinEntityExperienceOrb", 20 | "realtime.MixinEntityZombieVillager", 21 | "realtime.MixinPlayerInteractManager", 22 | "realtime.MixinTileEntityBrewingStand", 23 | ] 24 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Akarin inherits its licensing from upstream projects. 2 | 3 | As such, Akarin is licensed under the 4 | [GNU General Public License version 3](licenses/GPL.md); as it inherits it from Paper, 5 | who in turn inherits it from the original Spigot projects. 6 | 7 | Any author who is _not_ listed below should be presumed to have their work released 8 | under the original [GPL](licenses/GPL.md) license. 9 | 10 | In the interest of promoting a better Minecraft platform for everyone, contributors 11 | may choose to release their code under the more permissive [MIT License](licenses/MIT.md). 12 | 13 | The authors listed below have chosen to release their code under the more permissive 14 | [MIT License](licenses/MIT.md). Any contributor who wants their name added below 15 | should submit a Pull Request to this project and add their name. 16 | 17 | ```text 18 | Sotr 19 | MatrixTunnel 20 | ``` 21 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/MixinRestartCommand.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import org.spigotmc.RestartCommand; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import io.akarin.api.internal.Akari; 10 | import io.akarin.server.core.AkarinGlobalConfig; 11 | 12 | @Mixin(value = RestartCommand.class, remap = false) 13 | public abstract class MixinRestartCommand { 14 | @Inject(method = "restart()V", at = @At("HEAD")) 15 | private static void beforeRestart(CallbackInfo ci) { 16 | if (AkarinGlobalConfig.noResponseDoGC) { 17 | Akari.logger.warn("Attempting to garbage collect, may takes a few seconds"); 18 | System.runFinalization(); 19 | System.gc(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinContainerHorse.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | 7 | import net.minecraft.server.Container; 8 | import net.minecraft.server.ContainerHorse; 9 | import net.minecraft.server.Entity; 10 | import net.minecraft.server.EntityHorseAbstract; 11 | import net.minecraft.server.EntityHuman; 12 | import net.minecraft.server.IInventory; 13 | 14 | @Mixin(value = ContainerHorse.class, remap = false) 15 | public abstract class MixinContainerHorse extends Container { 16 | @Shadow private IInventory a; 17 | @Shadow private EntityHorseAbstract f; 18 | 19 | @Overwrite 20 | public boolean canUse(EntityHuman entityhuman) { 21 | return this.a.a(entityhuman) && this.f.isAlive() && this.f.valid && this.f.g((Entity) entityhuman) < 8.0F; // NeonPaper! - Fix MC-161754 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinAsyncCatcher.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spigotmc.AsyncCatcher; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | import io.akarin.api.internal.Akari; 9 | import io.akarin.server.core.AkarinGlobalConfig; 10 | 11 | @Mixin(value = AsyncCatcher.class, remap = false) 12 | public abstract class MixinAsyncCatcher { 13 | @Shadow public static boolean enabled; 14 | 15 | @Overwrite 16 | public static void catchOp(String reason) { 17 | if (enabled) { 18 | if (Akari.isPrimaryThread()) return; 19 | 20 | if (AkarinGlobalConfig.throwOnAsyncCaught) { 21 | throw new IllegalStateException("Asynchronous " + reason + "!"); 22 | } else { 23 | Akari.logger.warn("Asynchronous " + reason + "!"); 24 | Thread.dumpStack(); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinWorldManager.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Final; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | import net.minecraft.server.Entity; 9 | import net.minecraft.server.EntityPlayer; 10 | import net.minecraft.server.WorldManager; 11 | import net.minecraft.server.WorldServer; 12 | 13 | @Mixin(value = WorldManager.class, remap = false) 14 | public abstract class MixinWorldManager { 15 | @Shadow @Final private WorldServer world; 16 | 17 | @Overwrite 18 | public void a(Entity entity) { 19 | this.world.getTracker().entriesLock.writeLock().lock(); // Akarin 20 | this.world.getTracker().track(entity); 21 | this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin 22 | 23 | if (entity instanceof EntityPlayer) { 24 | this.world.worldProvider.a((EntityPlayer) entity); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/WeakBigTree.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import java.util.Random; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 10 | 11 | import net.minecraft.server.BlockPosition; 12 | import net.minecraft.server.World; 13 | import net.minecraft.server.WorldGenBigTree; 14 | 15 | /** 16 | * Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547) 17 | */ 18 | @Mixin(value = WorldGenBigTree.class, remap = false) 19 | public abstract class WeakBigTree { 20 | @Shadow(aliases = "l") private World worldReference; 21 | 22 | @Inject(method = "generate", at = @At("RETURN")) 23 | private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable info) { 24 | world = null; // Akarin - remove references to world objects to avoid memory leaks 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinBlockStationary.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | 6 | import net.minecraft.server.BlockFluids; 7 | import net.minecraft.server.BlockPosition; 8 | import net.minecraft.server.BlockStationary; 9 | import net.minecraft.server.IBlockData; 10 | import net.minecraft.server.Material; 11 | import net.minecraft.server.World; 12 | 13 | @Mixin(value = BlockStationary.class, remap = false) 14 | public abstract class MixinBlockStationary extends BlockFluids { 15 | protected MixinBlockStationary(Material material) { 16 | super(material); 17 | } 18 | @Overwrite 19 | private boolean d(World world, BlockPosition blockposition) { 20 | if (blockposition.getY() >= 0 && blockposition.getY() < 256) { 21 | IBlockData blockData = world.getTypeIfLoaded(blockposition); 22 | 23 | if (blockData != null) { 24 | return blockData.getMaterial().isBurnable(); 25 | } 26 | } 27 | 28 | return false; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /licenses/MIT.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the “Software”), to deal in the Software without 7 | restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 | OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinChunkIOExecutor.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; 4 | import org.bukkit.craftbukkit.util.AsynchronousExecutor; 5 | import org.spongepowered.asm.mixin.Final; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Mutable; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | 11 | import io.akarin.server.core.AkarinGlobalConfig; 12 | import net.minecraft.server.Chunk; 13 | 14 | @Mixin(value = ChunkIOExecutor.class, remap = false) 15 | public abstract class MixinChunkIOExecutor { 16 | @Shadow @Final static int BASE_THREADS; 17 | @Shadow @Mutable @Final static int PLAYERS_PER_THREAD; 18 | @Shadow @Final private static AsynchronousExecutor instance; 19 | 20 | @Overwrite 21 | public static void adjustPoolSize(int players) { 22 | int size = Math.max(BASE_THREADS, (int) Math.ceil(players / (PLAYERS_PER_THREAD = AkarinGlobalConfig.playersPerIOThread))); 23 | instance.setActiveThreads(size); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinWorldBorder.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | 7 | import net.minecraft.server.BlockPosition; 8 | import net.minecraft.server.WorldBorder; 9 | 10 | @Mixin(value = WorldBorder.class, remap = false) 11 | public abstract class MixinWorldBorder { 12 | @Shadow 13 | public abstract boolean isInBounds(BlockPosition blockposition); 14 | 15 | @Overwrite 16 | public boolean isBlockInBounds(int chunkX, int chunkZ) { 17 | BlockPosition.MutableBlockPosition mutPos = new BlockPosition.MutableBlockPosition(); // Dionysus - avoid collisions with other threads 18 | mutPos.setValues(chunkX, 64, chunkZ); 19 | return isInBounds(mutPos); 20 | } 21 | 22 | @Overwrite 23 | public boolean isChunkInBounds(int chunkX, int chunkZ) { 24 | BlockPosition.MutableBlockPosition mutPos = new BlockPosition.MutableBlockPosition(); // Dionysus - avoid collisions with other threads // threads 25 | mutPos.setValues(((chunkX << 4) + 15), 64, (chunkZ << 4) + 15); 26 | return isInBounds(mutPos); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinMinecraftServer.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.realtime; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.injection.At; 5 | import org.spongepowered.asm.mixin.injection.Inject; 6 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 7 | 8 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 9 | import net.minecraft.server.MinecraftServer; 10 | 11 | @Mixin(value = MinecraftServer.class, remap = false, priority = 1001) 12 | public abstract class MixinMinecraftServer implements IMixinRealTimeTicking { 13 | private static long lastTickNanos = System.nanoTime(); 14 | private static long realTimeTicks = 1; 15 | 16 | @Inject(method = "C()V", at = @At("HEAD")) // OBFHELPER: fullTick 17 | public void onTickUpdateRealTimeTicks(CallbackInfo ci) { 18 | long currentNanos = System.nanoTime(); 19 | realTimeTicks = (currentNanos - lastTickNanos) / 50000000; 20 | if (realTimeTicks < 1) { 21 | realTimeTicks = 1; 22 | } 23 | lastTickNanos = currentNanos; 24 | } 25 | 26 | @Override 27 | public long getRealTimeTicks() { 28 | return realTimeTicks; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /sources/src/main/resources/configurations/bukkit.yml: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for Bukkit. 2 | # As you can see, there's actually not that much to configure without any plugins. 3 | # For a reference for any variable inside this file, check out the Bukkit Wiki at 4 | # http://wiki.bukkit.org/Bukkit.yml 5 | # 6 | # If you need help on this file, feel free to join us on irc or leave a message 7 | # on the forums asking for advice. 8 | # 9 | # IRC: #spigot @ irc.spi.gt 10 | # (If this means nothing to you, just go to http://www.spigotmc.org/pages/irc/ ) 11 | # Forums: http://www.spigotmc.org/ 12 | # Bug tracker: http://www.spigotmc.org/go/bugs 13 | 14 | 15 | settings: 16 | allow-end: true 17 | warn-on-overload: true 18 | permissions-file: permissions.yml 19 | update-folder: update 20 | plugin-profiling: false 21 | connection-throttle: 4000 22 | query-plugins: false 23 | deprecated-verbose: default 24 | shutdown-message: "Server closed" 25 | spawn-limits: 26 | monsters: 70 27 | animals: 15 28 | water-animals: 5 29 | ambient: 15 30 | chunk-gc: 31 | period-in-ticks: 600 32 | load-threshold: 0 33 | ticks-per: 34 | animal-spawns: 400 35 | monster-spawns: 1 36 | autosave: 6000 37 | aliases: now-in-commands.yml 38 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinWorldServer.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.Random; 4 | 5 | import org.apache.logging.log4j.LogManager; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import io.akarin.api.internal.mixin.IMixinWorldServer; 8 | import net.minecraft.server.WorldServer; 9 | 10 | @Mixin(value = WorldServer.class, remap = false) 11 | public abstract class MixinWorldServer implements IMixinWorldServer { 12 | private final Object tickLock = new Object(); 13 | 14 | @Override 15 | public Object lock() { 16 | return tickLock; 17 | } 18 | 19 | private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() { 20 | private static final long serialVersionUID = 1L; 21 | private boolean locked = false; 22 | @Override 23 | public synchronized void setSeed(long seed) { 24 | if (locked) { 25 | LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); 26 | } else { 27 | super.setSeed(seed); 28 | locked = true; 29 | } 30 | } 31 | }; 32 | 33 | @Override 34 | public Random rand() { 35 | return sharedRandom; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinWorld.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.List; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | 11 | import net.minecraft.server.AxisAlignedBB; 12 | import net.minecraft.server.Entity; 13 | import net.minecraft.server.World; 14 | 15 | /** 16 | * Fixes MC-103516(https://bugs.mojang.com/browse/MC-103516) 17 | */ 18 | @Mixin(value = World.class, remap = false) 19 | public abstract class MixinWorld { 20 | @Shadow public abstract List getEntities(@Nullable Entity entity, AxisAlignedBB box); 21 | 22 | /** 23 | * Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity 24 | */ 25 | @Overwrite 26 | public boolean a(AxisAlignedBB box, @Nullable Entity target) { // OBFHELPER: checkNoEntityCollision 27 | List list = this.getEntities(null, box); 28 | 29 | for (Entity each : list) { 30 | if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // OBFHELPER: preventEntitySpawning - isRidingSameEntity 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerConnectionUtils.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import co.aikar.timings.MinecraftTimings; 6 | import co.aikar.timings.Timing; 7 | import io.akarin.api.internal.Akari; 8 | import net.minecraft.server.CancelledPacketHandleException; 9 | import net.minecraft.server.IAsyncTaskHandler; 10 | import net.minecraft.server.Packet; 11 | import net.minecraft.server.PacketListener; 12 | import net.minecraft.server.PlayerConnectionUtils; 13 | 14 | @Mixin(value = PlayerConnectionUtils.class, remap = false) 15 | public abstract class MixinPlayerConnectionUtils { 16 | @Overwrite 17 | public static void ensureMainThread(final Packet packet, final T listener, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException { 18 | if (!iasynctaskhandler.isMainThread()) { 19 | Timing timing = MinecraftTimings.getPacketTiming(packet); 20 | // MinecraftServer#postToMainThread inlined thread check, no twice 21 | Akari.callbackQueue.add(() -> { 22 | try (Timing ignored = timing.startTiming()) { 23 | packet.a(listener); 24 | } 25 | }); 26 | throw CancelledPacketHandleException.INSTANCE; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/random/LightRandom.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.utils.random; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * This is a "fake" LightRandom, backed by the LightRNG. 7 | * 8 | * This is useful if you want to quickly replace a Random variable with 9 | * LightRNG without breaking every code. 10 | */ 11 | public class LightRandom extends Random { 12 | public static LightRNG light = new LightRNG(); // LightRNG, static. 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | @Override 17 | public int next(int bits) { 18 | return light.next(bits); 19 | } 20 | 21 | @Override 22 | public void nextBytes(byte[] bytes) { 23 | light.nextBytes(bytes); 24 | } 25 | 26 | @Override 27 | public int nextInt() { 28 | return light.nextInt(); 29 | } 30 | 31 | @Override 32 | public int nextInt(int n) { 33 | return light.nextInt(n); 34 | } 35 | 36 | @Override 37 | public long nextLong() { 38 | return light.nextLong(); 39 | } 40 | 41 | @Override 42 | public boolean nextBoolean() { 43 | return light.nextBoolean(); 44 | } 45 | 46 | @Override 47 | public float nextFloat() { 48 | return light.nextFloat(); 49 | } 50 | 51 | @Override 52 | public double nextDouble() { 53 | return light.nextDouble(); 54 | } 55 | 56 | // Akarin start 57 | @Override 58 | public void setSeed(long seed) { 59 | light.setSeed(seed); 60 | } 61 | // Akarin end 62 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinMCUtil.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | import java.util.function.Supplier; 5 | 6 | import org.bukkit.craftbukkit.util.Waitable; 7 | import org.spigotmc.AsyncCatcher; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Overwrite; 10 | 11 | import io.akarin.api.internal.Akari; 12 | import net.minecraft.server.MCUtil; 13 | import net.minecraft.server.MinecraftServer; 14 | 15 | @Mixin(value = MCUtil.class, remap = false) 16 | public abstract class MixinMCUtil { 17 | @Overwrite 18 | public static T ensureMain(String reason, Supplier run) { 19 | if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) { 20 | new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); 21 | Waitable wait = new Waitable() { 22 | @Override 23 | protected T evaluate() { 24 | return run.get(); 25 | } 26 | }; 27 | MinecraftServer.getServer().processQueue.add(wait); 28 | try { 29 | return wait.get(); 30 | } catch (InterruptedException | ExecutionException e) { 31 | e.printStackTrace(); 32 | } 33 | return null; 34 | } 35 | 36 | return run.get(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinDataBits.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Final; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | import net.minecraft.server.DataBits; 9 | 10 | @Mixin(value = DataBits.class, remap = false) 11 | public abstract class MixinDataBits { 12 | @Shadow @Final private long[] a; 13 | @Shadow @Final private int b; 14 | @Shadow @Final private long c; 15 | 16 | @Overwrite 17 | public void a(int i, int j) { 18 | int k = i * this.b; 19 | int l = k >> 6; 20 | int i1 = (i + 1) * this.b - 1 >> 6; 21 | int j1 = k ^ l << 6; 22 | 23 | this.a[l] = this.a[l] & ~(this.c << j1) | ((long) j & this.c) << j1; 24 | if (l != i1) { 25 | int k1 = 64 - j1; 26 | int l1 = this.b - k1; 27 | 28 | this.a[i1] = this.a[i1] >>> l1 << l1 | ((long) j & this.c) >> k1; 29 | } 30 | 31 | } 32 | @Overwrite 33 | public int a(int i) { 34 | int j = i * this.b; 35 | int k = j >> 6; 36 | int l = (i + 1) * this.b - 1 >> 6; 37 | int i1 = j ^ k << 6; 38 | 39 | if (k == l) { 40 | return (int) (this.a[k] >>> i1 & this.c); 41 | } else { 42 | int j1 = 64 - i1; 43 | 44 | return (int) ((this.a[k] >>> i1 | this.a[l] << j1) & this.c); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/mixin/IMixinRealTimeTicking.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.api.internal.mixin; 26 | 27 | public interface IMixinRealTimeTicking { 28 | 29 | long getRealTimeTicks(); 30 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/cps/MixinCraftWorld.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.cps; 2 | 3 | import java.util.Set; 4 | 5 | import org.bukkit.craftbukkit.CraftWorld; 6 | import org.spongepowered.asm.lib.Opcodes; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Redirect; 12 | 13 | import net.minecraft.server.Chunk; 14 | import net.minecraft.server.WorldServer; 15 | 16 | @Mixin(value = CraftWorld.class, remap = false) 17 | public abstract class MixinCraftWorld { 18 | @Shadow @Final private WorldServer world; 19 | 20 | @Redirect(method = "processChunkGC()V", at = @At( 21 | value = "INVOKE", 22 | target = "java/util/Set.contains(Ljava/lang/Object;)Z", 23 | opcode = Opcodes.INVOKEINTERFACE 24 | )) 25 | public boolean checkUnloading(Set set, Object chunkHash) { 26 | return false; 27 | } 28 | 29 | @Redirect(method = "regenerateChunk", at = @At( 30 | value = "INVOKE", 31 | target = "java/util/Set.remove(Ljava/lang/Object;)Z", 32 | opcode = Opcodes.INVOKEINTERFACE 33 | )) 34 | public boolean regenChunk(Set set, Object chunkHash) { 35 | Chunk chunk = world.getChunkProviderServer().chunks.get(chunkHash); 36 | if (chunk != null) chunk.setShouldUnload(false); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/random/RandomnessSource.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.utils.random; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * This interface defines the interactions required of a random number 7 | * generator. It is a replacement for Java's built-in Random because for 8 | * improved performance. 9 | * 10 | * @author Eben Howard - http://squidpony.com - howard@squidpony.com 11 | */ 12 | public interface RandomnessSource extends Serializable { 13 | 14 | /** 15 | * Using this method, any algorithm that might use the built-in Java Random 16 | * can interface with this randomness source. 17 | * 18 | * @param bits the number of bits to be returned 19 | * @return the integer containing the appropriate number of bits 20 | */ 21 | int next(int bits); 22 | 23 | /** 24 | * 25 | * Using this method, any algorithm that needs to efficiently generate more 26 | * than 32 bits of random data can interface with this randomness source. 27 | * 28 | * Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive). 29 | * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive) 30 | */ 31 | long nextLong(); 32 | 33 | /** 34 | * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the 35 | * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to 36 | * copy the state so it isn't shared, usually, and produce a new value with the same exact state. 37 | * @return a copy of this RandomnessSource 38 | */ 39 | RandomnessSource copy(); 40 | } -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ( 4 | set -e 5 | basedir="$(cd "$1" && pwd -P)" 6 | workdir="$basedir/work" 7 | paperbasedir="$basedir/work/Paper" 8 | paperworkdir="$basedir/work/Paper/work" 9 | 10 | if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then 11 | echo "[Akarin] Setup Paper.." 12 | ( 13 | if [ "$2" == "--remote" ] || [ "$3" == "--remote" ] || [ "$4" == "--remote" ]; then 14 | cd "$paperworkdir" 15 | if [ -d "Minecraft" ]; then 16 | rm Minecraft/ -r 17 | fi 18 | git clone https://github.com/LegacyGamerHD/Minecraft.git 19 | fi 20 | 21 | cd "$paperbasedir" 22 | ./paper jar 23 | ) 24 | fi 25 | 26 | echo "[Akarin] Ready to build" 27 | ( 28 | echo "[Akarin] Touch sources.." 29 | 30 | cd "$paperbasedir" 31 | if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then 32 | echo "[Akarin] Test has been skipped" 33 | \cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/" 34 | \cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/" 35 | mvn clean install -DskipTests 36 | else 37 | rm -rf Paper-API/src 38 | rm -rf Paper-Server/src 39 | ./paper patch 40 | \cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/" 41 | \cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/" 42 | mvn clean install 43 | fi 44 | 45 | minecraftversion=$(cat "$paperworkdir/BuildData/info.json" | grep minecraftVersion | cut -d '"' -f 4) 46 | rawjar="$paperbasedir/Paper-Server/target/akarin-$minecraftversion.jar" 47 | \cp -rf "$rawjar" "$basedir/akarin-$minecraftversion.jar" 48 | 49 | echo "" 50 | echo "[Akarin] Build successful" 51 | echo "[Akarin] Migrated final jar to $basedir/akarin-$minecraftversion.jar" 52 | ) 53 | 54 | ) 55 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinCraftServer.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.bukkit.craftbukkit.CraftServer; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Mutable; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | import io.akarin.api.internal.Akari; 11 | import io.akarin.server.core.AkarinGlobalConfig; 12 | import net.minecraft.server.MinecraftServer; 13 | 14 | @Mixin(value = CraftServer.class, remap = false) 15 | public abstract class MixinCraftServer { 16 | @Shadow @Final @Mutable private String serverName; 17 | @Shadow @Final @Mutable private String serverVersion; 18 | @Shadow @Final protected MinecraftServer console; 19 | private boolean needApplyServerName = true; 20 | private boolean needApplyServerVersion = true; 21 | 22 | @Overwrite 23 | public String getName() { 24 | // We cannot apply the name modification in method, 25 | // cause the initializer will be added to the tail 26 | if (needApplyServerName) { 27 | serverName = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? "Akarin" : AkarinGlobalConfig.serverBrandName; 28 | needApplyServerName = false; 29 | } 30 | return serverName; 31 | } 32 | 33 | @Overwrite 34 | public String getVersion() { 35 | if (needApplyServerVersion) { 36 | serverVersion = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? serverVersion : serverVersion.replace("Akarin", AkarinGlobalConfig.serverBrandName); 37 | needApplyServerVersion = false; 38 | } 39 | return serverVersion + " (MC: " + console.getVersion() + ")"; 40 | } 41 | 42 | @Overwrite 43 | public boolean isPrimaryThread() { 44 | return Akari.isPrimaryThread(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinPersistentCollection.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import org.spongepowered.asm.mixin.Final; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | import com.destroystokyo.paper.exception.ServerInternalException; 11 | 12 | import net.minecraft.server.IDataManager; 13 | import net.minecraft.server.MCUtil; 14 | import net.minecraft.server.NBTCompressedStreamTools; 15 | import net.minecraft.server.NBTTagCompound; 16 | import net.minecraft.server.PersistentBase; 17 | import net.minecraft.server.PersistentCollection; 18 | 19 | @Mixin(value = PersistentCollection.class, remap = false) 20 | public abstract class MixinPersistentCollection { 21 | @Shadow(aliases = "b") @Final private IDataManager dataManager; 22 | 23 | @Overwrite 24 | private void a(PersistentBase persistentbase) { 25 | if (this.dataManager == null) return; 26 | 27 | File file = this.dataManager.getDataFile(persistentbase.id); 28 | if (file == null) return; 29 | 30 | NBTTagCompound nbttagcompound = new NBTTagCompound(); 31 | nbttagcompound.set("data", persistentbase.b(new NBTTagCompound())); 32 | 33 | // Akarin start 34 | MCUtil.scheduleAsyncTask(() -> { 35 | try { 36 | FileOutputStream fileoutputstream = new FileOutputStream(file); 37 | 38 | NBTCompressedStreamTools.a(nbttagcompound, fileoutputstream); 39 | fileoutputstream.close(); 40 | } catch (Exception exception) { 41 | exception.printStackTrace(); 42 | ServerInternalException.reportInternalException(exception); // Paper 43 | } 44 | }); 45 | // Akarin end 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinMathHelper.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | 6 | import net.minecraft.server.MathHelper; 7 | 8 | @Mixin(value = MathHelper.class, remap = false) 9 | public abstract class MixinMathHelper { 10 | private static final int[] SINE_TABLE_INT = new int[16384 + 1]; 11 | private static final float SINE_TABLE_MIDPOINT; 12 | @Overwrite 13 | public static float sin(float f) { 14 | return lookup((int) (f * 10430.38) & 0xFFFF); 15 | } 16 | @Overwrite 17 | public static float cos(float f) { 18 | return lookup((int) (f * 10430.38 + 16384.0) & 0xFFFF); 19 | } 20 | private static float lookup(int index) { 21 | if (index == 32768) { 22 | return SINE_TABLE_MIDPOINT; 23 | } 24 | int neg = (index & 0x8000) << 16; 25 | int mask = (index << 17) >> 31; 26 | int pos = (0x8001 & mask) + (index ^ mask); 27 | pos &= 0x7fff; 28 | return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg); 29 | } 30 | static { 31 | int i; 32 | 33 | final float[] SINE_TABLE = new float[65536]; 34 | for (i = 0; i < 65536; ++i) { 35 | SINE_TABLE[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D); 36 | } 37 | for (i = 0; i < SINE_TABLE_INT.length; i++) { 38 | SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]); 39 | } 40 | 41 | SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2]; 42 | for (i = 0; i < SINE_TABLE.length; i++) { 43 | float expected = SINE_TABLE[i]; 44 | float value = lookup(i); 45 | 46 | if (expected != value) { 47 | throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value)); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinBlockChest.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import java.util.Iterator; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | 9 | import net.minecraft.server.Block; 10 | import net.minecraft.server.BlockChest; 11 | import net.minecraft.server.BlockPosition; 12 | import net.minecraft.server.EnumDirection; 13 | import net.minecraft.server.IBlockData; 14 | import net.minecraft.server.Material; 15 | import net.minecraft.server.World; 16 | 17 | @Mixin(value = BlockChest.class, remap = false) 18 | public abstract class MixinBlockChest extends Block { 19 | protected MixinBlockChest(Material material) { 20 | super(material); 21 | } 22 | @Shadow public abstract IBlockData e(World world, BlockPosition blockposition, IBlockData iblockdata); 23 | @Overwrite 24 | public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) { 25 | //((BlockChest)(Object)this).e(world, blockposition, iblockdata); 26 | e(world, blockposition, iblockdata); 27 | Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); 28 | 29 | while (iterator.hasNext()) { 30 | EnumDirection enumdirection = iterator.next(); 31 | BlockPosition blockposition1 = blockposition.shift(enumdirection); 32 | // NeonPaper start - Dont load chunks for chests 33 | final IBlockData iblockdata1 = world.isLoaded(blockposition1) ? world.getType(blockposition1) : null; 34 | if (iblockdata1 == null) { 35 | continue; 36 | } 37 | // NeonPaper end 38 | 39 | if (iblockdata1.getBlock() == this) { 40 | //((BlockChest)(Object)this).e(world, blockposition1, iblockdata1); 41 | e(world, blockposition1, iblockdata1); 42 | } 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | working_directory: ~/Akarin-project/Akarin 5 | parallelism: 1 6 | shell: /bin/bash --login 7 | environment: 8 | CIRCLE_ARTIFACTS: /tmp/circleci-artifacts 9 | CIRCLE_TEST_REPORTS: /tmp/circleci-test-results 10 | docker: 11 | - image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37 12 | command: /sbin/init 13 | steps: 14 | # Machine Setup 15 | - checkout 16 | # Prepare for artifact 17 | - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS 18 | - run: 19 | working_directory: ~/Akarin-project/Akarin 20 | command: sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java; sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac; echo -e "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> $BASH_ENV 21 | # Dependencies 22 | # Restore the dependency cache 23 | - restore_cache: 24 | keys: 25 | # This branch if available 26 | - v1-dep-{{ .Branch }}- 27 | # Default branch if not 28 | - v1-dep-ver/1.12.2- 29 | # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly 30 | - v1-dep- 31 | - run: git config --global user.email "circle@circleci.com" 32 | - run: git config --global user.name "CircleCI" 33 | - run: chmod +x scripts/inst.sh 34 | - run: ./scripts/inst.sh --setup --remote 35 | # Save dependency cache 36 | - save_cache: 37 | key: v1-dep-{{ .Branch }}-{{ epoch }} 38 | paths: 39 | - ~/.m2 40 | # Test 41 | - run: yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS 42 | # Teardown 43 | # Save test results 44 | - store_test_results: 45 | path: /tmp/circleci-test-results 46 | # Save artifacts 47 | - store_artifacts: 48 | path: /tmp/circleci-artifacts 49 | - store_artifacts: 50 | path: /tmp/circleci-test-results 51 | -------------------------------------------------------------------------------- /sources/src/main/resources/mixins.akarin.core.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.7.10", 4 | "package": "io.akarin.server.mixin", 5 | "target": "@env(DEFAULT)", 6 | "compatibilityLevel": "JAVA_8", 7 | "server": [ 8 | "bootstrap.Watchcat", 9 | "bootstrap.Bootstrap", 10 | "bootstrap.DummyEula", 11 | "bootstrap.MixinMetrics", 12 | "bootstrap.ParallelRegistry", 13 | "bootstrap.MetricsBootstrap", 14 | "bootstrap.MixinRestartCommand", 15 | 16 | "core.MixinWorld", 17 | "core.MixinMCUtil", 18 | "core.MixinCommandBan", 19 | "core.MixinCommandKick", 20 | "core.MixinCraftServer", 21 | "core.MixinWorldServer", 22 | "core.MixinFileIOThread", 23 | "core.MixinWorldManager", 24 | "core.MixinCommandBanIp", 25 | "core.MixinChunkSection", 26 | "core.MixinAsyncCatcher", 27 | "core.MixinVersionCommand", 28 | "core.MixinMinecraftServer", 29 | "core.MixinChunkIOExecutor", 30 | "core.MixinPlayerConnectionUtils", 31 | "core.MixinPlayerChunk", 32 | "core.MixinWorldBorder", 33 | "core.MixinEntityLiving", 34 | "core.MixinTileEntityLootable", 35 | "core.MixinBlockMinecartDetector", 36 | 37 | "nsc.OptimisticNetworkManager", 38 | "nsc.NonblockingServerConnection", 39 | 40 | "optimization.MixinEntity", 41 | "optimization.WeakBigTree", 42 | "optimization.WeakEnchantmentManager", 43 | "optimization.MixinEntityHorseAbstract", 44 | "optimization.MixinEntityTameableAnimal", 45 | "optimization.MixinPersistentCollection", 46 | "optimization.MixinTileEntityEnchantTable", 47 | "optimization.MixinBlockChest", 48 | "optimization.MixinEntityMushroomCow", 49 | "optimization.MixinContainerHorse", 50 | "optimization.MixinExplosion", 51 | "optimization.MixinMathHelper", 52 | "optimization.MixinBlockStationary", 53 | "optimization.MixinDataBits" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinEntityLiving.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import javax.annotation.Nullable; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | 9 | import net.minecraft.server.EntityLiving; 10 | import net.minecraft.server.EntityPlayer; 11 | import net.minecraft.server.MathHelper; 12 | import net.minecraft.server.MobEffect; 13 | import net.minecraft.server.MobEffectList; 14 | import net.minecraft.server.MobEffects; 15 | 16 | @Mixin(value = EntityLiving.class, remap = false) 17 | public abstract class MixinEntityLiving { 18 | @Shadow public abstract boolean hasEffect(MobEffectList mobeffectlist); 19 | @Shadow @Nullable public abstract MobEffect getEffect(MobEffectList mobeffectlist); 20 | @Shadow protected abstract float ct(); 21 | protected long lastJumpTime = 0L; // Dionysus - Backport ArrowDMG fix 22 | 23 | @Overwrite 24 | protected void cu() { 25 | // Dionysus start - Backport ArrowDMG fix 26 | long time = System.nanoTime(); 27 | boolean canCrit = true; 28 | if ((Object)this instanceof EntityPlayer) { 29 | canCrit = false; 30 | if (time - lastJumpTime > (long)(0.250e9)) { 31 | lastJumpTime = time; 32 | canCrit = true; 33 | } 34 | } 35 | // Dionysus end - Backport ArrowDMG fix 36 | ((EntityLiving)(Object)this).motY = (double) ct(); 37 | if (hasEffect(MobEffects.JUMP)) { 38 | ((EntityLiving)(Object)this).motY += (double) ((float) (getEffect(MobEffects.JUMP).getAmplifier() + 1) * 0.1F); 39 | } 40 | 41 | if (canCrit&&((EntityLiving)(Object)this).isSprinting()) { 42 | float f = ((EntityLiving)(Object)this).yaw * 0.017453292F; 43 | 44 | ((EntityLiving)(Object)this).motX -= (double) (MathHelper.sin(f) * 0.2F); 45 | ((EntityLiving)(Object)this).motZ += (double) (MathHelper.cos(f) * 0.2F); 46 | } 47 | 48 | ((EntityLiving)(Object)this).impulse = true; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinWorld.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import javax.annotation.Nullable; 28 | 29 | import org.spongepowered.asm.mixin.Mixin; 30 | import org.spongepowered.asm.mixin.Shadow; 31 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 32 | import net.minecraft.server.MinecraftServer; 33 | import net.minecraft.server.World; 34 | 35 | @Mixin(value = World.class, remap = false, priority = 1001) 36 | public abstract class MixinWorld implements IMixinRealTimeTicking { 37 | @Shadow @Nullable public abstract MinecraftServer getMinecraftServer(); 38 | 39 | @Override 40 | public long getRealTimeTicks() { 41 | if (this.getMinecraftServer() != null) { 42 | return ((IMixinRealTimeTicking) this.getMinecraftServer()).getRealTimeTicks(); 43 | } 44 | return 1; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinCommandKick.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import io.akarin.server.core.AkarinGlobalConfig; 6 | import net.minecraft.server.CommandAbstract; 7 | import net.minecraft.server.CommandException; 8 | import net.minecraft.server.CommandKick; 9 | import net.minecraft.server.EntityPlayer; 10 | import net.minecraft.server.ExceptionPlayerNotFound; 11 | import net.minecraft.server.ExceptionUsage; 12 | import net.minecraft.server.ICommand; 13 | import net.minecraft.server.ICommandListener; 14 | import net.minecraft.server.MinecraftServer; 15 | 16 | @Mixin(value = CommandKick.class, remap = false) 17 | public abstract class MixinCommandKick { 18 | @Overwrite 19 | public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException { 20 | if (args.length > 0 && args[0].length() > 1) { 21 | EntityPlayer target = server.getPlayerList().getPlayer(args[0]); 22 | 23 | if (target == null) { 24 | throw new ExceptionPlayerNotFound("commands.generic.player.notFound", args[0]); 25 | } else { 26 | if (args.length >= 2) { 27 | // Akarin start - use string 28 | String message = ""; 29 | for (int i = 2; i < args.length; i++) { 30 | message = message + args[i]; 31 | } 32 | target.playerConnection.disconnect(message); 33 | CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // OBFHELPER: notifyCommandListener 34 | // Akarin end 35 | } else { 36 | target.playerConnection.disconnect(AkarinGlobalConfig.messageKick); // Akarin 37 | CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // OBFHELPER: notifyCommandListener 38 | } 39 | } 40 | } else { 41 | throw new ExceptionUsage("commands.kick.usage"); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /removed/com/destroystokyo/paper/antixray/DataBitsReader.java: -------------------------------------------------------------------------------- 1 | package com.destroystokyo.paper.antixray; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | public class DataBitsReader { 6 | 7 | private ByteBuf dataBits; // Akarin 8 | private int bitsPerValue; 9 | private int mask; 10 | private int longInDataBitsIndex; 11 | private int bitInLongIndex; 12 | private long current; 13 | 14 | public void setDataBits(ByteBuf dataBits) { // Akarin 15 | this.dataBits = dataBits; 16 | } 17 | 18 | public void setBitsPerValue(int bitsPerValue) { 19 | this.bitsPerValue = bitsPerValue; 20 | mask = (1 << bitsPerValue) - 1; 21 | } 22 | 23 | public void setIndex(int index) { 24 | this.longInDataBitsIndex = index; 25 | bitInLongIndex = 0; 26 | init(); 27 | } 28 | 29 | private void init() { 30 | if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin 31 | // Akarin start 32 | dataBits.getLong(longInDataBitsIndex); 33 | /* 34 | current = ((((long) dataBits[longInDataBitsIndex]) << 56) 35 | | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) 36 | | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) 37 | | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) 38 | | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) 39 | | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) 40 | | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) 41 | | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); 42 | */ // Akarin end 43 | } 44 | } 45 | 46 | public int read() { 47 | int value = (int) (current >>> bitInLongIndex) & mask; 48 | bitInLongIndex += bitsPerValue; 49 | 50 | if (bitInLongIndex > 63) { 51 | bitInLongIndex -= 64; 52 | longInDataBitsIndex += 8; 53 | init(); 54 | 55 | if (bitInLongIndex > 0) { 56 | value |= current << bitsPerValue - bitInLongIndex & mask; 57 | } 58 | } 59 | 60 | return value; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/Bootstrap.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import java.io.File; 4 | import java.io.PrintStream; 5 | 6 | import org.bukkit.craftbukkit.Main; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | import io.akarin.api.internal.Akari; 14 | import io.akarin.server.core.AkarinGlobalConfig; 15 | 16 | @Mixin(value = Main.class, remap = false) 17 | public abstract class Bootstrap { 18 | @Inject(method = "main([Ljava/lang/String;)V", at = @At("HEAD")) 19 | private static void premain(CallbackInfo info) { 20 | AkarinGlobalConfig.init(new File("akarin.yml")); 21 | } 22 | 23 | /* 24 | * Redirect notify message 25 | */ 26 | @Redirect(method = "main", at = @At( 27 | value = "INVOKE_STRING", 28 | target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V", 29 | args = "ldc=*** Warning, you've not updated in a while! ***" 30 | )) 31 | private static void notifyUpdate(PrintStream stream, String text) { 32 | Akari.logger.warn("You've not updated in a while, the current version may outdated"); 33 | } 34 | 35 | @Redirect(method = "main", at = @At( 36 | value = "INVOKE_STRING", 37 | target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V", 38 | args = "ldc=*** Please download a new build as per instructions from https://paperci.emc.gs/ ***" 39 | )) 40 | private static void notifyWebsite(PrintStream stream, String text) { 41 | Akari.logger.warn("Visit our website for latest information https://akarin.io/"); 42 | } 43 | 44 | @Redirect(method = "main", at = @At( 45 | value = "INVOKE_STRING", 46 | target = "Ljava/io/PrintStream;println(Ljava/lang/String;)V", 47 | args = "ldc=Loading libraries, please wait..." 48 | )) 49 | private static void notifyLoading(PrintStream stream, String text) { 50 | Akari.logger.info("Loading libraries as parallel capable.."); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinCommandBanIp.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.Date; 4 | import java.util.List; 5 | import javax.annotation.Nullable; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | 10 | import io.akarin.api.internal.Akari; 11 | import io.akarin.server.core.AkarinGlobalConfig; 12 | import net.minecraft.server.CommandAbstract; 13 | import net.minecraft.server.CommandBanIp; 14 | import net.minecraft.server.EntityPlayer; 15 | import net.minecraft.server.ICommand; 16 | import net.minecraft.server.ICommandListener; 17 | import net.minecraft.server.IpBanEntry; 18 | import net.minecraft.server.MinecraftServer; 19 | 20 | @Mixin(value = CommandBanIp.class, remap = false) 21 | public abstract class MixinCommandBanIp { 22 | @Overwrite // OBFHELPER: banIp 23 | protected void a(MinecraftServer server, ICommandListener sender, String args, @Nullable String banReason) { 24 | // Akarin start - modify message 25 | boolean hasReason = true; 26 | if (banReason == null) { 27 | banReason = Akari.EMPTY_STRING; 28 | hasReason = false; 29 | } 30 | // Akarin end 31 | IpBanEntry ipbanentry = new IpBanEntry(args, (Date) null, sender.getName(), (Date) null, banReason); 32 | 33 | server.getPlayerList().getIPBans().add(ipbanentry); 34 | List withIpPlayers = server.getPlayerList().b(args); // OBFHELPER: getPlayersMatchingAddress 35 | String[] banPlayerNames = new String[withIpPlayers.size()]; 36 | 37 | for (int i = 0; i < banPlayerNames.length; i++) { 38 | EntityPlayer each = withIpPlayers.get(i); 39 | banPlayerNames[i] = each.getName(); 40 | each.playerConnection.disconnect(hasReason ? banReason : AkarinGlobalConfig.messageBanIp); // Akarin 41 | } 42 | 43 | if (withIpPlayers.isEmpty()) { 44 | CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // OBFHELPER: notifyCommandListener 45 | } else { 46 | CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // OBFHELPER: notifyCommandListener - joinNiceString 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinFileIOThread.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.concurrent.Executor; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | 11 | import com.destroystokyo.paper.PaperConfig; 12 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 13 | 14 | import io.akarin.server.core.AkarinGlobalConfig; 15 | import net.minecraft.server.FileIOThread; 16 | import net.minecraft.server.IAsyncChunkSaver; 17 | 18 | @Mixin(value = FileIOThread.class, remap = false) 19 | public abstract class MixinFileIOThread { 20 | private final Executor executor = Executors.newFixedThreadPool(AkarinGlobalConfig.fileIOThreads, new ThreadFactoryBuilder().setNameFormat("Akarin File IO Thread - %1$d").setPriority(1).build()); 21 | private final AtomicInteger queuedChunkCounter = new AtomicInteger(0); 22 | 23 | @Shadow(aliases = "e") private volatile boolean isAwaitFinish; 24 | 25 | @Overwrite // OBFHELPER: saveChunk 26 | public void a(IAsyncChunkSaver iasyncchunksaver) { 27 | queuedChunkCounter.incrementAndGet(); 28 | executor.execute(() -> writeChunk(iasyncchunksaver)); 29 | } 30 | 31 | /** 32 | * Process a chunk, re-add to the queue if unsuccessful 33 | */ 34 | private void writeChunk(IAsyncChunkSaver iasyncchunksaver) { 35 | if (!iasyncchunksaver.a()) { // PAIL: WriteNextIO() -> Returns if the write was unsuccessful 36 | queuedChunkCounter.decrementAndGet(); 37 | 38 | if (PaperConfig.enableFileIOThreadSleep) { 39 | try { 40 | Thread.sleep(isAwaitFinish ? 0L : 2L); 41 | } catch (InterruptedException ex) { 42 | ex.printStackTrace(); 43 | } 44 | } 45 | } else { 46 | writeChunk(iasyncchunksaver); 47 | } 48 | } 49 | 50 | @Overwrite // OBFHELPER: waitForFinish 51 | public void b() throws InterruptedException { 52 | isAwaitFinish = true; 53 | while (queuedChunkCounter.get() != 0) Thread.sleep(9L); 54 | isAwaitFinish = false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/MetricsBootstrap.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.UUID; 6 | 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.configuration.file.YamlConfiguration; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Overwrite; 11 | 12 | import com.destroystokyo.paper.Metrics; 13 | 14 | import net.minecraft.server.MinecraftServer; 15 | 16 | @Mixin(targets = "com.destroystokyo.paper.Metrics$PaperMetrics", remap = false) 17 | public abstract class MetricsBootstrap { 18 | @Overwrite 19 | static void startMetrics() { 20 | // Get the config file 21 | File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "config.yml"); 22 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 23 | 24 | // Check if the config file exists 25 | if (!config.isSet("serverUuid")) { 26 | // Add default values 27 | config.addDefault("enabled", true); 28 | // Every server gets it's unique random id. 29 | config.addDefault("serverUuid", UUID.randomUUID().toString()); 30 | // Should failed request be logged? 31 | config.addDefault("logFailedRequests", false); 32 | 33 | // Inform the server owners about bStats 34 | config.options().header( 35 | "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + 36 | "To honor their work, you should not disable it.\n" + 37 | "This has nearly no effect on the server performance!\n" + 38 | "Check out https://bStats.org/ to learn more :)" 39 | ).copyDefaults(true); 40 | try { 41 | config.save(configFile); 42 | } catch (IOException ignored) { 43 | ; 44 | } 45 | } 46 | // Load the data 47 | String serverUUID = config.getString("serverUuid"); 48 | boolean logFailedRequests = config.getBoolean("logFailedRequests", false); 49 | // Only start Metrics, if it's enabled in the config 50 | if (config.getBoolean("enabled", true)) { 51 | new Metrics("Torch", serverUUID, logFailedRequests, Bukkit.getLogger()); // Paper -> Torch 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/FileIOThread.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Akarin Changes Note 7 | * 1) Multi-threaded chunk saving (performance) 8 | */ 9 | public class FileIOThread implements Runnable { 10 | 11 | private static final FileIOThread a = new FileIOThread(); 12 | private final List b = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this 13 | private volatile long c; 14 | private volatile long d; 15 | private volatile boolean e; 16 | 17 | private FileIOThread() { 18 | // Thread thread = new Thread(this, "File IO Thread"); // Akarin 19 | 20 | // thread.setPriority(1); // Akarin 21 | // thread.start(); // Akarin 22 | } 23 | 24 | public static FileIOThread a() { 25 | return FileIOThread.a; 26 | } 27 | 28 | public void run() { 29 | while (true) { 30 | this.c(); 31 | } 32 | } 33 | 34 | private void c() { 35 | for (int i = 0; i < this.b.size(); ++i) { 36 | IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i); 37 | boolean flag = iasyncchunksaver.a(); 38 | 39 | if (!flag) { 40 | this.b.remove(i--); 41 | ++this.d; 42 | } 43 | 44 | // Paper start - Add toggle 45 | if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { 46 | try { 47 | Thread.sleep(this.e ? 0L : 2L); 48 | } catch (InterruptedException interruptedexception) { 49 | interruptedexception.printStackTrace(); 50 | } 51 | } 52 | // Paper end 53 | } 54 | 55 | if (this.b.isEmpty()) { 56 | try { 57 | Thread.sleep(25L); 58 | } catch (InterruptedException interruptedexception1) { 59 | interruptedexception1.printStackTrace(); 60 | } 61 | } 62 | 63 | } 64 | 65 | public void a(IAsyncChunkSaver iasyncchunksaver) { 66 | if (!this.b.contains(iasyncchunksaver)) { 67 | ++this.c; 68 | this.b.add(iasyncchunksaver); 69 | } 70 | } 71 | 72 | public void b() throws InterruptedException { 73 | this.e = true; 74 | 75 | while (this.c != this.d) { 76 | Thread.sleep(10L); 77 | } 78 | 79 | this.e = false; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityZombieVillager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.mixin.Mixin; 28 | import org.spongepowered.asm.mixin.Shadow; 29 | import org.spongepowered.asm.mixin.injection.At; 30 | import org.spongepowered.asm.mixin.injection.Redirect; 31 | 32 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 33 | import net.minecraft.server.EntityZombieVillager; 34 | 35 | @Mixin(value = EntityZombieVillager.class, remap = false) 36 | public abstract class MixinEntityZombieVillager { 37 | private static final String ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD = "Lnet/minecraft/entity/monster/EntityZombieVillager;du()I"; // INVOKE: getConversionProgress 38 | 39 | @Shadow(aliases = "du") protected abstract int getConversionProgress(); 40 | 41 | // OBFHELPER: onUpdate 42 | @Redirect(method = "B_()V", at = @At(value = "INVOKE", target = ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD, ordinal = 0)) 43 | public int fixupConversionTimeBoost(EntityZombieVillager self) { 44 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 45 | return this.getConversionProgress() * ticks; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityInsentient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.injection.At; 30 | import org.spongepowered.asm.mixin.injection.Redirect; 31 | 32 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 33 | import net.minecraft.server.EntityInsentient; 34 | import net.minecraft.server.EntityLiving; 35 | import net.minecraft.server.World; 36 | 37 | @Mixin(value = EntityInsentient.class, remap = false) 38 | public abstract class MixinEntityInsentient extends EntityLiving { 39 | private static final String ENTITY_LIVING_AGE_FIELD = "Lnet/minecraft/entity/EntityInsentient;ticksFarFromPlayer:I"; 40 | 41 | public MixinEntityInsentient(World world) { 42 | super(world); 43 | } 44 | 45 | @Redirect(method = "doTick()V", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 46 | public void fixupEntityDespawnAge(EntityInsentient self, int modifier) { 47 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 48 | this.ticksFarFromPlayer += ticks; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinTileEntityBrewingStand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.TileEntity; 35 | import net.minecraft.server.TileEntityBrewingStand; 36 | 37 | @Mixin(value = TileEntityBrewingStand.class, remap = false) 38 | public abstract class MixinTileEntityBrewingStand extends TileEntity { 39 | private static final String BREWING_STAND_BREW_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityBrewingStand;brewTime:I"; 40 | @Shadow private int brewTime; 41 | 42 | // OBFHELPER: update 43 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = BREWING_STAND_BREW_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 44 | public void fixupBrewTime(TileEntityBrewingStand self, int modifier) { 45 | int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks(); 46 | this.brewTime = Math.max(0, this.brewTime - ticks); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinPlayerInteractManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.PlayerInteractManager; 35 | import net.minecraft.server.World; 36 | 37 | @Mixin(value = PlayerInteractManager.class, remap = false) 38 | public abstract class MixinPlayerInteractManager { 39 | private static final String PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD = "Lnet/minecraft/server/management/PlayerInteractManager;currentTick:I"; 40 | @Shadow public World world; 41 | @Shadow private int currentTick; 42 | 43 | // OBFHELPER: updateBlockRemoving 44 | @Redirect(method = "a()V", at = @At(value = "FIELD", target = PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 45 | public void fixupDiggingTime(PlayerInteractManager self, int modifier) { 46 | int ticks = (int) ((IMixinRealTimeTicking) this.world.getMinecraftServer()).getRealTimeTicks(); 47 | this.currentTick += ticks; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityPlayer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.injection.At; 30 | import org.spongepowered.asm.mixin.injection.Redirect; 31 | 32 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 33 | import net.minecraft.server.Entity; 34 | import net.minecraft.server.EntityPlayer; 35 | import net.minecraft.server.World; 36 | 37 | @Mixin(value = EntityPlayer.class, remap = false) 38 | public abstract class MixinEntityPlayer extends Entity { 39 | private static final String ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityPlayer;portalCooldown:I"; 40 | 41 | public MixinEntityPlayer(World worldIn) { 42 | super(worldIn); 43 | } 44 | 45 | // OBFHELPER: decrementTimeUntilPortal 46 | @Redirect(method = "I()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 47 | public void fixupPortalCooldown(EntityPlayer self, int modifier) { 48 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 49 | this.portalCooldown = Math.max(0, this.portalCooldown - ticks); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinCommandBan.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.Date; 4 | 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import com.mojang.authlib.GameProfile; 9 | 10 | import io.akarin.api.internal.Akari; 11 | import io.akarin.server.core.AkarinGlobalConfig; 12 | import net.minecraft.server.CommandAbstract; 13 | import net.minecraft.server.CommandBan; 14 | import net.minecraft.server.CommandException; 15 | import net.minecraft.server.EntityPlayer; 16 | import net.minecraft.server.ExceptionUsage; 17 | import net.minecraft.server.GameProfileBanEntry; 18 | import net.minecraft.server.ICommand; 19 | import net.minecraft.server.ICommandListener; 20 | import net.minecraft.server.MinecraftServer; 21 | 22 | @Mixin(value = CommandBan.class, remap = false) 23 | public abstract class MixinCommandBan { 24 | @Overwrite 25 | public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException { 26 | if (args.length >= 1 && args[0].length() > 1) { 27 | GameProfile profile = server.getUserCache().getProfile(args[0]); 28 | 29 | if (profile == null) { 30 | throw new CommandException("commands.ban.failed", new Object[] {args[0]}); 31 | } else { 32 | // Akarin start - use string 33 | boolean hasReason = true; // Akarin 34 | String message = null; 35 | if (args.length >= 2) { 36 | message = ""; 37 | for (int i = 2; i < args.length; i++) { 38 | message = message + args[i]; 39 | } 40 | } else { 41 | hasReason = false; // Akarin 42 | message = Akari.EMPTY_STRING; // Akarin - modify message 43 | } 44 | // Akarin end 45 | 46 | GameProfileBanEntry entry = new GameProfileBanEntry(profile, (Date) null, sender.getName(), (Date) null, message); 47 | 48 | server.getPlayerList().getProfileBans().add(entry); 49 | EntityPlayer entityplayer = server.getPlayerList().getPlayer(args[0]); 50 | 51 | if (entityplayer != null) { 52 | entityplayer.playerConnection.disconnect(hasReason ? message : AkarinGlobalConfig.messageBan); 53 | } 54 | 55 | CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // OBFHELPER: notifyCommandListener 56 | } 57 | } else { 58 | throw new ExceptionUsage("commands.ban.usage"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityAgeable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.mixin.Mixin; 28 | import org.spongepowered.asm.mixin.injection.At; 29 | import org.spongepowered.asm.mixin.injection.Redirect; 30 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 31 | import net.minecraft.server.EntityAgeable; 32 | 33 | @Mixin(value = EntityAgeable.class, remap = false) 34 | public abstract class MixinEntityAgeable { 35 | private static final String ENTITY_AGEABLE_SET_GROWING_AGE_METHOD = "Lnet/minecraft/entity/EntityAgeable;setAgeRaw(I)V"; 36 | 37 | // OBFHELPER: onLivingUpdate 38 | @Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 0)) 39 | public void fixupGrowingUp(EntityAgeable self, int age) { 40 | // Subtract the one the original update method added 41 | int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1; 42 | self.setAgeRaw(Math.min(0, age + diff)); 43 | } 44 | 45 | @Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 1)) 46 | public void fixupBreedingCooldown(EntityAgeable self, int age) { 47 | // Subtract the one the original update method added 48 | int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1; 49 | self.setAgeRaw(Math.max(0, age - diff)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntityTameableAnimal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.optimization; 26 | 27 | import java.util.UUID; 28 | 29 | import javax.annotation.Nullable; 30 | 31 | import org.spongepowered.asm.mixin.Final; 32 | import org.spongepowered.asm.mixin.Mixin; 33 | import org.spongepowered.asm.mixin.Overwrite; 34 | import org.spongepowered.asm.mixin.Shadow; 35 | 36 | import com.google.common.base.Optional; 37 | 38 | import net.minecraft.server.DataWatcherObject; 39 | import net.minecraft.server.Entity; 40 | import net.minecraft.server.EntityTameableAnimal; 41 | import net.minecraft.server.World; 42 | 43 | @Mixin(value = EntityTameableAnimal.class, remap = false) 44 | public abstract class MixinEntityTameableAnimal extends Entity { 45 | @Shadow @Final protected static DataWatcherObject> by; 46 | 47 | @Nullable private Optional cachedOwnerId; 48 | 49 | @Nullable 50 | @Overwrite 51 | public UUID getOwnerUUID() { 52 | if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by); 53 | return cachedOwnerId.orNull(); 54 | } 55 | 56 | @Overwrite 57 | public void setOwnerUUID(@Nullable UUID uuid) { 58 | cachedOwnerId = Optional.fromNullable(uuid); 59 | datawatcher.set(by, cachedOwnerId); 60 | } 61 | 62 | /** 63 | * Extends from superclass 64 | * @param world 65 | */ 66 | public MixinEntityTameableAnimal(World world) { 67 | super(world); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntity.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.Overwrite; 5 | import org.spongepowered.asm.mixin.Shadow; 6 | 7 | import net.minecraft.server.AxisAlignedBB; 8 | import net.minecraft.server.Entity; 9 | import net.minecraft.server.Material; 10 | import net.minecraft.server.MathHelper; 11 | import net.minecraft.server.MinecraftServer; 12 | import net.minecraft.server.World; 13 | 14 | @Mixin(value = Entity.class, remap = false) 15 | public abstract class MixinEntity { 16 | @Shadow public World world; 17 | @Shadow public abstract AxisAlignedBB getBoundingBox(); 18 | 19 | @Shadow public boolean noclip; 20 | @Shadow public float R; 21 | @Shadow public double locX; 22 | @Shadow public double locZ; 23 | @Shadow public abstract boolean x(Entity entity); 24 | @Shadow public abstract boolean isVehicle(); 25 | @Shadow public abstract void f(double d0, double d1, double d2); 26 | 27 | private boolean isInLava; 28 | private int lastLavaCheck = Integer.MIN_VALUE; 29 | 30 | @Overwrite // OBFHELPER: isInLava 31 | public boolean au() { 32 | /* 33 | * This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285) 34 | * Thanks @Poweruser 35 | */ 36 | int currentTick = MinecraftServer.currentTick; 37 | if (this.lastLavaCheck != currentTick) { 38 | this.lastLavaCheck = currentTick; 39 | this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA); 40 | } 41 | return this.isInLava; 42 | } 43 | @Overwrite 44 | public void collide(Entity entity) { 45 | if (entity.noclip || this.noclip || this.x(entity)) return; // NeonPaper - Test this earlier 46 | double d0 = entity.locX - this.locX; 47 | double d1 = entity.locZ - this.locZ; 48 | double d2 = MathHelper.a(d0, d1); 49 | 50 | if (d2 >= 0.009999999776482582D) { 51 | d2 = (double) MathHelper.sqrt(d2); 52 | d0 /= d2; 53 | d1 /= d2; 54 | double d3 = 1.0D / d2; 55 | 56 | if (d3 > 1.0D) { 57 | d3 = 1.0D; 58 | } 59 | 60 | d0 *= d3; 61 | d1 *= d3; 62 | d0 *= 0.05000000074505806D; 63 | d1 *= 0.05000000074505806D; 64 | d0 *= (double) (1.0F - this.R); 65 | d1 *= (double) (1.0F - this.R); 66 | if (!this.isVehicle()) { 67 | this.f(-d0, 0.0D, -d1); 68 | } 69 | 70 | if (!entity.isVehicle()) { 71 | entity.f(d0, 0.0D, d1); 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntityHorseAbstract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.optimization; 26 | 27 | import java.util.UUID; 28 | 29 | import javax.annotation.Nullable; 30 | 31 | import org.spongepowered.asm.mixin.Final; 32 | import org.spongepowered.asm.mixin.Mixin; 33 | import org.spongepowered.asm.mixin.Overwrite; 34 | import org.spongepowered.asm.mixin.Shadow; 35 | 36 | import com.google.common.base.Optional; 37 | 38 | import net.minecraft.server.DataWatcherObject; 39 | import net.minecraft.server.Entity; 40 | import net.minecraft.server.EntityHorseAbstract; 41 | import net.minecraft.server.World; 42 | 43 | @Mixin(value = EntityHorseAbstract.class, remap = false) 44 | public abstract class MixinEntityHorseAbstract extends Entity { 45 | @Shadow(aliases = "bJ") @Final private static DataWatcherObject> OWNER_UNIQUE_ID; 46 | 47 | @Nullable private Optional cachedOwnerId; 48 | 49 | @Nullable 50 | @Overwrite 51 | public UUID getOwnerUUID() { 52 | if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(OWNER_UNIQUE_ID); 53 | return cachedOwnerId.orNull(); 54 | } 55 | 56 | @Overwrite 57 | public void setOwnerUUID(@Nullable UUID uuid) { 58 | cachedOwnerId = Optional.fromNullable(uuid); 59 | datawatcher.set(OWNER_UNIQUE_ID, cachedOwnerId); 60 | } 61 | 62 | /** 63 | * Extends from superclass 64 | * @param world 65 | */ 66 | public MixinEntityHorseAbstract(World world) { 67 | super(world); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Akarin FaceAkarin 2 | [![Powered by](https://img.shields.io/badge/Powered_by-Akarin_project-ee6aa7.svg?style=flat)](https://akarin.io) 3 | [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/fw2pJAj) 4 | [![bStats](https://img.shields.io/badge/bStats-Torch-0099ff.svg?style=flat)](https://bstats.org/plugin/bukkit/Torch) 5 | 6 | Akarin is currently **under heavy development** and contributions are welcome! 7 | 8 | Introduction 9 | --- 10 | > Akarin is a powerful server software from the 'new dimension', formerly known as Torch. 11 | 12 | As a [Paper](https://github.com/PaperMC/Paper) fork, it should support almost all plugins that work on [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse). 13 | 14 | Our project has a few key goals: 15 | 16 | * **Open Access** - Make more game mechanics configurable. 17 | * **Bedrock** - Make the server more safe and stable. 18 | * **Fast** - Simplify the logic and implement multi-threaded computing. 19 | 20 | *Issues and Pull Requests will be labeled accordingly* 21 | 22 | Get Akarin 23 | --- 24 | ### Download 25 | #### Recommended 26 | + [**Jenkins**](http://ci.josephworks.net/job/Akarin/job/ver%252F1.12.2/) - Kudos to [JosephWorks](https://github.com/josephworks) 27 | 28 | *Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here* 29 | 30 | ### Build 31 | #### Requirements 32 | * Java (JDK) 8 or above 33 | * Maven 34 | 35 | #### Compile 36 | ```sh 37 | ./scripts/inst.sh --setup --fast 38 | ``` 39 | 40 | **Notes** 41 | * You must use `--setup` at least once to deploy necessary dependencies otherwise some imports cannot be organized. 42 | * For non-modified projects, it is recommended to add the `--fast` option to skip any tests. 43 | * If your machine has insufficient memory, you may want to add the `--remote` option to avoid decompiling locally. 44 | 45 | Demo Servers 46 | --- 47 | * `demo.akarin.io` (official) 48 | * `omc.hk` (auth required) 49 | 50 | *Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here* 51 | 52 | Contributing 53 | --- 54 | * Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more. 55 | * Add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md). 56 | * If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can [send](mailto://kira@kira.moe) us an email with your experience and necessary information. 57 | 58 | ![Akarin project](https://i.loli.net/2018/05/13/5af7fbbfbcddf.png) 59 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityItem.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.EntityItem; 35 | 36 | @Mixin(value = EntityItem.class, remap = false) 37 | public abstract class MixinEntityItem { 38 | private static final String ENTITY_ITEM_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityItem;pickupDelay:I"; 39 | private static final String ENTITY_ITEM_AGE_FIELD = "Lnet/minecraft/entity/item/EntityItem;age:I"; 40 | @Shadow public int age; 41 | @Shadow private int pickupDelay; 42 | 43 | // OBFHELPER: onUpdate 44 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 45 | public void fixupPickupDelay(EntityItem self, int modifier) { 46 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 47 | this.pickupDelay = Math.max(0, this.pickupDelay - ticks); 48 | } 49 | 50 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 51 | public void fixupAge(EntityItem self, int modifier) { 52 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 53 | this.age += ticks; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /removed/com/destroystokyo/paper/antixray/PacketPlayOutMapChunkInfo.java: -------------------------------------------------------------------------------- 1 | package com.destroystokyo.paper.antixray; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import net.minecraft.server.Chunk; 5 | import net.minecraft.server.DataPalette; 6 | import net.minecraft.server.IBlockData; 7 | import net.minecraft.server.PacketPlayOutMapChunk; 8 | 9 | /** 10 | * Akarin Changes Note 11 | * 1) byte[] -> ByteBuf (compatibility) 12 | */ 13 | public class PacketPlayOutMapChunkInfo { 14 | 15 | private final PacketPlayOutMapChunk packetPlayOutMapChunk; 16 | private final Chunk chunk; 17 | private final int chunkSectionSelector; 18 | private ByteBuf data; // Akarin 19 | private final int[] bitsPerValue = new int[16]; 20 | private final DataPalette[] dataPalettes = new DataPalette[16]; 21 | private final int[] dataBitsIndexes = new int[16]; 22 | private final IBlockData[][] predefinedBlockData = new IBlockData[16][]; 23 | 24 | public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { 25 | this.packetPlayOutMapChunk = packetPlayOutMapChunk; 26 | this.chunk = chunk; 27 | this.chunkSectionSelector = chunkSectionSelector; 28 | } 29 | 30 | public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { 31 | return packetPlayOutMapChunk; 32 | } 33 | 34 | public Chunk getChunk() { 35 | return chunk; 36 | } 37 | 38 | public int getChunkSectionSelector() { 39 | return chunkSectionSelector; 40 | } 41 | 42 | public ByteBuf getData() { // Akarin 43 | return data; 44 | } 45 | 46 | public void setData(ByteBuf data) { // Akarin 47 | this.data = data; 48 | } 49 | 50 | public int getBitsPerValue(int chunkSectionIndex) { 51 | return bitsPerValue[chunkSectionIndex]; 52 | } 53 | 54 | public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) { 55 | this.bitsPerValue[chunkSectionIndex] = bitsPerValue; 56 | } 57 | 58 | public DataPalette getDataPalette(int chunkSectionIndex) { 59 | return dataPalettes[chunkSectionIndex]; 60 | } 61 | 62 | public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { 63 | dataPalettes[chunkSectionIndex] = dataPalette; 64 | } 65 | 66 | public int getDataBitsIndex(int chunkSectionIndex) { 67 | return dataBitsIndexes[chunkSectionIndex]; 68 | } 69 | 70 | public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { 71 | dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; 72 | } 73 | 74 | public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) { 75 | return predefinedBlockData[chunkSectionIndex]; 76 | } 77 | 78 | public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) { 79 | this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData; 80 | } 81 | 82 | public boolean isWritten(int chunkSectionIndex) { 83 | return bitsPerValue[chunkSectionIndex] != 0; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 33 | import net.minecraft.server.Entity; 34 | import net.minecraft.server.World; 35 | 36 | @Mixin(value = Entity.class, remap = false, priority = 1001) 37 | public abstract class MixinEntity { 38 | private static final String ENTITY_RIDABLE_COOLDOWN_FIELD = "Lnet/minecraft/entity/Entity;j:I"; // PUTFIELD: rideCooldown 39 | private static final String ENTITY_PORTAL_COUNTER_FIELD = "Lnet/minecraft/entity/Entity;al:I"; // PUTFIELD: portalCounter 40 | @Shadow protected int j; 41 | @Shadow protected int al; 42 | @Shadow public World world; 43 | 44 | // OBFHELPER: onEntityUpdate 45 | @Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_RIDABLE_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 46 | public void fixupEntityCooldown(Entity self, int modifier) { 47 | int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks(); 48 | this.j = Math.max(0, this.j - ticks); // OBFHELPER: rideCooldown 49 | } 50 | 51 | @Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_PORTAL_COUNTER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 52 | public void fixupPortalCounter(Entity self, int modifier) { 53 | int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks(); 54 | this.al += ticks; // OBFHELPER: portalCounter 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/nsc/OptimisticNetworkManager.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.nsc; 2 | 3 | import java.util.Queue; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | 9 | import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock; 10 | 11 | import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue; 12 | import io.netty.channel.Channel; 13 | import io.netty.util.concurrent.Future; 14 | import io.netty.util.concurrent.GenericFutureListener; 15 | import net.minecraft.server.NetworkManager; 16 | import net.minecraft.server.NetworkManager.QueuedPacket; 17 | import net.minecraft.server.Packet; 18 | import net.minecraft.server.PacketPlayOutMapChunk; 19 | 20 | @Mixin(value = NetworkManager.class, remap = false) 21 | public abstract class OptimisticNetworkManager { 22 | @Shadow public Channel channel; 23 | @Shadow(aliases = "i") @Final private Queue packets; 24 | @Shadow(aliases = "j") @Final private ReentrantReadWriteUpdateLock queueLock; 25 | 26 | @Shadow public abstract Queue getPacketQueue(); 27 | @Shadow public abstract void dispatchPacket(Packet packet, GenericFutureListener>[] genericFutureListeners); 28 | 29 | @SuppressWarnings("unchecked") 30 | private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null); 31 | 32 | @Overwrite // OBFHELPER: trySendQueue 33 | private boolean m() { 34 | if (this.channel != null && this.channel.isOpen()) { 35 | if (this.packets.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all 36 | return true; 37 | } 38 | 39 | this.queueLock.updateLock().lock(); 40 | try { 41 | while (!this.packets.isEmpty()) { 42 | NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue) getPacketQueue()).poll(item -> { 43 | return item.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) item.getPacket()).isReady(); 44 | }, SIGNAL_PACKET); 45 | 46 | if (packet != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) 47 | if (packet == SIGNAL_PACKET) { 48 | return false; // Return false if the peeked packet is a chunk packet which is not ready 49 | } else { 50 | dispatchPacket(packet.getPacket(), packet.getGenericFutureListeners()); // dispatch the packet 51 | } 52 | } 53 | } 54 | } finally { 55 | this.queueLock.updateLock().unlock(); 56 | } 57 | 58 | } 59 | return true; // Return true if all packets were dispatched 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinWorldServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.bukkit.World.Environment; 28 | import org.bukkit.generator.ChunkGenerator; 29 | import org.spongepowered.asm.mixin.Mixin; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Inject; 32 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 33 | 34 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 35 | import net.minecraft.server.IDataManager; 36 | import net.minecraft.server.MethodProfiler; 37 | import net.minecraft.server.World; 38 | import net.minecraft.server.WorldData; 39 | import net.minecraft.server.WorldProvider; 40 | import net.minecraft.server.WorldServer; 41 | 42 | @Mixin(value = WorldServer.class, remap = false, priority = 1001) 43 | public abstract class MixinWorldServer extends World implements IMixinRealTimeTicking { 44 | 45 | protected MixinWorldServer(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, Environment env) { 46 | super(idatamanager, worlddata, worldprovider, methodprofiler, flag, gen, env); 47 | } 48 | 49 | @Inject(method = "doTick()V", at = @At("HEAD")) 50 | public void fixTimeOfDay(CallbackInfo ci) { 51 | if (this.getGameRules().getBoolean("doDaylightCycle")) { 52 | // Subtract the one the original tick method is going to add 53 | long diff = this.getRealTimeTicks() - 1; 54 | // Don't set if we're not changing it as other mods might be listening for changes 55 | if (diff > 0) { 56 | this.worldData.setDayTime(this.worldData.getDayTime() + diff); 57 | } 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityExperienceOrb.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.EntityExperienceOrb; 35 | 36 | @Mixin(value = EntityExperienceOrb.class, remap = false) 37 | public abstract class MixinEntityExperienceOrb { 38 | private static final String ENTITY_XP_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;c:I"; // PUTFIELD: delayBeforeCanPickup 39 | private static final String ENTITY_XP_AGE_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;b:I"; // PUTFIELD: xpOrbAge 40 | @Shadow public int c; // OBFHELPER: delayBeforeCanPickup 41 | @Shadow public int b; // OBFHELPER: xpOrbAge 42 | 43 | // OBFHELPER: onUpdate 44 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 45 | public void fixupPickupDelay(EntityExperienceOrb self, int modifier) { 46 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 47 | this.c = Math.max(0, this.c - ticks); // OBFHELPER: delayBeforeCanPickup 48 | } 49 | 50 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 51 | public void fixupAge(EntityExperienceOrb self, int modifier) { 52 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 53 | this.b += ticks; // OBFHELPER: xpOrbAge 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinPlayerConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Final; 29 | import org.spongepowered.asm.mixin.Mixin; 30 | import org.spongepowered.asm.mixin.Shadow; 31 | import org.spongepowered.asm.mixin.injection.At; 32 | import org.spongepowered.asm.mixin.injection.Redirect; 33 | 34 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 35 | import net.minecraft.server.MinecraftServer; 36 | import net.minecraft.server.PlayerConnection; 37 | 38 | @Mixin(value = PlayerConnection.class, remap = false) 39 | public abstract class MixinPlayerConnection { 40 | private static final String NET_HANDLER_PLAY_CHAT_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;chatThrottle:I"; 41 | private static final String NET_HANDLER_PLAY_DROP_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;itemDropThreshold:I"; 42 | @Shadow private volatile int chatThrottle; 43 | @Shadow(aliases = "j") private int itemDropThreshold; 44 | @Shadow @Final private MinecraftServer minecraftServer; 45 | 46 | // OBFHELPER: update 47 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_CHAT_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 48 | public void fixupChatSpamCheck(PlayerConnection self, int modifier) { 49 | int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks(); 50 | this.chatThrottle = Math.max(0, this.chatThrottle - ticks); 51 | } 52 | 53 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_DROP_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 54 | public void fixupDropSpamCheck(PlayerConnection self, int modifier) { 55 | int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks(); 56 | this.itemDropThreshold = Math.max(0, this.itemDropThreshold - ticks); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinEntityHuman.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.EntityHuman; 35 | 36 | @Mixin(value = EntityHuman.class, remap = false) 37 | public abstract class MixinEntityHuman { 38 | private static final String ENTITY_PLAYER_XP_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityHuman;bD:I"; // PUTFIELD: xpCooldown 39 | private static final String ENTITY_PLAYER_SLEEP_TIMER_FIELD = "Lnet/minecraft/entity/player/EntityHuman;sleepTicks:I"; 40 | @Shadow public int bD; 41 | @Shadow private int sleepTicks; 42 | 43 | // OBFHELPER: onUpdate 44 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_XP_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 45 | public void fixupXpCooldown(EntityHuman self, int modifier) { 46 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 47 | this.bD = Math.max(0, this.bD - ticks); // OBFHELPER: xpCooldown 48 | } 49 | 50 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 51 | public void fixupSleepTimer(EntityHuman self, int modifier) { 52 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 53 | this.sleepTicks += ticks; 54 | } 55 | 56 | @Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 2)) 57 | public void fixupWakeTimer(EntityHuman self, int modifier) { 58 | int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks(); 59 | this.sleepTicks += ticks; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntityMushroomCow.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.optimization; 2 | 3 | import org.bukkit.event.player.PlayerShearEntityEvent; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | import net.minecraft.server.Blocks; 8 | import net.minecraft.server.EntityCow; 9 | import net.minecraft.server.EntityHuman; 10 | import net.minecraft.server.EntityItem; 11 | import net.minecraft.server.EntityMushroomCow; 12 | import net.minecraft.server.EnumHand; 13 | import net.minecraft.server.EnumParticle; 14 | import net.minecraft.server.ItemStack; 15 | import net.minecraft.server.Items; 16 | import net.minecraft.server.SoundEffects; 17 | import net.minecraft.server.World; 18 | 19 | @Mixin(value = EntityMushroomCow.class, remap = false) 20 | public abstract class MixinEntityMushroomCow extends EntityCow { 21 | 22 | public MixinEntityMushroomCow(World world) { 23 | super(world); 24 | } 25 | 26 | @Overwrite 27 | public boolean a(EntityHuman entityhuman, EnumHand enumhand) { 28 | ItemStack itemstack = entityhuman.b(enumhand); 29 | 30 | if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) { 31 | itemstack.subtract(1); 32 | if (itemstack.isEmpty()) { 33 | entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW)); 34 | } else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) { 35 | entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false); 36 | } 37 | 38 | return true; 39 | } else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) { 40 | if (this.dead) return false; // Reaper - Fix cow dupe 41 | // CraftBukkit start 42 | PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); 43 | this.world.getServer().getPluginManager().callEvent(event); 44 | 45 | if (event.isCancelled()) { 46 | return false; 47 | } 48 | // CraftBukkit end 49 | this.die(); 50 | this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D, new int[0]); 51 | if (!this.world.isClientSide) { 52 | EntityCow entitycow = new EntityCow(this.world); 53 | 54 | entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); 55 | entitycow.setHealth(this.getHealth()); 56 | entitycow.aN = this.aN; 57 | if (this.hasCustomName()) { 58 | entitycow.setCustomName(this.getCustomName()); 59 | } 60 | 61 | this.world.addEntity(entitycow); 62 | 63 | for (int i = 0; i < 5; ++i) { 64 | this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM))); 65 | } 66 | 67 | itemstack.damage(1, entityhuman); 68 | this.a(SoundEffects.ei, 1.0F, 1.0F); 69 | } 70 | 71 | return true; 72 | } else { 73 | return super.a(entityhuman, enumhand); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/ReentrantSpinningLock.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal.utils; 2 | 3 | import java.util.concurrent.atomic.AtomicBoolean; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class ReentrantSpinningLock { 7 | /* 8 | * Impl Note: 9 | * A write lock can reentrant as a read lock, while a 10 | * read lock is not allowed to reentrant as a write lock. 11 | * READ LOCK IS UNTESTED, USE WITH CATION. 12 | */ 13 | private final AtomicBoolean writeLocked = new AtomicBoolean(false); 14 | 15 | // --------- Thread local restricted fields --------- 16 | private long heldThreadId = 0; 17 | private int reentrantLocks = 0; 18 | 19 | /** 20 | * Lock as a typical reentrant write lock 21 | */ 22 | public void lock() { 23 | long currentThreadId = Thread.currentThread().getId(); 24 | if (heldThreadId == currentThreadId) { 25 | reentrantLocks++; 26 | } else { 27 | while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently 28 | heldThreadId = currentThreadId; 29 | } 30 | } 31 | 32 | public void unlock() { 33 | if (reentrantLocks == 0) { 34 | heldThreadId = 0; 35 | //if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract 36 | writeLocked.set(false); 37 | //} 38 | } else { 39 | --reentrantLocks; 40 | } 41 | } 42 | 43 | private final AtomicInteger readerThreads = new AtomicInteger(0); 44 | 45 | /** 46 | * Lock as a typical reentrant read lock 47 | */ 48 | @Deprecated 49 | public void lockWeak() { 50 | long currentThreadId = Thread.currentThread().getId(); 51 | if (heldThreadId == currentThreadId) { 52 | reentrantLocks++; 53 | } else { 54 | if (readerThreads.get() == 0) { 55 | while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock 56 | } 57 | heldThreadId = currentThreadId; 58 | readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus 59 | } 60 | } 61 | 62 | @Deprecated 63 | public void unlockWeak() { 64 | if (reentrantLocks == 0) { 65 | heldThreadId = 0; 66 | writeLocked.set(false); 67 | } else { 68 | --reentrantLocks; 69 | } 70 | } 71 | 72 | // --------- Wrappers to allow typical usages --------- 73 | private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock(); 74 | private SpinningReadLock wrappedReadLock = new SpinningReadLock(); 75 | 76 | public class SpinningWriteLock { 77 | public void lock() { 78 | lock(); 79 | } 80 | public void unlock() { 81 | unlock(); 82 | } 83 | } 84 | 85 | @Deprecated 86 | public class SpinningReadLock { 87 | public void lock() { 88 | lockWeak(); 89 | } 90 | public void unlock() { 91 | unlockWeak(); 92 | } 93 | } 94 | 95 | public SpinningWriteLock writeLock() { 96 | return wrappedWriteLock; 97 | } 98 | 99 | public SpinningReadLock readLock() { 100 | return wrappedReadLock; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/realtime/MixinTileEntityFurnace.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.realtime; 26 | 27 | import org.spongepowered.asm.lib.Opcodes; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Shadow; 30 | import org.spongepowered.asm.mixin.injection.At; 31 | import org.spongepowered.asm.mixin.injection.Redirect; 32 | 33 | import io.akarin.api.internal.mixin.IMixinRealTimeTicking; 34 | import net.minecraft.server.MathHelper; 35 | import net.minecraft.server.TileEntity; 36 | import net.minecraft.server.TileEntityFurnace; 37 | 38 | @Mixin(value = TileEntityFurnace.class, remap = false) 39 | public abstract class MixinTileEntityFurnace extends TileEntity { 40 | private static final String FURNACE_BURN_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;burnTime:I"; 41 | private static final String FURNACE_COOK_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;cookTime:I"; 42 | @Shadow private int burnTime; 43 | @Shadow private int cookTime; 44 | @Shadow private int cookTimeTotal; 45 | 46 | // OBFHELPER: update 47 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_BURN_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 48 | public void fixupBurnTime(TileEntityFurnace self, int modifier) { 49 | int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks(); 50 | this.burnTime = Math.max(0, this.burnTime - ticks); 51 | } 52 | 53 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0)) 54 | public void fixupCookTime(TileEntityFurnace self, int modifier) { 55 | int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks(); 56 | this.cookTime = Math.min(this.cookTimeTotal, this.cookTime + ticks); 57 | } 58 | 59 | @Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 3)) 60 | public void fixupCookTimeCooldown(TileEntityFurnace self, int modifier) { 61 | int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks(); 62 | this.cookTime = MathHelper.clamp(this.cookTime - (2 * ticks), 0, this.cookTimeTotal); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /removed/com/destroystokyo/paper/antixray/DataBitsWriter.java: -------------------------------------------------------------------------------- 1 | package com.destroystokyo.paper.antixray; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | 5 | public class DataBitsWriter { 6 | 7 | private ByteBuf dataBits; // Akarin 8 | private int bitsPerValue; 9 | private long mask; 10 | private int longInDataBitsIndex; 11 | private int bitInLongIndex; 12 | private long current; 13 | private boolean dirty; 14 | 15 | public void setDataBits(ByteBuf dataBits) { // Akarin 16 | this.dataBits = dataBits; 17 | } 18 | 19 | public void setBitsPerValue(int bitsPerValue) { 20 | this.bitsPerValue = bitsPerValue; 21 | mask = (1 << bitsPerValue) - 1; 22 | } 23 | 24 | public void setIndex(int index) { 25 | this.longInDataBitsIndex = index; 26 | bitInLongIndex = 0; 27 | init(); 28 | } 29 | 30 | private void init() { 31 | if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin 32 | // Akarin start 33 | current = dataBits.getLong(longInDataBitsIndex); 34 | /* 35 | current = ((((long) dataBits[longInDataBitsIndex]) << 56) 36 | | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) 37 | | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) 38 | | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) 39 | | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) 40 | | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) 41 | | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) 42 | | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); 43 | */ // Akarin end 44 | } 45 | 46 | dirty = false; 47 | } 48 | 49 | public void finish() { 50 | if (dirty && dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin 51 | // Akarin start 52 | dataBits.setLong(longInDataBitsIndex, current); 53 | /* 54 | dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff); 55 | dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff); 56 | dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff); 57 | dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff); 58 | dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff); 59 | dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff); 60 | dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff); 61 | dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff); 62 | */ // Akarin end 63 | } 64 | } 65 | 66 | public void write(int value) { 67 | current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; 68 | dirty = true; 69 | bitInLongIndex += bitsPerValue; 70 | 71 | if (bitInLongIndex > 63) { 72 | finish(); 73 | bitInLongIndex -= 64; 74 | longInDataBitsIndex += 8; 75 | init(); 76 | 77 | if (bitInLongIndex > 0) { 78 | current = current & ~(mask >>> bitsPerValue - bitInLongIndex) | (value & mask) >>> bitsPerValue - bitInLongIndex; 79 | dirty = true; 80 | } 81 | } 82 | } 83 | 84 | public void skip() { 85 | bitInLongIndex += bitsPerValue; 86 | 87 | if (bitInLongIndex > 63) { 88 | finish(); 89 | bitInLongIndex -= 64; 90 | longInDataBitsIndex += 8; 91 | init(); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/core/MixinPlayerChunk.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.core; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | 6 | import org.apache.logging.log4j.Logger; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Overwrite; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | import io.akarin.server.core.AkarinGlobalConfig; 13 | import net.minecraft.server.Chunk; 14 | import net.minecraft.server.ChunkCoordIntPair; 15 | import net.minecraft.server.EntityPlayer; 16 | import net.minecraft.server.PacketPlayOutMapChunk; 17 | import net.minecraft.server.PlayerChunk; 18 | import net.minecraft.server.PlayerChunkMap; 19 | import net.minecraft.server.WorldBorder; 20 | 21 | @Mixin(value = PlayerChunk.class, remap = false) 22 | public abstract class MixinPlayerChunk { 23 | @Shadow @Final private static Logger a; 24 | @Shadow @Final private ChunkCoordIntPair location; 25 | @Shadow @Final private PlayerChunkMap playerChunkMap; 26 | @Shadow @Final public List c; 27 | @Shadow public Chunk chunk; 28 | @Shadow private int dirtyCount; 29 | @Shadow private int h; 30 | @Shadow private long i; 31 | @Shadow private boolean done; 32 | 33 | @Shadow public abstract void sendChunk(EntityPlayer entityplayer); 34 | 35 | @Overwrite 36 | public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument 37 | if (this.c.contains(entityplayer)) { 38 | a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z)); 39 | return; 40 | } 41 | if (AkarinGlobalConfig.noChunksPastWorldBorder) { 42 | WorldBorder worldborder = playerChunkMap.getWorld().getWorldBorder(); 43 | int centerchunkx = ((int)worldborder.getCenterX()) >> 4; 44 | int centerchunkz = ((int)worldborder.getCenterZ()) >> 4; 45 | int sizechunks = ((int)worldborder.getSize()) >> 5; 46 | ++sizechunks; 47 | if(location.x=centerchunkx + sizechunks||location.z=centerchunkz + sizechunks) { 48 | return; 49 | } 50 | } 51 | if (this.c.isEmpty()) { 52 | this.i = this.playerChunkMap.getWorld().getTime(); 53 | } 54 | 55 | this.c.add(entityplayer); 56 | 57 | if (this.done) { 58 | this.sendChunk(entityplayer); 59 | } 60 | } 61 | 62 | @Overwrite 63 | public boolean b() { 64 | if (this.done) { 65 | return true; 66 | } 67 | if (this.chunk == null) { 68 | return false; 69 | } 70 | if (!this.chunk.isReady()) { 71 | return false; 72 | } 73 | if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary 74 | return false; 75 | } 76 | this.dirtyCount = 0; 77 | this.h = 0; 78 | this.done = true; 79 | if (c.isEmpty()) return true; // Akarin - Fixes MC-120780 80 | PacketPlayOutMapChunk packetplayoutmapchunk = new PacketPlayOutMapChunk(this.chunk, '\uffff'); 81 | Iterator iterator = this.c.iterator(); 82 | 83 | while (iterator.hasNext()) { 84 | EntityPlayer entityplayer = iterator.next(); 85 | 86 | entityplayer.playerConnection.sendPacket(packetplayoutmapchunk); 87 | this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); 88 | } 89 | 90 | return true; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.cps; 2 | 3 | import org.spigotmc.SlackActivityAccountant; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; 12 | import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; 13 | import it.unimi.dsi.fastutil.objects.ObjectIterator; 14 | import net.minecraft.server.Chunk; 15 | import net.minecraft.server.ChunkProviderServer; 16 | import net.minecraft.server.IChunkLoader; 17 | import net.minecraft.server.WorldServer; 18 | 19 | @Mixin(value = ChunkProviderServer.class, remap = false) 20 | public abstract class MixinChunkProviderServer { 21 | @Shadow @Final public WorldServer world; 22 | @Shadow public Long2ObjectOpenHashMap chunks; 23 | 24 | public void unload(Chunk chunk) { 25 | if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) { 26 | // Akarin - avoid using the queue and simply check the unloaded flag during unloads 27 | // this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ))); 28 | chunk.setShouldUnload(true); 29 | } 30 | } 31 | 32 | @Shadow public abstract boolean unloadChunk(Chunk chunk, boolean save); 33 | @Shadow @Final private IChunkLoader chunkLoader; 34 | @Shadow @Final private static double UNLOAD_QUEUE_RESIZE_FACTOR; 35 | 36 | @Overwrite 37 | public boolean unloadChunks() { 38 | if (!this.world.savingDisabled) { 39 | long now = System.currentTimeMillis(); 40 | long unloadAfter = world.paperConfig.delayChunkUnloadsBy; 41 | SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant; 42 | activityAccountant.startActivity(0.5); 43 | 44 | ObjectIterator> it = chunks.long2ObjectEntrySet().fastIterator(); 45 | int remainingChunks = chunks.size(); 46 | int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive 47 | 48 | while (it.hasNext()) { 49 | Entry entry = it.next(); 50 | Chunk chunk = entry.getValue(); 51 | 52 | if (chunk != null && chunk.isUnloading()) { 53 | if (chunk.scheduledForUnload != null) { 54 | if (now - chunk.scheduledForUnload <= unloadAfter) continue; 55 | } 56 | 57 | if (unloadChunk(chunk, true)) { 58 | it.remove(); 59 | } 60 | chunk.setShouldUnload(false); 61 | chunk.scheduledForUnload = null; 62 | 63 | if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break; 64 | } 65 | } 66 | activityAccountant.endActivity(); 67 | this.chunkLoader.b(); // OBFHELPER: chunkTick 68 | } 69 | return false; 70 | } 71 | 72 | @Redirect(method = "unloadChunk", at = @At( 73 | value = "INVOKE", 74 | target = "it/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap.remove(J)Ljava/lang/Object;" 75 | )) 76 | private Object remove(Long2ObjectOpenHashMap chunks, long chunkHash) { 77 | return null; 78 | } 79 | 80 | @Overwrite 81 | public String getName() { 82 | return "ServerChunkCache: " + chunks.size(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/IntCache.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | // NeonPaper start 4 | import it.unimi.dsi.fastutil.objects.ObjectArrayList; 5 | import java.lang.ref.WeakReference; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | // NeonPaper end 9 | 10 | public class IntCache { 11 | // NeonPaper start - Refactored IntCache to be thread local instead of static 12 | private static final ThreadLocal caches = new ThreadLocal() { 13 | @Override 14 | protected IntCache initialValue() { 15 | IntCache cache = new IntCache(); 16 | synchronized (ALL_CACHES) { 17 | ALL_CACHES.add(new WeakReference<>(cache)); 18 | } 19 | return new IntCache(); 20 | } 21 | }; 22 | 23 | private static final List> ALL_CACHES = new ObjectArrayList<>(); 24 | 25 | private int a = 256; 26 | private final List b = new ObjectArrayList<>(); 27 | private final List c = new ObjectArrayList<>(); 28 | private final List d = new ObjectArrayList<>(); 29 | private final List e = new ObjectArrayList<>(); 30 | 31 | private final int cacheLimit = org.spigotmc.SpigotConfig.intCacheLimit; 32 | 33 | public static int[] a(int i) { 34 | return caches.get().aNonStatic(i); 35 | } 36 | 37 | public int[] aNonStatic(int i) { 38 | int[] aint; 39 | 40 | if (i <= 256) { 41 | if (this.b.isEmpty()) { 42 | aint = new int[256]; 43 | if (c.size() < cacheLimit) this.c.add(aint); 44 | return aint; 45 | } else { 46 | aint = this.b.remove(this.b.size() - 1); 47 | if (c.size() < cacheLimit) this.c.add(aint); 48 | return aint; 49 | } 50 | } else if (i > this.a) { 51 | this.a = i; 52 | this.d.clear(); 53 | this.e.clear(); 54 | aint = new int[this.a]; 55 | if (e.size() < cacheLimit) this.e.add(aint); 56 | return aint; 57 | } else if (this.d.isEmpty()) { 58 | aint = new int[this.a]; 59 | if (e.size() < cacheLimit) this.e.add(aint); 60 | return aint; 61 | } else { 62 | aint = this.d.remove(this.d.size() - 1); 63 | if (e.size() < cacheLimit) this.e.add(aint); 64 | return aint; 65 | } 66 | } 67 | 68 | public static void a() { 69 | caches.get().aNonStatic(); 70 | } 71 | 72 | public void aNonStatic() { 73 | if (!this.d.isEmpty()) { 74 | this.d.remove(this.d.size() - 1); 75 | } 76 | 77 | if (!this.b.isEmpty()) { 78 | this.b.remove(this.b.size() - 1); 79 | } 80 | 81 | this.d.addAll(this.e); 82 | this.b.addAll(this.c); 83 | this.e.clear(); 84 | this.c.clear(); 85 | } 86 | 87 | public static String b() { 88 | int cache = 0; 89 | int tcache = 0; 90 | int allocated = 0; 91 | int tallocated = 0; 92 | int numberOfCaches; 93 | 94 | synchronized (ALL_CACHES) { 95 | numberOfCaches = ALL_CACHES.size(); 96 | Iterator> iter = ALL_CACHES.iterator(); 97 | while (iter.hasNext()) { 98 | WeakReference reference = iter.next(); 99 | IntCache intcache = reference.get(); 100 | if (intcache != null) { 101 | cache += intcache.d.size(); 102 | tcache += intcache.b.size(); 103 | allocated += intcache.e.size(); 104 | tallocated += intcache.c.size(); 105 | } else { 106 | iter.remove(); 107 | } 108 | } 109 | } 110 | return numberOfCaches + " IntCaches. In Total => cache: " + cache + ", tcache: " + tcache + ", allocated: " + allocated + ", tallocated: " + tallocated; 111 | } 112 | // NeonPaper end 113 | } -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/optimization/WeakEnchantmentManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Sponge, licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) SpongePowered 5 | * Copyright (c) contributors 6 | * 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | package io.akarin.server.mixin.optimization; 26 | 27 | import org.spongepowered.asm.mixin.Final; 28 | import org.spongepowered.asm.mixin.Mixin; 29 | import org.spongepowered.asm.mixin.Overwrite; 30 | import org.spongepowered.asm.mixin.Shadow; 31 | 32 | import net.minecraft.server.DamageSource; 33 | import net.minecraft.server.EnchantmentManager; 34 | import net.minecraft.server.Entity; 35 | import net.minecraft.server.EntityHuman; 36 | import net.minecraft.server.EntityLiving; 37 | import net.minecraft.server.ItemStack; 38 | 39 | /** 40 | * Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547) 41 | */ 42 | @Mixin(value = EnchantmentManager.class, remap = false) 43 | public abstract class WeakEnchantmentManager { 44 | @Shadow(aliases = "a") @Final private static EnchantmentManager.EnchantmentModifierProtection protection; 45 | @Shadow(aliases = "c") @Final private static EnchantmentManager.EnchantmentModifierThorns thorns; 46 | @Shadow(aliases = "d") @Final private static EnchantmentManager.EnchantmentModifierArthropods arthropods; 47 | 48 | @Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, Iterable iterable) {} 49 | @Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, ItemStack itemstack) {} 50 | 51 | @Overwrite 52 | public static int a(Iterable iterable, DamageSource damageSource) { 53 | protection.a = 0; // OBFHELPER: damageModifier 54 | protection.b = damageSource; 55 | a(protection, iterable); // OBFHELPER: applyEnchantmentModifierArray 56 | protection.b = null; // Akarin - Remove reference to Damagesource 57 | return protection.a; 58 | } 59 | 60 | @Overwrite 61 | public static void a(EntityLiving user, Entity attacker) { // OBFHELPER: applyThornEnchantments 62 | thorns.b = attacker; 63 | thorns.a = user; 64 | if (user != null) { 65 | a(thorns, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor 66 | } 67 | 68 | if (attacker instanceof EntityHuman) { 69 | a(thorns, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier 70 | } 71 | 72 | // Akarin Start - remove references to entity objects to avoid memory leaks 73 | thorns.b = null; 74 | thorns.a = null; 75 | // Akarin end 76 | } 77 | 78 | @Overwrite 79 | public static void b(EntityLiving user, Entity target) { // OBFHELPER: applyArthropodEnchantments 80 | arthropods.a = user; 81 | arthropods.b = target; 82 | if (user != null) { 83 | a(arthropods, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor 84 | } 85 | 86 | if (user instanceof EntityHuman) { 87 | a(arthropods, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier 88 | } 89 | 90 | // Akarin Start - remove references to entity objects to avoid memory leaks 91 | arthropods.a = null; 92 | arthropods.b = null; 93 | // Akarin end 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/RegistryID.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | import com.google.common.base.Predicates; 4 | import com.google.common.collect.Iterators; 5 | 6 | import java.util.BitSet; 7 | import java.util.Iterator; 8 | import javax.annotation.Nullable; 9 | 10 | /** 11 | * Akarin Changes Note 12 | * 1) BitSet for faster access (performance) 13 | */ 14 | public class RegistryID implements Registry { 15 | 16 | private static final Object a = null; 17 | private K[] b; 18 | private int[] c; 19 | private K[] d; 20 | private int e; 21 | private int f; 22 | private java.util.BitSet usedIds; // Akarin - 1.13 backport 23 | 24 | public RegistryID(int i) { 25 | i = (int) ((float) i / 0.8F); 26 | this.b = (K[]) (new Object[i]); 27 | this.c = new int[i]; 28 | this.d = (K[]) (new Object[i]); 29 | this.usedIds = new BitSet(); // Akarin - 1.13 backport 30 | } 31 | 32 | public int getId(@Nullable K k0) { 33 | return this.c(this.b(k0, this.d(k0))); 34 | } 35 | 36 | @Nullable 37 | public K fromId(int i) { 38 | return i >= 0 && i < this.d.length ? this.d[i] : null; 39 | } 40 | 41 | private int c(int i) { 42 | return i == -1 ? -1 : this.c[i]; 43 | } 44 | 45 | public int c(K k0) { 46 | int i = this.c(); 47 | 48 | this.a(k0, i); 49 | return i; 50 | } 51 | 52 | private int c() { 53 | // Akarin start - 1.13 backport 54 | /* 55 | while (this.e < this.d.length && this.d[this.e] != null) { 56 | ++this.e; 57 | } 58 | */ 59 | this.e = this.usedIds.nextClearBit(0); 60 | // Akarin end - 1.13 backport 61 | 62 | return this.e; 63 | } 64 | 65 | private void d(int i) { 66 | K[] aobject = this.b; 67 | int[] aint = this.c; 68 | 69 | this.b = (K[]) (new Object[i]); 70 | this.c = new int[i]; 71 | this.d = (K[]) (new Object[i]); 72 | this.e = 0; 73 | this.f = 0; 74 | this.usedIds.clear(); // Akarin - 1.13 backport 75 | 76 | for (int j = 0; j < aobject.length; ++j) { 77 | if (aobject[j] != null) { 78 | this.a(aobject[j], aint[j]); 79 | } 80 | } 81 | 82 | } 83 | 84 | public void a(K k0, int i) { 85 | int j = Math.max(i, this.f + 1); 86 | int k; 87 | 88 | if ((float) j >= (float) this.b.length * 0.8F) { 89 | for (k = this.b.length << 1; k < i; k <<= 1) { 90 | ; 91 | } 92 | 93 | this.d(k); 94 | } 95 | 96 | k = this.e(this.d(k0)); 97 | this.b[k] = k0; 98 | this.c[k] = i; 99 | this.d[i] = k0; 100 | this.usedIds.set(i); // Akarin - 1.13 backport 101 | ++this.f; 102 | if (i == this.e) { 103 | ++this.e; 104 | } 105 | 106 | } 107 | 108 | private int d(@Nullable K k0) { 109 | return (MathHelper.f(System.identityHashCode(k0)) & Integer.MAX_VALUE) % this.b.length; 110 | } 111 | 112 | private int b(@Nullable K k0, int i) { 113 | int j; 114 | 115 | for (j = i; j < this.b.length; ++j) { 116 | if (this.b[j] == k0) { 117 | return j; 118 | } 119 | 120 | if (this.b[j] == RegistryID.a) { 121 | return -1; 122 | } 123 | } 124 | 125 | for (j = 0; j < i; ++j) { 126 | if (this.b[j] == k0) { 127 | return j; 128 | } 129 | 130 | if (this.b[j] == RegistryID.a) { 131 | return -1; 132 | } 133 | } 134 | 135 | return -1; 136 | } 137 | 138 | private int e(int i) { 139 | int j; 140 | 141 | for (j = i; j < this.b.length; ++j) { 142 | if (this.b[j] == RegistryID.a) { 143 | return j; 144 | } 145 | } 146 | 147 | for (j = 0; j < i; ++j) { 148 | if (this.b[j] == RegistryID.a) { 149 | return j; 150 | } 151 | } 152 | 153 | throw new RuntimeException("Overflowed :("); 154 | } 155 | 156 | public Iterator iterator() { 157 | return Iterators.filter(Iterators.forArray(this.d), Predicates.notNull()); 158 | } 159 | 160 | public int b() { 161 | return this.f; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/utils/thread/SuspendableExecutorCompletionService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. 3 | * 4 | * 5 | * 6 | * 7 | * 8 | * 9 | * 10 | * 11 | * 12 | * 13 | * 14 | * 15 | * 16 | * 17 | * 18 | * 19 | * 20 | * 21 | * 22 | * 23 | */ 24 | 25 | /* 26 | * 27 | * 28 | * 29 | * 30 | * 31 | * Written by Doug Lea with assistance from members of JCP JSR-166 32 | * Expert Group and released to the public domain, as explained at 33 | * http://creativecommons.org/publicdomain/zero/1.0/ 34 | */ 35 | 36 | package io.akarin.api.internal.utils.thread; 37 | 38 | import java.util.concurrent.BlockingQueue; 39 | import java.util.concurrent.Callable; 40 | import java.util.concurrent.CompletionService; 41 | import java.util.concurrent.Future; 42 | import java.util.concurrent.FutureTask; 43 | import java.util.concurrent.LinkedBlockingQueue; 44 | import java.util.concurrent.RunnableFuture; 45 | import java.util.concurrent.TimeUnit; 46 | 47 | public class SuspendableExecutorCompletionService implements CompletionService { 48 | private final SuspendableThreadPoolExecutor executor; 49 | private final BlockingQueue> completionQueue; 50 | 51 | public void suspend() { 52 | executor.suspend(); 53 | } 54 | 55 | public void resume() { 56 | executor.resume(); 57 | } 58 | 59 | /** 60 | * FutureTask extension to enqueue upon completion 61 | */ 62 | private class QueueingFuture extends FutureTask { 63 | QueueingFuture(RunnableFuture task) { 64 | super(task, null); 65 | this.task = task; 66 | } 67 | protected void done() { completionQueue.add(task); } 68 | private final Future task; 69 | } 70 | 71 | private RunnableFuture newTaskFor(Callable task) { 72 | return new FutureTask(task); 73 | } 74 | 75 | private RunnableFuture newTaskFor(Runnable task, V result) { 76 | return new FutureTask(task, result); 77 | } 78 | 79 | /** 80 | * Creates an ExecutorCompletionService using the supplied 81 | * executor for base task execution and a 82 | * {@link LinkedBlockingQueue} as a completion queue. 83 | * 84 | * @param executor the executor to use 85 | * @throws NullPointerException if executor is {@code null} 86 | */ 87 | public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) { 88 | if (executor == null) 89 | throw new NullPointerException(); 90 | this.executor = executor; 91 | this.completionQueue = new LinkedBlockingQueue>(); 92 | } 93 | 94 | /** 95 | * Creates an ExecutorCompletionService using the supplied 96 | * executor for base task execution and the supplied queue as its 97 | * completion queue. 98 | * 99 | * @param executor the executor to use 100 | * @param completionQueue the queue to use as the completion queue 101 | * normally one dedicated for use by this service. This 102 | * queue is treated as unbounded -- failed attempted 103 | * {@code Queue.add} operations for completed tasks cause 104 | * them not to be retrievable. 105 | * @throws NullPointerException if executor or completionQueue are {@code null} 106 | */ 107 | public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor, 108 | BlockingQueue> completionQueue) { 109 | if (executor == null || completionQueue == null) 110 | throw new NullPointerException(); 111 | this.executor = executor; 112 | this.completionQueue = completionQueue; 113 | } 114 | 115 | public Future submit(Callable task) { 116 | if (task == null) throw new NullPointerException(); 117 | RunnableFuture f = newTaskFor(task); 118 | executor.execute(new QueueingFuture(f)); 119 | return f; 120 | } 121 | 122 | public Future submit(Runnable task, V result) { 123 | if (task == null) throw new NullPointerException(); 124 | RunnableFuture f = newTaskFor(task, result); 125 | executor.execute(new QueueingFuture(f)); 126 | return f; 127 | } 128 | 129 | public Future take() throws InterruptedException { 130 | return completionQueue.take(); 131 | } 132 | 133 | public Future poll() { 134 | return completionQueue.poll(); 135 | } 136 | 137 | public Future poll(long timeout, TimeUnit unit) 138 | throws InterruptedException { 139 | return completionQueue.poll(timeout, unit); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/WorldManager.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | import java.util.Iterator; 4 | import javax.annotation.Nullable; 5 | 6 | public class WorldManager implements IWorldAccess { 7 | 8 | private final MinecraftServer a; 9 | private final WorldServer world; 10 | 11 | public WorldManager(MinecraftServer minecraftserver, WorldServer worldserver) { 12 | this.a = minecraftserver; 13 | this.world = worldserver; 14 | } 15 | 16 | public void a(int i, boolean flag, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {} 17 | 18 | public void a(int i, boolean flag, boolean flag1, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {} 19 | 20 | public void a(Entity entity) { 21 | this.world.getTracker().track(entity); 22 | if (entity instanceof EntityPlayer) { 23 | this.world.worldProvider.a((EntityPlayer) entity); 24 | } 25 | 26 | } 27 | 28 | public void b(Entity entity) { 29 | this.world.getTracker().untrackEntity(entity); 30 | this.world.getScoreboard().a(entity); 31 | if (entity instanceof EntityPlayer) { 32 | this.world.worldProvider.b((EntityPlayer) entity); 33 | } 34 | 35 | } 36 | 37 | public void a(@Nullable EntityHuman entityhuman, SoundEffect soundeffect, SoundCategory soundcategory, double d0, double d1, double d2, float f, float f1) { 38 | // CraftBukkit - this.world.dimension, // Paper - this.world.dimension -> this.world 39 | this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world, new PacketPlayOutNamedSoundEffect(soundeffect, soundcategory, d0, d1, d2, f, f1)); 40 | } 41 | 42 | public void a(int i, int j, int k, int l, int i1, int j1) {} 43 | 44 | public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) { 45 | this.world.getPlayerChunkMap().flagDirty(blockposition); 46 | } 47 | 48 | public void a(BlockPosition blockposition) {} 49 | 50 | public void a(SoundEffect soundeffect, BlockPosition blockposition) {} 51 | 52 | public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) { 53 | // CraftBukkit - this.world.dimension 54 | this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world, new PacketPlayOutWorldEvent(i, blockposition, j, false)); 55 | } 56 | 57 | public void a(int i, BlockPosition blockposition, int j) { 58 | this.a.getPlayerList().sendAll(new PacketPlayOutWorldEvent(i, blockposition, j, true)); 59 | } 60 | 61 | public void b(int i, BlockPosition blockposition, int j) { 62 | // Iterator iterator = this.a.getPlayerList().v().iterator(); // Paper 63 | 64 | // CraftBukkit start 65 | EntityHuman entityhuman = null; 66 | Entity entity = world.getEntity(i); 67 | if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity; 68 | // CraftBukkit end 69 | 70 | // Paper start 71 | java.util.List list = entity != null ? entity.world.players : this.a.getPlayerList().v(); 72 | Iterator iterator = list.iterator(); 73 | PacketPlayOutBlockBreakAnimation packet = null; // NeonPaper - cache packet 74 | while (iterator.hasNext()) { 75 | EntityHuman human = iterator.next(); 76 | if (!(human instanceof EntityPlayer)) continue; 77 | EntityPlayer entityplayer = (EntityPlayer) human; 78 | // Paper end 79 | 80 | if (entityplayer != null && entityplayer.world == this.world && entityplayer.getId() != i) { 81 | double d0 = (double) blockposition.getX() - entityplayer.locX; 82 | double d1 = (double) blockposition.getY() - entityplayer.locY; 83 | double d2 = (double) blockposition.getZ() - entityplayer.locZ; 84 | 85 | // CraftBukkit start 86 | if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { 87 | continue; 88 | } 89 | // CraftBukkit end 90 | 91 | if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { 92 | // NeonPaper start 93 | if (packet == null) packet = new PacketPlayOutBlockBreakAnimation(i, blockposition, j); 94 | entityplayer.playerConnection.sendPacket(packet); 95 | // NeonPaper end 96 | } 97 | } 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/MixinMetrics.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import java.io.DataOutputStream; 4 | import java.net.URL; 5 | import java.util.List; 6 | 7 | import javax.net.ssl.HttpsURLConnection; 8 | 9 | import org.bukkit.Bukkit; 10 | import org.json.simple.JSONArray; 11 | import org.json.simple.JSONObject; 12 | import org.spongepowered.asm.mixin.Final; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Overwrite; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import com.destroystokyo.paper.Metrics; 17 | import com.destroystokyo.paper.Metrics.CustomChart; 18 | 19 | @Mixin(value = Metrics.class, remap = false) 20 | public abstract class MixinMetrics { 21 | // The url to which the data is sent - bukkit/Torch (keep our old name) 22 | private final static String URL = "https://bStats.org/submitData/bukkit"; 23 | 24 | @Shadow @Final private static int B_STATS_VERSION; 25 | @Shadow private static byte[] compress(String str) { return null; } 26 | 27 | /** 28 | * Sends the data to the bStats server. 29 | * 30 | * @param data The data to send. 31 | * @throws Exception If the request failed. 32 | */ 33 | @Overwrite 34 | private static void sendData(JSONObject data) throws Exception { 35 | if (data == null) { 36 | throw new IllegalArgumentException("Data cannot be null!"); 37 | } 38 | HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); 39 | 40 | // Compress the data to save bandwidth 41 | byte[] compressedData = compress(data.toString()); 42 | 43 | // Add headers 44 | connection.setRequestMethod("POST"); 45 | connection.addRequestProperty("Accept", "application/json"); 46 | connection.addRequestProperty("Connection", "close"); 47 | connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request 48 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); 49 | connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format 50 | connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); 51 | 52 | // Send data 53 | connection.setDoOutput(true); 54 | DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); 55 | outputStream.write(compressedData); 56 | outputStream.flush(); 57 | outputStream.close(); 58 | 59 | connection.getInputStream().close(); // We don't care about the response - Just send our data :) 60 | } 61 | 62 | // The name of the server software 63 | @Shadow @Final private String name; 64 | 65 | // A list with all custom charts 66 | @Shadow @Final private List charts; 67 | 68 | /** 69 | * Injects the plugin specific data - insert version 70 | * 71 | * @return The plugin specific data. 72 | */ 73 | @Overwrite 74 | private JSONObject getPluginData() { 75 | JSONObject data = new JSONObject(); 76 | 77 | data.put("pluginName", name); // Append the name of the server software 78 | data.put("pluginVersion", Metrics.class.getPackage().getImplementationVersion() != null ? Metrics.class.getPackage().getImplementationVersion() : "unknown"); // Akarin 79 | JSONArray customCharts = new JSONArray(); 80 | data.put("customCharts", customCharts); 81 | 82 | return data; 83 | } 84 | 85 | // The uuid of the server 86 | @Shadow @Final private String serverUUID; 87 | 88 | /** 89 | * Gets the server specific data - insert minecraft data 90 | * 91 | * @return The server specific data. 92 | */ 93 | @Overwrite 94 | private JSONObject getServerData() { 95 | // Minecraft specific data 96 | int playerAmount = Bukkit.getOnlinePlayers().size(); 97 | int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; 98 | String bukkitVersion = org.bukkit.Bukkit.getVersion(); 99 | bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1); 100 | 101 | JSONObject data = new JSONObject(); 102 | data.put("playerAmount", playerAmount); 103 | data.put("onlineMode", onlineMode); 104 | data.put("bukkitVersion", bukkitVersion); 105 | 106 | // OS specific data 107 | String osName = System.getProperty("os.name"); 108 | String osArch = System.getProperty("os.arch"); 109 | String osVersion = System.getProperty("os.version"); 110 | int coreCount = Runtime.getRuntime().availableProcessors(); 111 | 112 | data.put("serverUUID", serverUUID); 113 | 114 | data.put("osName", osName); 115 | data.put("osArch", osArch); 116 | data.put("osVersion", osVersion); 117 | data.put("coreCount", coreCount); 118 | 119 | return data; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/ItemEnderEye.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | import io.akarin.server.core.AkarinGlobalConfig; 4 | 5 | /** 6 | * Akarin Changes Note 7 | * 1) Add end portal disable feature (feature) 8 | */ 9 | public class ItemEnderEye extends Item { 10 | 11 | public ItemEnderEye() { 12 | this.b(CreativeModeTab.f); 13 | } 14 | 15 | @Override 16 | public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { 17 | IBlockData iblockdata = world.getType(blockposition); 18 | ItemStack itemstack = entityhuman.b(enumhand); 19 | 20 | if (entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack) && iblockdata.getBlock() == Blocks.END_PORTAL_FRAME && !iblockdata.get(BlockEnderPortalFrame.EYE).booleanValue()) { 21 | if (world.isClientSide) { 22 | return EnumInteractionResult.SUCCESS; 23 | } else { 24 | world.setTypeAndData(blockposition, iblockdata.set(BlockEnderPortalFrame.EYE, Boolean.valueOf(true)), 2); 25 | world.updateAdjacentComparators(blockposition, Blocks.END_PORTAL_FRAME); 26 | itemstack.subtract(1); 27 | 28 | for (int i = 0; i < 16; ++i) { 29 | double d0 = blockposition.getX() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F; 30 | double d1 = blockposition.getY() + 0.8125F; 31 | double d2 = blockposition.getZ() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F; 32 | double d3 = 0.0D; 33 | double d4 = 0.0D; 34 | double d5 = 0.0D; 35 | 36 | world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, 0.0D, 0.0D, 0.0D, new int[0]); 37 | } 38 | 39 | world.a((EntityHuman) null, blockposition, SoundEffects.bp, SoundCategory.BLOCKS, 1.0F, 1.0F); 40 | if (AkarinGlobalConfig.disableEndPortalCreate) return EnumInteractionResult.SUCCESS; // Akarin 41 | ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockEnderPortalFrame.e().a(world, blockposition); 42 | 43 | if (shapedetector_shapedetectorcollection != null) { 44 | BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a().a(-3, 0, -3); 45 | 46 | for (int j = 0; j < 3; ++j) { 47 | for (int k = 0; k < 3; ++k) { 48 | world.setTypeAndData(blockposition1.a(j, 0, k), Blocks.END_PORTAL.getBlockData(), 2); 49 | } 50 | } 51 | 52 | world.a(1038, blockposition1.a(1, 0, 1), 0); 53 | } 54 | 55 | return EnumInteractionResult.SUCCESS; 56 | } 57 | } else { 58 | return EnumInteractionResult.FAIL; 59 | } 60 | } 61 | 62 | @Override 63 | public InteractionResultWrapper a(World world, EntityHuman entityhuman, EnumHand enumhand) { 64 | ItemStack itemstack = entityhuman.b(enumhand); 65 | MovingObjectPosition movingobjectposition = this.a(world, entityhuman, false); 66 | 67 | if (movingobjectposition != null && movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK && world.getType(movingobjectposition.a()).getBlock() == Blocks.END_PORTAL_FRAME) { 68 | return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack); 69 | } else { 70 | entityhuman.c(enumhand); 71 | if (!world.isClientSide) { 72 | BlockPosition blockposition = ((WorldServer) world).getChunkProviderServer().a(world, "Stronghold", new BlockPosition(entityhuman), false); 73 | 74 | if (blockposition != null) { 75 | EntityEnderSignal entityendersignal = new EntityEnderSignal(world, entityhuman.locX, entityhuman.locY + entityhuman.length / 2.0F, entityhuman.locZ); 76 | 77 | entityendersignal.a(blockposition); 78 | world.addEntity(entityendersignal); 79 | if (entityhuman instanceof EntityPlayer) { 80 | CriterionTriggers.l.a((EntityPlayer) entityhuman, blockposition); 81 | } 82 | 83 | world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.bc, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemEnderEye.j.nextFloat() * 0.4F + 0.8F)); 84 | world.a((EntityHuman) null, 1003, new BlockPosition(entityhuman), 0); 85 | if (!entityhuman.abilities.canInstantlyBuild) { 86 | itemstack.subtract(1); 87 | } 88 | 89 | entityhuman.b(StatisticList.b(this)); 90 | return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack); 91 | } 92 | } 93 | 94 | return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /sources/src/main/java/co/aikar/timings/TimedChunkGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is licensed under the MIT License (MIT). 3 | * 4 | * Copyright (c) 2014-2016 Daniel Ennis 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | package co.aikar.timings; 26 | 27 | import net.minecraft.server.BiomeBase.BiomeMeta; 28 | import net.minecraft.server.BlockPosition; 29 | import net.minecraft.server.Chunk; 30 | import net.minecraft.server.EnumCreatureType; 31 | import net.minecraft.server.World; 32 | import net.minecraft.server.WorldServer; 33 | import org.bukkit.Location; 34 | import org.bukkit.craftbukkit.generator.InternalChunkGenerator; 35 | import org.bukkit.generator.BlockPopulator; 36 | 37 | import javax.annotation.Nullable; 38 | import java.util.List; 39 | import java.util.Random; 40 | 41 | public class TimedChunkGenerator extends InternalChunkGenerator { 42 | private final WorldServer world; 43 | private final InternalChunkGenerator timedGenerator; 44 | 45 | public TimedChunkGenerator(WorldServer worldServer, InternalChunkGenerator gen) { 46 | world = worldServer; 47 | timedGenerator = gen; 48 | } 49 | 50 | @Override 51 | @Deprecated 52 | public byte[] generate(org.bukkit.World world, Random random, int x, int z) { 53 | return timedGenerator.generate(world, random, x, z); 54 | } 55 | 56 | @Override 57 | @Deprecated 58 | public short[][] generateExtBlockSections(org.bukkit.World world, Random random, int x, int z, 59 | BiomeGrid biomes) { 60 | return timedGenerator.generateExtBlockSections(world, random, x, z, biomes); 61 | } 62 | 63 | @Override 64 | @Deprecated 65 | public byte[][] generateBlockSections(org.bukkit.World world, Random random, int x, int z, 66 | BiomeGrid biomes) { 67 | return timedGenerator.generateBlockSections(world, random, x, z, biomes); 68 | } 69 | 70 | @Override 71 | public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) { 72 | return timedGenerator.generateChunkData(world, random, x, z, biome); 73 | } 74 | 75 | @Override 76 | public boolean canSpawn(org.bukkit.World world, int x, int z) { 77 | return timedGenerator.canSpawn(world, x, z); 78 | } 79 | 80 | @Override 81 | public List getDefaultPopulators(org.bukkit.World world) { 82 | return timedGenerator.getDefaultPopulators(world); 83 | } 84 | 85 | @Override 86 | public Location getFixedSpawnLocation(org.bukkit.World world, Random random) { 87 | return timedGenerator.getFixedSpawnLocation(world, random); 88 | } 89 | 90 | @Override 91 | public Chunk getOrCreateChunk(int i, int j) { 92 | try (Timing ignored = world.timings.chunkGeneration.startTiming()) { 93 | return timedGenerator.getOrCreateChunk(i, j); 94 | } 95 | } 96 | 97 | @Override 98 | public void recreateStructures(int i, int j) { 99 | try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) { 100 | timedGenerator.recreateStructures(i, j); 101 | } 102 | } 103 | 104 | @Override 105 | public boolean a(Chunk chunk, int i, int j) { 106 | return timedGenerator.a(chunk, i, j); 107 | } 108 | 109 | @Override 110 | public List getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { 111 | return timedGenerator.getMobsFor(enumcreaturetype, blockposition); 112 | } 113 | 114 | @Override 115 | @Nullable 116 | public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, boolean flag) { 117 | return timedGenerator.findNearestMapFeature(world, s, blockposition, flag); 118 | } 119 | 120 | @Override 121 | public void recreateStructures(Chunk chunk, int i, int j) { 122 | try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) { 123 | timedGenerator.recreateStructures(chunk, i, j); 124 | } 125 | } 126 | 127 | @Override 128 | public boolean a(World world, String s, BlockPosition blockPosition) { 129 | return timedGenerator.a(world, s, blockPosition); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/core/AkarinSlackScheduler.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.core; 2 | 3 | import com.google.common.base.Predicate; 4 | import com.google.common.collect.Iterables; 5 | 6 | import io.akarin.api.internal.Akari; 7 | import net.minecraft.server.EntityPlayer; 8 | import net.minecraft.server.EnumDifficulty; 9 | import net.minecraft.server.MinecraftServer; 10 | import net.minecraft.server.PacketPlayOutKeepAlive; 11 | import net.minecraft.server.PacketPlayOutPlayerInfo; 12 | import net.minecraft.server.PacketPlayOutUpdateTime; 13 | import net.minecraft.server.PlayerConnection; 14 | import net.minecraft.server.WorldServer; 15 | 16 | public class AkarinSlackScheduler extends Thread { 17 | public static AkarinSlackScheduler get() { 18 | return Singleton.instance; 19 | } 20 | 21 | public void boot() { 22 | setName("Akarin Slack Scheduler Thread"); 23 | setDaemon(true); 24 | start(); 25 | Akari.logger.info("Slack scheduler service started"); 26 | } 27 | 28 | private static class Singleton { 29 | private static final AkarinSlackScheduler instance = new AkarinSlackScheduler(); 30 | } 31 | 32 | /* 33 | * Timers 34 | */ 35 | private long updateTime; 36 | private long resendPlayersInfo; 37 | 38 | @Override 39 | public void run() { 40 | MinecraftServer server = MinecraftServer.getServer(); 41 | 42 | while (server.isRunning()) { 43 | long startProcessTiming = System.currentTimeMillis(); 44 | // Send time updates to everyone, it will get the right time from the world the player is in. 45 | // Time update, from MinecraftServer#D 46 | if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) { 47 | for (EntityPlayer player : server.getPlayerList().players) { 48 | // Add support for per player time 49 | player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle"))); 50 | } 51 | updateTime = 0; 52 | } 53 | 54 | // Keep alive, from PlayerConnection#e 55 | for (EntityPlayer player : server.getPlayerList().players) { 56 | PlayerConnection conn = player.playerConnection; 57 | // Paper - give clients a longer time to respond to pings as per pre 1.12.2 timings 58 | // This should effectively place the keepalive handling back to "as it was" before 1.12.2 59 | long currentTime = System.nanoTime() / 1000000L; 60 | long elapsedTime = currentTime - conn.getLastPing(); 61 | if (conn.isPendingPing()) { 62 | // We're pending a ping from the client 63 | if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout) { // check keepalive limit, don't fire if already disconnected 64 | Akari.callbackQueue.add(() -> { 65 | Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info 66 | conn.disconnect("disconnect.timeout"); 67 | }); 68 | } 69 | } else { 70 | if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval) { // 15 seconds default 71 | conn.setPendingPing(true); 72 | conn.setLastPing(currentTime); 73 | conn.setKeepAliveID(currentTime); 74 | conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); // 15s lagg you should stop your server 75 | } 76 | } 77 | } 78 | 79 | // Force hardcore difficulty, from WorldServer#doTick 80 | if (AkarinGlobalConfig.forceHardcoreDifficulty) 81 | for (WorldServer world : server.worlds) { 82 | if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) { 83 | world.getWorldData().setDifficulty(EnumDifficulty.HARD); 84 | } 85 | } 86 | 87 | // Update player info, from PlayerList#tick 88 | if (++resendPlayersInfo > AkarinGlobalConfig.playersInfoUpdateInterval) { 89 | for (EntityPlayer player : server.getPlayerList().players) { 90 | player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(server.getPlayerList().players, new Predicate() { 91 | @Override 92 | public boolean apply(EntityPlayer each) { 93 | return player.getBukkitEntity().canSee(each.getBukkitEntity()); 94 | } 95 | }))); 96 | } 97 | resendPlayersInfo = 0; 98 | } 99 | 100 | try { 101 | long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming); 102 | if (sleepFixed > 0) Thread.sleep(sleepFixed); 103 | } catch (InterruptedException interrupted) { 104 | continue; 105 | } 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /sources/src/main/java/co/aikar/timings/WorldTimingsHandler.java: -------------------------------------------------------------------------------- 1 | package co.aikar.timings; 2 | 3 | import net.minecraft.server.World; 4 | 5 | /** 6 | * Set of timers per world, to track world specific timings. 7 | */ 8 | public class WorldTimingsHandler { 9 | public final Timing mobSpawn; 10 | public final Timing doChunkUnload; 11 | public final Timing doPortalForcer; 12 | public final Timing scheduledBlocks; 13 | public final Timing scheduledBlocksCleanup; 14 | public final Timing scheduledBlocksTicking; 15 | public final Timing chunkTicks; 16 | public final Timing lightChunk; 17 | public final Timing chunkTicksBlocks; 18 | public final Timing doVillages; 19 | public final Timing doChunkMap; 20 | public final Timing doChunkMapUpdate; 21 | public final Timing doChunkMapToUpdate; 22 | public final Timing doChunkMapSortMissing; 23 | public final Timing doChunkMapSortSendToPlayers; 24 | public final Timing doChunkMapPlayersNeedingChunks; 25 | public final Timing doChunkMapPendingSendToPlayers; 26 | public final Timing doChunkMapUnloadChunks; 27 | public final Timing doChunkGC; 28 | public final Timing doSounds; 29 | public final Timing entityRemoval; 30 | public final Timing entityTick; 31 | public final Timing tileEntityTick; 32 | public final Timing tileEntityPending; 33 | public final Timing tracker1; 34 | public final Timing tracker2; 35 | public final Timing doTick; 36 | public final Timing tickEntities; 37 | 38 | public final Timing syncChunkLoadTimer; 39 | public final Timing syncChunkLoadDataTimer; 40 | public final Timing syncChunkLoadStructuresTimer; 41 | public final Timing syncChunkLoadPostTimer; 42 | public final Timing syncChunkLoadNBTTimer; 43 | public final Timing syncChunkLoadPopulateNeighbors; 44 | public final Timing chunkGeneration; 45 | public final Timing chunkIOStage1; 46 | public final Timing chunkIOStage2; 47 | public final Timing worldSave; 48 | public final Timing worldSaveChunks; 49 | public final Timing worldSaveLevel; 50 | public final Timing chunkSaveData; 51 | 52 | public final Timing lightingQueueTimer; 53 | 54 | public WorldTimingsHandler(World server) { 55 | String name = server.worldData.getName() +" - "; 56 | 57 | mobSpawn = Timings.ofSafe(name + "mobSpawn"); 58 | doChunkUnload = Timings.ofSafe(name + "doChunkUnload"); 59 | scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks"); 60 | scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup"); 61 | scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking"); 62 | chunkTicks = Timings.ofSafe(name + "Chunk Ticks"); 63 | lightChunk = Timings.ofSafe(name + "Light Chunk"); 64 | chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks"); 65 | doVillages = Timings.ofSafe(name + "doVillages"); 66 | doChunkMap = Timings.ofSafe(name + "doChunkMap"); 67 | doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update"); 68 | doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update"); 69 | doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing"); 70 | doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players"); 71 | doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks"); 72 | doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players"); 73 | doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks"); 74 | doSounds = Timings.ofSafe(name + "doSounds"); 75 | doChunkGC = Timings.ofSafe(name + "doChunkGC"); 76 | doPortalForcer = Timings.ofSafe(name + "doPortalForcer"); 77 | entityTick = Timings.ofSafe(name + "entityTick"); 78 | entityRemoval = Timings.ofSafe(name + "entityRemoval"); 79 | tileEntityTick = Timings.ofSafe(name + "tileEntityTick"); 80 | tileEntityPending = Timings.ofSafe(name + "tileEntityPending"); 81 | 82 | syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad"); 83 | syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data"); 84 | syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures"); 85 | syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post"); 86 | syncChunkLoadNBTTimer = Timings.ofSafe(name + "chunkLoad - NBT"); 87 | syncChunkLoadPopulateNeighbors = Timings.ofSafe(name + "chunkLoad - Populate Neighbors"); 88 | chunkGeneration = Timings.ofSafe(name + "chunkGeneration"); 89 | chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO"); 90 | chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load"); 91 | worldSave = Timings.ofSafe(name + "World Save"); 92 | worldSaveLevel = Timings.ofSafe(name + "World Save - Level"); 93 | worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks"); 94 | chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data"); 95 | 96 | tracker1 = Timings.ofSafe(name + "tracker stage 1"); 97 | tracker2 = Timings.ofSafe(name + "tracker stage 2"); 98 | doTick = Timings.ofSafe(name + "doTick"); 99 | tickEntities = Timings.ofSafe(name + "tickEntities"); 100 | 101 | lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue"); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/server/mixin/bootstrap/ParallelRegistry.java: -------------------------------------------------------------------------------- 1 | package io.akarin.server.mixin.bootstrap; 2 | 3 | import java.util.concurrent.ExecutorService; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | import io.akarin.api.internal.Akari; 14 | import net.minecraft.server.BiomeBase; 15 | import net.minecraft.server.Block; 16 | import net.minecraft.server.BlockFire; 17 | import net.minecraft.server.DispenserRegistry; 18 | import net.minecraft.server.Enchantment; 19 | import net.minecraft.server.EntityTypes; 20 | import net.minecraft.server.Item; 21 | import net.minecraft.server.MobEffectList; 22 | import net.minecraft.server.PotionBrewer; 23 | import net.minecraft.server.PotionRegistry; 24 | import net.minecraft.server.SoundEffect; 25 | 26 | @Mixin(value = DispenserRegistry.class, remap = false) 27 | public abstract class ParallelRegistry { 28 | /** 29 | * Registry order: SoundEffect -> Block 30 | */ 31 | private static final ExecutorService STAGE_BLOCK = Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY); 32 | /** 33 | * Registry order: Item -> PotionBrewer & orderless: BlockFire, BiomeBase (After STAGE_BLOCK) 34 | */ 35 | private static final ExecutorService STAGE_BLOCK_BASE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY); 36 | 37 | /** 38 | * Registry order: MobEffectList -> PotionRegistry & orderless: Enchantment, EntityTypes 39 | */ 40 | private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY); 41 | 42 | // We should keep the original order in codes thought orderless in runtime 43 | @Redirect(method = "c()V", at = @At( 44 | value = "INVOKE", 45 | target = "net/minecraft/server/SoundEffect.b()V" 46 | )) 47 | private static void soundEffect() { 48 | STAGE_BLOCK.execute(() -> { 49 | SoundEffect.b(); 50 | Block.w(); 51 | 52 | STAGE_BLOCK_BASE.execute(() -> BlockFire.e()); // This single task only cost ~4ms, however, firing a task only takes ~1ms 53 | STAGE_BLOCK_BASE.execute(() -> { 54 | Item.t(); 55 | PotionBrewer.a(); 56 | }); 57 | STAGE_BLOCK_BASE.execute(() -> BiomeBase.q()); 58 | }); 59 | } 60 | 61 | @Redirect(method = "c()V", at = @At( 62 | value = "INVOKE", 63 | target = "net/minecraft/server/Block.w()V" 64 | )) 65 | private static void block() {} // STAGE_BLOCK 66 | 67 | @Redirect(method = "c()V", at = @At( 68 | value = "INVOKE", 69 | target = "net/minecraft/server/BlockFire.e()V" 70 | )) 71 | private static void blockFire() {} // STAGE_BLOCK_BASE 72 | 73 | @Redirect(method = "c()V", at = @At( 74 | value = "INVOKE", 75 | target = "net/minecraft/server/MobEffectList.k()V" 76 | )) 77 | private static void mobEffectList() {} // STAGE_STANDALONE 78 | 79 | @Redirect(method = "c()V", at = @At( 80 | value = "INVOKE", 81 | target = "net/minecraft/server/Enchantment.g()V" 82 | )) 83 | private static void enchantment() { 84 | STAGE_STANDALONE.execute(() -> Enchantment.g()); 85 | STAGE_STANDALONE.execute(() -> EntityTypes.c()); 86 | STAGE_STANDALONE.execute(() -> { 87 | MobEffectList.k(); 88 | PotionRegistry.b(); 89 | }); 90 | } 91 | 92 | @Redirect(method = "c()V", at = @At( 93 | value = "INVOKE", 94 | target = "net/minecraft/server/Item.t()V" 95 | )) 96 | private static void item() {} // STAGE_BLOCK_BASE 97 | 98 | @Redirect(method = "c()V", at = @At( 99 | value = "INVOKE", 100 | target = "net/minecraft/server/PotionRegistry.b()V" 101 | )) 102 | private static void potionRegistry() {} // STAGE_STANDALONE 103 | 104 | @Redirect(method = "c()V", at = @At( 105 | value = "INVOKE", 106 | target = "net/minecraft/server/PotionBrewer.a()V" 107 | )) 108 | private static void potionBrewer() {} // STAGE_BLOCK_BASE 109 | 110 | @Redirect(method = "c()V", at = @At( 111 | value = "INVOKE", 112 | target = "net/minecraft/server/EntityTypes.c()V" 113 | )) 114 | private static void entityTypes() {} // STAGE_STANDALONE 115 | 116 | @Redirect(method = "c()V", at = @At( 117 | value = "INVOKE", 118 | target = "net/minecraft/server/BiomeBase.q()V" 119 | )) 120 | private static void biomeBase() {} // STAGE_BLOCK_BASE 121 | 122 | @Inject(method = "c()V", at = @At( 123 | value = "INVOKE", 124 | target = "net/minecraft/server/DispenserRegistry.b()V", 125 | shift = At.Shift.BEFORE 126 | )) 127 | private static void await(CallbackInfo info) throws InterruptedException { 128 | // Shutdown BLOCK and STANDALONE stage 129 | STAGE_STANDALONE.shutdown(); 130 | STAGE_BLOCK.shutdown(); 131 | STAGE_BLOCK.awaitTermination(10, TimeUnit.MINUTES); 132 | 133 | STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated 134 | STAGE_BLOCK_BASE.awaitTermination(20, TimeUnit.MINUTES); 135 | 136 | STAGE_STANDALONE.awaitTermination(30, TimeUnit.MINUTES); // Behind the shutdown of BLOCK_BASE should faster 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /sources/src/main/java/co/aikar/timings/MinecraftTimings.java: -------------------------------------------------------------------------------- 1 | package co.aikar.timings; 2 | 3 | import com.google.common.collect.MapMaker; 4 | import net.minecraft.server.*; 5 | import org.bukkit.plugin.Plugin; 6 | import org.bukkit.scheduler.BukkitTask; 7 | 8 | import org.bukkit.craftbukkit.scheduler.CraftTask; 9 | 10 | import java.util.Map; 11 | 12 | public final class MinecraftTimings { 13 | 14 | public static final Timing playerListTimer = Timings.ofSafe("Player List"); 15 | public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); 16 | public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); 17 | public static final Timing tickablesTimer = Timings.ofSafe("Tickables"); 18 | public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler"); 19 | public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler"); 20 | public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending"); 21 | public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing"); 22 | public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick"); 23 | public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update"); 24 | public static final Timing serverCommandTimer = Timings.ofSafe("Server Command"); 25 | public static final Timing savePlayers = Timings.ofSafe("Save Players"); 26 | 27 | public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity"); 28 | public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity"); 29 | public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing"); 30 | public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks"); 31 | public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation"); 32 | 33 | public static final Timing processQueueTimer = Timings.ofSafe("processQueue"); 34 | 35 | public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand"); 36 | 37 | public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck"); 38 | 39 | public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); 40 | public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); 41 | 42 | private static final Map, String> taskNameCache = new MapMaker().weakKeys().makeMap(); 43 | 44 | private MinecraftTimings() {} 45 | 46 | /** 47 | * Gets a timer associated with a plugins tasks. 48 | * @param bukkitTask 49 | * @param period 50 | * @return 51 | */ 52 | public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) { 53 | if (!bukkitTask.isSync()) { 54 | return NullTimingHandler.NULL; 55 | } 56 | Plugin plugin; 57 | 58 | Runnable task = ((CraftTask) bukkitTask).task; 59 | 60 | final Class taskClass = task.getClass(); 61 | if (bukkitTask.getOwner() != null) { 62 | plugin = bukkitTask.getOwner(); 63 | } else { 64 | plugin = TimingsManager.getPluginByClassloader(taskClass); 65 | } 66 | 67 | final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz -> 68 | clazz.isAnonymousClass() || clazz.isLocalClass() 69 | ? clazz.getName() 70 | : clazz.getCanonicalName()); 71 | 72 | StringBuilder name = new StringBuilder(64); 73 | name.append("Task: ").append(taskname); 74 | if (period > 0) { 75 | name.append(" (interval:").append(period).append(")"); 76 | } else { 77 | name.append(" (Single)"); 78 | } 79 | 80 | if (plugin == null) { 81 | return Timings.ofSafe(null, name.toString()); 82 | } 83 | 84 | return Timings.ofSafe(plugin, name.toString()); 85 | } 86 | 87 | /** 88 | * Get a named timer for the specified entity type to track type specific timings. 89 | * @param entity 90 | * @return 91 | */ 92 | public static Timing getEntityTimings(Entity entity) { 93 | String entityType = entity.getClass().getName(); 94 | return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer); 95 | } 96 | 97 | /** 98 | * Get a named timer for the specified tile entity type to track type specific timings. 99 | * @param entity 100 | * @return 101 | */ 102 | public static Timing getTileEntityTimings(TileEntity entity) { 103 | String entityType = entity.getClass().getName(); 104 | return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer); 105 | } 106 | public static Timing getCancelTasksTimer() { 107 | return Timings.ofSafe("Cancel Tasks"); 108 | } 109 | public static Timing getCancelTasksTimer(Plugin plugin) { 110 | return Timings.ofSafe(plugin, "Cancel Tasks"); 111 | } 112 | 113 | public static void stopServer() { 114 | TimingsManager.stopServer(); 115 | } 116 | 117 | public static Timing getBlockTiming(Block block) { 118 | return Timings.ofSafe("## Scheduled Block: " + block.getName(), scheduledBlocksTimer); 119 | } 120 | 121 | public static Timing getStructureTiming(StructureGenerator structureGenerator) { 122 | return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer); 123 | } 124 | 125 | public static Timing getPacketTiming(Packet packet) { 126 | return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sources/src/main/java/io/akarin/api/internal/Akari.java: -------------------------------------------------------------------------------- 1 | package io.akarin.api.internal; 2 | 3 | import java.lang.reflect.Field; 4 | import java.lang.reflect.Method; 5 | import java.util.Queue; 6 | import java.util.concurrent.ExecutorCompletionService; 7 | import java.util.concurrent.LinkedBlockingQueue; 8 | import java.util.concurrent.ThreadFactory; 9 | import java.util.concurrent.TimeUnit; 10 | import java.util.concurrent.locks.ReentrantLock; 11 | 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.apache.logging.log4j.LogManager; 14 | import org.apache.logging.log4j.Logger; 15 | import com.google.common.collect.Queues; 16 | import com.google.common.util.concurrent.ThreadFactoryBuilder; 17 | 18 | import co.aikar.timings.Timing; 19 | import co.aikar.timings.Timings; 20 | import io.akarin.api.internal.Akari.AssignableFactory; 21 | import io.akarin.api.internal.Akari.TimingSignal; 22 | import io.akarin.api.internal.utils.ReentrantSpinningLock; 23 | import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService; 24 | import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor; 25 | import io.akarin.server.core.AkarinGlobalConfig; 26 | import net.minecraft.server.MinecraftServer; 27 | import net.minecraft.server.World; 28 | import net.minecraft.server.WorldServer; 29 | 30 | @SuppressWarnings("restriction") 31 | public abstract class Akari { 32 | /** 33 | * A common logger used by mixin classes 34 | */ 35 | public final static Logger logger = LogManager.getLogger("Akarin"); 36 | 37 | /** 38 | * A common thread pool factory 39 | */ 40 | public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Parallel Registry Thread - %1$d").build(); 41 | 42 | /** 43 | * Main thread callback tasks 44 | */ 45 | public static final Queue callbackQueue = Queues.newConcurrentLinkedQueue(); 46 | 47 | public static class AssignableThread extends Thread { 48 | public AssignableThread(Runnable run) { 49 | super(run); 50 | } 51 | public AssignableThread() { 52 | super(); 53 | } 54 | } 55 | 56 | public static class AssignableFactory implements ThreadFactory { 57 | private final String threadName; 58 | private int threadNumber; 59 | 60 | public AssignableFactory(String name) { 61 | threadName = name; 62 | } 63 | 64 | @Override 65 | public Thread newThread(Runnable run) { 66 | Thread thread = new AssignableThread(run); 67 | thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++))); 68 | thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair 69 | return thread; 70 | } 71 | } 72 | 73 | public static class TimingSignal { 74 | public final World tickedWorld; 75 | public final boolean isEntities; 76 | 77 | public TimingSignal(World world, boolean entities) { 78 | tickedWorld = world; 79 | isEntities = entities; 80 | } 81 | } 82 | 83 | public static SuspendableExecutorCompletionService STAGE_TICK; 84 | 85 | static { 86 | resizeTickExecutors(3); 87 | } 88 | 89 | public static void resizeTickExecutors(int worlds) { 90 | int parallelism; 91 | switch (AkarinGlobalConfig.parallelMode) { 92 | case -1: 93 | return; 94 | case 0: 95 | parallelism = 2; 96 | break; 97 | case 1: 98 | parallelism = worlds + 1; 99 | break; 100 | case 2: 101 | default: 102 | parallelism = worlds * 2; 103 | break; 104 | } 105 | STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism, 106 | 0L, TimeUnit.MILLISECONDS, 107 | new LinkedBlockingQueue(), 108 | new AssignableFactory("Akarin Parallel Ticking Thread - $"))); 109 | } 110 | 111 | public static boolean isPrimaryThread() { 112 | return isPrimaryThread(true); 113 | } 114 | 115 | public static boolean isPrimaryThread(boolean assign) { 116 | Thread current = Thread.currentThread(); 117 | return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false); 118 | } 119 | 120 | public static final String EMPTY_STRING = ""; 121 | 122 | /* 123 | * The unsafe 124 | */ 125 | public final static sun.misc.Unsafe UNSAFE = getUnsafe(); 126 | 127 | private static sun.misc.Unsafe getUnsafe() { 128 | try { 129 | Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); 130 | theUnsafe.setAccessible(true); 131 | return (sun.misc.Unsafe) theUnsafe.get(null); 132 | } catch (Throwable t) { 133 | t.printStackTrace(); 134 | return null; 135 | } 136 | } 137 | 138 | private static final String serverVersion = Akari.class.getPackage().getImplementationVersion(); 139 | 140 | public static String getServerVersion() { 141 | return serverVersion + " (MC: " + MinecraftServer.getServer().getVersion() + ")"; 142 | } 143 | 144 | /* 145 | * Timings 146 | */ 147 | public final static Timing worldTiming = getTiming("Akarin - Full World Tick"); 148 | 149 | public final static Timing callbackTiming = getTiming("Akarin - Callback Queue"); 150 | 151 | private static Timing getTiming(String name) { 152 | try { 153 | Method ofSafe = Timings.class.getDeclaredMethod("ofSafe", String.class); 154 | ofSafe.setAccessible(true); 155 | return (Timing) ofSafe.invoke(null, name); 156 | } catch (Throwable t) { 157 | t.printStackTrace(); 158 | return null; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /sources/src/main/java/net/minecraft/server/MethodProfiler.java: -------------------------------------------------------------------------------- 1 | package net.minecraft.server; 2 | 3 | import com.google.common.collect.Lists; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.Iterator; 7 | import java.util.List; 8 | import java.util.function.Supplier; 9 | 10 | 11 | import it.unimi.dsi.fastutil.longs.LongArrayList; 12 | import it.unimi.dsi.fastutil.objects.Object2LongMap; 13 | import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; 14 | import it.unimi.dsi.fastutil.objects.ObjectArrayList; 15 | import it.unimi.dsi.fastutil.objects.ObjectIterator; 16 | import org.apache.logging.log4j.LogManager; 17 | import org.apache.logging.log4j.Logger; 18 | 19 | public class MethodProfiler { 20 | 21 | public static final boolean ENABLED = Boolean.getBoolean("enableDebugMethodProfiler"); // CraftBukkit - disable unless specified in JVM arguments 22 | private static final Logger b = LogManager.getLogger(); 23 | private final ObjectArrayList c = new ObjectArrayList<>(); // Dionysus 24 | private final LongArrayList d = new LongArrayList(); // Dionysus 25 | public boolean a; 26 | private String e = ""; 27 | private final Object2LongOpenHashMap f = new Object2LongOpenHashMap<>(); 28 | 29 | public MethodProfiler() {} 30 | 31 | public void a() { 32 | if (!ENABLED) return; // CraftBukkit 33 | this.f.clear(); 34 | this.e = ""; 35 | this.c.clear(); 36 | } 37 | 38 | public void a(String s) { 39 | if (!ENABLED) return; // CraftBukkit 40 | if (this.a) { 41 | if (!this.e.isEmpty()) { 42 | this.e = this.e + "."; 43 | } 44 | 45 | this.e = this.e + s; 46 | this.c.add(this.e); 47 | this.d.add(Long.valueOf(System.nanoTime())); 48 | } 49 | } 50 | 51 | public void a(Supplier supplier) { 52 | if (!ENABLED) return; // CraftBukkit 53 | if (this.a) { 54 | this.a((String) supplier.get()); 55 | } 56 | } 57 | 58 | public void b() { 59 | if (!ENABLED) return; // CraftBukkit 60 | if (this.a) { 61 | long i = System.nanoTime(); 62 | long j = this.d.removeLong(this.d.size() - 1); 63 | 64 | this.c.remove(this.c.size() - 1); 65 | long k = i - j; 66 | 67 | if (this.f.containsKey(this.e)) { 68 | this.f.put(this.e, this.f.get(this.e) + k); 69 | } else { 70 | this.f.put(this.e, k); 71 | } 72 | 73 | if (k > 100000000L) { 74 | MethodProfiler.b.warn("Something\'s taking too long! \'{}\' took aprox {} ms", this.e, Double.valueOf((double) k / 1000000.0D)); 75 | } 76 | 77 | this.e = this.c.isEmpty() ? "" : (String) this.c.get(this.c.size() - 1); 78 | } 79 | } 80 | 81 | public List b(String s) { 82 | if (!ENABLED || !this.a) { // CraftBukkit 83 | return Collections.emptyList(); 84 | } else { 85 | long i = this.f.getOrDefault("root", 0L); 86 | long j = this.f.getOrDefault(s, -1L); 87 | ArrayList arraylist = Lists.newArrayList(); 88 | 89 | if (!s.isEmpty()) { 90 | s = s + "."; 91 | } 92 | 93 | long k = 0L; 94 | for (String s1 : this.f.keySet()) { 95 | if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) { 96 | k += this.f.getLong(s1); 97 | } 98 | } 99 | 100 | float f = (float) k; 101 | 102 | if (k < j) { 103 | k = j; 104 | } 105 | 106 | if (i < k) { 107 | i = k; 108 | } 109 | 110 | for (Object2LongMap.Entry entry : this.f.object2LongEntrySet()) { 111 | String s2 = entry.getKey(); 112 | if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) { 113 | long l = this.f.getLong(s2); 114 | double d0 = (double) l * 100.0D / (double) k; 115 | double d1 = (double) l * 100.0D / (double) i; 116 | String s3 = s2.substring(s.length()); 117 | 118 | arraylist.add(new MethodProfiler.ProfilerInfo(s3, d0, d1)); 119 | } 120 | entry.setValue(entry.getLongValue() * 999L / 1000L); 121 | } 122 | 123 | if ((float) k > f) { 124 | arraylist.add(new MethodProfiler.ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i)); 125 | } 126 | 127 | Collections.sort(arraylist); 128 | arraylist.add(0, new MethodProfiler.ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i)); 129 | return arraylist; 130 | } 131 | } 132 | 133 | public void c(String s) { 134 | if (!ENABLED) return; // CraftBukkit 135 | this.b(); 136 | this.a(s); 137 | } 138 | 139 | public String c() { 140 | if (!ENABLED) return "[DISABLED]"; // CraftBukkit 141 | return this.c.isEmpty() ? "[UNKNOWN]" : (String) this.c.get(this.c.size() - 1); 142 | } 143 | 144 | public static final class ProfilerInfo implements Comparable { 145 | 146 | public double a; 147 | public double b; 148 | public String c; 149 | 150 | public ProfilerInfo(String s, double d0, double d1) { 151 | this.c = s; 152 | this.a = d0; 153 | this.b = d1; 154 | } 155 | 156 | public int a(MethodProfiler.ProfilerInfo methodprofiler_profilerinfo) { 157 | return methodprofiler_profilerinfo.a < this.a ? -1 : (methodprofiler_profilerinfo.a > this.a ? 1 : methodprofiler_profilerinfo.c.compareTo(this.c)); 158 | } 159 | 160 | public int compareTo(MethodProfiler.ProfilerInfo object) { // CraftBukkit: decompile error 161 | return this.a((MethodProfiler.ProfilerInfo) object); 162 | } 163 | } 164 | } 165 | --------------------------------------------------------------------------------