├── .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, Chunk, Runnable, RuntimeException> 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
2 | [](https://akarin.io)
3 | [](https://discord.gg/fw2pJAj)
4 | [](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 | 
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 extends Future super Void>>[] 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 extends EntityHuman> list = entity != null ? entity.world.players : this.a.getPlayerList().v();
72 | Iterator extends EntityHuman> 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 extends Runnable> 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 |
--------------------------------------------------------------------------------