diff --git a/gradle.properties b/gradle.properties index c8fc390e..e53dd202 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled javaVersion=25 mcVersion=26.1.2 group=dev.slne.surf.api -version=3.20.0 +version=3.21.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false 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 9446f803..d6c294a3 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 @@ -11035,6 +11035,10 @@ public abstract interface class dev/slne/surf/api/core/util/ToByteFunction { public abstract fun applyAsByte (Ljava/lang/Object;)B } +public final class dev/slne/surf/api/core/util/Var_handle_utilKt { + public static final fun setIfGreater (Ljava/lang/invoke/VarHandle;Ljava/lang/Object;I)Z +} + public abstract interface class dev/slne/surf/api/core/util/blockstate/BlockStateFactory { public static final field Companion Ldev/slne/surf/api/core/util/blockstate/BlockStateFactory$Companion; public static fun builder (Lcom/github/retrooper/packetevents/protocol/world/states/WrappedBlockState;)Ldev/slne/surf/api/core/util/blockstate/BlockStateFactory$Builder; diff --git a/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/var-handle-util.kt b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/var-handle-util.kt new file mode 100644 index 00000000..6431028e --- /dev/null +++ b/surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/core/util/var-handle-util.kt @@ -0,0 +1,27 @@ +package dev.slne.surf.api.core.util + +import java.lang.invoke.VarHandle + +/** + * Sets the value at the specified variable handle to the new value if the new value is greater than the current value. + * + * This method ensures that the value is updated only when the provided `newValue` is strictly greater + * than the current value stored at the variable handle. The operation is performed atomically. + * + * @param receiver the object on which the variable handle operates + * @param newValue the new value to set if greater than the current value + * @return `true` if the value was updated to `newValue`, `false` if `newValue` was not greater and no update occurred + */ +fun VarHandle.setIfGreater(receiver: Any, newValue: Int): Boolean { + while (true) { + val current = getVolatile(receiver) as Int + + if (newValue <= current) { + return false + } + + if (compareAndSet(receiver, current, newValue)) { + return true + } + } +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts b/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts index c631e60f..48638430 100644 --- a/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts +++ b/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts @@ -12,7 +12,7 @@ plugins { group = groupId version = buildString { - append("1.0.0") + append("1.0.1") if (snapshot) append("-SNAPSHOT") } diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt index 18680563..a17733a3 100644 --- a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt @@ -7,10 +7,7 @@ import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.symbol.* import com.google.devtools.ksp.validate -import com.palantir.javapoet.ClassName -import com.palantir.javapoet.ParameterizedTypeName -import com.palantir.javapoet.TypeName -import com.palantir.javapoet.WildcardTypeName +import com.palantir.javapoet.* import dev.slne.surf.api.processor.ClassNames import dev.slne.surf.api.processor.ShortClassNames import dev.slne.surf.api.processor.ShortClassNames.CONSTANT_BOOLEAN_ARGUMENT @@ -412,6 +409,79 @@ class ReflectionSymbolProcessor(environment: SymbolProcessorEnvironment) : Symbo "kotlin.Double" -> if (isNullable) boxed("java.lang.Double", javaNullability) else JavaType.Double "kotlin.Char" -> if (isNullable) boxed("java.lang.Character", javaNullability) else JavaType.Char + "kotlin.Array" -> { + val componentType = arguments.firstOrNull() + ?.type + ?.resolve() + ?.toJavaType() + ?: JavaType.Object + + val arrayType = ArrayTypeName.of(componentType.className.box()) + + JavaType( + sourceName = "${componentType.sourceName}[]", + className = arrayType, + erasedClassName = ArrayTypeName.of(componentType.erasedClassName.box()), + nullability = javaNullability, + ) + } + + "kotlin.BooleanArray" -> JavaType( + sourceName = "boolean[]", + className = ArrayTypeName.of(TypeName.BOOLEAN), + erasedClassName = ArrayTypeName.of(TypeName.BOOLEAN), + nullability = javaNullability, + ) + + "kotlin.ByteArray" -> JavaType( + sourceName = "byte[]", + className = ArrayTypeName.of(TypeName.BYTE), + erasedClassName = ArrayTypeName.of(TypeName.BYTE), + nullability = javaNullability, + ) + + "kotlin.ShortArray" -> JavaType( + sourceName = "short[]", + className = ArrayTypeName.of(TypeName.SHORT), + erasedClassName = ArrayTypeName.of(TypeName.SHORT), + nullability = javaNullability, + ) + + "kotlin.IntArray" -> JavaType( + sourceName = "int[]", + className = ArrayTypeName.of(TypeName.INT), + erasedClassName = ArrayTypeName.of(TypeName.INT), + nullability = javaNullability, + ) + + "kotlin.LongArray" -> JavaType( + sourceName = "long[]", + className = ArrayTypeName.of(TypeName.LONG), + erasedClassName = ArrayTypeName.of(TypeName.LONG), + nullability = javaNullability, + ) + + "kotlin.FloatArray" -> JavaType( + sourceName = "float[]", + className = ArrayTypeName.of(TypeName.FLOAT), + erasedClassName = ArrayTypeName.of(TypeName.FLOAT), + nullability = javaNullability, + ) + + "kotlin.DoubleArray" -> JavaType( + sourceName = "double[]", + className = ArrayTypeName.of(TypeName.DOUBLE), + erasedClassName = ArrayTypeName.of(TypeName.DOUBLE), + nullability = javaNullability, + ) + + "kotlin.CharArray" -> JavaType( + sourceName = "char[]", + className = ArrayTypeName.of(TypeName.CHAR), + erasedClassName = ArrayTypeName.of(TypeName.CHAR), + nullability = javaNullability, + ) + else -> { val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(qualifiedName)) val javaFqName = javaClassId?.asSingleFqName()?.asString() ?: qualifiedName diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/bridges/V1_21_11SurfPaperNmsPlayerBridgeImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/bridges/V1_21_11SurfPaperNmsPlayerBridgeImpl.kt index 8df0e0d1..f9f38103 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/bridges/V1_21_11SurfPaperNmsPlayerBridgeImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/bridges/V1_21_11SurfPaperNmsPlayerBridgeImpl.kt @@ -36,6 +36,7 @@ import net.minecraft.world.entity.EntityEquipment import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.npc.InventoryCarrier import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.ProfilePublicKey import net.minecraft.world.level.storage.* import org.bukkit.craftbukkit.CraftEquipmentSlot import org.bukkit.craftbukkit.inventory.CraftItemStack @@ -63,6 +64,35 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { ) } + @Suppress("USELESS_ELVIS") + override fun resetPlayerChatState(player: Player, chatSession: RemoteChatSessionData) { + val nmsPlayer = player.toNms() + val connection = nmsPlayer.connection ?: return + + val newChatSessionData = RemoteChatSession.Data( + chatSession.sessionId, + ProfilePublicKey.Data( + chatSession.expiresAt, + chatSession.key, + chatSession.keySignature + ) + ) + + + val signatureValidator = MinecraftServer.getServer().services().profileKeySignatureValidator() + if (signatureValidator == null) { + CHAT_LOGGER.warn("Ignoring chat session from {} due to missing Services public key", player.name) + return + } + + val newChatSession = newChatSessionData.validate( + nmsPlayer.gameProfile, + signatureValidator + ) + + V1_21_11NmsReflections.resetPlayerChatState(connection, newChatSession) + } + override fun runOnChatMessageChain(player: Player, scope: CoroutineScope, block: suspend () -> Unit) { val nmsPlayer = player.toNms() val connection = nmsPlayer.connection @@ -347,5 +377,6 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { companion object { private val OFFLINE_INVENTORY_EDIT_LOGGER = ComponentLogger.logger("OfflinePlayer Inventory Edit") + private val CHAT_LOGGER = ComponentLogger.logger("SurfPaperNmsPlayerBridge Chat") } } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/V1_21_11NmsReflections.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/V1_21_11NmsReflections.kt index 3a7c5e3a..25aaf114 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/V1_21_11NmsReflections.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/V1_21_11NmsReflections.kt @@ -5,10 +5,7 @@ import com.google.gson.JsonElement import dev.slne.surf.api.shared.api.reflection.* import io.netty.channel.ChannelFuture import io.papermc.paper.adventure.ChatProcessor -import net.minecraft.network.chat.ChatType -import net.minecraft.network.chat.FilterMask -import net.minecraft.network.chat.LastSeenMessagesValidator -import net.minecraft.network.chat.MessageSignatureCache +import net.minecraft.network.chat.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.resources.ResourceKey import net.minecraft.server.network.ServerConnectionListener @@ -86,5 +83,11 @@ interface V1_21_11NmsReflections { ) fun getServerStatsCounterGson(): Gson + @ReflectedMethod("resetPlayerChatState") + fun resetPlayerChatState( + instance: ServerGamePacketListenerImpl, + chatSession: RemoteChatSession + ) + companion object : V1_21_11NmsReflections by generatedReflectionAccessor() } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/bridges/V26_1SurfPaperNmsPlayerBridgeImpl.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/bridges/V26_1SurfPaperNmsPlayerBridgeImpl.kt index befa29e7..4fc2721a 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/bridges/V26_1SurfPaperNmsPlayerBridgeImpl.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/bridges/V26_1SurfPaperNmsPlayerBridgeImpl.kt @@ -7,8 +7,7 @@ import dev.slne.surf.api.paper.extensions.server import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsPlayerBridge import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsPlayerBridge.PlayerInventoryEdit -import dev.slne.surf.api.paper.nms.bridges.data.chat.PlayerChatMessageMirror -import dev.slne.surf.api.paper.nms.bridges.data.chat.RemoteChatSessionData +import dev.slne.surf.api.paper.nms.bridges.data.chat.* import dev.slne.surf.api.paper.nms.common.dummy.DummyEntityEquipment import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNms import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections @@ -36,6 +35,7 @@ import net.minecraft.world.entity.EntityEquipment import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.npc.InventoryCarrier import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.ProfilePublicKey import net.minecraft.world.level.storage.* import org.bukkit.craftbukkit.CraftEquipmentSlot import org.bukkit.craftbukkit.inventory.CraftItemStack @@ -44,24 +44,57 @@ import org.bukkit.inventory.EquipmentSlot import org.bukkit.inventory.ItemStack import java.io.File import java.nio.file.Path +import java.util.* import java.util.concurrent.CompletableFuture import kotlin.io.path.createTempFile import kotlin.jvm.optionals.getOrNull @NmsUseWithCaution +@Suppress("ClassName") class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { + + @Suppress("USELESS_ELVIS") override fun getRemoteChatSessionData(player: Player): RemoteChatSessionData? { - val session = player.toNms().chatSession?.asData() ?: return null + val connection = player.toNms().connection ?: return null + val session = V26_1NmsReflections.getRemoteChatSession(connection) ?: return null val profilePublicKey = session.profilePublicKey() return RemoteChatSessionData( sessionId = session.sessionId(), - expiresAt = profilePublicKey.expiresAt(), - key = profilePublicKey.key(), - keySignature = profilePublicKey.keySignature() + expiresAt = profilePublicKey.data().expiresAt(), + key = profilePublicKey.data().key(), + keySignature = profilePublicKey.data().keySignature() ) } + @Suppress("USELESS_ELVIS") + override fun resetPlayerChatState(player: Player, chatSession: RemoteChatSessionData) { + val nmsPlayer = player.toNms() + val connection = nmsPlayer.connection ?: return + + val newChatSessionData = RemoteChatSession.Data( + chatSession.sessionId, + ProfilePublicKey.Data( + chatSession.expiresAt, + chatSession.key, + chatSession.keySignature + ) + ) + + val signatureValidator = MinecraftServer.getServer().services().profileKeySignatureValidator() + if (signatureValidator == null) { + CHAT_LOGGER.warn("Ignoring chat session from {} due to missing Services public key", player.name) + return + } + + val newChatSession = newChatSessionData.validate( + nmsPlayer.gameProfile, + signatureValidator + ) + + V26_1NmsReflections.resetPlayerChatState(connection, newChatSession,) + } + override fun runOnChatMessageChain(player: Player, scope: CoroutineScope, block: suspend () -> Unit) { val nmsPlayer = player.toNms() val connection = nmsPlayer.connection @@ -346,5 +379,6 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { companion object { private val OFFLINE_INVENTORY_EDIT_LOGGER = ComponentLogger.logger("OfflinePlayer Inventory Edit") + private val CHAT_LOGGER = ComponentLogger.logger("SurfPaperNmsPlayerBridge Chat") } } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/reflection/V26_1NmsReflections.kt b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/reflection/V26_1NmsReflections.kt index 2a2539d6..b0844ca9 100644 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/reflection/V26_1NmsReflections.kt +++ b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v26_1/reflection/V26_1NmsReflections.kt @@ -5,10 +5,7 @@ import com.google.gson.JsonElement import dev.slne.surf.api.shared.api.reflection.* import io.netty.channel.ChannelFuture import io.papermc.paper.adventure.ChatProcessor -import net.minecraft.network.chat.ChatType -import net.minecraft.network.chat.FilterMask -import net.minecraft.network.chat.LastSeenMessagesValidator -import net.minecraft.network.chat.MessageSignatureCache +import net.minecraft.network.chat.* import net.minecraft.network.syncher.EntityDataAccessor import net.minecraft.resources.ResourceKey import net.minecraft.server.network.ServerConnectionListener @@ -85,5 +82,14 @@ interface V26_1NmsReflections { ) fun getServerStatsCounterGson(): Gson + @ReflectedMethod("resetPlayerChatState") + fun resetPlayerChatState( + instance: ServerGamePacketListenerImpl, + chatSession: RemoteChatSession + ) + + @ReflectedField("chatSession") + fun getRemoteChatSession(instance: ServerGamePacketListenerImpl): RemoteChatSession? + companion object : V26_1NmsReflections by generatedReflectionAccessor() } \ No newline at end of file diff --git a/surf-api-paper/surf-api-paper/api/surf-api-paper.api b/surf-api-paper/surf-api-paper/api/surf-api-paper.api index 0d2a6892..b60c8551 100644 --- a/surf-api-paper/surf-api-paper/api/surf-api-paper.api +++ b/surf-api-paper/surf-api-paper/api/surf-api-paper.api @@ -1735,6 +1735,7 @@ public abstract interface class dev/slne/surf/api/paper/nms/bridges/SurfPaperNms public abstract fun getRemoteChatSessionData (Lorg/bukkit/entity/Player;)Ldev/slne/surf/api/paper/nms/bridges/data/chat/RemoteChatSessionData; public abstract fun getStatsDataPath ()Ljava/nio/file/Path; public abstract fun increaseNextChatIndex (Lorg/bukkit/entity/Player;)Ljava/lang/Integer; + public abstract fun resetPlayerChatState (Lorg/bukkit/entity/Player;Ldev/slne/surf/api/paper/nms/bridges/data/chat/RemoteChatSessionData;)V public abstract fun runOnChatMessageChain (Lorg/bukkit/entity/Player;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function1;)V public abstract fun sendPlayerChatMessage (Lorg/bukkit/entity/Player;Lnet/kyori/adventure/chat/SignedMessage;Lnet/kyori/adventure/chat/ChatType$Bound;)V public abstract fun sendSignedMessageWithChangedContent (Lorg/bukkit/entity/Player;Lnet/kyori/adventure/chat/SignedMessage;Lnet/kyori/adventure/chat/ChatType$Bound;Lnet/kyori/adventure/text/Component;)V @@ -1750,6 +1751,7 @@ public final class dev/slne/surf/api/paper/nms/bridges/SurfPaperNmsPlayerBridge$ public fun getRemoteChatSessionData (Lorg/bukkit/entity/Player;)Ldev/slne/surf/api/paper/nms/bridges/data/chat/RemoteChatSessionData; public fun getStatsDataPath ()Ljava/nio/file/Path; public fun increaseNextChatIndex (Lorg/bukkit/entity/Player;)Ljava/lang/Integer; + public fun resetPlayerChatState (Lorg/bukkit/entity/Player;Ldev/slne/surf/api/paper/nms/bridges/data/chat/RemoteChatSessionData;)V public fun runOnChatMessageChain (Lorg/bukkit/entity/Player;Lkotlinx/coroutines/CoroutineScope;Lkotlin/jvm/functions/Function1;)V public fun sendPlayerChatMessage (Lorg/bukkit/entity/Player;Lnet/kyori/adventure/chat/SignedMessage;Lnet/kyori/adventure/chat/ChatType$Bound;)V public fun sendSignedMessageWithChangedContent (Lorg/bukkit/entity/Player;Lnet/kyori/adventure/chat/SignedMessage;Lnet/kyori/adventure/chat/ChatType$Bound;Lnet/kyori/adventure/text/Component;)V diff --git a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/nms/bridges/SurfPaperNmsPlayerBridge.kt b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/nms/bridges/SurfPaperNmsPlayerBridge.kt index 5e886970..43a84730 100644 --- a/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/nms/bridges/SurfPaperNmsPlayerBridge.kt +++ b/surf-api-paper/surf-api-paper/src/main/kotlin/dev/slne/surf/api/paper/nms/bridges/SurfPaperNmsPlayerBridge.kt @@ -23,6 +23,8 @@ interface SurfPaperNmsPlayerBridge { fun getRemoteChatSessionData(player: Player): RemoteChatSessionData? + fun resetPlayerChatState(player: Player, chatSession: RemoteChatSessionData) + fun runOnChatMessageChain(player: Player, scope: CoroutineScope, block: suspend () -> Unit) fun sendSignedMessageWithChangedContent(