diff --git a/sources/src/main/java/io/akarin/api/LogWrapper.java b/sources/src/main/java/io/akarin/api/LogWrapper.java new file mode 100644 index 000000000..d86981b76 --- /dev/null +++ b/sources/src/main/java/io/akarin/api/LogWrapper.java @@ -0,0 +1,8 @@ +package io.akarin.api; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public abstract class LogWrapper { + public final static Logger logger = LogManager.getLogger("Akarin"); +} diff --git a/sources/src/main/java/io/akarin/api/mixin/MixinVersionCommand.java b/sources/src/main/java/io/akarin/api/mixin/MixinVersionCommand.java deleted file mode 100644 index 1f1bafe2e..000000000 --- a/sources/src/main/java/io/akarin/api/mixin/MixinVersionCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.akarin.api.mixin; - -import org.bukkit.Bukkit; -import org.bukkit.command.defaults.VersionCommand; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -@Mixin(value = VersionCommand.class, remap = false) -public class MixinVersionCommand { - @Shadow private static int getFromRepo(String repo, String hash) { return 0; } - - /** - * Match current version with repository and calculate the distance - * @param repo - * @param verInfo - * @return Version distance from lastest - */ - @Overwrite - private static int getDistance(String repo, String verInfo) { - verInfo = verInfo.replace("\"", ""); - return getFromRepo("Akarin-project/Akarin", verInfo); - } - - /** - * A workaround for unexpected calling - * @param currentVer - * @return Version distance from lastest - */ - @Overwrite - private static int getFromJenkins(int currentVer) { - String[] parts = Bukkit.getVersion().substring("git-Akarin-".length()).split("[-\\s]"); - return getFromRepo("Akarin-project/Akarin", parts[0]); - } - - @Shadow private void setVersionMessage(String msg) {} - private static boolean customVersion; - - @Overwrite - private void obtainVersion() { - // Skipping if detected - if (customVersion) return; - - String version = Bukkit.getVersion(); - if (version == null) version = "Unique"; // Custom - > Unique - - if (version.startsWith("git-Akarin-")) { - String[] parts = version.substring("git-Akarin-".length()).split("[-\\s]"); - int distance = getDistance(null, parts[0]); - switch (distance) { - case -1: - setVersionMessage("Error obtaining version information"); - break; - case 0: - setVersionMessage("You are running the latest version"); - break; - case -2: - setVersionMessage("Unknown version"); - break; - default: - setVersionMessage("You are " + distance + " version(s) behind"); - } - } else { - customVersion = true; - } - } -} diff --git a/sources/src/main/java/io/akarin/server/mixin/core/Bootstrap.java b/sources/src/main/java/io/akarin/server/mixin/core/Bootstrap.java index 6460540d9..9ef5f801b 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/Bootstrap.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/Bootstrap.java @@ -2,8 +2,6 @@ package io.akarin.server.mixin.core; import java.io.PrintStream; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.bukkit.craftbukkit.Main; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -11,10 +9,10 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import io.akarin.api.LogWrapper; + @Mixin(value = Main.class, remap = false) public class Bootstrap { - private final static Logger logger = LogManager.getLogger("Akarin"); - @Inject(method = "main([Ljava/lang/String;)V", at = @At("HEAD")) private static void configureMixin(CallbackInfo info) { ; @@ -29,7 +27,7 @@ public class Bootstrap { args = "ldc=*** Warning, you've not updated in a while! ***" )) private static void notifyUpdate(PrintStream stream, String text) { - logger.warn("You've not updated in a while, the current version may outdated"); + LogWrapper.logger.warn("You've not updated in a while, the current version may outdated"); } @Redirect(method = "main", at = @At( @@ -38,7 +36,7 @@ public class Bootstrap { args = "ldc=*** Please download a new build as per instructions from https://paperci.emc.gs/ ***" )) private static void notifyWebsite(PrintStream stream, String text) { - logger.warn("Visit our website for latest information https://akarin.io/"); + LogWrapper.logger.warn("Visit our website for latest information https://akarin.io/"); } @Redirect(method = "main", at = @At( @@ -47,6 +45,6 @@ public class Bootstrap { args = "ldc=Loading libraries, please wait..." )) private static void notifyLoading(PrintStream stream, String text) { - logger.info("Loading libraries, please wait.."); + LogWrapper.logger.info("Loading libraries, please wait.."); } } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinVersionCommand.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinVersionCommand.java new file mode 100644 index 000000000..37a965e91 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinVersionCommand.java @@ -0,0 +1,144 @@ +package io.akarin.server.mixin.core; + +import java.util.Set; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.VersionCommand; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import io.akarin.api.LogWrapper; +import net.minecraft.server.MCUtil; + +@Mixin(value = VersionCommand.class, remap = false) +public class MixinVersionCommand { + @Shadow private static int getFromRepo(String repo, String hash) { return 0; } + + /** + * Match current version with repository and calculate the distance + * @param repo + * @param verInfo + * @return Version distance from lastest + */ + @Overwrite + private static int getDistance(String repo, String verInfo) { + verInfo = verInfo.replace("\"", ""); + return getFromRepo("Akarin-project/Akarin", verInfo); + } + + /** + * A workaround for unexpected calling + * @param currentVer + * @return Version distance from lastest + */ + @Overwrite + private static int getFromJenkins(int currentVer) { + String[] parts = Bukkit.getVersion().substring("git-Akarin-".length()).split("[-\\s]"); + return getFromRepo("Akarin-project/Akarin", parts[0]); + } + + @Shadow private boolean hasVersion; + @Shadow private String versionMessage; + @Shadow private @Final Set versionWaiters; + + private volatile boolean versionObtaining; + private long lastCheckMillis; + + // The name can lead to misunderstand, + // this method doesn't send the whole version message (e.g. 'This server is running {} version' or 'Previous version'), + // it is only responsible for checking the version distance! + @Overwrite + private void sendVersion(CommandSender sender) { + // Skipping if already detected a custom version identifier (e.g. 'git-Akarin-') + // This should be lying in 'obtainVersion' method, but bump for faster returning + if (customVersion) return; + + if (versionObtaining) return; + // The volatile guarantees the safety between different threads. + // Remembers that we are still on main thread now, + // it's not guarantee that what time the last check ends, + // but it's guarantee that only one request will be accepted at the same time. + // After this, de-sync operations are safety - without lock - + // (this is really a special case that we cancel new tasks instead let them wait). + versionObtaining = true; + + if (hasVersion) { + long current = System.currentTimeMillis(); + if (current - lastCheckMillis > 7200000 /* 2 hours */) { + lastCheckMillis = current; + obtainVersion(sender); + /* TODO Option: legacy-versioning-compat */ currentSender = sender; + } else { + sender.sendMessage(versionMessage); + return; + } + } + } + + private CommandSender currentSender; + private boolean customVersion; + + @Overwrite + private void obtainVersion() { + if (false /* TODO Option: legacy-versioning-compat */) { + /* TODO Option: legacy-versioning-compat = true */ + LogWrapper.logger.warn("A legacy version lookup caught, legacy-versioning-compat enabled forcely!"); + throw new UnsupportedOperationException(); + } else { + obtainVersion(currentSender); + currentSender = null; + } + } + + private void obtainVersion(CommandSender sender) { + // We post all things because a custom version is rare (expiring is not rare), + // and we'd better post this task as early as we can, since it's a will (horrible destiny). + MCUtil.scheduleAsyncTask(() -> { + // This should be lying in 'sendVersion' method, but comes here for relax main thread + versionWaiters.add(sender); + + String version = Bukkit.getVersion(); + if (version == null) { + version = "Unique"; // Custom - > Unique + customVersion = true; + return; + } + + if (version.startsWith("git-Akarin-")) { + + String[] parts = version.substring("git-Akarin-".length()).split("[-\\s]"); + int distance = getDistance(null, parts[0]); + switch (distance) { + case -1: + setVersionMessage("Error obtaining version information"); + break; + case 0: + setVersionMessage("You are running the latest version"); + break; + case -2: + setVersionMessage("Unknown version"); + break; + default: + setVersionMessage("You are " + distance + " version(s) behind"); + } + } else { + customVersion = true; + } + + versionObtaining = false; + }); + } + + @Overwrite + private void setVersionMessage(String message) { + versionMessage = message; + hasVersion = true; + + for (CommandSender sender : versionWaiters) { + sender.sendMessage(versionMessage); + } + versionWaiters.clear(); + } +}