From f105fcb584f1999cd63efc02ee62486defffa4d8 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 19 Jun 2026 14:53:42 +0200 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=94=A7=20chore:=20update=20dependenci?= =?UTF-8?q?es=20and=20fix=20type=20annotations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - update version in gradle.properties from 3.25.0 to 3.26.0 - update adventure-api version from 4.26.1 to 5.1.1 in libs.versions.toml - add slf4j as a new dependency in build.gradle.kts - fix type annotations for ClickEvent in multiple files --- gradle.properties | 2 +- gradle/libs.versions.toml | 4 +- .../impl/nbt/FastCompoundBinaryTagImpl.kt | 18 +-- .../surf-api-core/api/surf-api-core.api | 140 +++++++++++------- .../api/core/messages/CommonComponents.kt | 55 +++++++ .../messages/adventure/callback-extension.kt | 20 +-- .../messages/adventure/component-extension.kt | 24 +-- .../messages/adventure/identity-extensions.kt | 2 +- .../messages/builder/SurfComponentBuilder.kt | 105 ++++++++----- .../builder/SurfComponentBuilderImpl.kt | 40 +++-- .../PaginationClickEventProvider.kt | 8 +- .../messages/pagination/PaginationRenderer.kt | 12 +- .../api/core/nbt/FastCompoundBinaryTag.kt | 80 ++++++---- .../slne/surf/api/core/util/service-util.kt | 8 +- .../dialog/builder/DialogActionBuilder.kt | 2 +- .../surf-api-shared-public/build.gradle.kts | 1 + 16 files changed, 319 insertions(+), 202 deletions(-) diff --git a/gradle.properties b/gradle.properties index 3a2181ad7..1ad722fc1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.2 group=dev.slne.surf.api -version=3.25.0 +version=3.26.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c874b51c6..3fa8f7aa7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ luckpermsplugin-bukkit = "v5.5.17-bukkit" # "luckpermsplugin" is written without scoreboard-library = "2.7.4" # Adventure -adventure-api = "4.26.1" +adventure-api = "5.1.1" # Velocity velocity-api = "3.5.0-SNAPSHOT" @@ -35,6 +35,7 @@ placeholder-api = "2.12.2" mccoroutine = "2.22.2" # Miscellaneous Libraries +slf4j = "2.0.1" guava = "33.6.0-jre" caffeine = "3.2.4" caffeine-courotines = "3.0.4" @@ -128,6 +129,7 @@ mccoroutine-velocity-api = { module = "dev.slne.forks.mccoroutine:mccoroutine-ve mccoroutine-velocity-core = { module = "dev.slne.forks.mccoroutine:mccoroutine-velocity-core", version.ref = "mccoroutine" } # Miscellaneous Libraries +slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } guava = { module = "com.google.guava:guava", version.ref = "guava" } caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" } caffeine-courotines = { module = "com.sksamuel.aedile:aedile-core", version.ref = "caffeine-courotines" } diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/nbt/FastCompoundBinaryTagImpl.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/nbt/FastCompoundBinaryTagImpl.kt index bd838b692..c60a63fc2 100644 --- a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/nbt/FastCompoundBinaryTagImpl.kt +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/impl/nbt/FastCompoundBinaryTagImpl.kt @@ -5,8 +5,6 @@ import dev.slne.surf.api.core.util.freeze import dev.slne.surf.api.core.util.mutableObject2ObjectMapOf import dev.slne.surf.api.core.util.synchronize import net.kyori.adventure.nbt.* -import net.kyori.examination.ExaminableProperty -import net.kyori.examination.string.StringExaminer import java.util.function.Consumer import java.util.stream.Stream @@ -22,7 +20,6 @@ class FastCompoundBinaryTagImpl(synchronize: Boolean) : FastCompoundBinaryTag { override fun contains(key: String, type: BinaryTagType<*>): Boolean { val tag = tags[key] ?: return false - return type.test(tag.type()) } @@ -91,7 +88,6 @@ class FastCompoundBinaryTagImpl(synchronize: Boolean) : FastCompoundBinaryTag { override fun getByteArray(key: String, defaultValue: ByteArray?): ByteArray? { val tag = tags[key] as? ByteArrayBinaryTag ?: return defaultValue - return tag.value() } @@ -137,21 +133,15 @@ class FastCompoundBinaryTagImpl(synchronize: Boolean) : FastCompoundBinaryTag { override fun stream(): Stream> = tags.entries.stream() - override fun examinableProperties(): Stream = Stream.of( - ExaminableProperty.of("tags", tags) - ) - override fun iterator() = tags.object2ObjectEntrySet().iterator() override fun forEach(action: Consumer>) { tags.object2ObjectEntrySet().forEach(action) } - override fun examinableName(): String { - return type().toString() + override fun asBinaryTag(): CompoundBinaryTag { + return CompoundBinaryTag.from(tags) } - override fun toString(): String { - return examine(StringExaminer.simpleEscaping()) - } -} \ No newline at end of file + override fun toString(): String = "FastCompoundBinaryTag{tags=$tags}" +} diff --git a/surf-api-core/surf-api-core/api/surf-api-core.api b/surf-api-core/surf-api-core/api/surf-api-core.api index 7a087f62a..53872fe00 100644 --- a/surf-api-core/surf-api-core/api/surf-api-core.api +++ b/surf-api-core/surf-api-core/api/surf-api-core.api @@ -7160,16 +7160,22 @@ public final class dev/slne/surf/api/core/messages/CommonComponents { public final fun formatTime-gRj5Bb8 (JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;)Lnet/kyori/adventure/text/Component; public static synthetic fun formatTime-gRj5Bb8$default (Ldev/slne/surf/api/core/messages/CommonComponents;JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Lnet/kyori/adventure/text/Component; public final synthetic fun getEM_DASH ()Lnet/kyori/adventure/text/TextComponent; + public final fun renderDisconnectMessage (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; + public final fun renderDisconnectMessage (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; public final fun renderDisconnectMessage (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; public final fun renderDisconnectMessage (Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; public final fun renderDisconnectMessage (Lnet/kyori/adventure/text/TextComponent$Builder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; public final fun renderDisconnectMessage (Lnet/kyori/adventure/text/TextComponent$Builder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; + public static synthetic fun renderDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; public static synthetic fun renderDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; public static synthetic fun renderDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Lnet/kyori/adventure/text/TextComponent$Builder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; + public final fun renderKickDisconnectMessage (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; + public final fun renderKickDisconnectMessage (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; public final fun renderKickDisconnectMessage (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; public final fun renderKickDisconnectMessage (Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; public final fun renderKickDisconnectMessage (Lnet/kyori/adventure/text/TextComponent$Builder;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Lnet/kyori/adventure/text/TextComponent; public final fun renderKickDisconnectMessage (Lnet/kyori/adventure/text/TextComponent$Builder;Lkotlin/jvm/functions/Function1;Z)Lnet/kyori/adventure/text/TextComponent; + public static synthetic fun renderKickDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; public static synthetic fun renderKickDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; public static synthetic fun renderKickDisconnectMessage$default (Ldev/slne/surf/api/core/messages/CommonComponents;Lnet/kyori/adventure/text/TextComponent$Builder;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lnet/kyori/adventure/text/TextComponent; } @@ -7646,21 +7652,15 @@ public final class dev/slne/surf/api/core/messages/builder/ComponentBuilderColor public static fun yellow (Ldev/slne/surf/api/core/messages/builder/ComponentBuilderColors;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Z[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; } -public abstract interface class dev/slne/surf/api/core/messages/builder/SurfComponentBuilder : dev/slne/surf/api/core/messages/builder/ComponentBuilderColors, net/kyori/adventure/text/TextComponent$Builder { +public abstract interface class dev/slne/surf/api/core/messages/builder/SurfComponentBuilder : dev/slne/surf/api/core/messages/builder/ComponentBuilderColors, net/kyori/adventure/text/ComponentLike { public static final field Companion Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder$Companion; public abstract fun append (Ljava/lang/Iterable;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append (Ljava/lang/Iterable;)Lnet/kyori/adventure/text/ComponentBuilder; public fun append (Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun append (Lnet/kyori/adventure/text/Component;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append (Lnet/kyori/adventure/text/Component;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun append (Lnet/kyori/adventure/text/ComponentBuilder;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append (Lnet/kyori/adventure/text/ComponentBuilder;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun append (Lnet/kyori/adventure/text/ComponentLike;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append (Lnet/kyori/adventure/text/ComponentLike;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun append ([Lnet/kyori/adventure/text/Component;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append ([Lnet/kyori/adventure/text/Component;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun append ([Lnet/kyori/adventure/text/ComponentLike;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun append ([Lnet/kyori/adventure/text/ComponentLike;)Lnet/kyori/adventure/text/ComponentBuilder; public fun appendAsync (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun appendCollection (Ljava/lang/Iterable;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun appendCollectionNewLine (Ljava/lang/Iterable;Lnet/kyori/adventure/text/Component;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; @@ -7675,87 +7675,54 @@ public abstract interface class dev/slne/surf/api/core/messages/builder/SurfComp public fun appendMap (Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/Component;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static synthetic fun appendMap$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/util/Map;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/Component;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun appendNewline ()Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun appendNewline ()Lnet/kyori/adventure/text/ComponentBuilder; + public fun appendNewline (I)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun appendNewline (Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun appendNewlineAsync (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun appendSpace ()Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun appendSpace ()Lnet/kyori/adventure/text/ComponentBuilder; + public fun appendText (Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public fun appendText (Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static synthetic fun appendText$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static synthetic fun appendText$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun appendTime-gRj5Bb8 (JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static synthetic fun appendTime-gRj5Bb8$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun applicableApply (Lnet/kyori/adventure/text/ComponentBuilderApplicable;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun applicableApply (Lnet/kyori/adventure/text/ComponentBuilderApplicable;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun apply (Ljava/util/function/Consumer;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun apply (Ljava/util/function/Consumer;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun applyDeep (Ljava/util/function/Consumer;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun applyDeep (Ljava/util/function/Consumer;)Lnet/kyori/adventure/text/ComponentBuilder; + public abstract fun build ()Lnet/kyori/adventure/text/TextComponent; public static fun builder ()Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public abstract fun children ()Ljava/util/List; + public fun clickCopiesToClipboard (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun clickEvent (Lnet/kyori/adventure/text/event/ClickEvent;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun clickEvent (Lnet/kyori/adventure/text/event/ClickEvent;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun clickEvent (Lnet/kyori/adventure/text/event/ClickEvent;)Lnet/kyori/adventure/text/format/StyleSetter; + public fun clickOpensUrl (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public fun clickRunsCommand (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public fun clickSuggestsCommand (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun color (Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun color (Lnet/kyori/adventure/text/format/TextColor;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun color (Lnet/kyori/adventure/text/format/TextColor;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun colorIfAbsent (Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun colorIfAbsent (Lnet/kyori/adventure/text/format/TextColor;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun colorIfAbsent (Lnet/kyori/adventure/text/format/TextColor;)Lnet/kyori/adventure/text/format/StyleSetter; + public abstract fun content ()Ljava/lang/String; public abstract fun content (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun content (Ljava/lang/String;)Lnet/kyori/adventure/text/TextComponent$Builder; public abstract fun decorate (Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decorate (Lnet/kyori/adventure/text/format/TextDecoration;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decorate (Lnet/kyori/adventure/text/format/TextDecoration;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decorate ([Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decorate ([Lnet/kyori/adventure/text/format/TextDecoration;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decorate ([Lnet/kyori/adventure/text/format/TextDecoration;)Lnet/kyori/adventure/text/format/MutableStyleSetter; - public synthetic fun decorate ([Lnet/kyori/adventure/text/format/TextDecoration;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Z)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Z)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decoration (Lnet/kyori/adventure/text/format/TextDecoration;Z)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decorationIfAbsent (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decorationIfAbsent (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decorationIfAbsent (Lnet/kyori/adventure/text/format/TextDecoration;Lnet/kyori/adventure/text/format/TextDecoration$State;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decorations (Ljava/util/Map;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decorations (Ljava/util/Map;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decorations (Ljava/util/Map;)Lnet/kyori/adventure/text/format/MutableStyleSetter; - public synthetic fun decorations (Ljava/util/Map;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun decorations (Ljava/util/Set;Z)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun decorations (Ljava/util/Set;Z)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun decorations (Ljava/util/Set;Z)Lnet/kyori/adventure/text/format/MutableStyleSetter; - public synthetic fun decorations (Ljava/util/Set;Z)Lnet/kyori/adventure/text/format/StyleSetter; public fun ellipsis (Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static synthetic fun ellipsis$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun font (Lnet/kyori/adventure/key/Key;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun font (Lnet/kyori/adventure/key/Key;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun font (Lnet/kyori/adventure/key/Key;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun hoverEvent (Lnet/kyori/adventure/text/event/HoverEventSource;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun hoverEvent (Lnet/kyori/adventure/text/event/HoverEventSource;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun hoverEvent (Lnet/kyori/adventure/text/event/HoverEventSource;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun insertion (Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun insertion (Ljava/lang/String;)Lnet/kyori/adventure/text/ComponentBuilder; - public synthetic fun insertion (Ljava/lang/String;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun mapChildren (Ljava/util/function/Function;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun mapChildren (Ljava/util/function/Function;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun mapChildrenDeep (Ljava/util/function/Function;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun mapChildrenDeep (Ljava/util/function/Function;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun mergeStyle (Lnet/kyori/adventure/text/Component;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun mergeStyle (Lnet/kyori/adventure/text/Component;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun mergeStyle (Lnet/kyori/adventure/text/Component;Ljava/util/Set;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun mergeStyle (Lnet/kyori/adventure/text/Component;Ljava/util/Set;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun mergeStyle (Lnet/kyori/adventure/text/Component;[Lnet/kyori/adventure/text/format/Style$Merge;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun mergeStyle (Lnet/kyori/adventure/text/Component;[Lnet/kyori/adventure/text/format/Style$Merge;)Lnet/kyori/adventure/text/ComponentBuilder; public fun note (Ljava/lang/Object;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public abstract fun resetStyle ()Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun resetStyle ()Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun shadowColor (Lnet/kyori/adventure/util/ARGBLike;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun shadowColor (Lnet/kyori/adventure/util/ARGBLike;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun shadowColorIfAbsent (Lnet/kyori/adventure/util/ARGBLike;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun shadowColorIfAbsent (Lnet/kyori/adventure/util/ARGBLike;)Lnet/kyori/adventure/text/format/StyleSetter; public abstract fun style (Ljava/util/function/Consumer;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun style (Ljava/util/function/Consumer;)Lnet/kyori/adventure/text/ComponentBuilder; public abstract fun style (Lnet/kyori/adventure/text/format/Style;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; - public synthetic fun style (Lnet/kyori/adventure/text/format/Style;)Lnet/kyori/adventure/text/ComponentBuilder; public fun text (CLnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun text (Ljava/lang/Number;Lnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public fun text (Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; @@ -7803,9 +7770,14 @@ public final class dev/slne/surf/api/core/messages/builder/SurfComponentBuilder$ public static fun appendNewWarningPrefixedLine (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;I)V public static fun appendNewWarningPrefixedLine (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun appendNewWarningPrefixedLineAsync (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun appendNewline (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;I)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun appendNewline (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun appendNewlineAsync (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static fun appendSuccessPrefix (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun appendText (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun appendText (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;Lkotlin/jvm/functions/Function1;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static synthetic fun appendText$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static synthetic fun appendText$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun appendTime-gRj5Bb8 (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static synthetic fun appendTime-gRj5Bb8$default (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;JZZLnet/kyori/adventure/text/Component;Lnet/kyori/adventure/text/format/TextColor;ILjava/lang/Object;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun appendWarningPrefix (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; @@ -7821,6 +7793,10 @@ public final class dev/slne/surf/api/core/messages/builder/SurfComponentBuilder$ public static fun blue (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/Number;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun blue (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun blue (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Z[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun clickCopiesToClipboard (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun clickOpensUrl (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun clickRunsCommand (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; + public static fun clickSuggestsCommand (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun coloredComponent (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;CLnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun coloredComponent (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/Number;Lnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; public static fun coloredComponent (Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder;Ljava/lang/String;Lnet/kyori/adventure/text/format/TextColor;[Lnet/kyori/adventure/text/format/TextDecoration;)Ldev/slne/surf/api/core/messages/builder/SurfComponentBuilder; @@ -8919,12 +8895,66 @@ public final class dev/slne/surf/api/core/nbt/CollectionBinaryTagKt { public static final fun asCollectionOrNull (Lnet/kyori/adventure/nbt/BinaryTag;)Lnet/kyori/adventure/nbt/BinaryTag; } -public abstract interface class dev/slne/surf/api/core/nbt/FastCompoundBinaryTag : net/kyori/adventure/nbt/CompoundBinaryTag { +public abstract interface class dev/slne/surf/api/core/nbt/FastCompoundBinaryTag : net/kyori/adventure/nbt/BinaryTagLike { + public synthetic fun asBinaryTag ()Lnet/kyori/adventure/nbt/BinaryTag; + public abstract fun asBinaryTag ()Lnet/kyori/adventure/nbt/CompoundBinaryTag; public abstract fun clear ()V + public abstract fun contains (Ljava/lang/String;)Z + public abstract fun contains (Ljava/lang/String;Lnet/kyori/adventure/nbt/BinaryTagType;)Z + public abstract fun forEach (Ljava/util/function/Consumer;)V + public abstract fun get (Ljava/lang/String;)Lnet/kyori/adventure/nbt/BinaryTag; + public abstract fun getByte (Ljava/lang/String;B)B + public static synthetic fun getByte$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;BILjava/lang/Object;)B + public abstract fun getByteArray (Ljava/lang/String;)[B + public abstract fun getByteArray (Ljava/lang/String;[B)[B + public abstract fun getCompound (Ljava/lang/String;Lnet/kyori/adventure/nbt/CompoundBinaryTag;)Lnet/kyori/adventure/nbt/CompoundBinaryTag; + public static synthetic fun getCompound$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/CompoundBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/CompoundBinaryTag; + public abstract fun getDouble (Ljava/lang/String;D)D + public static synthetic fun getDouble$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;DILjava/lang/Object;)D + public abstract fun getFloat (Ljava/lang/String;F)F + public static synthetic fun getFloat$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;FILjava/lang/Object;)F + public abstract fun getInt (Ljava/lang/String;I)I + public static synthetic fun getInt$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;IILjava/lang/Object;)I + public abstract fun getIntArray (Ljava/lang/String;)[I + public abstract fun getIntArray (Ljava/lang/String;[I)[I + public abstract fun getList (Ljava/lang/String;Lnet/kyori/adventure/nbt/BinaryTagType;Lnet/kyori/adventure/nbt/ListBinaryTag;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public abstract fun getList (Ljava/lang/String;Lnet/kyori/adventure/nbt/ListBinaryTag;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public static synthetic fun getList$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/BinaryTagType;Lnet/kyori/adventure/nbt/ListBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public static synthetic fun getList$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/ListBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public abstract fun getLong (Ljava/lang/String;J)J + public static synthetic fun getLong$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;JILjava/lang/Object;)J + public abstract fun getLongArray (Ljava/lang/String;)[J + public abstract fun getLongArray (Ljava/lang/String;[J)[J + public abstract fun getShort (Ljava/lang/String;S)S + public static synthetic fun getShort$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;SILjava/lang/Object;)S + public abstract fun getString (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + public static synthetic fun getString$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String; + public abstract fun isEmpty ()Z public abstract fun iterator ()Lit/unimi/dsi/fastutil/objects/ObjectIterator; - public synthetic fun iterator ()Ljava/util/Iterator; public abstract fun keySet ()Lit/unimi/dsi/fastutil/objects/ObjectSet; - public synthetic fun keySet ()Ljava/util/Set; + public abstract fun put (Ljava/lang/String;Lnet/kyori/adventure/nbt/BinaryTag;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public abstract fun put (Ljava/util/Map;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public abstract fun put (Lnet/kyori/adventure/nbt/CompoundBinaryTag;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public abstract fun remove (Ljava/lang/String;Ljava/util/function/Consumer;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public static synthetic fun remove$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Ljava/util/function/Consumer;ILjava/lang/Object;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public abstract fun size ()I + public abstract fun stream ()Ljava/util/stream/Stream; + public fun type ()Lnet/kyori/adventure/nbt/BinaryTagType; +} + +public final class dev/slne/surf/api/core/nbt/FastCompoundBinaryTag$DefaultImpls { + public static synthetic fun getByte$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;BILjava/lang/Object;)B + public static synthetic fun getCompound$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/CompoundBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/CompoundBinaryTag; + public static synthetic fun getDouble$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;DILjava/lang/Object;)D + public static synthetic fun getFloat$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;FILjava/lang/Object;)F + public static synthetic fun getInt$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;IILjava/lang/Object;)I + public static synthetic fun getList$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/BinaryTagType;Lnet/kyori/adventure/nbt/ListBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public static synthetic fun getList$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Lnet/kyori/adventure/nbt/ListBinaryTag;ILjava/lang/Object;)Lnet/kyori/adventure/nbt/ListBinaryTag; + public static synthetic fun getLong$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;JILjava/lang/Object;)J + public static synthetic fun getShort$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;SILjava/lang/Object;)S + public static synthetic fun getString$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Ljava/lang/String; + public static synthetic fun remove$default (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;Ljava/lang/String;Ljava/util/function/Consumer;ILjava/lang/Object;)Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag; + public static fun type (Ldev/slne/surf/api/core/nbt/FastCompoundBinaryTag;)Lnet/kyori/adventure/nbt/BinaryTagType; } public final class dev/slne/surf/api/core/nbt/FastCompoundBinaryTagKt { diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/CommonComponents.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/CommonComponents.kt index 11729c98b..4940819bb 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/CommonComponents.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/CommonComponents.kt @@ -11,6 +11,7 @@ import dev.slne.surf.api.core.messages.adventure.appendNewline import dev.slne.surf.api.core.messages.adventure.appendText import dev.slne.surf.api.core.messages.adventure.clickOpensUrl import dev.slne.surf.api.core.messages.adventure.text +import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder import dev.slne.surf.api.core.util.mutableObjectListOf import net.kyori.adventure.text.Component import net.kyori.adventure.text.JoinConfiguration @@ -205,6 +206,32 @@ object CommonComponents { return builder.build() } + inline fun renderKickDisconnectMessage( + builder: SurfComponentBuilder, + messageRenderer: SurfComponentBuilder.() -> Unit, + footerRenderer: SurfComponentBuilder.() -> Unit = { }, + ): TextComponent { + with(builder) { + append(DISCONNECT_HEADER) + appendText("DU WURDEST VOM SERVER GEWORFEN", ERROR) + appendNewline(3) + messageRenderer() + appendNewline(3) + footerRenderer() + } + + return builder.build() + } + + inline fun renderKickDisconnectMessage( + builder: SurfComponentBuilder, + messageRenderer: SurfComponentBuilder.() -> Unit, + issue: Boolean, + ) = renderKickDisconnectMessage(builder, messageRenderer) { + if (issue) append(ISSUE_FOOTER) + else append(RETRY_LATER_FOOTER) + } + /** * Renders a structured kick message with an automatic issue or retry footer. * @@ -397,6 +424,24 @@ object CommonComponents { return builder.build() } + inline fun renderDisconnectMessage( + builder: SurfComponentBuilder, + disconnectReason: @NoLowercase String, + suggestHelp: SurfComponentBuilder.() -> Unit, + footerRenderer: SurfComponentBuilder.() -> Unit = { }, + ): TextComponent { + with(builder) { + append(DISCONNECT_HEADER) + appendText(disconnectReason.uppercase(), ERROR) + appendNewline(3) + suggestHelp() + appendNewline(3) + footerRenderer() + } + + return builder.build() + } + /** * Renders a structured disconnection message with an automatic issue or retry footer. * @@ -469,6 +514,16 @@ object CommonComponents { else append(RETRY_LATER_FOOTER) } + inline fun renderDisconnectMessage( + builder: SurfComponentBuilder, + disconnectReason: @NoLowercase String, + suggestHelp: SurfComponentBuilder.() -> Unit, + issue: Boolean, + ) = renderDisconnectMessage(builder, disconnectReason, suggestHelp) { + if (issue) append(ISSUE_FOOTER) + else append(RETRY_LATER_FOOTER) + } + /** * Formats a collection into a comma-separated list component. * diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/callback-extension.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/callback-extension.kt index 90fa51510..dbad1a96a 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/callback-extension.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/callback-extension.kt @@ -1,9 +1,7 @@ -@file:Suppress("DEPRECATION") - package dev.slne.surf.api.core.messages.adventure import net.kyori.adventure.audience.Audience -import net.kyori.adventure.text.BuildableComponent +import net.kyori.adventure.text.Component import net.kyori.adventure.text.ComponentBuilder import net.kyori.adventure.text.event.ClickCallback import net.kyori.adventure.text.event.ClickEvent @@ -12,24 +10,20 @@ import kotlin.experimental.ExperimentalTypeInference import kotlin.time.Duration import kotlin.time.toJavaDuration -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickCallback( +fun > ComponentBuilder.clickCallback( callback: ClickCallback, ) = clickEvent(ClickEvent.callback(callback)) -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickCallbackWithOptions( +fun > ComponentBuilder.clickCallbackWithOptions( builder: ClickCallbackWithOptionsBuilder.() -> Unit, ) = clickEvent(ClickCallbackWithOptionsBuilder(Audience::class.java).apply(builder).build()) -@Suppress("DEPRECATION") -inline fun , B : ComponentBuilder> ComponentBuilder.clickCallbackTyped( +inline fun > ComponentBuilder.clickCallbackTyped( callback: ClickCallback, ) = clickEvent(ClickEvent.callback(ClickCallback.widen(callback, T::class.java))) @OptIn(ExperimentalTypeInference::class) -@Suppress("DEPRECATION") -inline fun , B : ComponentBuilder> ComponentBuilder.clickCallbackTypedWithOptions( +inline fun > ComponentBuilder.clickCallbackTypedWithOptions( @BuilderInference builder: ClickCallbackWithOptionsBuilder.() -> Unit ) = clickEvent(ClickCallbackWithOptionsBuilder(T::class.java).apply(builder).build()) @@ -66,7 +60,7 @@ class ClickCallbackWithOptionsBuilder @PublishedApi internal const } @PublishedApi - internal fun build(): ClickEvent { + internal fun build(): ClickEvent<*> { val callback = callback ?: return ClickEvent.callback { } val callbackWithPermission = permission ?.let { permission -> callback.requiringPermission(permission, permissionOtherwise) } @@ -74,4 +68,4 @@ class ClickCallbackWithOptionsBuilder @PublishedApi internal const return ClickEvent.callback(ClickCallback.widen(callbackWithPermission, type), options) } -} \ No newline at end of file +} diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt index a12a101d1..31bf0af8f 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt @@ -1,9 +1,6 @@ -@file:Suppress("DEPRECATION") - package dev.slne.surf.api.core.messages.adventure import dev.slne.surf.api.core.messages.builder.SurfComponentBuilder -import net.kyori.adventure.text.BuildableComponent import net.kyori.adventure.text.Component import net.kyori.adventure.text.ComponentBuilder import net.kyori.adventure.text.TextComponent @@ -28,8 +25,7 @@ inline fun buildText(block: SurfComponentBuilder.() -> Unit): TextComponent { * @param color The optional text color. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.appendText( +fun > ComponentBuilder.appendText( text: String, color: TextColor? = null, ) = append(Component.text(text, color)) @@ -43,8 +39,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param block The configuration block for the text component. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.appendText( +fun > ComponentBuilder.appendText( text: String, color: TextColor? = null, block: TextComponent.Builder.() -> Unit, @@ -56,8 +51,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param url The URL to open on click. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickOpensUrl( +fun > ComponentBuilder.clickOpensUrl( url: String, ) = clickEvent(ClickEvent.openUrl(url)) @@ -67,8 +61,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param command The command to run on click. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickRunsCommand( +fun > ComponentBuilder.clickRunsCommand( command: String, ) = clickEvent(ClickEvent.runCommand(command)) @@ -78,8 +71,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param command The command to suggest on click. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickSuggestsCommand( +fun > ComponentBuilder.clickSuggestsCommand( command: String, ) = clickEvent(ClickEvent.suggestCommand(command)) @@ -89,8 +81,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param value The value to copy to the clipboard on click. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.clickCopiesToClipboard( +fun > ComponentBuilder.clickCopiesToClipboard( value: String, ) = clickEvent(ClickEvent.copyToClipboard(value)) @@ -100,8 +91,7 @@ fun , B : ComponentBuilder> ComponentBuilder< * @param amount The number of newline characters to append. * @return The modified builder instance. */ -@Suppress("DEPRECATION") -fun , B : ComponentBuilder> ComponentBuilder.appendNewline( +fun > ComponentBuilder.appendNewline( amount: Int, ) = repeat(amount) { appendNewline() } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/identity-extensions.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/identity-extensions.kt index 32d5fb26a..7ada34c67 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/identity-extensions.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/identity-extensions.kt @@ -17,7 +17,7 @@ import java.util.* * val myPointer = pointer(myKey) * ``` */ -inline fun pointer(key: Key) = Pointer.pointer(V::class.java, key) +inline fun pointer(key: Key) = Pointer.pointer(V::class.java, key) /** * Creates an [Identity] instance using the provided [UUID]. diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilder.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilder.kt index 55bb50c78..06a4c17d0 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilder.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilder.kt @@ -22,7 +22,7 @@ import java.util.function.Function import kotlin.time.Duration @ApiStatus.NonExtendable -interface SurfComponentBuilder : TextComponent.Builder, ComponentBuilderColors { +interface SurfComponentBuilder : ComponentBuilderColors, ComponentLike { companion object { @JvmStatic fun builder(): SurfComponentBuilder = SurfComponentBuilderImpl(Component.text()) @@ -44,6 +44,8 @@ interface SurfComponentBuilder : TextComponent.Builder, ComponentBuilderColors { suspend fun appendNewlineAsync(block: suspend SurfComponentBuilder.() -> Unit) = appendNewline().appendAsync(block) + fun appendNewline(amount: Int): SurfComponentBuilder = apply { repeat(amount) { appendNewline() } } + fun text(text: String, color: TextColor? = null, vararg decoration: TextDecoration) = append(Component.text(text, color, *decoration)) @@ -63,6 +65,24 @@ interface SurfComponentBuilder : TextComponent.Builder, ComponentBuilderColors { } ) + fun appendText(text: String, color: TextColor? = null) = this.text(text, color) + + fun appendText( + text: String, + color: TextColor? = null, + block: TextComponent.Builder.() -> Unit + ): SurfComponentBuilder { + val builder = Component.text().content(text) + if (color != null) builder.color(color) + builder.apply(block) + return append(builder.build()) + } + + fun clickOpensUrl(url: String) = clickEvent(ClickEvent.openUrl(url)) + fun clickRunsCommand(command: String) = clickEvent(ClickEvent.runCommand(command)) + fun clickSuggestsCommand(command: String) = clickEvent(ClickEvent.suggestCommand(command)) + fun clickCopiesToClipboard(value: String) = clickEvent(ClickEvent.copyToClipboard(value)) + fun note(any: Any, vararg decoration: TextDecoration) = text(any.toString(), NOTE, *decoration) fun ellipsis(color: TextColor? = SPACER) = append(CommonComponents.ELLIPSIS.color(color)) @@ -128,55 +148,58 @@ interface SurfComponentBuilder : TextComponent.Builder, ComponentBuilderColors { timeColor: TextColor = VARIABLE_VALUE, ) = append(CommonComponents.formatTime(time, showSeconds, shortForms, separator, timeColor)) - override fun content(content: String): SurfComponentBuilder - override fun append(builder: ComponentBuilder<*, *>): SurfComponentBuilder - override fun append(component: Component): SurfComponentBuilder - override fun append(component: ComponentLike): SurfComponentBuilder - override fun append(components: Iterable): SurfComponentBuilder - override fun append(vararg components: Component): SurfComponentBuilder - override fun append(vararg components: ComponentLike): SurfComponentBuilder - override fun appendNewline(): SurfComponentBuilder - override fun appendSpace(): SurfComponentBuilder - override fun applicableApply(applicable: ComponentBuilderApplicable): SurfComponentBuilder - override fun apply(consumer: Consumer>): SurfComponentBuilder - override fun applyDeep(action: Consumer>): SurfComponentBuilder - override fun clickEvent(event: ClickEvent?): SurfComponentBuilder - override fun color(color: TextColor?): SurfComponentBuilder - override fun colorIfAbsent(color: TextColor?): SurfComponentBuilder - override fun decorate(decoration: TextDecoration): SurfComponentBuilder - override fun decorate(vararg decorations: TextDecoration): SurfComponentBuilder - override fun decoration(decoration: TextDecoration, flag: Boolean): SurfComponentBuilder - override fun decoration( + fun content(): String + fun content(content: String): SurfComponentBuilder + fun children(): List + + fun build(): TextComponent + + fun append(builder: ComponentBuilder<*, *>): SurfComponentBuilder + fun append(component: Component): SurfComponentBuilder + fun append(component: ComponentLike): SurfComponentBuilder + fun append(components: Iterable): SurfComponentBuilder + fun append(vararg components: Component): SurfComponentBuilder + fun append(vararg components: ComponentLike): SurfComponentBuilder + fun appendNewline(): SurfComponentBuilder + fun appendSpace(): SurfComponentBuilder + fun applicableApply(applicable: ComponentBuilderApplicable): SurfComponentBuilder + fun apply(consumer: Consumer>): SurfComponentBuilder + fun applyDeep(action: Consumer>): SurfComponentBuilder + fun clickEvent(event: ClickEvent<*>?): SurfComponentBuilder + fun color(color: TextColor?): SurfComponentBuilder + fun colorIfAbsent(color: TextColor?): SurfComponentBuilder + fun decorate(decoration: TextDecoration): SurfComponentBuilder + fun decorate(vararg decorations: TextDecoration): SurfComponentBuilder + fun decoration(decoration: TextDecoration, flag: Boolean): SurfComponentBuilder + fun decoration( decoration: TextDecoration, state: TextDecoration.State, ): SurfComponentBuilder - override fun decorationIfAbsent( + fun decorationIfAbsent( decoration: TextDecoration, state: TextDecoration.State, ): SurfComponentBuilder - override fun decorations(decorations: Map): SurfComponentBuilder - override fun decorations( - decorations: Set, + fun decorations(decorations: Map): SurfComponentBuilder + fun decorations( + decorations: Set, flag: Boolean, ): SurfComponentBuilder - override fun font(font: Key?): SurfComponentBuilder - override fun hoverEvent(source: HoverEventSource<*>?): SurfComponentBuilder - override fun insertion(insertion: String?): SurfComponentBuilder - - @Suppress("DEPRECATION") - override fun mapChildren(function: Function?, out BuildableComponent<*, *>?>): SurfComponentBuilder - - @Suppress("DEPRECATION") - override fun mapChildrenDeep(function: Function?, out BuildableComponent<*, *>?>): SurfComponentBuilder - override fun mergeStyle(that: Component): SurfComponentBuilder - override fun mergeStyle(that: Component, merges: Set): SurfComponentBuilder - override fun mergeStyle(that: Component, vararg merges: Style.Merge): SurfComponentBuilder - override fun resetStyle(): SurfComponentBuilder - override fun style(consumer: Consumer): SurfComponentBuilder - override fun style(style: Style): SurfComponentBuilder - override fun shadowColor(argb: ARGBLike?): SurfComponentBuilder - override fun shadowColorIfAbsent(argb: ARGBLike?): SurfComponentBuilder + fun font(font: Key?): SurfComponentBuilder + fun hoverEvent(source: HoverEventSource<*>?): SurfComponentBuilder + fun insertion(insertion: String?): SurfComponentBuilder + + fun mapChildren(function: Function): SurfComponentBuilder + + fun mapChildrenDeep(function: Function): SurfComponentBuilder + fun mergeStyle(that: Component): SurfComponentBuilder + fun mergeStyle(that: Component, merges: Set): SurfComponentBuilder + fun mergeStyle(that: Component, vararg merges: Style.Merge): SurfComponentBuilder + fun resetStyle(): SurfComponentBuilder + fun style(consumer: Consumer): SurfComponentBuilder + fun style(style: Style): SurfComponentBuilder + fun shadowColor(argb: ARGBLike?): SurfComponentBuilder + fun shadowColorIfAbsent(argb: ARGBLike?): SurfComponentBuilder } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl.kt index aa79a0778..f49c5206b 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl.kt @@ -19,10 +19,14 @@ internal class SurfComponentBuilderImpl(private val delegate: TextComponent.Buil } override fun content(content: String) = withDelegate { content(content) } + override fun content() = delegate.content() + override fun children(): List = delegate.children() + override fun build() = delegate.build() + override fun append(builder: ComponentBuilder<*, *>) = withDelegate { append(builder) } override fun append(component: Component) = withDelegate { append(component) } override fun append(component: ComponentLike) = withDelegate { append(component) } - override fun append(components: Iterable) = withDelegate { append(components) } + override fun append(components: Iterable) = withDelegate { append(components) } override fun append(vararg components: Component) = withDelegate { append(*components) } override fun append(vararg components: ComponentLike) = withDelegate { append(*components) } override fun appendNewline() = withDelegate { appendNewline() } @@ -36,7 +40,7 @@ internal class SurfComponentBuilderImpl(private val delegate: TextComponent.Buil override fun applyDeep(action: Consumer>) = withDelegate { applyDeep(action) } - override fun clickEvent(event: ClickEvent?) = withDelegate { clickEvent(event) } + override fun clickEvent(event: ClickEvent<*>?) = withDelegate { clickEvent(event) } override fun color(color: TextColor?) = withDelegate { color(color) } override fun colorIfAbsent(color: TextColor?) = withDelegate { colorIfAbsent(color) } override fun decorate(decoration: TextDecoration) = withDelegate { decorate(decoration) } @@ -58,30 +62,33 @@ internal class SurfComponentBuilderImpl(private val delegate: TextComponent.Buil state: TextDecoration.State, ) = withDelegate { decorationIfAbsent(decoration, state) } - override fun decorations(decorations: Map) = - withDelegate { decorations(decorations) } + override fun decorations(decorations: Map): SurfComponentBuilderImpl { + delegate.decorations(decorations) + return this + } override fun decorations( - decorations: Set, + decorations: Set, flag: Boolean, - ) = withDelegate { decorations(decorations, flag) } + ): SurfComponentBuilderImpl { + delegate.decorations(decorations, flag) + return this + } override fun font(font: Key?) = withDelegate { font(font) } override fun hoverEvent(source: HoverEventSource<*>?) = withDelegate { hoverEvent(source) } override fun insertion(insertion: String?) = withDelegate { insertion(insertion) } - @Suppress("DEPRECATION") - override fun mapChildren(function: Function?, out BuildableComponent<*, *>?>) = + override fun mapChildren(function: Function) = withDelegate { mapChildren(function) } - @Suppress("DEPRECATION") - override fun mapChildrenDeep(function: Function?, out BuildableComponent<*, *>?>) = + override fun mapChildrenDeep(function: Function) = withDelegate { mapChildrenDeep(function) } override fun mergeStyle(that: Component) = withDelegate { mergeStyle(that) } override fun mergeStyle( that: Component, - merges: Set, + merges: Set, ) = withDelegate { mergeStyle(that, merges) } override fun mergeStyle( @@ -90,11 +97,12 @@ internal class SurfComponentBuilderImpl(private val delegate: TextComponent.Buil ) = withDelegate { mergeStyle(that, *merges) } override fun resetStyle() = withDelegate { resetStyle() } - override fun style(consumer: Consumer) = withDelegate { style(consumer) } + override fun style(consumer: Consumer) = withDelegate { style(consumer) } override fun style(style: Style) = withDelegate { style(style) } override fun shadowColor(argb: ARGBLike?) = withDelegate { shadowColor(argb) } override fun shadowColorIfAbsent(argb: ARGBLike?) = withDelegate { shadowColorIfAbsent(argb) } - override fun content() = delegate.content() - override fun children() = delegate.children() - override fun build() = delegate.build() -} \ No newline at end of file + + override fun asComponent(): Component { + return build() + } +} diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationClickEventProvider.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationClickEventProvider.kt index 2bd3e622b..edb6f0219 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationClickEventProvider.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationClickEventProvider.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.launch import net.kyori.adventure.text.event.ClickEvent fun interface PaginationClickEventProvider { - fun getCallback(targetPage: Int, pagination: Pagination, content: Collection): ClickEvent + fun getCallback(targetPage: Int, pagination: Pagination, content: Collection): ClickEvent<*> companion object { private object DEFAULT : PaginationClickEventProvider { @@ -13,7 +13,7 @@ fun interface PaginationClickEventProvider { targetPage: Int, pagination: Pagination, content: Collection, - ): ClickEvent = ClickEvent.callback { clicker -> + ): ClickEvent<*> = ClickEvent.callback { clicker -> clicker.sendMessage(pagination.renderComponent(content, targetPage)) } } @@ -30,7 +30,7 @@ fun interface SuspendPaginationClickEventProvider { targetPage: Int, pagination: SuspendPagination, content: Collection - ): ClickEvent + ): ClickEvent<*> companion object { private object DEFAULT : SuspendPaginationClickEventProvider { @@ -38,7 +38,7 @@ fun interface SuspendPaginationClickEventProvider { targetPage: Int, pagination: SuspendPagination, content: Collection - ): ClickEvent = ClickEvent.callback { clicker -> + ): ClickEvent<*> = ClickEvent.callback { clicker -> launch { clicker.sendMessage(pagination.renderComponent(content, targetPage)) } diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationRenderer.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationRenderer.kt index a5e3b424c..4d49a201c 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationRenderer.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/pagination/PaginationRenderer.kt @@ -47,7 +47,7 @@ interface SuspendPaginationRenderer { previousPage: PageButton, nextPage: PageButton, lastPage: PageButton, - changePageEvent: suspend CoroutineScope.(Int) -> ClickEvent?, + changePageEvent: suspend CoroutineScope.(Int) -> ClickEvent<*>?, ): Component = buildText { if (page == 1 && pages == 1) { append(renderFooterSingle(width)) @@ -91,13 +91,13 @@ interface SuspendPaginationRenderer { suspend fun CoroutineScope.renderPreviousPageButton( button: PageButton, - clickEvent: ClickEvent?, + clickEvent: ClickEvent<*>?, enabled: Boolean, ): Component = PaginationRenderer.DEFAULT.renderPreviousPageButton(button, clickEvent, enabled) suspend fun CoroutineScope.renderNextPageButton( button: PageButton, - clickEvent: ClickEvent?, + clickEvent: ClickEvent<*>?, enabled: Boolean, ): Component = PaginationRenderer.DEFAULT.renderNextPageButton(button, clickEvent, enabled) @@ -155,7 +155,7 @@ interface PaginationRenderer { previousPage: PageButton, nextPage: PageButton, lastPage: PageButton, - changePageEvent: (Int) -> ClickEvent?, + changePageEvent: (Int) -> ClickEvent<*>?, ): Component = buildText { if (page == 1 && pages == 1) { append(renderFooterSingle(width)) @@ -180,7 +180,7 @@ interface PaginationRenderer { fun renderPreviousPageButton( button: PageButton, - clickEvent: ClickEvent?, + clickEvent: ClickEvent<*>?, enabled: Boolean, ): Component = buildText { appendSpace() @@ -194,7 +194,7 @@ interface PaginationRenderer { fun renderNextPageButton( button: PageButton, - clickEvent: ClickEvent?, + clickEvent: ClickEvent<*>?, enabled: Boolean, ): Component = buildText { appendSpace() diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/nbt/FastCompoundBinaryTag.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/nbt/FastCompoundBinaryTag.kt index 45c2585f1..40e7258fd 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/nbt/FastCompoundBinaryTag.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/nbt/FastCompoundBinaryTag.kt @@ -3,50 +3,70 @@ package dev.slne.surf.api.core.nbt import it.unimi.dsi.fastutil.objects.Object2ObjectMap import it.unimi.dsi.fastutil.objects.ObjectIterator import it.unimi.dsi.fastutil.objects.ObjectSet -import net.kyori.adventure.nbt.BinaryTag -import net.kyori.adventure.nbt.CompoundBinaryTag +import net.kyori.adventure.nbt.* import org.jetbrains.annotations.UnmodifiableView +import java.util.function.Consumer +import java.util.stream.Stream /** - * A mutable, high-performance implementation of CompoundBinaryTag backed by fastutil collections. + * A mutable, high-performance replacement for CompoundBinaryTag backed by fastutil collections. * - * This interface extends CompoundBinaryTag with additional mutation capabilities and optimized - * iteration through fastutil's specialized collection types. - * - * **Important:** This implementation violates the immutability principle of CompoundBinaryTag. - * All operations (put, remove, etc.) mutate the tag directly, unlike the standard CompoundBinaryTag - * which is immutable and returns a new tag for each operation. + * This interface mirrors the CompoundBinaryTag API but cannot extend it since CompoundBinaryTag + * became sealed in Adventure 5.1.1. All operations (put, remove, etc.) mutate the tag directly, + * unlike the standard CompoundBinaryTag which is immutable. */ -@Suppress("NonExtendableApiUsage") -interface FastCompoundBinaryTag : CompoundBinaryTag { +interface FastCompoundBinaryTag : BinaryTagLike { + + fun type(): BinaryTagType = BinaryTagTypes.COMPOUND + + fun contains(key: String): Boolean + fun contains(key: String, type: BinaryTagType<*>): Boolean + + fun keySet(): @UnmodifiableView ObjectSet + + fun get(key: String): BinaryTag? + fun size(): Int + fun isEmpty(): Boolean + + fun put(key: String, tag: BinaryTag): FastCompoundBinaryTag + fun put(tag: CompoundBinaryTag): FastCompoundBinaryTag + fun put(tags: Map): FastCompoundBinaryTag + fun remove(key: String, removed: Consumer? = null): FastCompoundBinaryTag - /** - * Removes all key-value mappings from this compound tag. - */ fun clear() + fun getByte(key: String, defaultValue: Byte = 0): Byte + fun getShort(key: String, defaultValue: Short = 0): Short + fun getInt(key: String, defaultValue: Int = 0): Int + fun getLong(key: String, defaultValue: Long = 0L): Long + fun getFloat(key: String, defaultValue: Float = 0f): Float + fun getDouble(key: String, defaultValue: Double = 0.0): Double + fun getByteArray(key: String): ByteArray + fun getByteArray(key: String, defaultValue: ByteArray?): ByteArray? + fun getString(key: String, defaultValue: String? = null): String? + fun getList(key: String, defaultValue: ListBinaryTag? = null): ListBinaryTag? + fun getList( + key: String, + expectedType: BinaryTagType, + defaultValue: ListBinaryTag? = null, + ): ListBinaryTag? - /** - * Returns an optimized set view of the keys contained in this compound tag. - * - * @return An ObjectSet providing efficient key iteration - */ - override fun keySet(): @UnmodifiableView ObjectSet + fun getCompound(key: String, defaultValue: CompoundBinaryTag? = null): CompoundBinaryTag? + fun getIntArray(key: String): IntArray + fun getIntArray(key: String, defaultValue: IntArray?): IntArray? + fun getLongArray(key: String): LongArray + fun getLongArray(key: String, defaultValue: LongArray?): LongArray? - /** - * Returns an optimized iterator over the entries in this compound tag. - * - * @return An ObjectIterator for efficient entry traversal - */ - override fun iterator(): ObjectIterator> + fun stream(): Stream> + fun iterator(): ObjectIterator> + fun forEach(action: Consumer>) + + override fun asBinaryTag(): CompoundBinaryTag } /** * Wraps this CompoundBinaryTag in a mutable FastCompoundBinaryTag for improved performance. * - * The returned tag is mutable and all operations modify the tag directly, unlike the immutable - * CompoundBinaryTag interface. - * * @param synchronize If true, wraps the underlying map with synchronization for thread-safe access * @return A mutable FastCompoundBinaryTag backed by fastutil collections */ @@ -59,4 +79,4 @@ fun CompoundBinaryTag.fast(synchronize: Boolean = false) = * @param synchronize If true, wraps the underlying map with synchronization for thread-safe access * @return A mutable FastCompoundBinaryTag backed by fastutil collections */ -fun CompoundBinaryTag.Builder.buildFast(synchronize: Boolean = false) = build().fast(synchronize) \ No newline at end of file +fun CompoundBinaryTag.Builder.buildFast(synchronize: Boolean = false) = build().fast(synchronize) diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/service-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/service-util.kt index 5c9bba25e..f47da559e 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/service-util.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/service-util.kt @@ -12,5 +12,9 @@ import java.util.* * @return the service instance of type [T] * @throws ServiceConfigurationError if the service of type [T] is not available */ -inline fun requiredService(): T = Services.serviceWithFallback(T::class.java) - .orElseThrow { ServiceConfigurationError("Service ${T::class.java.name} not available") } \ No newline at end of file +inline fun requiredService(): T = Services.serviceWithFallback( + ServiceLoader.load( + T::class.java, + getCallerClass()?.classLoader ?: T::class.java.classLoader + ), T::class.java +).orElseThrow { ServiceConfigurationError("Service ${T::class.java.name} not available") } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/dialog/builder/DialogActionBuilder.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/dialog/builder/DialogActionBuilder.kt index 57146603f..4717aeceb 100644 --- a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/dialog/builder/DialogActionBuilder.kt +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/dialog/builder/DialogActionBuilder.kt @@ -23,7 +23,7 @@ class DialogActionBuilder { private var action: DialogAction? = null - fun staticAction(clickEvent: ClickEvent) { + fun staticAction(clickEvent: ClickEvent<*>) { action = DialogAction.staticAction(clickEvent) } diff --git a/surf-api-shared/surf-api-shared-public/build.gradle.kts b/surf-api-shared/surf-api-shared-public/build.gradle.kts index a4934ac7e..4d7b722d8 100644 --- a/surf-api-shared/surf-api-shared-public/build.gradle.kts +++ b/surf-api-shared/surf-api-shared-public/build.gradle.kts @@ -13,6 +13,7 @@ kotlin { dependencies { compileOnlyApi(libs.adventure.api) compileOnlyApi(libs.adventure.text.logger.slf4j) + compileOnlyApi(libs.slf4j) compileOnlyApi(libs.adventure.text.minimessage) compileOnlyApi(libs.adventure.serializer.gson) compileOnlyApi(libs.adventure.serializer.legacy) From b44c24fd328e465cd288d9c32673de454cb1a712 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 19 Jun 2026 18:09:29 +0200 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat(patcher):=20add=20SurfAdve?= =?UTF-8?q?nture5AbiPatcher=20for=20plugin=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - implement SurfAdventure5AbiPatcher to update Adventure ABI references in plugins - add methods for patching plugin JAR files and handling plugin metadata - introduce SurfComponentBuilderBridgePatcher for component builder interface adjustments - enhance build.gradle.kts to include new dependencies and patching tasks --- buildSrc/build.gradle.kts | 1 + .../SurfComponentBuilderBridgePatcher.kt | 242 +++++++ gradle/libs.versions.toml | 2 + .../messages/SurfAdventure5AbiPatcher.kt | 593 ++++++++++++++++++ .../messages/adventure/component-extension.kt | 69 +- .../surf-api-paper-server/build.gradle.kts | 11 + .../surf/api/paper/server/PaperBoostrapper.kt | 17 +- 7 files changed, 932 insertions(+), 3 deletions(-) create mode 100644 buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt create mode 100644 surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a2dad42c2..0eb86dd70 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -12,4 +12,5 @@ dependencies { implementation(libs.ksp.gradle.plugin) implementation(libs.kotlin.serialization) implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(libs.asm) } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt b/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt new file mode 100644 index 000000000..366855640 --- /dev/null +++ b/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt @@ -0,0 +1,242 @@ +package dev.slne.surf.api.gradle + +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassVisitor +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.util.Locale +import java.util.jar.JarEntry +import java.util.jar.JarInputStream +import java.util.jar.JarOutputStream + +object SurfComponentBuilderBridgePatcher { + private const val SURF_COMPONENT_BUILDER = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilder" + private const val SURF_COMPONENT_BUILDER_IMPL = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl" + + private const val TEXT_COMPONENT_DESC = "()Lnet/kyori/adventure/text/TextComponent;" + private const val COMPONENT_DESC = "()Lnet/kyori/adventure/text/Component;" + + fun patchJar(jar: Path) { + require(Files.isRegularFile(jar)) { + "Jar does not exist: $jar" + } + + val tmp = Files.createTempFile( + jar.parent, + jar.fileName.toString(), + ".surf-builder-bridge.tmp", + ) + + + var changed = false + + JarInputStream(Files.newInputStream(jar)).use { input -> + JarOutputStream(Files.newOutputStream(tmp)).use { output -> + val written = hashSetOf() + + while (true) { + val entry = input.nextJarEntry ?: break + val name = entry.name + + if (!written.add(name)) { + continue + } + + if (isSignatureEntry(name)) { + continue + } + + val originalBytes = input.readBytes() + + val patchedBytes = when (name) { + "$SURF_COMPONENT_BUILDER.class" -> patchSurfComponentBuilderInterface(originalBytes) + "$SURF_COMPONENT_BUILDER_IMPL.class" -> patchSurfComponentBuilderImpl(originalBytes) + + else -> originalBytes + } + + if (!patchedBytes.contentEquals(originalBytes)) { + changed = true + } + + val newEntry = JarEntry(name) + newEntry.time = entry.time + + output.putNextEntry(newEntry) + output.write(patchedBytes) + output.closeEntry() + } + } + } + + if (changed) { + Files.move( + tmp, + jar, + StandardCopyOption.REPLACE_EXISTING, + ) + + println("[SurfComponentBuilderBridgePatcher] Patched $jar") + } else { + Files.deleteIfExists(tmp) + + println("[SurfComponentBuilderBridgePatcher] Nothing to patch in $jar") + } + } + + private fun patchSurfComponentBuilderInterface(bytes: ByteArray): ByteArray { + val reader = ClassReader(bytes) + val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS) + + var hasTextComponentBuild = false + var hasComponentBuild = false + + val visitor = object : ClassVisitor(Opcodes.ASM9, writer) { + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array?, + ): MethodVisitor { + if (name == "build" && descriptor == TEXT_COMPONENT_DESC) { + hasTextComponentBuild = true + } + + if (name == "build" && descriptor == COMPONENT_DESC) { + hasComponentBuild = true + } + + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + + override fun visitEnd() { + if (hasTextComponentBuild && !hasComponentBuild) { + addComponentBridge() + } + + super.visitEnd() + } + + private fun addComponentBridge() { + val mv = super.visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_BRIDGE, + "build", + COMPONENT_DESC, + null, + null, + ) + + mv.visitCode() + mv.visitVarInsn(Opcodes.ALOAD, 0) + mv.visitMethodInsn( + Opcodes.INVOKEINTERFACE, + SURF_COMPONENT_BUILDER, + "build", + TEXT_COMPONENT_DESC, + true, + ) + mv.visitInsn(Opcodes.ARETURN) + mv.visitMaxs(0, 0) + mv.visitEnd() + } + } + + reader.accept(visitor, 0) + + return writer.toByteArray() + } + + private fun patchSurfComponentBuilderImpl(bytes: ByteArray): ByteArray { + val reader = ClassReader(bytes) + val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS) + + var className: String? = null + var hasTextComponentBuild = false + var hasComponentBuild = false + + val visitor = object : ClassVisitor(Opcodes.ASM9, writer) { + override fun visit( + version: Int, + access: Int, + name: String, + signature: String?, + superName: String?, + interfaces: Array?, + ) { + className = name + super.visit(version, access, name, signature, superName, interfaces) + } + + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array?, + ): MethodVisitor { + if (name == "build" && descriptor == TEXT_COMPONENT_DESC) { + hasTextComponentBuild = true + } + + if (name == "build" && descriptor == COMPONENT_DESC) { + hasComponentBuild = true + } + + return super.visitMethod(access, name, descriptor, signature, exceptions) + } + + override fun visitEnd() { + if (hasTextComponentBuild && !hasComponentBuild) { + addComponentBridge(className ?: SURF_COMPONENT_BUILDER_IMPL) + } + + super.visitEnd() + } + + private fun addComponentBridge(owner: String) { + val mv = super.visitMethod( + Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_BRIDGE, + "build", + COMPONENT_DESC, + null, + null, + ) + + mv.visitCode() + mv.visitVarInsn(Opcodes.ALOAD, 0) + mv.visitMethodInsn( + Opcodes.INVOKEVIRTUAL, + owner, + "build", + TEXT_COMPONENT_DESC, + false, + ) + mv.visitInsn(Opcodes.ARETURN) + mv.visitMaxs(0, 0) + mv.visitEnd() + } + } + + reader.accept(visitor, 0) + + return writer.toByteArray() + } + + private fun isSignatureEntry(name: String): Boolean { + val upper = name.uppercase(Locale.ROOT) + + if (!upper.startsWith("META-INF/")) { + return false + } + + return upper.endsWith(".SF") || + upper.endsWith(".RSA") || + upper.endsWith(".DSA") || + upper.endsWith(".EC") + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3fa8f7aa7..df93bacfd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,6 +35,7 @@ placeholder-api = "2.12.2" mccoroutine = "2.22.2" # Miscellaneous Libraries +asm = "9.8" slf4j = "2.0.1" guava = "33.6.0-jre" caffeine = "3.2.4" @@ -129,6 +130,7 @@ mccoroutine-velocity-api = { module = "dev.slne.forks.mccoroutine:mccoroutine-ve mccoroutine-velocity-core = { module = "dev.slne.forks.mccoroutine:mccoroutine-velocity-core", version.ref = "mccoroutine" } # Miscellaneous Libraries +asm = { module = "org.ow2.asm:asm", version.ref = "asm" } slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } guava = { module = "com.google.guava:guava", version.ref = "guava" } caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version.ref = "caffeine" } diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt new file mode 100644 index 000000000..ae8388185 --- /dev/null +++ b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt @@ -0,0 +1,593 @@ +package dev.slne.surf.api.core.server.messages + +import net.bytebuddy.jar.asm.* +import net.bytebuddy.jar.asm.commons.ClassRemapper +import net.bytebuddy.jar.asm.commons.Remapper +import org.spongepowered.configurate.CommentedConfigurationNode +import org.spongepowered.configurate.kotlin.extensions.getList +import org.spongepowered.configurate.yaml.YamlConfigurationLoader +import java.io.BufferedReader +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.time.Duration +import java.time.Instant +import java.util.* +import java.util.concurrent.atomic.AtomicBoolean +import java.util.jar.JarEntry +import java.util.jar.JarFile +import java.util.jar.JarInputStream +import java.util.jar.JarOutputStream +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile + +object SurfAdventure5AbiPatcher { + private const val MARKER_ENTRY = "META-INF/surf-adventure5-abi-patched-v2" + + private const val COMPONENT_BUILDER_INTERNAL = "net/kyori/adventure/text/ComponentBuilder" + private const val SURF_COMPONENT_BUILDER_INTERNAL = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilder" + private const val COMPONENT_EXTENSION_KT = "dev/slne/surf/api/core/messages/adventure/Component_extensionKt" + + + private val defaultDependencyNames = setOf( + "surf-paper-api", + ) + + private val internalNameMappings = mapOf( + "net/kyori/adventure/text/BuildableComponent" to + "net/kyori/adventure/text/Component", + + $$"net/kyori/adventure/util/Buildable$Builder" to + "net/kyori/adventure/builder/AbstractBuilder", + ) + + private val remapper = object : Remapper(Opcodes.ASM9) { + override fun map(internalName: String): String { + return internalNameMappings[internalName] ?: internalName + } + } + + data class PatchResult( + val jar: Path, + val pluginName: String?, + val status: Status, + val changedClasses: Int = 0, + val reason: String? = null, + ) + + enum class Status { + PATCHED, + SKIPPED_NO_PLUGIN_METADATA, + SKIPPED_SELF, + SKIPPED_NO_SURF_DEPENDENCY, + SKIPPED_ALREADY_PATCHED, + SKIPPED_NO_RELEVANT_BYTECODE, + } + + data class PluginMetadata( + val name: String?, + val dependencies: Set, + ) + + fun patchPluginsDirectory( + pluginsDir: Path = Path.of("plugins"), + dependencyNames: Set = defaultDependencyNames, + log: (String) -> Unit = {}, + warn: (String) -> Unit = {}, + ): List { + if (!pluginsDir.exists()) { + log("Plugin folder does not exist: $pluginsDir") + return emptyList() + } + + val normalizedDependencyNames = dependencyNames.normalized() + + val results = mutableListOf() + + val warningLogged = AtomicBoolean(false) + + Files.list(pluginsDir).use { stream -> + stream + .filter { path -> + path.isRegularFile() && + path.fileName.toString().endsWith(".jar", ignoreCase = true) + } + .forEach { jar -> + val result = patchPluginJarIfNecessary( + jar = jar, + dependencyNames = normalizedDependencyNames, + log = log, + warn = warn, + loggedWarning = { warningLogged.getAndSet(true) } + ) + + results += result + + when (result.status) { + Status.PATCHED -> log( + "Patched ${jar.fileName}: ${result.changedClasses} class(es) rewritten" + ) + + Status.SKIPPED_NO_RELEVANT_BYTECODE -> log( + "Skipped ${jar.fileName}: depends on surf-api, but has no old Adventure ABI refs" + ) + + Status.SKIPPED_ALREADY_PATCHED -> log( + "Skipped ${jar.fileName}: already patched" + ) + + else -> Unit + } + } + } + + return results + } + + private fun patchPluginJarIfNecessary( + jar: Path, + dependencyNames: Set, + log: (String) -> Unit, + warn: (String) -> Unit, + loggedWarning: () -> Boolean + ): PatchResult { + val metadata = readPluginMetadata(jar) + ?: return PatchResult( + jar = jar, + pluginName = null, + status = Status.SKIPPED_NO_PLUGIN_METADATA, + ) + + val pluginName = metadata.name + val normalizedPluginName = pluginName?.normalizePluginName() + + if (normalizedPluginName != null && normalizedPluginName in dependencyNames) { + return PatchResult( + jar = jar, + pluginName = pluginName, + status = Status.SKIPPED_SELF, + ) + } + + val dependsOnSurfApi = metadata.dependencies + .normalized() + .any { it in dependencyNames } + + if (!dependsOnSurfApi) { + return PatchResult( + jar = jar, + pluginName = pluginName, + status = Status.SKIPPED_NO_SURF_DEPENDENCY, + ) + } + + if (hasMarker(jar)) { + return PatchResult( + jar = jar, + pluginName = pluginName, + status = Status.SKIPPED_ALREADY_PATCHED, + ) + } + + val rewriteResult = rewriteJar(jar, log, warn, loggedWarning) + + if (rewriteResult.changedClasses == 0) { + return PatchResult( + jar = jar, + pluginName = pluginName, + status = Status.SKIPPED_NO_RELEVANT_BYTECODE, + ) + } + + return PatchResult( + jar = jar, + pluginName = pluginName, + status = Status.PATCHED, + changedClasses = rewriteResult.changedClasses, + ) + } + + private data class RewriteResult( + val changedClasses: Int, + ) + + private fun rewriteJar( + jar: Path, + log: (String) -> Unit, + warn: (String) -> Unit, + loggedWarning: () -> Boolean + ): RewriteResult { + val tmp = Files.createTempFile( + jar.parent, + jar.fileName.toString(), + ".surf-abi.tmp", + ) + + var changedClasses = 0 + + JarInputStream(Files.newInputStream(jar)).use { input -> + JarOutputStream(Files.newOutputStream(tmp)).use { output -> + val writtenEntries = hashSetOf() + + while (true) { + val entry = input.nextJarEntry ?: break + val name = entry.name + + if (!writtenEntries.add(name)) { + continue + } + + if (name == MARKER_ENTRY) { + continue + } + + if (isSignatureEntry(name)) { + continue + } + + val originalBytes = input.readBytes() + + val newBytes = if (name.endsWith(".class")) { + val rewritten = rewriteClass(originalBytes, name, warn, loggedWarning) + + if (!rewritten.contentEquals(originalBytes)) { + changedClasses++ + } + + rewritten + } else { + originalBytes + } + + val newEntry = JarEntry(name) + newEntry.time = entry.time + + output.putNextEntry(newEntry) + output.write(newBytes) + output.closeEntry() + } + + if (changedClasses > 0) { + val marker = JarEntry(MARKER_ENTRY) + output.putNextEntry(marker) + output.write( + "patched-at=${Instant.now()}\n".encodeToByteArray() + ) + output.closeEntry() + } + } + } + + if (changedClasses == 0) { + Files.deleteIfExists(tmp) + return RewriteResult(changedClasses = 0) + } + + createBackup(jar, log) + moveReplacing(tmp, jar) + + return RewriteResult(changedClasses = changedClasses) + } + + private fun rewriteClass( + bytes: ByteArray, + entryName: String, + warn: (String) -> Unit, + loggedWarning: () -> Boolean + ): ByteArray { + if (!containsAnyOldAdventureReference(bytes)) { + return bytes + } + + if (!loggedWarning()) { + warn("----------------- SurfAdventure5AbiPatcher -----------------") + warn( + "One or more plugins in the plugins directory appear to depend on surf-api and may be using the old Adventure 5 ABI. " + + "These plugins will be patched to update their Adventure references to the new API. " + + "This may not not cover all cases and you should prefer recompiling the plugins against the new API. " + + "If you are running this on a production server, please make a backup before proceeding. " + + "Stop the server within the next 30 seconds to abort patching." + ) + warn("You will have to restart the server again after patching is complete for the changes to take effect.") + warn("-------------------------------------------------------") + Thread.sleep(Duration.ofSeconds(30)) + } + + try { + val reader = ClassReader(bytes) + val writer = ClassWriter(reader, 0) + + val visitor = LegacySurfApiCallRewriter( + ClassRemapper(writer, remapper) + ) + + reader.accept(visitor, 0) + + return writer.toByteArray() + } catch (throwable: Throwable) { + throw IllegalStateException( + "Failed to rewrite class entry $entryName", + throwable, + ) + } + } + + private fun containsAnyOldAdventureReference(bytes: ByteArray): Boolean { + return bytes.containsAscii("net/kyori/adventure/text/BuildableComponent") || + bytes.containsAscii($$"net/kyori/adventure/util/Buildable$Builder") + } + + private fun readPluginMetadata(jar: Path): PluginMetadata? { + JarFile(jar.toFile()).use { jarFile -> + val pluginYml = jarFile.readTextEntry("plugin.yml") + val paperPluginYml = jarFile.readTextEntry("paper-plugin.yml") + + if (pluginYml == null && paperPluginYml == null) { + return null + } + + var name: String? = null + val dependencies = linkedSetOf() + + if (pluginYml != null) { + val parsed = parseBukkitPluginYml(pluginYml) + name = parsed.name + dependencies += parsed.dependencies + } + + if (paperPluginYml != null) { + val parsed = parsePaperPluginYml(paperPluginYml) + name = name ?: parsed.name + dependencies += parsed.dependencies + } + + return PluginMetadata( + name = name, + dependencies = dependencies, + ) + } + } + + private fun parseBukkitPluginYml(text: String): PluginMetadata { + val root = parseYaml(text) + + val name = root.node("name").string + + val dependencies = linkedSetOf() + dependencies += root.node("depend").getList(String::class).orEmpty() + dependencies += root.node("softdepend").getList(String::class).orEmpty() + + return PluginMetadata( + name = name, + dependencies = dependencies, + ) + } + + private fun parsePaperPluginYml(text: String): PluginMetadata { + val root = parseYaml(text) + + val name = root.node("name").string + + val dependencies = linkedSetOf() + val dependenciesSection = root.node("dependencies") + + dependenciesSection + .node("bootstrap") + .childrenMap() + .forEach { (name, _) -> + dependencies.add(name.toString()) + } + + dependenciesSection + .node("server") + .childrenMap() + .forEach { (name, _) -> + dependencies.add(name.toString()) + } + + return PluginMetadata( + name = name, + dependencies = dependencies, + ) + } + + private fun parseYaml(text: String): CommentedConfigurationNode { + return BufferedReader(text.reader()).use { reader -> + YamlConfigurationLoader.builder() + .source { reader } + .build() + .load() + } + } + + private fun hasMarker(jar: Path): Boolean { + JarFile(jar.toFile()).use { jarFile -> + return jarFile.getJarEntry(MARKER_ENTRY) != null + } + } + + private fun createBackup( + jar: Path, + log: (String) -> Unit, + ) { + val backupDir = jar.parent.resolve(".surf-abi-backups") + Files.createDirectories(backupDir) + + val backup = backupDir.resolve("${jar.fileName}.original") + + if (!Files.exists(backup)) { + Files.copy(jar, backup) + log("Backup created: $backup") + } + } + + private fun moveReplacing( + source: Path, + target: Path, + ) { + try { + Files.move( + source, + target, + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.ATOMIC_MOVE, + ) + } catch (_: UnsupportedOperationException) { + Files.move( + source, + target, + StandardCopyOption.REPLACE_EXISTING, + ) + } + } + + private fun JarFile.readTextEntry(name: String): String? { + val entry = getJarEntry(name) ?: return null + + return getInputStream(entry).use { input -> + input.readBytes().toString(Charsets.UTF_8) + } + } + + private fun isSignatureEntry(name: String): Boolean { + val upper = name.uppercase(Locale.ROOT) + + if (!upper.startsWith("META-INF/")) { + return false + } + + return upper.endsWith(".SF") || + upper.endsWith(".RSA") || + upper.endsWith(".DSA") || + upper.endsWith(".EC") + } + + private fun Set.normalized(): Set { + return mapTo(hashSetOf()) { it.normalizePluginName() } + } + + private fun String.normalizePluginName(): String { + return trim().lowercase(Locale.ROOT) + } + + private fun ByteArray.containsAscii(value: String): Boolean { + val needle = value.encodeToByteArray() + + if (needle.isEmpty() || needle.size > size) { + return false + } + + outer@ for (i in 0..size - needle.size) { + for (j in needle.indices) { + if (this[i + j] != needle[j]) { + continue@outer + } + } + + return true + } + + return false + } + + private class LegacySurfApiCallRewriter( + next: ClassVisitor, + ) : ClassVisitor(Opcodes.ASM9, next) { + + override fun visitMethod( + access: Int, + name: String, + descriptor: String, + signature: String?, + exceptions: Array?, + ): MethodVisitor { + val delegate = super.visitMethod( + access, + name, + descriptor, + signature, + exceptions, + ) + + return object : MethodVisitor(Opcodes.ASM9, delegate) { + + override fun visitTypeInsn( + opcode: Int, + type: String, + ) { + if (opcode == Opcodes.CHECKCAST && type == COMPONENT_BUILDER_INTERNAL) { + return + } + + super.visitTypeInsn(opcode, type) + } + + override fun visitMethodInsn( + opcode: Int, + owner: String, + name: String, + descriptor: String, + isInterface: Boolean, + ) { + if (opcode == Opcodes.INVOKESTATIC && owner == COMPONENT_EXTENSION_KT) { + val rewrittenDescriptor = rewriteComponentExtensionDescriptor(descriptor) + + if (rewrittenDescriptor != null) { + super.visitMethodInsn( + opcode, + owner, + name, + rewrittenDescriptor, + isInterface, + ) + return + } + } + + super.visitMethodInsn( + opcode, + owner, + name, + descriptor, + isInterface, + ) + } + } + } + } + + private fun rewriteComponentExtensionDescriptor( + descriptor: String, + ): String? { + val arguments = Type.getArgumentTypes(descriptor) + + if (arguments.isEmpty()) { + return null + } + + if (arguments[0].sort != Type.OBJECT) { + return null + } + + if (arguments[0].internalName != COMPONENT_BUILDER_INTERNAL) { + return null + } + + val rewrittenArguments = arguments.copyOf() + rewrittenArguments[0] = Type.getObjectType(SURF_COMPONENT_BUILDER_INTERNAL) + + val returnType = Type.getReturnType(descriptor) + + val rewrittenReturnType = + if (returnType.sort == Type.OBJECT && returnType.internalName == COMPONENT_BUILDER_INTERNAL) { + Type.getObjectType(SURF_COMPONENT_BUILDER_INTERNAL) + } else { + returnType + } + + return Type.getMethodDescriptor( + rewrittenReturnType, + *rewrittenArguments, + ) + } +} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt index 31bf0af8f..d9e21b34a 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt @@ -142,4 +142,71 @@ fun text(char: Char, color: TextColor? = null): TextComponent = Component.text(c */ fun text(any: Any, color: TextColor? = null): TextComponent = Component.text(any.toString(), color) -fun Component.plain() = PlainTextComponentSerializer.plainText().serialize(this) \ No newline at end of file +fun Component.plain() = PlainTextComponentSerializer.plainText().serialize(this) + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.appendText( + text: String, + color: TextColor? = null, +): SurfComponentBuilder { + return append(Component.text(text, color)) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.appendText( + text: String, + color: TextColor? = null, + block: TextComponent.Builder.() -> Unit, +): SurfComponentBuilder { + val builder = Component.text().content(text) + + if (color != null) { + builder.color(color) + } + + builder.apply(block) + + return append(builder.build()) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.clickOpensUrl( + url: String, +): SurfComponentBuilder { + return clickEvent(ClickEvent.openUrl(url)) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.clickRunsCommand( + command: String, +): SurfComponentBuilder { + return clickEvent(ClickEvent.runCommand(command)) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.clickSuggestsCommand( + command: String, +): SurfComponentBuilder { + return clickEvent(ClickEvent.suggestCommand(command)) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.clickCopiesToClipboard( + value: String, +): SurfComponentBuilder { + return clickEvent(ClickEvent.copyToClipboard(value)) +} + +@Suppress("EXTENSION_SHADOWED_BY_MEMBER") +@Deprecated("abi", level = DeprecationLevel.HIDDEN) +fun SurfComponentBuilder.appendNewline( + amount: Int, +): Unit = repeat(amount) { + appendNewline() +} \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-server/build.gradle.kts b/surf-api-paper/surf-api-paper-server/build.gradle.kts index 425e99c17..79082c146 100644 --- a/surf-api-paper/surf-api-paper-server/build.gradle.kts +++ b/surf-api-paper/surf-api-paper-server/build.gradle.kts @@ -1,3 +1,4 @@ +import dev.slne.surf.api.gradle.SurfComponentBuilderBridgePatcher import net.minecrell.pluginyml.paper.PaperPluginDescription.DependencyDefinition import net.minecrell.pluginyml.paper.PaperPluginDescription.RelativeLoadOrder @@ -110,6 +111,16 @@ configurations.all { exclude(group = "org.spigotmc", module = "spigot-api") } +tasks.withType().configureEach { + doLast { + val output = archiveFile.get().asFile + + if (name == "shadowJar" || output.name.endsWith("-all.jar")) { + SurfComponentBuilderBridgePatcher.patchJar(output.toPath()) + } + } +} + /** * Registers a soft dependency. * diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt index 0cc227811..7e530b94a 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt @@ -1,5 +1,6 @@ package dev.slne.surf.api.paper.server +import dev.slne.surf.api.core.server.messages.SurfAdventure5AbiPatcher import io.papermc.paper.plugin.bootstrap.BootstrapContext import io.papermc.paper.plugin.bootstrap.PluginBootstrap import io.papermc.paper.plugin.bootstrap.PluginProviderContext @@ -7,8 +8,20 @@ import kotlinx.coroutines.runBlocking @Suppress("unused", "UnstableApiUsage") class PaperBoostrapper : PluginBootstrap { - override fun bootstrap(bootstrapContext: BootstrapContext) = runBlocking { - PaperInstance.bootstrap() + override fun bootstrap(bootstrapContext: BootstrapContext) { + SurfAdventure5AbiPatcher.patchPluginsDirectory( + pluginsDir = bootstrapContext.pluginSource.parent, + log = { message -> + bootstrapContext.logger.info("[Adventure5AbiPatcher] $message") + }, + warn = { message -> + bootstrapContext.logger.warn("[Adventure5AbiPatcher] $message") + }, + ) + + runBlocking { + PaperInstance.bootstrap() + } } override fun createPlugin(context: PluginProviderContext) = PaperMain() From 385a03673def06fcc87758b6902c8bc78ae85bf6 Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:27:10 +0200 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=94=A7=20chore:=20remove=20deprecated?= =?UTF-8?q?=20SurfComponentBuilder=20methods=20and=20patcher=20references?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - delete deprecated methods from SurfComponentBuilder related to text and click events - remove SurfComponentBuilderBridgePatcher references from build.gradle.kts and PaperBoostrapper --- .../SurfComponentBuilderBridgePatcher.kt | 242 ------- .../messages/SurfAdventure5AbiPatcher.kt | 593 ------------------ .../messages/adventure/component-extension.kt | 69 +- .../surf-api-paper-server/build.gradle.kts | 11 - .../surf/api/paper/server/PaperBoostrapper.kt | 11 - 5 files changed, 1 insertion(+), 925 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt delete mode 100644 surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt diff --git a/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt b/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt deleted file mode 100644 index 366855640..000000000 --- a/buildSrc/src/main/kotlin/dev/slne/surf/api/gradle/SurfComponentBuilderBridgePatcher.kt +++ /dev/null @@ -1,242 +0,0 @@ -package dev.slne.surf.api.gradle - -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardCopyOption -import java.util.Locale -import java.util.jar.JarEntry -import java.util.jar.JarInputStream -import java.util.jar.JarOutputStream - -object SurfComponentBuilderBridgePatcher { - private const val SURF_COMPONENT_BUILDER = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilder" - private const val SURF_COMPONENT_BUILDER_IMPL = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilderImpl" - - private const val TEXT_COMPONENT_DESC = "()Lnet/kyori/adventure/text/TextComponent;" - private const val COMPONENT_DESC = "()Lnet/kyori/adventure/text/Component;" - - fun patchJar(jar: Path) { - require(Files.isRegularFile(jar)) { - "Jar does not exist: $jar" - } - - val tmp = Files.createTempFile( - jar.parent, - jar.fileName.toString(), - ".surf-builder-bridge.tmp", - ) - - - var changed = false - - JarInputStream(Files.newInputStream(jar)).use { input -> - JarOutputStream(Files.newOutputStream(tmp)).use { output -> - val written = hashSetOf() - - while (true) { - val entry = input.nextJarEntry ?: break - val name = entry.name - - if (!written.add(name)) { - continue - } - - if (isSignatureEntry(name)) { - continue - } - - val originalBytes = input.readBytes() - - val patchedBytes = when (name) { - "$SURF_COMPONENT_BUILDER.class" -> patchSurfComponentBuilderInterface(originalBytes) - "$SURF_COMPONENT_BUILDER_IMPL.class" -> patchSurfComponentBuilderImpl(originalBytes) - - else -> originalBytes - } - - if (!patchedBytes.contentEquals(originalBytes)) { - changed = true - } - - val newEntry = JarEntry(name) - newEntry.time = entry.time - - output.putNextEntry(newEntry) - output.write(patchedBytes) - output.closeEntry() - } - } - } - - if (changed) { - Files.move( - tmp, - jar, - StandardCopyOption.REPLACE_EXISTING, - ) - - println("[SurfComponentBuilderBridgePatcher] Patched $jar") - } else { - Files.deleteIfExists(tmp) - - println("[SurfComponentBuilderBridgePatcher] Nothing to patch in $jar") - } - } - - private fun patchSurfComponentBuilderInterface(bytes: ByteArray): ByteArray { - val reader = ClassReader(bytes) - val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS) - - var hasTextComponentBuild = false - var hasComponentBuild = false - - val visitor = object : ClassVisitor(Opcodes.ASM9, writer) { - override fun visitMethod( - access: Int, - name: String, - descriptor: String, - signature: String?, - exceptions: Array?, - ): MethodVisitor { - if (name == "build" && descriptor == TEXT_COMPONENT_DESC) { - hasTextComponentBuild = true - } - - if (name == "build" && descriptor == COMPONENT_DESC) { - hasComponentBuild = true - } - - return super.visitMethod(access, name, descriptor, signature, exceptions) - } - - override fun visitEnd() { - if (hasTextComponentBuild && !hasComponentBuild) { - addComponentBridge() - } - - super.visitEnd() - } - - private fun addComponentBridge() { - val mv = super.visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_BRIDGE, - "build", - COMPONENT_DESC, - null, - null, - ) - - mv.visitCode() - mv.visitVarInsn(Opcodes.ALOAD, 0) - mv.visitMethodInsn( - Opcodes.INVOKEINTERFACE, - SURF_COMPONENT_BUILDER, - "build", - TEXT_COMPONENT_DESC, - true, - ) - mv.visitInsn(Opcodes.ARETURN) - mv.visitMaxs(0, 0) - mv.visitEnd() - } - } - - reader.accept(visitor, 0) - - return writer.toByteArray() - } - - private fun patchSurfComponentBuilderImpl(bytes: ByteArray): ByteArray { - val reader = ClassReader(bytes) - val writer = ClassWriter(reader, ClassWriter.COMPUTE_MAXS) - - var className: String? = null - var hasTextComponentBuild = false - var hasComponentBuild = false - - val visitor = object : ClassVisitor(Opcodes.ASM9, writer) { - override fun visit( - version: Int, - access: Int, - name: String, - signature: String?, - superName: String?, - interfaces: Array?, - ) { - className = name - super.visit(version, access, name, signature, superName, interfaces) - } - - override fun visitMethod( - access: Int, - name: String, - descriptor: String, - signature: String?, - exceptions: Array?, - ): MethodVisitor { - if (name == "build" && descriptor == TEXT_COMPONENT_DESC) { - hasTextComponentBuild = true - } - - if (name == "build" && descriptor == COMPONENT_DESC) { - hasComponentBuild = true - } - - return super.visitMethod(access, name, descriptor, signature, exceptions) - } - - override fun visitEnd() { - if (hasTextComponentBuild && !hasComponentBuild) { - addComponentBridge(className ?: SURF_COMPONENT_BUILDER_IMPL) - } - - super.visitEnd() - } - - private fun addComponentBridge(owner: String) { - val mv = super.visitMethod( - Opcodes.ACC_PUBLIC or Opcodes.ACC_SYNTHETIC or Opcodes.ACC_BRIDGE, - "build", - COMPONENT_DESC, - null, - null, - ) - - mv.visitCode() - mv.visitVarInsn(Opcodes.ALOAD, 0) - mv.visitMethodInsn( - Opcodes.INVOKEVIRTUAL, - owner, - "build", - TEXT_COMPONENT_DESC, - false, - ) - mv.visitInsn(Opcodes.ARETURN) - mv.visitMaxs(0, 0) - mv.visitEnd() - } - } - - reader.accept(visitor, 0) - - return writer.toByteArray() - } - - private fun isSignatureEntry(name: String): Boolean { - val upper = name.uppercase(Locale.ROOT) - - if (!upper.startsWith("META-INF/")) { - return false - } - - return upper.endsWith(".SF") || - upper.endsWith(".RSA") || - upper.endsWith(".DSA") || - upper.endsWith(".EC") - } -} \ No newline at end of file diff --git a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt b/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt deleted file mode 100644 index ae8388185..000000000 --- a/surf-api-core/surf-api-core-server/src/main/kotlin/dev/slne/surf/api/core/server/messages/SurfAdventure5AbiPatcher.kt +++ /dev/null @@ -1,593 +0,0 @@ -package dev.slne.surf.api.core.server.messages - -import net.bytebuddy.jar.asm.* -import net.bytebuddy.jar.asm.commons.ClassRemapper -import net.bytebuddy.jar.asm.commons.Remapper -import org.spongepowered.configurate.CommentedConfigurationNode -import org.spongepowered.configurate.kotlin.extensions.getList -import org.spongepowered.configurate.yaml.YamlConfigurationLoader -import java.io.BufferedReader -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.StandardCopyOption -import java.time.Duration -import java.time.Instant -import java.util.* -import java.util.concurrent.atomic.AtomicBoolean -import java.util.jar.JarEntry -import java.util.jar.JarFile -import java.util.jar.JarInputStream -import java.util.jar.JarOutputStream -import kotlin.io.path.exists -import kotlin.io.path.isRegularFile - -object SurfAdventure5AbiPatcher { - private const val MARKER_ENTRY = "META-INF/surf-adventure5-abi-patched-v2" - - private const val COMPONENT_BUILDER_INTERNAL = "net/kyori/adventure/text/ComponentBuilder" - private const val SURF_COMPONENT_BUILDER_INTERNAL = "dev/slne/surf/api/core/messages/builder/SurfComponentBuilder" - private const val COMPONENT_EXTENSION_KT = "dev/slne/surf/api/core/messages/adventure/Component_extensionKt" - - - private val defaultDependencyNames = setOf( - "surf-paper-api", - ) - - private val internalNameMappings = mapOf( - "net/kyori/adventure/text/BuildableComponent" to - "net/kyori/adventure/text/Component", - - $$"net/kyori/adventure/util/Buildable$Builder" to - "net/kyori/adventure/builder/AbstractBuilder", - ) - - private val remapper = object : Remapper(Opcodes.ASM9) { - override fun map(internalName: String): String { - return internalNameMappings[internalName] ?: internalName - } - } - - data class PatchResult( - val jar: Path, - val pluginName: String?, - val status: Status, - val changedClasses: Int = 0, - val reason: String? = null, - ) - - enum class Status { - PATCHED, - SKIPPED_NO_PLUGIN_METADATA, - SKIPPED_SELF, - SKIPPED_NO_SURF_DEPENDENCY, - SKIPPED_ALREADY_PATCHED, - SKIPPED_NO_RELEVANT_BYTECODE, - } - - data class PluginMetadata( - val name: String?, - val dependencies: Set, - ) - - fun patchPluginsDirectory( - pluginsDir: Path = Path.of("plugins"), - dependencyNames: Set = defaultDependencyNames, - log: (String) -> Unit = {}, - warn: (String) -> Unit = {}, - ): List { - if (!pluginsDir.exists()) { - log("Plugin folder does not exist: $pluginsDir") - return emptyList() - } - - val normalizedDependencyNames = dependencyNames.normalized() - - val results = mutableListOf() - - val warningLogged = AtomicBoolean(false) - - Files.list(pluginsDir).use { stream -> - stream - .filter { path -> - path.isRegularFile() && - path.fileName.toString().endsWith(".jar", ignoreCase = true) - } - .forEach { jar -> - val result = patchPluginJarIfNecessary( - jar = jar, - dependencyNames = normalizedDependencyNames, - log = log, - warn = warn, - loggedWarning = { warningLogged.getAndSet(true) } - ) - - results += result - - when (result.status) { - Status.PATCHED -> log( - "Patched ${jar.fileName}: ${result.changedClasses} class(es) rewritten" - ) - - Status.SKIPPED_NO_RELEVANT_BYTECODE -> log( - "Skipped ${jar.fileName}: depends on surf-api, but has no old Adventure ABI refs" - ) - - Status.SKIPPED_ALREADY_PATCHED -> log( - "Skipped ${jar.fileName}: already patched" - ) - - else -> Unit - } - } - } - - return results - } - - private fun patchPluginJarIfNecessary( - jar: Path, - dependencyNames: Set, - log: (String) -> Unit, - warn: (String) -> Unit, - loggedWarning: () -> Boolean - ): PatchResult { - val metadata = readPluginMetadata(jar) - ?: return PatchResult( - jar = jar, - pluginName = null, - status = Status.SKIPPED_NO_PLUGIN_METADATA, - ) - - val pluginName = metadata.name - val normalizedPluginName = pluginName?.normalizePluginName() - - if (normalizedPluginName != null && normalizedPluginName in dependencyNames) { - return PatchResult( - jar = jar, - pluginName = pluginName, - status = Status.SKIPPED_SELF, - ) - } - - val dependsOnSurfApi = metadata.dependencies - .normalized() - .any { it in dependencyNames } - - if (!dependsOnSurfApi) { - return PatchResult( - jar = jar, - pluginName = pluginName, - status = Status.SKIPPED_NO_SURF_DEPENDENCY, - ) - } - - if (hasMarker(jar)) { - return PatchResult( - jar = jar, - pluginName = pluginName, - status = Status.SKIPPED_ALREADY_PATCHED, - ) - } - - val rewriteResult = rewriteJar(jar, log, warn, loggedWarning) - - if (rewriteResult.changedClasses == 0) { - return PatchResult( - jar = jar, - pluginName = pluginName, - status = Status.SKIPPED_NO_RELEVANT_BYTECODE, - ) - } - - return PatchResult( - jar = jar, - pluginName = pluginName, - status = Status.PATCHED, - changedClasses = rewriteResult.changedClasses, - ) - } - - private data class RewriteResult( - val changedClasses: Int, - ) - - private fun rewriteJar( - jar: Path, - log: (String) -> Unit, - warn: (String) -> Unit, - loggedWarning: () -> Boolean - ): RewriteResult { - val tmp = Files.createTempFile( - jar.parent, - jar.fileName.toString(), - ".surf-abi.tmp", - ) - - var changedClasses = 0 - - JarInputStream(Files.newInputStream(jar)).use { input -> - JarOutputStream(Files.newOutputStream(tmp)).use { output -> - val writtenEntries = hashSetOf() - - while (true) { - val entry = input.nextJarEntry ?: break - val name = entry.name - - if (!writtenEntries.add(name)) { - continue - } - - if (name == MARKER_ENTRY) { - continue - } - - if (isSignatureEntry(name)) { - continue - } - - val originalBytes = input.readBytes() - - val newBytes = if (name.endsWith(".class")) { - val rewritten = rewriteClass(originalBytes, name, warn, loggedWarning) - - if (!rewritten.contentEquals(originalBytes)) { - changedClasses++ - } - - rewritten - } else { - originalBytes - } - - val newEntry = JarEntry(name) - newEntry.time = entry.time - - output.putNextEntry(newEntry) - output.write(newBytes) - output.closeEntry() - } - - if (changedClasses > 0) { - val marker = JarEntry(MARKER_ENTRY) - output.putNextEntry(marker) - output.write( - "patched-at=${Instant.now()}\n".encodeToByteArray() - ) - output.closeEntry() - } - } - } - - if (changedClasses == 0) { - Files.deleteIfExists(tmp) - return RewriteResult(changedClasses = 0) - } - - createBackup(jar, log) - moveReplacing(tmp, jar) - - return RewriteResult(changedClasses = changedClasses) - } - - private fun rewriteClass( - bytes: ByteArray, - entryName: String, - warn: (String) -> Unit, - loggedWarning: () -> Boolean - ): ByteArray { - if (!containsAnyOldAdventureReference(bytes)) { - return bytes - } - - if (!loggedWarning()) { - warn("----------------- SurfAdventure5AbiPatcher -----------------") - warn( - "One or more plugins in the plugins directory appear to depend on surf-api and may be using the old Adventure 5 ABI. " + - "These plugins will be patched to update their Adventure references to the new API. " + - "This may not not cover all cases and you should prefer recompiling the plugins against the new API. " + - "If you are running this on a production server, please make a backup before proceeding. " + - "Stop the server within the next 30 seconds to abort patching." - ) - warn("You will have to restart the server again after patching is complete for the changes to take effect.") - warn("-------------------------------------------------------") - Thread.sleep(Duration.ofSeconds(30)) - } - - try { - val reader = ClassReader(bytes) - val writer = ClassWriter(reader, 0) - - val visitor = LegacySurfApiCallRewriter( - ClassRemapper(writer, remapper) - ) - - reader.accept(visitor, 0) - - return writer.toByteArray() - } catch (throwable: Throwable) { - throw IllegalStateException( - "Failed to rewrite class entry $entryName", - throwable, - ) - } - } - - private fun containsAnyOldAdventureReference(bytes: ByteArray): Boolean { - return bytes.containsAscii("net/kyori/adventure/text/BuildableComponent") || - bytes.containsAscii($$"net/kyori/adventure/util/Buildable$Builder") - } - - private fun readPluginMetadata(jar: Path): PluginMetadata? { - JarFile(jar.toFile()).use { jarFile -> - val pluginYml = jarFile.readTextEntry("plugin.yml") - val paperPluginYml = jarFile.readTextEntry("paper-plugin.yml") - - if (pluginYml == null && paperPluginYml == null) { - return null - } - - var name: String? = null - val dependencies = linkedSetOf() - - if (pluginYml != null) { - val parsed = parseBukkitPluginYml(pluginYml) - name = parsed.name - dependencies += parsed.dependencies - } - - if (paperPluginYml != null) { - val parsed = parsePaperPluginYml(paperPluginYml) - name = name ?: parsed.name - dependencies += parsed.dependencies - } - - return PluginMetadata( - name = name, - dependencies = dependencies, - ) - } - } - - private fun parseBukkitPluginYml(text: String): PluginMetadata { - val root = parseYaml(text) - - val name = root.node("name").string - - val dependencies = linkedSetOf() - dependencies += root.node("depend").getList(String::class).orEmpty() - dependencies += root.node("softdepend").getList(String::class).orEmpty() - - return PluginMetadata( - name = name, - dependencies = dependencies, - ) - } - - private fun parsePaperPluginYml(text: String): PluginMetadata { - val root = parseYaml(text) - - val name = root.node("name").string - - val dependencies = linkedSetOf() - val dependenciesSection = root.node("dependencies") - - dependenciesSection - .node("bootstrap") - .childrenMap() - .forEach { (name, _) -> - dependencies.add(name.toString()) - } - - dependenciesSection - .node("server") - .childrenMap() - .forEach { (name, _) -> - dependencies.add(name.toString()) - } - - return PluginMetadata( - name = name, - dependencies = dependencies, - ) - } - - private fun parseYaml(text: String): CommentedConfigurationNode { - return BufferedReader(text.reader()).use { reader -> - YamlConfigurationLoader.builder() - .source { reader } - .build() - .load() - } - } - - private fun hasMarker(jar: Path): Boolean { - JarFile(jar.toFile()).use { jarFile -> - return jarFile.getJarEntry(MARKER_ENTRY) != null - } - } - - private fun createBackup( - jar: Path, - log: (String) -> Unit, - ) { - val backupDir = jar.parent.resolve(".surf-abi-backups") - Files.createDirectories(backupDir) - - val backup = backupDir.resolve("${jar.fileName}.original") - - if (!Files.exists(backup)) { - Files.copy(jar, backup) - log("Backup created: $backup") - } - } - - private fun moveReplacing( - source: Path, - target: Path, - ) { - try { - Files.move( - source, - target, - StandardCopyOption.REPLACE_EXISTING, - StandardCopyOption.ATOMIC_MOVE, - ) - } catch (_: UnsupportedOperationException) { - Files.move( - source, - target, - StandardCopyOption.REPLACE_EXISTING, - ) - } - } - - private fun JarFile.readTextEntry(name: String): String? { - val entry = getJarEntry(name) ?: return null - - return getInputStream(entry).use { input -> - input.readBytes().toString(Charsets.UTF_8) - } - } - - private fun isSignatureEntry(name: String): Boolean { - val upper = name.uppercase(Locale.ROOT) - - if (!upper.startsWith("META-INF/")) { - return false - } - - return upper.endsWith(".SF") || - upper.endsWith(".RSA") || - upper.endsWith(".DSA") || - upper.endsWith(".EC") - } - - private fun Set.normalized(): Set { - return mapTo(hashSetOf()) { it.normalizePluginName() } - } - - private fun String.normalizePluginName(): String { - return trim().lowercase(Locale.ROOT) - } - - private fun ByteArray.containsAscii(value: String): Boolean { - val needle = value.encodeToByteArray() - - if (needle.isEmpty() || needle.size > size) { - return false - } - - outer@ for (i in 0..size - needle.size) { - for (j in needle.indices) { - if (this[i + j] != needle[j]) { - continue@outer - } - } - - return true - } - - return false - } - - private class LegacySurfApiCallRewriter( - next: ClassVisitor, - ) : ClassVisitor(Opcodes.ASM9, next) { - - override fun visitMethod( - access: Int, - name: String, - descriptor: String, - signature: String?, - exceptions: Array?, - ): MethodVisitor { - val delegate = super.visitMethod( - access, - name, - descriptor, - signature, - exceptions, - ) - - return object : MethodVisitor(Opcodes.ASM9, delegate) { - - override fun visitTypeInsn( - opcode: Int, - type: String, - ) { - if (opcode == Opcodes.CHECKCAST && type == COMPONENT_BUILDER_INTERNAL) { - return - } - - super.visitTypeInsn(opcode, type) - } - - override fun visitMethodInsn( - opcode: Int, - owner: String, - name: String, - descriptor: String, - isInterface: Boolean, - ) { - if (opcode == Opcodes.INVOKESTATIC && owner == COMPONENT_EXTENSION_KT) { - val rewrittenDescriptor = rewriteComponentExtensionDescriptor(descriptor) - - if (rewrittenDescriptor != null) { - super.visitMethodInsn( - opcode, - owner, - name, - rewrittenDescriptor, - isInterface, - ) - return - } - } - - super.visitMethodInsn( - opcode, - owner, - name, - descriptor, - isInterface, - ) - } - } - } - } - - private fun rewriteComponentExtensionDescriptor( - descriptor: String, - ): String? { - val arguments = Type.getArgumentTypes(descriptor) - - if (arguments.isEmpty()) { - return null - } - - if (arguments[0].sort != Type.OBJECT) { - return null - } - - if (arguments[0].internalName != COMPONENT_BUILDER_INTERNAL) { - return null - } - - val rewrittenArguments = arguments.copyOf() - rewrittenArguments[0] = Type.getObjectType(SURF_COMPONENT_BUILDER_INTERNAL) - - val returnType = Type.getReturnType(descriptor) - - val rewrittenReturnType = - if (returnType.sort == Type.OBJECT && returnType.internalName == COMPONENT_BUILDER_INTERNAL) { - Type.getObjectType(SURF_COMPONENT_BUILDER_INTERNAL) - } else { - returnType - } - - return Type.getMethodDescriptor( - rewrittenReturnType, - *rewrittenArguments, - ) - } -} \ No newline at end of file diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt index d9e21b34a..31bf0af8f 100644 --- a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/messages/adventure/component-extension.kt @@ -142,71 +142,4 @@ fun text(char: Char, color: TextColor? = null): TextComponent = Component.text(c */ fun text(any: Any, color: TextColor? = null): TextComponent = Component.text(any.toString(), color) -fun Component.plain() = PlainTextComponentSerializer.plainText().serialize(this) - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.appendText( - text: String, - color: TextColor? = null, -): SurfComponentBuilder { - return append(Component.text(text, color)) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.appendText( - text: String, - color: TextColor? = null, - block: TextComponent.Builder.() -> Unit, -): SurfComponentBuilder { - val builder = Component.text().content(text) - - if (color != null) { - builder.color(color) - } - - builder.apply(block) - - return append(builder.build()) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.clickOpensUrl( - url: String, -): SurfComponentBuilder { - return clickEvent(ClickEvent.openUrl(url)) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.clickRunsCommand( - command: String, -): SurfComponentBuilder { - return clickEvent(ClickEvent.runCommand(command)) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.clickSuggestsCommand( - command: String, -): SurfComponentBuilder { - return clickEvent(ClickEvent.suggestCommand(command)) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.clickCopiesToClipboard( - value: String, -): SurfComponentBuilder { - return clickEvent(ClickEvent.copyToClipboard(value)) -} - -@Suppress("EXTENSION_SHADOWED_BY_MEMBER") -@Deprecated("abi", level = DeprecationLevel.HIDDEN) -fun SurfComponentBuilder.appendNewline( - amount: Int, -): Unit = repeat(amount) { - appendNewline() -} \ No newline at end of file +fun Component.plain() = PlainTextComponentSerializer.plainText().serialize(this) \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-server/build.gradle.kts b/surf-api-paper/surf-api-paper-server/build.gradle.kts index 79082c146..425e99c17 100644 --- a/surf-api-paper/surf-api-paper-server/build.gradle.kts +++ b/surf-api-paper/surf-api-paper-server/build.gradle.kts @@ -1,4 +1,3 @@ -import dev.slne.surf.api.gradle.SurfComponentBuilderBridgePatcher import net.minecrell.pluginyml.paper.PaperPluginDescription.DependencyDefinition import net.minecrell.pluginyml.paper.PaperPluginDescription.RelativeLoadOrder @@ -111,16 +110,6 @@ configurations.all { exclude(group = "org.spigotmc", module = "spigot-api") } -tasks.withType().configureEach { - doLast { - val output = archiveFile.get().asFile - - if (name == "shadowJar" || output.name.endsWith("-all.jar")) { - SurfComponentBuilderBridgePatcher.patchJar(output.toPath()) - } - } -} - /** * Registers a soft dependency. * diff --git a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt index 7e530b94a..1f3f95652 100644 --- a/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt +++ b/surf-api-paper/surf-api-paper-server/src/main/kotlin/dev/slne/surf/api/paper/server/PaperBoostrapper.kt @@ -1,6 +1,5 @@ package dev.slne.surf.api.paper.server -import dev.slne.surf.api.core.server.messages.SurfAdventure5AbiPatcher import io.papermc.paper.plugin.bootstrap.BootstrapContext import io.papermc.paper.plugin.bootstrap.PluginBootstrap import io.papermc.paper.plugin.bootstrap.PluginProviderContext @@ -9,16 +8,6 @@ import kotlinx.coroutines.runBlocking @Suppress("unused", "UnstableApiUsage") class PaperBoostrapper : PluginBootstrap { override fun bootstrap(bootstrapContext: BootstrapContext) { - SurfAdventure5AbiPatcher.patchPluginsDirectory( - pluginsDir = bootstrapContext.pluginSource.parent, - log = { message -> - bootstrapContext.logger.info("[Adventure5AbiPatcher] $message") - }, - warn = { message -> - bootstrapContext.logger.warn("[Adventure5AbiPatcher] $message") - }, - ) - runBlocking { PaperInstance.bootstrap() } From d13e46e822b46a505b0757bec12d90ab59f9e30f Mon Sep 17 00:00:00 2001 From: twisti-dev <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 19 Jun 2026 21:28:02 +0200 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=94=A7=20chore:=20set=20snapshot=20pr?= =?UTF-8?q?operty=20to=20true=20in=20gradle.properties?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1ad722fc1..8fbe82cde 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,4 +9,4 @@ mcVersion=26.2 group=dev.slne.surf.api version=3.26.0 relocationPrefix=dev.slne.surf.api.libs -snapshot=false +snapshot=true