From 660c893fa2a277f09c1e83702608355535c6a17a Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 19:13:31 +0200 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20feat(reflection):=20add=20refle?= =?UTF-8?q?ction=20model=20and=20related=20classes=20for=20Java=20reflecti?= =?UTF-8?q?on=20processing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - introduce JavaNullability enum to represent nullability states - create ReflectionModel data class to encapsulate reflection data - add JavaParameter data class for method parameter representation - implement ConstantArgument sealed interface for constant values - define ReflectedMember interface and its implementations for methods, constructors, fields, and var handles - add ReflectionSymbolProcessorProvider for symbol processing integration --- gradle/libs.versions.toml | 3 +- .../surf-api-processor/build.gradle.kts | 2 + .../dev/slne/surf/api/processor/ClassNames.kt | 21 + .../reflection/ReflectionSymbolProcessor.kt | 552 ++++++++++++++++ .../ReflectionSymbolProcessorProvider.kt | 11 + .../emitter/ReflectionJavaEmitter.kt | 606 ++++++++++++++++++ .../exception/ReflectionProcessorException.kt | 3 + .../reflection/model/ConstantArgument.kt | 41 ++ .../reflection/model/JavaNullability.kt | 7 + .../reflection/model/JavaParameter.kt | 9 + .../reflection/model/ReflectedMember.kt | 70 ++ .../reflection/model/ReflectionModel.kt | 12 + .../api/processor/util/AnnotationUtils.kt | 30 +- .../dev/slne/surf/api/processor/util/util.kt | 3 +- ...ols.ksp.processing.SymbolProcessorProvider | 1 + .../v1_21_11/reflection/NmsReflections.java | 114 ---- ...fPaperNmsCommandArgumentTypesBridgeImpl.kt | 85 --- .../V1_21_11SurfPaperNmsCommonBridgeImpl.kt | 5 +- .../V1_21_11SurfPaperNmsGlowingBridgeImpl.kt | 6 +- .../V1_21_11SurfPaperNmsPlayerBridgeImpl.kt | 18 +- .../V1_21_11SurfPaperNmsStatsBridgeImpl.kt | 6 +- .../glow/V1_21_11SurfGlowingApiImpl.kt | 4 +- .../v1_21_11/glow/block/BlockGlowingData.kt | 4 +- .../listener/V1_21_11GlowingPacketListener.kt | 4 +- .../reflection/V1_21_11EntityProxy.kt | 23 - .../reflection/V1_21_11NmsReflections.kt | 89 +++ .../v1_21_11/reflection/V1_21_11Reflection.kt | 20 - .../V1_21_11ServerConnectionListenerProxy.kt | 12 - .../V1_21_11ServerStatsCounterProxy.kt | 20 - ...1_21_11VanillaArgumentProviderImplProxy.kt | 15 - .../V1_21_11VanillaArgumentProviderProxy.kt | 14 - .../api/reflection/GeneratedReflection.kt | 479 ++++++++++++++ .../reflection/GeneratedReflectionAccessor.kt | 127 ++++ 33 files changed, 2083 insertions(+), 333 deletions(-) create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessorProvider.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/emitter/ReflectionJavaEmitter.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/exception/ReflectionProcessorException.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ConstantArgument.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaNullability.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaParameter.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectedMember.kt create mode 100644 surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectionModel.kt delete mode 100644 surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/java/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/NmsReflections.java delete mode 100644 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_11EntityProxy.kt create mode 100644 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 delete mode 100644 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_11ServerConnectionListenerProxy.kt delete mode 100644 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_11ServerStatsCounterProxy.kt delete mode 100644 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_11VanillaArgumentProviderImplProxy.kt delete mode 100644 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_11VanillaArgumentProviderProxy.kt create mode 100644 surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflection.kt create mode 100644 surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 62994d07b..84d907e98 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -80,6 +80,7 @@ kotlinxCoroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-co kotlinxCoroutines-reactor = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactor", version.ref = "kotlinxCoroutines" } kotlinxCoroutines-reactive = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-reactive", version.ref = "kotlinxCoroutines" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlinVersion" } +kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlinVersion" } kotlin-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } kotlin-serialization-protobuf = { module = "org.jetbrains.kotlinx:kotlinx-serialization-protobuf", version.ref = "kotlinx-serialization" } kotlin-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-cbor", version.ref = "kotlinx-serialization" } @@ -115,7 +116,7 @@ adventure-nbt = { module = "net.kyori:adventure-nbt", version.ref = "adventure-a # Velocity velocity-api = { module = "com.velocitypowered:velocity-api", version.ref = "velocity-api" } -velocity-proxy-ctd = { module = "com.velocityctd:velocity-proxy", version.ref = "velocity-api"} +velocity-proxy-ctd = { module = "com.velocityctd:velocity-proxy", version.ref = "velocity-api" } # PAPI placeholder-api = { module = "me.clip:placeholderapi", version.ref = "placeholder-api" } 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 c64249d63..c631e60fa 100644 --- a/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts +++ b/surf-api-gradle-plugin/surf-api-processor/build.gradle.kts @@ -23,4 +23,6 @@ dependencies { // https://mvnrepository.com/artifact/com.squareup/kotlinpoet implementation("com.squareup:kotlinpoet:2.3.0") + implementation("com.palantir.javapoet:javapoet:0.7.0") + implementation(libs.kotlin.compiler) } \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/ClassNames.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/ClassNames.kt index a4c39eebf..a6e0d14f1 100644 --- a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/ClassNames.kt +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/ClassNames.kt @@ -1,10 +1,12 @@ package dev.slne.surf.api.processor import dev.slne.surf.api.processor.util.nameOf +import dev.slne.surf.api.processor.util.shortNameOf import dev.slne.surf.api.shared.api.component.Priority import dev.slne.surf.api.shared.api.component.SurfComponentMeta import dev.slne.surf.api.shared.api.component.processor.ComponentPostProcessor import dev.slne.surf.api.shared.api.component.requirement.* +import dev.slne.surf.api.shared.api.reflection.* import dev.slne.surf.api.shared.internal.nms.NmsProviderMarker object ClassNames { @@ -21,4 +23,23 @@ object ClassNames { val CONDITIONAL_ON_PROPERTY = nameOf() val COMPONENT_POST_PROCESSOR = nameOf() val NMS_PROVIDER = nameOf() + + val GENERATE_REFLECTION = nameOf() + val UNSPECIFIED_REFLECTION_TARGET = nameOf() + val REFLECTED_METHOD = nameOf() + val REFLECTED_CONSTRUCTOR = nameOf() + val REFLECTED_FIELD = nameOf() + val REFLECTED_VAR_HANDLE = nameOf() + val CONSTANT_INT_ARGUMENT = nameOf() + val CONSTANT_LONG_ARGUMENT = nameOf() + val CONSTANT_BOOLEAN_ARGUMENT = nameOf() + val CONSTANT_STRING_ARGUMENT = nameOf() +} + +object ShortClassNames { + val GENERATE_REFLECTION = shortNameOf() + val CONSTANT_INT_ARGUMENT = shortNameOf() + val CONSTANT_LONG_ARGUMENT = shortNameOf() + val CONSTANT_BOOLEAN_ARGUMENT = shortNameOf() + val CONSTANT_STRING_ARGUMENT = shortNameOf() } \ No newline at end of file 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 new file mode 100644 index 000000000..54755bc0f --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessor.kt @@ -0,0 +1,552 @@ +package dev.slne.surf.api.processor.reflection + +import com.google.devtools.ksp.getDeclaredFunctions +import com.google.devtools.ksp.processing.Dependencies +import com.google.devtools.ksp.processing.Resolver +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 dev.slne.surf.api.processor.ClassNames +import dev.slne.surf.api.processor.ShortClassNames +import dev.slne.surf.api.processor.reflection.emitter.ReflectionJavaEmitter +import dev.slne.surf.api.processor.reflection.exception.ReflectionProcessorException +import dev.slne.surf.api.processor.reflection.model.* +import dev.slne.surf.api.processor.util.* +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.name.FqNameUnsafe +import java.lang.invoke.VarHandle +import java.util.* + +class ReflectionSymbolProcessor(environment: SymbolProcessorEnvironment) : SymbolProcessor { + private companion object { + private val ignoredInterfaceMethods = setOf("equals", "hashCode", "toString") + } + + private val codeGenerator = environment.codeGenerator + private val logger = environment.logger + + override fun process(resolver: Resolver): List { + val deferred = mutableListOf() + + resolver.getSymbolsWithAnnotation(ClassNames.GENERATE_REFLECTION) + .filterIsInstance() + .forEach { declaration -> + if (!declaration.validate()) { + deferred += declaration + return@forEach + } + + try { + processInterface(declaration) + } catch (exception: ReflectionProcessorException) { + logger.error(exception.message ?: "Invalid reflection declaration", declaration) + } catch (exception: Throwable) { + exception.printStackTrace() + logger.exception(exception) + } + } + + return deferred + } + + private fun processInterface(declaration: KSClassDeclaration) { + if (declaration.classKind != ClassKind.INTERFACE) { + throw ReflectionProcessorException("@${ShortClassNames.GENERATE_REFLECTION} can only be used on interfaces") + } + + val annotation = declaration.requireAnnotation(ClassNames.GENERATE_REFLECTION) + val packageName = annotation.stringArg("packageName") + .ifBlank { declaration.packageName.asString() } + + val className = annotation.stringArg("className") + .ifBlank { "${declaration.simpleName.asString()}Impl" } + + val defaultTarget = annotation.targetRef() + + val singleton = annotation.booleanArg("singleton", true) + + val model = ReflectionModel( + packageName = packageName, + className = className, + interfaceName = declaration.qualifiedName?.asString() + ?: throw ReflectionProcessorException("Reflection interface must have a qualified name"), + singleton = singleton, + members = declaration.getDeclaredFunctions() + .filterNot { it.simpleName.asString() in ignoredInterfaceMethods } + .map { parseMember(defaultTarget, it) } + .toList(), + source = declaration, + ) + + generateJava(model) + } + + private fun parseMember(defaultTarget: TypeRef?, function: KSFunctionDeclaration): ReflectedMember { + if (Modifier.SUSPEND in function.modifiers) { + throw ReflectionProcessorException("Suspend functions are not supported: ${function.simpleName.asString()}") + } + + val annotations = listOfNotNull( + function.findAnnotation(ClassNames.REFLECTED_METHOD), + function.findAnnotation(ClassNames.REFLECTED_CONSTRUCTOR), + function.findAnnotation(ClassNames.REFLECTED_FIELD), + function.findAnnotation(ClassNames.REFLECTED_VAR_HANDLE), + ) + + if (annotations.size != 1) { + throw ReflectionProcessorException( + "Exactly one reflection member annotation is required on ${function.simpleName.asString()}" + ) + } + + val wrapperParameters = function.parameters.mapIndexed { index, parameter -> + val resolvedType = parameter.type.resolve() + + JavaParameter( + name = parameter.name?.asString()?.sanitizeJavaIdentifier() ?: "p$index", + type = resolvedType.toJavaType(), + nullability = resolvedType.toJavaNullability(), + ) + } + + val returnType = function.returnType?.resolve()?.toJavaType() ?: JavaType.Void + val constants = function.constantArguments() + + function.findAnnotation(ClassNames.REFLECTED_METHOD)?.let { annotation -> + val isStatic = annotation.booleanArg("isStatic", false) + return ReflectedMember.MethodMember( + id = "mh\$${function.simpleName.asString().sanitizeJavaIdentifier()}", + wrapperName = function.simpleName.asString(), + target = resolveTarget( + explicitTarget = annotation.targetRef(), + defaultTarget = defaultTarget, + function = function, + wrapperParameters = wrapperParameters, + returnType = returnType, + isStatic = isStatic, + ), + targetName = annotation.stringArg("name").ifBlank { function.simpleName.asString() }, + isStatic = isStatic, + descriptor = annotation.stringArg("descriptor"), + invocation = annotation.enumArg("invocation", "EXACT"), + wrapperParameters = wrapperParameters, + targetParameters = if (isStatic) wrapperParameters else wrapperParameters.drop(1), + returnType = returnType, + constants = constants, + ) + } + + function.findAnnotation(ClassNames.REFLECTED_CONSTRUCTOR)?.let { annotation -> + return ReflectedMember.ConstructorMember( + id = "mh\$${function.simpleName.asString().sanitizeJavaIdentifier()}", + wrapperName = function.simpleName.asString(), + target = resolveTarget( + explicitTarget = annotation.targetRef(), + defaultTarget = defaultTarget, + function = function, + wrapperParameters = wrapperParameters, + returnType = returnType, + isStatic = false, + isConstructor = true, + ), + descriptor = annotation.stringArg("descriptor"), + invocation = annotation.enumArg("invocation", "EXACT"), + wrapperParameters = wrapperParameters, + returnType = returnType, + constants = constants, + ) + } + + function.findAnnotation(ClassNames.REFLECTED_FIELD)?.let { annotation -> + val isStatic = annotation.booleanArg("isStatic", false) + val access = annotation.enumArg("access", "GET") + return ReflectedMember.FieldMember( + id = "vh\$${function.simpleName.asString().sanitizeJavaIdentifier()}", + wrapperName = function.simpleName.asString(), + target = resolveTarget( + explicitTarget = annotation.targetRef(), + defaultTarget = defaultTarget, + function = function, + wrapperParameters = wrapperParameters, + returnType = returnType, + isStatic = isStatic, + ), + fieldName = annotation.stringArg("name").ifBlank { function.simpleName.asString() }, + isStatic = isStatic, + access = access, + fieldType = annotation.fieldTypeRef() + ?: inferFieldType(access, isStatic, wrapperParameters, returnType), + wrapperParameters = wrapperParameters, + returnType = returnType, + constants = constants, + ) + } + + function.findAnnotation(ClassNames.REFLECTED_VAR_HANDLE)?.let { annotation -> + val isStatic = annotation.booleanArg("isStatic", false) + val modeString = annotation.enumArg("mode", VarHandle.AccessMode.GET.name) + + val mode = try { + VarHandle.AccessMode.valueOf(modeString) + } catch (e: IllegalArgumentException) { + throw ReflectionProcessorException("Invalid VarHandle mode: $modeString") + } + + return ReflectedMember.VarHandleMember( + id = "vh\$${function.simpleName.asString().sanitizeJavaIdentifier()}", + wrapperName = function.simpleName.asString(), + target = resolveTarget( + explicitTarget = annotation.targetRef(), + defaultTarget = defaultTarget, + function = function, + wrapperParameters = wrapperParameters, + returnType = returnType, + isStatic = isStatic, + ), + fieldName = annotation.stringArg("name").ifBlank { function.simpleName.asString() }, + isStatic = isStatic, + mode = mode, + invocation = annotation.enumArg("invocation", "EXACT"), + fieldType = annotation.fieldTypeRef() + ?: inferVarHandleFieldType(mode, isStatic, wrapperParameters, returnType, constants), + wrapperParameters = wrapperParameters, + returnType = returnType, + constants = constants, + ) + } + + throw ReflectionProcessorException("Unsupported reflected member: ${function.simpleName.asString()}") + } + + private fun generateJava(model: ReflectionModel) { + val code = ReflectionJavaEmitter(model).emit() + + codeGenerator.createNewFile( + dependencies = Dependencies( + aggregating = false, + sources = listOfNotNull(model.source.containingFile).toTypedArray(), + ), + packageName = model.packageName, + fileName = model.className, + extensionName = "java", + ).bufferedWriter().use { writer -> + writer.write(code) + } + } + + private fun inferFieldType( + access: String, + isStatic: Boolean, + parameters: List, + returnType: JavaType, + ): TypeRef { + return when (access) { + "GET" -> TypeRef.Known(returnType) + "SET" -> { + val valueIndex = if (isStatic) 0 else 1 + TypeRef.Known(parameters.getOrNull(valueIndex)?.type ?: JavaType.Object) + } + + else -> throw ReflectionProcessorException("Unsupported field access mode: $access") + } + } + + private fun inferVarHandleFieldType( + mode: VarHandle.AccessMode, + isStatic: Boolean, + parameters: List, + returnType: JavaType, + constants: List, + ): TypeRef { + if (returnType != JavaType.Void && returnType != JavaType.Boolean) { + return TypeRef.Known(returnType) + } + + val receiverOffset = if (isStatic) 0 else 1 + val valueParameter = parameters.drop(receiverOffset).lastOrNull()?.type + if (valueParameter != null) { + return TypeRef.Known(valueParameter) + } + + val constantType = constants.lastOrNull()?.type + if (constantType != null) { + return TypeRef.Known(constantType) + } + + val getter = EnumSet.of( + VarHandle.AccessMode.GET, + VarHandle.AccessMode.GET_VOLATILE, + VarHandle.AccessMode.GET_OPAQUE, + VarHandle.AccessMode.GET_ACQUIRE + ) + + return when (mode) { + in getter -> TypeRef.Known(returnType) + else -> throw ReflectionProcessorException( + "Unable to infer VarHandle field type for mode $mode. Specify type, typeName or typeDescriptor." + ) + } + } + + private fun resolveTarget( + explicitTarget: TypeRef?, + defaultTarget: TypeRef?, + function: KSFunctionDeclaration, + wrapperParameters: List, + returnType: JavaType, + isStatic: Boolean, + isConstructor: Boolean = false, + ): TypeRef { + if (explicitTarget != null) return explicitTarget + if (defaultTarget != null) return defaultTarget + + if (isConstructor) { + if (returnType.isVoid || returnType == JavaType.Object) { + throw ReflectionProcessorException( + "Target class not specified for constructor '${function.simpleName.asString()}': " + + "provide 'target'/'targetName' or use a concrete return type" + ) + } + + return TypeRef.Known(returnType) + } + + if (!isStatic) { + val receiverType = wrapperParameters.firstOrNull()?.type + ?: throw ReflectionProcessorException( + "Target class not specified for '${function.simpleName.asString()}': " + + "instance members need either a receiver parameter or an explicit target" + ) + + if (receiverType == JavaType.Object) { + throw ReflectionProcessorException( + "Target class not specified for '${function.simpleName.asString()}': " + + "receiver type is Object/Any, so target cannot be inferred" + ) + } + + return TypeRef.Known(receiverType) + } + + throw ReflectionProcessorException( + "Target class not specified for static member '${function.simpleName.asString()}': " + + "provide 'target'/'targetName' in the member annotation or @${ShortClassNames.GENERATE_REFLECTION}" + ) + } + + private fun KSFunctionDeclaration.constantArguments(): List { + val result = mutableListOf() + + annotations + .filter { it.shortName.asString() == ShortClassNames.CONSTANT_INT_ARGUMENT } + .forEach { result += ConstantArgument.IntValue(it.intArg("value", 0)) } + + annotations + .filter { it.shortName.asString() == ShortClassNames.CONSTANT_LONG_ARGUMENT } + .forEach { result += ConstantArgument.LongValue(it.longArg("value", 0L)) } + + annotations + .filter { it.shortName.asString() == ShortClassNames.CONSTANT_BOOLEAN_ARGUMENT } + .forEach { result += ConstantArgument.BooleanValue(it.booleanArg("value", false)) } + + annotations + .filter { it.shortName.asString() == ShortClassNames.CONSTANT_STRING_ARGUMENT } + .forEach { result += ConstantArgument.StringValue(it.stringArg("value")) } + + return result + } + + + private fun KSAnnotated.findAnnotation(fqName: String): KSAnnotation? { + return annotations.firstOrNull { + val declaration = it.annotationType.resolve().declaration as? KSClassDeclaration + declaration?.qualifiedName?.asString() == fqName + } + } + + private fun KSAnnotated.requireAnnotation(fqName: String): KSAnnotation { + return findAnnotation(fqName) ?: throw ReflectionProcessorException("Missing annotation $fqName") + } + + private fun KSAnnotation.targetRef(): TypeRef? { + return typeArg("target") + ?: stringArg("targetName").takeIf { it.isNotBlank() }?.let(TypeRef::Named) + } + + private fun KSAnnotation.fieldTypeRef(): TypeRef? { + return typeArg("type") + ?: stringArg("typeName").takeIf { it.isNotBlank() }?.let(TypeRef::Named) + ?: stringArg("typeDescriptor").takeIf { it.isNotBlank() }?.let(TypeRef::Descriptor) + } + + private fun KSAnnotation.typeArg(name: String): TypeRef? { + val type = getArgumentValueAs(name) ?: return null + val declaration = type.declaration as? KSClassDeclaration ?: return null + val qualifiedName = declaration.qualifiedName?.asString() ?: return null + + println("typeArg: $qualifiedName") + if (qualifiedName == ClassNames.UNSPECIFIED_REFLECTION_TARGET) { + return null + } + + return TypeRef.Known(type.toJavaType()) + } + + private fun KSType.toJavaType(): JavaType { + val declaration = declaration as? KSClassDeclaration ?: return JavaType.Object + val qualifiedName = declaration.qualifiedName?.asString() ?: return JavaType.Object + val javaNullability = toJavaNullability() + val isNullable = javaNullability == JavaNullability.NULLABLE + + return when (qualifiedName) { + "kotlin.Unit" -> JavaType.Void + + "kotlin.Boolean" -> if (isNullable) boxed("java.lang.Boolean", javaNullability) else JavaType.Boolean + "kotlin.Byte" -> if (isNullable) boxed("java.lang.Byte", javaNullability) else JavaType.Byte + "kotlin.Short" -> if (isNullable) boxed("java.lang.Short", javaNullability) else JavaType.Short + "kotlin.Int" -> if (isNullable) boxed("java.lang.Integer", javaNullability) else JavaType.Int + "kotlin.Long" -> if (isNullable) boxed("java.lang.Long", javaNullability) else JavaType.Long + "kotlin.Float" -> if (isNullable) boxed("java.lang.Float", javaNullability) else JavaType.Float + "kotlin.Double" -> if (isNullable) boxed("java.lang.Double", javaNullability) else JavaType.Double + "kotlin.Char" -> if (isNullable) boxed("java.lang.Character", javaNullability) else JavaType.Char + + else -> { + val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(FqNameUnsafe(qualifiedName)) + val javaFqName = javaClassId?.asSingleFqName()?.asString() ?: qualifiedName + val erasedClassName = ClassName.bestGuess(javaFqName) + + val declarationTypeName = if (arguments.isEmpty()) { + erasedClassName + } else { + ParameterizedTypeName.get( + erasedClassName, + *arguments.map { it.toJavaTypeArgumentName() }.toTypedArray(), + ) + } + + JavaType( + sourceName = erasedClassName.canonicalName(), + className = declarationTypeName, + erasedClassName = erasedClassName, + nullability = javaNullability, + ) + } + } + } + + private fun boxed( + qualifiedName: String, + nullability: JavaNullability, + ): JavaType { + val className = ClassName.bestGuess(qualifiedName) + + return JavaType( + sourceName = qualifiedName, + className = className, + erasedClassName = className, + nullability = nullability, + ) + } + + private fun KSTypeArgument.toJavaTypeArgumentName(): TypeName { + val resolvedType = type?.resolve() + + if (variance == Variance.STAR || resolvedType == null) { + return WildcardTypeName.subtypeOf(ClassName.OBJECT) + } + + val typeName = resolvedType.toJavaType().className.box() + + return when (variance) { + Variance.INVARIANT -> typeName + Variance.COVARIANT -> WildcardTypeName.subtypeOf(typeName) + Variance.CONTRAVARIANT -> WildcardTypeName.supertypeOf(typeName) + Variance.STAR -> WildcardTypeName.subtypeOf(ClassName.OBJECT) + } + } + + private fun KSType.toJavaNullability(): JavaNullability { + return when (nullability) { + Nullability.NULLABLE -> JavaNullability.NULLABLE + Nullability.NOT_NULL -> JavaNullability.NON_NULL + Nullability.PLATFORM -> JavaNullability.UNKNOWN + } + } + + private fun String.sanitizeJavaIdentifier(): String { + if (isEmpty()) return "_" + + val builder = StringBuilder(length) + forEachIndexed { index, char -> + val valid = if (index == 0) Character.isJavaIdentifierStart(char) + else Character.isJavaIdentifierPart(char) + + builder.append(if (valid) char else '_') + } + + return builder.toString() + } + + sealed interface TypeRef { + data class Known(val type: JavaType) : TypeRef + data class Named(val binaryName: String) : TypeRef + data class Descriptor(val descriptor: String) : TypeRef + } + + data class JavaType( + val sourceName: String, + /** + * Type used in Java declarations and casts. + * + * This may be parameterized, for example `java.util.List`. + */ + val className: TypeName = ClassName.bestGuess(sourceName), + + /** + * Type used for `.class` literals and MethodType construction. + * + * This must always be erased, for example `java.util.List.class`, not + * `java.util.List.class`. + */ + val erasedClassName: TypeName = className, + + val nullability: JavaNullability = JavaNullability.UNKNOWN, + ) { + val isVoid: Boolean + get() = erasedClassName == TypeName.VOID + + val isPrimitiveOrVoid: Boolean + get() = erasedClassName == TypeName.VOID || + erasedClassName == TypeName.BOOLEAN || + erasedClassName == TypeName.BYTE || + erasedClassName == TypeName.SHORT || + erasedClassName == TypeName.INT || + erasedClassName == TypeName.LONG || + erasedClassName == TypeName.FLOAT || + erasedClassName == TypeName.DOUBLE || + erasedClassName == TypeName.CHAR + + companion object { + val Void = JavaType("void", TypeName.VOID, TypeName.VOID, JavaNullability.NON_NULL) + val Object = JavaType("java.lang.Object", ClassName.OBJECT, ClassName.OBJECT) + val Boolean = JavaType("boolean", TypeName.BOOLEAN, TypeName.BOOLEAN, JavaNullability.NON_NULL) + val Byte = JavaType("byte", TypeName.BYTE, TypeName.BYTE, JavaNullability.NON_NULL) + val Short = JavaType("short", TypeName.SHORT, TypeName.SHORT, JavaNullability.NON_NULL) + val Int = JavaType("int", TypeName.INT, TypeName.INT, JavaNullability.NON_NULL) + val Long = JavaType("long", TypeName.LONG, TypeName.LONG, JavaNullability.NON_NULL) + val Float = JavaType("float", TypeName.FLOAT, TypeName.FLOAT, JavaNullability.NON_NULL) + val Double = JavaType("double", TypeName.DOUBLE, TypeName.DOUBLE, JavaNullability.NON_NULL) + val Char = JavaType("char", TypeName.CHAR, TypeName.CHAR, JavaNullability.NON_NULL) + val String = JavaType( + sourceName = "java.lang.String", + className = ClassName.get("java.lang", "String"), + erasedClassName = ClassName.get("java.lang", "String"), + ) + } + } +} diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessorProvider.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessorProvider.kt new file mode 100644 index 000000000..04a467f63 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/ReflectionSymbolProcessorProvider.kt @@ -0,0 +1,11 @@ +package dev.slne.surf.api.processor.reflection + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class ReflectionSymbolProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return ReflectionSymbolProcessor(environment) + } +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/emitter/ReflectionJavaEmitter.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/emitter/ReflectionJavaEmitter.kt new file mode 100644 index 000000000..ac73b1fa2 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/emitter/ReflectionJavaEmitter.kt @@ -0,0 +1,606 @@ +package dev.slne.surf.api.processor.reflection.emitter + +import com.palantir.javapoet.* +import dev.slne.surf.api.processor.reflection.ReflectionSymbolProcessor +import dev.slne.surf.api.processor.reflection.model.JavaNullability +import dev.slne.surf.api.processor.reflection.model.ReflectedMember +import dev.slne.surf.api.processor.reflection.model.ReflectedMember.* +import dev.slne.surf.api.processor.reflection.model.ReflectionModel +import java.lang.invoke.MethodHandle +import java.lang.invoke.MethodHandles +import java.lang.invoke.MethodType +import java.lang.invoke.VarHandle +import java.lang.reflect.Type +import javax.lang.model.element.Modifier +import javax.lang.model.element.Modifier.* + +@Suppress("CanConvertToMultiDollarString", "CanUnescapeDollarLiteral") +class ReflectionJavaEmitter(private val model: ReflectionModel) { + companion object { + private val nullableAnnotationClassName = ClassName.get("org.jspecify.annotations", "Nullable") + private val nonNullAnnotationClassName = ClassName.get("org.jspecify.annotations", "NonNull") + + private val nullableAnnotationSpec = AnnotationSpec.builder(nullableAnnotationClassName).build() + private val nonNullAnnotationSpec = AnnotationSpec.builder(nonNullAnnotationClassName).build() + } + + private val modelInterfaceClassName = ClassName.bestGuess(model.interfaceName) + private val modelClassName = ClassName.get(model.packageName, model.className) + + fun emit(): String { + val javaClassBuilder = TypeSpec.classBuilder(model.className) + .addModifiers(PUBLIC, FINAL) + .addAnnotation( + AnnotationSpec.builder(SuppressWarnings::class.java) + .addMember("value", "{\$S, \$S}", "unchecked", "rawtypes") + .build() + ) + .addSuperinterface(modelInterfaceClassName) + .addFields(emitFields()) + .addMethod(emitConstructor()) + .addStaticBlock(emitStaticInitializer()) + .addMethods(emitMethods()) + .addMethods(emitHelperMethods()) + + return JavaFile.builder(model.packageName, javaClassBuilder.build()) + .build() + .toString() + } + + private fun emitFields(): List { + val fields = mutableListOf() + + if (model.singleton) { + fields += FieldSpec.builder(modelInterfaceClassName, "INSTANCE") + .addModifiers(PUBLIC, STATIC, FINAL) + .initializer("new \$T()", modelClassName) + .build() + } + + for (member in model.members) { + val fieldType = when (member) { + is MethodMember -> TypeName.get(MethodHandle::class.java) + is ConstructorMember -> TypeName.get(MethodHandle::class.java) + is FieldMember -> TypeName.get(VarHandle::class.java) + is VarHandleMember -> { + if (member.invocation == "EXACT") { + TypeName.get(VarHandle::class.java) + } else { + TypeName.get(MethodHandle::class.java) + } + } + } + + fields += FieldSpec.builder(fieldType, member.id) + .addModifiers(PRIVATE, STATIC, FINAL) + .build() + } + + return fields + } + + private fun emitConstructor(): MethodSpec { + val visibility = if (model.singleton) PRIVATE else PUBLIC + + return MethodSpec.constructorBuilder() + .addModifiers(visibility) + .apply { + if (model.singleton) { + addComment("Singleton instance is exposed through INSTANCE.") + } + } + .build() + } + + private fun emitStaticInitializer(): CodeBlock { + val block = CodeBlock.builder() + + block.addStatement( + "\$T lookup = \$T.lookup()", + MethodHandles.Lookup::class.java, + MethodHandles::class.java, + ) + block.addStatement("\$T classLoader = \$T.class.getClassLoader()", ClassLoader::class.java, modelClassName) + + block.beginControlFlow("try") + + model.members.forEachIndexed { index, member -> + emitMemberInitializer(block, index, member) + block.add("\n") + } + + block.nextControlFlow("catch (\$T throwable)", Throwable::class.java) + block.addStatement("throw new \$T(throwable)", ExceptionInInitializerError::class.java) + block.endControlFlow() + + return block.build() + } + + private fun emitMemberInitializer( + block: CodeBlock.Builder, + index: Int, + member: ReflectedMember, + ) { + val ownerVariable = "owner\$$index" + val lookupVariable = "lookup\$$index" + + block.addStatement( + "\$T \$L = \$L", + Class::class.java, + ownerVariable, + member.target.classExpression(), + ) + block.addStatement( + "\$T \$L = \$T.privateLookupIn(\$L, lookup)", + MethodHandles.Lookup::class.java, + lookupVariable, + MethodHandles::class.java, + ownerVariable, + ) + + when (member) { + is MethodMember -> emitMethodInitializer(block, member, ownerVariable, lookupVariable) + is ConstructorMember -> emitConstructorInitializer(block, member, ownerVariable, lookupVariable) + is FieldMember -> emitFieldInitializer(block, member, ownerVariable, lookupVariable) + is VarHandleMember -> emitVarHandleInitializer(block, index, member, ownerVariable, lookupVariable) + } + } + + private fun emitMethodInitializer( + block: CodeBlock.Builder, + member: MethodMember, + ownerVariable: String, + lookupVariable: String, + ) { + val targetMethodType = if (member.descriptor.isNotBlank()) { + CodeBlock.of( + "\$T.fromMethodDescriptorString(\$S, classLoader)", + MethodType::class.java, + member.descriptor, + ) + } else { + methodTypeExpression( + returnType = member.returnType, + parameterTypes = member.targetParameters.map { it.type } + member.constants.map { it.type }, + ) + } + + val rawHandle = if (member.isStatic) { + CodeBlock.of( + "\$L.findStatic(\$L, \$S, \$L)", + lookupVariable, + ownerVariable, + member.targetName, + targetMethodType, + ) + } else { + CodeBlock.of( + "\$L.findVirtual(\$L, \$S, \$L)", + lookupVariable, + ownerVariable, + member.targetName, + targetMethodType, + ) + } + + if (member.invocation == "AS_TYPE_EXACT") { + block.addStatement( + "\$N = \$L.asType(\$L)", + member.id, + rawHandle, + methodTypeExpression( + returnType = member.returnType, + parameterTypes = member.wrapperParameters.map { it.type } + member.constants.map { it.type }, + ), + ) + } else { + block.addStatement("\$N = \$L", member.id, rawHandle) + } + } + + private fun emitConstructorInitializer( + block: CodeBlock.Builder, + member: ConstructorMember, + ownerVariable: String, + lookupVariable: String, + ) { + val constructorType = if (member.descriptor.isNotBlank()) { + CodeBlock.of( + "\$T.fromMethodDescriptorString(\$S, classLoader)", + MethodType::class.java, + member.descriptor, + ) + } else { + methodTypeExpression( + returnType = ReflectionSymbolProcessor.JavaType.Void, + parameterTypes = member.wrapperParameters.map { it.type } + member.constants.map { it.type }, + ) + } + + val rawHandle = CodeBlock.of( + "\$L.findConstructor(\$L, \$L)", + lookupVariable, + ownerVariable, + constructorType, + ) + + if (member.invocation == "AS_TYPE_EXACT") { + block.addStatement( + "\$N = \$L.asType(\$L)", + member.id, + rawHandle, + methodTypeExpression( + returnType = member.returnType, + parameterTypes = member.wrapperParameters.map { it.type } + member.constants.map { it.type }, + ), + ) + } else { + block.addStatement("\$N = \$L", member.id, rawHandle) + } + } + + private fun emitFieldInitializer( + block: CodeBlock.Builder, + member: FieldMember, + ownerVariable: String, + lookupVariable: String, + ) { + val finderName = if (member.isStatic) "findStaticVarHandle" else "findVarHandle" + + block.addStatement( + "\$N = \$L.\$L(\$L, \$S, \$L)", + member.id, + lookupVariable, + finderName, + ownerVariable, + member.fieldName, + member.fieldType.classExpression(), + ) + } + + private fun emitVarHandleInitializer( + block: CodeBlock.Builder, + index: Int, + member: VarHandleMember, + ownerVariable: String, + lookupVariable: String, + ) { + val finderName = if (member.isStatic) "findStaticVarHandle" else "findVarHandle" + + if (member.invocation == "EXACT") { + block.addStatement( + "\$N = \$L.\$L(\$L, \$S, \$L)", + member.id, + lookupVariable, + finderName, + ownerVariable, + member.fieldName, + member.fieldType.classExpression(), + ) + return + } + + val rawVarHandleName = "rawVarHandle\$$index" + + block.addStatement( + "\$T \$L = \$L.\$L(\$L, \$S, \$L)", + VarHandle::class.java, + rawVarHandleName, + lookupVariable, + finderName, + ownerVariable, + member.fieldName, + member.fieldType.classExpression(), + ) + block.addStatement( + "\$N = \$L.toMethodHandle(\$T.AccessMode.\$L).asType(\$L)", + member.id, + rawVarHandleName, + VarHandle::class.java, + member.mode, + methodTypeExpression( + returnType = member.returnType, + parameterTypes = member.wrapperParameters.map { it.type } + member.constants.map { it.type }, + ), + ) + } + + private fun emitMethods(): List { + val baseMethods = model.members.map { member -> + MethodSpec.methodBuilder(member.wrapperName) + .returns(member.returnType.asDeclarationType()) + .addModifiers(PUBLIC) + .addParameters( + member.wrapperParameters.mapIndexed { index, parameter -> + ParameterSpec.builder( + parameter.type.asDeclarationType( + forceNonNull = member.isInstanceReceiverParameter(index), + ), + parameter.name, + ) + .addModifiers(FINAL) + .build() + }, + ) + .beginControlFlow("try") + .apply { + when (member) { + is MethodMember -> emitMethodBody(member) + is ConstructorMember -> emitConstructorBody(member) + is FieldMember -> emitFieldBody(member) + is VarHandleMember -> emitVarHandleBody(member) + } + } + .nextControlFlow("catch (\$T e)", Throwable::class.java) + .invokeSneakyThrow(member.returnType) + .endControlFlow() + .build() + } + + val instanceMethods = baseMethods.map { method -> + method.toBuilder() + .addAnnotation(Override::class.java) + .build() + } + + // Not possible — another method name would be required +// val staticAccessors = baseMethods.map {method -> +// method.toBuilder() +// .addModifiers(STATIC) +// .build() +// } + + return instanceMethods + } + + private fun MethodSpec.Builder.emitMethodBody(member: MethodMember) { + val invocation = if (member.invocation == "INVOKE") "invoke" else "invokeExact" + + emitCall( + returnType = member.returnType, + invocation = CodeBlock.of("\$N.\$L", member.id, invocation), + arguments = member.callArguments(), + ) + } + + private fun MethodSpec.Builder.emitConstructorBody(member: ConstructorMember) { + val invocation = if (member.invocation == "INVOKE") "invoke" else "invokeExact" + + emitCall( + returnType = member.returnType, + invocation = CodeBlock.of("\$N.\$L", member.id, invocation), + arguments = member.callArguments(), + ) + } + + private fun MethodSpec.Builder.emitFieldBody(member: FieldMember) { + when (member.access) { + "GET" -> emitCall( + returnType = member.returnType, + invocation = CodeBlock.of("\$N.get", member.id), + arguments = member.callArguments(), + ) + + "SET" -> { + require(member.returnType.isVoid) { + "Field SET wrapper '${member.wrapperName}' must return void / Unit" + } + + addStatement("\$N.set(\$L)", member.id, member.callArguments().joinToCode()) + } + + else -> error("Unsupported field access mode '${member.access}' on '${member.wrapperName}'") + } + } + + private fun MethodSpec.Builder.emitVarHandleBody(member: VarHandleMember) { + if (member.invocation == "EXACT") { + emitCall( + returnType = member.returnType, + invocation = CodeBlock.of("\$N.\$L", member.id, member.mode.methodName()), + arguments = member.callArguments(), + ) + return + } + + val invocation = if (member.invocation == "INVOKE") "invoke" else "invokeExact" + + emitCall( + returnType = member.returnType, + invocation = CodeBlock.of("\$N.\$L", member.id, invocation), + arguments = member.callArguments(), + ) + } + + private fun MethodSpec.Builder.emitCall( + returnType: ReflectionSymbolProcessor.JavaType, + invocation: CodeBlock, + arguments: List, + ) { + val argumentCode = arguments.joinToCode() + + if (returnType.isVoid) { + addStatement("\$L(\$L)", invocation, argumentCode) + return + } + + addStatement("return (\$T) \$L(\$L)", returnType.className, invocation, argumentCode) + } + + private fun emitHelperMethods(): List { + return listOf( + emitClassForNameMethod(), + emitClassFromDescriptorMethod(), + emitSneakyThrowMethod(), + ) + } + + private fun emitClassForNameMethod(): MethodSpec { + val classWildcard = ParameterizedTypeName.get( + ClassName.get(Class::class.java), + WildcardTypeName.subtypeOf(Any::class.java), + ) + + return MethodSpec.methodBuilder("classForName") + .addModifiers(PRIVATE, STATIC) + .returns(classWildcard.annotated(nonNullAnnotationSpec)) + .addNullableParameter(ClassLoader::class.java, "classLoader") + .addNonNullParameter(String::class.java, "name") + .addException(ClassNotFoundException::class.java) + .beginControlFlow("return switch (name)") + .addStatement("case \$S -> boolean.class", "boolean") + .addStatement("case \$S -> byte.class", "byte") + .addStatement("case \$S -> short.class", "short") + .addStatement("case \$S -> int.class", "int") + .addStatement("case \$S -> long.class", "long") + .addStatement("case \$S -> float.class", "float") + .addStatement("case \$S -> double.class", "double") + .addStatement("case \$S -> char.class", "char") + .addStatement("case \$S -> void.class", "void") + .addStatement("default -> \$T.forName(name, false, classLoader)", Class::class.java) + .endControlFlow("") + .build() + } + + private fun emitClassFromDescriptorMethod(): MethodSpec { + val classWildcard = ParameterizedTypeName.get( + ClassName.get(Class::class.java), + WildcardTypeName.subtypeOf(Any::class.java), + ) + + return MethodSpec.methodBuilder("classFromDescriptor") + .addModifiers(PRIVATE, STATIC) + .returns(classWildcard.annotated(nonNullAnnotationSpec)) + .addNullableParameter(ClassLoader::class.java, "classLoader") + .addNonNullParameter(String::class.java, "descriptor") + .addStatement( + "return \$T.fromMethodDescriptorString(\$S + descriptor, classLoader).returnType()", + MethodType::class.java, + "()", + ) + .build() + } + + private fun emitSneakyThrowMethod(): MethodSpec { + val throwableType = TypeVariableName.get("E", Throwable::class.java) + val returnType = TypeVariableName.get("R") + + return MethodSpec.methodBuilder("sneakyThrow") + .addAnnotation( + AnnotationSpec.builder(SuppressWarnings::class.java) + .addMember("value", "\$S", "unchecked") + .build() + ) + .addModifiers(PRIVATE, STATIC) + .addTypeVariable(throwableType) + .addTypeVariable(returnType) + .returns(returnType) + .addParameter(Throwable::class.java, "throwable") + .addException(throwableType) + .addStatement("throw (E) throwable") + .build() + } + + private fun MethodSpec.Builder.invokeSneakyThrow( + returnType: ReflectionSymbolProcessor.JavaType, + ): MethodSpec.Builder = apply { + if (returnType.isVoid) { + addStatement("\$T.sneakyThrow(e)", modelClassName) + } else { + addStatement("return \$T.sneakyThrow(e)", modelClassName) + } + } + + private fun MethodSpec.Builder.addNullableParameter( + type: Type, + name: String, + vararg modifier: Modifier + ) = apply { + addParameter( + ParameterSpec.builder(type, name, *modifier) + .addAnnotation(nullableAnnotationClassName) + .build() + ) + } + + private fun MethodSpec.Builder.addNonNullParameter( + type: Type, + name: String, + vararg modifier: Modifier + ) = apply { + addParameter( + ParameterSpec.builder(type, name, *modifier) + .addAnnotation(nonNullAnnotationClassName) + .build() + ) + } + + private fun methodTypeExpression( + returnType: ReflectionSymbolProcessor.JavaType, + parameterTypes: List, + ): CodeBlock { + val block = CodeBlock.builder() + block.add("\$T.methodType(\$T.class", MethodType::class.java, returnType.erasedClassName) + + for (parameterType in parameterTypes) { + block.add(", \$T.class", parameterType.erasedClassName) + } + + block.add(")") + return block.build() + } + + private fun ReflectionSymbolProcessor.JavaType.asDeclarationType( + forceNonNull: Boolean = false, + ): TypeName { + if (isPrimitiveOrVoid) { + return className + } + + val effectiveNullability = if (forceNonNull) { + JavaNullability.NON_NULL + } else { + nullability + } + + return when (effectiveNullability) { + JavaNullability.NON_NULL -> className.annotated(nonNullAnnotationSpec) + JavaNullability.NULLABLE -> className.annotated(nullableAnnotationSpec) + JavaNullability.UNKNOWN -> className + } + } + + private fun ReflectedMember.isInstanceReceiverParameter(index: Int): Boolean { + return index == 0 && this is WithStaticModifier && !isStatic + } + + private fun ReflectionSymbolProcessor.TypeRef.classExpression(): CodeBlock { + return when (this) { + is ReflectionSymbolProcessor.TypeRef.Known -> CodeBlock.of("\$T.class", type.erasedClassName) + is ReflectionSymbolProcessor.TypeRef.Named -> CodeBlock.of("classForName(classLoader, \$S)", binaryName) + is ReflectionSymbolProcessor.TypeRef.Descriptor -> CodeBlock.of( + "classFromDescriptor(classLoader, \$S)", + descriptor + ) + } + } + + private fun ReflectedMember.callArguments(): List { + return wrapperParameters.map { CodeBlock.of("\$N", it.name) } + + constants.map { CodeBlock.of("\$L", it.javaExpression) } + } + + private fun List.joinToCode(separator: String = ", "): CodeBlock { + val block = CodeBlock.builder() + + forEachIndexed { index, codeBlock -> + if (index > 0) { + block.add(separator) + } + + block.add("\$L", codeBlock) + } + + return block.build() + } +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/exception/ReflectionProcessorException.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/exception/ReflectionProcessorException.kt new file mode 100644 index 000000000..b2a38ad74 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/exception/ReflectionProcessorException.kt @@ -0,0 +1,3 @@ +package dev.slne.surf.api.processor.reflection.exception + +class ReflectionProcessorException(message: String) : RuntimeException(message) \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ConstantArgument.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ConstantArgument.kt new file mode 100644 index 000000000..134a0dcf4 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ConstantArgument.kt @@ -0,0 +1,41 @@ +package dev.slne.surf.api.processor.reflection.model + +import dev.slne.surf.api.processor.reflection.ReflectionSymbolProcessor + +sealed interface ConstantArgument { + val type: ReflectionSymbolProcessor.JavaType + val javaExpression: String + + data class IntValue(val value: Int) : ConstantArgument { + override val type = ReflectionSymbolProcessor.JavaType.Int + override val javaExpression = value.toString() + } + + data class LongValue(val value: Long) : ConstantArgument { + override val type = ReflectionSymbolProcessor.JavaType.Long + override val javaExpression = "${value}L" + } + + data class BooleanValue(val value: Boolean) : ConstantArgument { + override val type = ReflectionSymbolProcessor.JavaType.Boolean + override val javaExpression = value.toString() + } + + data class StringValue(val value: String) : ConstantArgument { + override val type = ReflectionSymbolProcessor.JavaType.String + override val javaExpression = buildString { + append('"') + value.forEach { char -> + when (char) { + '\\' -> append("\\\\") + '"' -> append("\\\"") + '\n' -> append("\\n") + '\r' -> append("\\r") + '\t' -> append("\\t") + else -> append(char) + } + } + append('"') + } + } +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaNullability.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaNullability.kt new file mode 100644 index 000000000..c41fa06e9 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaNullability.kt @@ -0,0 +1,7 @@ +package dev.slne.surf.api.processor.reflection.model + +enum class JavaNullability { + NON_NULL, + NULLABLE, + UNKNOWN, +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaParameter.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaParameter.kt new file mode 100644 index 000000000..d60859d44 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/JavaParameter.kt @@ -0,0 +1,9 @@ +package dev.slne.surf.api.processor.reflection.model + +import dev.slne.surf.api.processor.reflection.ReflectionSymbolProcessor + +data class JavaParameter( + val name: String, + val type: ReflectionSymbolProcessor.JavaType, + val nullability: JavaNullability, +) \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectedMember.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectedMember.kt new file mode 100644 index 000000000..6e226abce --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectedMember.kt @@ -0,0 +1,70 @@ +package dev.slne.surf.api.processor.reflection.model + +import dev.slne.surf.api.processor.reflection.ReflectionSymbolProcessor +import java.lang.invoke.VarHandle + +sealed interface ReflectedMember { + val id: String + val wrapperName: String + val target: ReflectionSymbolProcessor.TypeRef + val wrapperParameters: List + val returnType: ReflectionSymbolProcessor.JavaType + val constants: List + + sealed interface WithStaticModifier : ReflectedMember { + val isStatic: Boolean + } + + data class MethodMember( + override val id: String, + override val wrapperName: String, + override val target: ReflectionSymbolProcessor.TypeRef, + val targetName: String, + override val isStatic: Boolean, + val descriptor: String, + val invocation: String, + override val wrapperParameters: List, + val targetParameters: List, + override val returnType: ReflectionSymbolProcessor.JavaType, + override val constants: List, + ) : WithStaticModifier + + data class ConstructorMember( + override val id: String, + override val wrapperName: String, + override val target: ReflectionSymbolProcessor.TypeRef, + val descriptor: String, + val invocation: String, + override val wrapperParameters: List, + override val returnType: ReflectionSymbolProcessor.JavaType, + override val constants: List, + ) : ReflectedMember + + data class FieldMember( + override val id: String, + override val wrapperName: String, + override val target: ReflectionSymbolProcessor.TypeRef, + val fieldName: String, + override val isStatic: Boolean, + val access: String, + val fieldType: ReflectionSymbolProcessor.TypeRef, + override val wrapperParameters: List, + override val returnType: ReflectionSymbolProcessor.JavaType, + override val constants: List, + ) : WithStaticModifier + + data class VarHandleMember( + override val id: String, + override val wrapperName: String, + override val target: ReflectionSymbolProcessor.TypeRef, + val fieldName: String, + override val isStatic: Boolean, + val mode: VarHandle.AccessMode, + val invocation: String, + val fieldType: ReflectionSymbolProcessor.TypeRef, + override val wrapperParameters: List, + override val returnType: ReflectionSymbolProcessor.JavaType, + override val constants: List, + ) : WithStaticModifier + +} \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectionModel.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectionModel.kt new file mode 100644 index 000000000..b6ae0c031 --- /dev/null +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/reflection/model/ReflectionModel.kt @@ -0,0 +1,12 @@ +package dev.slne.surf.api.processor.reflection.model + +import com.google.devtools.ksp.symbol.KSClassDeclaration + +data class ReflectionModel( + val packageName: String, + val className: String, + val interfaceName: String, + val singleton: Boolean, + val members: List, + val source: KSClassDeclaration, +) diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/AnnotationUtils.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/AnnotationUtils.kt index 3ab37e69f..f11ea33c0 100644 --- a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/AnnotationUtils.kt +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/AnnotationUtils.kt @@ -2,10 +2,7 @@ package dev.slne.surf.api.processor.util import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.symbol.ClassKind -import com.google.devtools.ksp.symbol.KSAnnotated -import com.google.devtools.ksp.symbol.KSAnnotation -import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.* object AnnotationUtils { private const val MAX_RECURSION_DEPTH = 25 @@ -149,6 +146,31 @@ fun KSAnnotation.getArgumentValue(name: String): Any? { return arguments.find { it.name?.asString() == name }?.value } +fun KSAnnotation.stringArg(name: String, default: String = ""): String { + return getArgumentValue(name) as? String ?: default +} + +fun KSAnnotation.booleanArg(name: String, default: Boolean): Boolean { + return getArgumentValue(name) as? Boolean ?: default +} + +fun KSAnnotation.intArg(name: String, default: Int): Int { + return (getArgumentValue(name) as? Number)?.toInt() ?: default +} + +fun KSAnnotation.longArg(name: String, default: Long): Long { + return (getArgumentValue(name) as? Number)?.toLong() ?: default +} + +fun KSAnnotation.enumArg(name: String, default: String): String { + val value = getArgumentValue(name) ?: return default + + return when (value) { + is KSName -> value.getShortName() + else -> value.toString().substringAfterLast('.') + } +} + inline fun KSAnnotation.getArgumentValueAs(name: String): T? { return getArgumentValue(name) as? T } diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/util.kt b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/util.kt index a1372b721..27e60a3f5 100644 --- a/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/util.kt +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/kotlin/dev/slne/surf/api/processor/util/util.kt @@ -14,4 +14,5 @@ fun KSClassDeclaration.toClassName(): ClassName { fun KSClassDeclaration.toBinaryName(): String = toClassName().reflectionName() -inline fun nameOf(): String = T::class.java.name \ No newline at end of file +inline fun nameOf(): String = T::class.java.name +inline fun shortNameOf(): String = T::class.java.simpleName \ No newline at end of file diff --git a/surf-api-gradle-plugin/surf-api-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/surf-api-gradle-plugin/surf-api-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider index 8bbb4d91d..24f4e1077 100644 --- a/surf-api-gradle-plugin/surf-api-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ b/surf-api-gradle-plugin/surf-api-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -1,3 +1,4 @@ dev.slne.surf.api.processor.autoservice.AutoServiceSymbolProcessorProvider dev.slne.surf.api.processor.nms.NmsProvidersCollectorProvider dev.slne.surf.api.processor.component.ComponentSymbolProcessorProvider +dev.slne.surf.api.processor.reflection.ReflectionSymbolProcessorProvider \ 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/java/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/NmsReflections.java b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/java/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/NmsReflections.java deleted file mode 100644 index 2e2ee828b..000000000 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v1-21-11/src/main/java/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/NmsReflections.java +++ /dev/null @@ -1,114 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection; - -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.resources.ResourceKey; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.util.FutureChain; -import org.jspecify.annotations.NullMarked; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.VarHandle; -import java.util.BitSet; - -@NullMarked -public final class NmsReflections { - private NmsReflections() { - throw new UnsupportedOperationException(); - } - - private static final VarHandle serverGamePacketListenerImpl$chatMessageChain; - private static final VarHandle serverGamePacketListenerImpl$nextChatIndex; - private static final VarHandle serverGamePacketListenerImpl$messageSignatureCache; - private static final VarHandle serverGamePacketListenerImpl$lastSeenMessages; - private static final VarHandle chatProcessor$PAPER_RAW; - - private static final MethodHandle filterMask$mask; - private static final MethodHandle filterMask$constructorBitSet; - - public static FutureChain getChatMessageChain(ServerGamePacketListenerImpl instance) { - return (FutureChain) serverGamePacketListenerImpl$chatMessageChain.get(instance); - } - - public static int getAndIncreaseNextChatIndex(ServerGamePacketListenerImpl instance) { - return (int) serverGamePacketListenerImpl$nextChatIndex.getAndAdd(instance, 1); - } - - public static MessageSignatureCache getMessageSignatureCache(ServerGamePacketListenerImpl instance) { - return (MessageSignatureCache) serverGamePacketListenerImpl$messageSignatureCache.get(instance); - } - - public static LastSeenMessagesValidator getLastSeenMessages(ServerGamePacketListenerImpl instance) { - return (LastSeenMessagesValidator) serverGamePacketListenerImpl$lastSeenMessages.get(instance); - } - - public static FilterMask createFilterMask(BitSet bitSet) throws Throwable { - return (FilterMask) filterMask$constructorBitSet.invokeExact(bitSet); - } - - public static BitSet getMask(FilterMask filterMask) throws Throwable { - return (BitSet) filterMask$mask.invokeExact(filterMask); - } - - @SuppressWarnings("unchecked") - public static ResourceKey getPaperRaw() { - return (ResourceKey) chatProcessor$PAPER_RAW.get(); - } - - static { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - try { - MethodHandles.Lookup privateLookupInServerGamePacketListener = MethodHandles.privateLookupIn(ServerGamePacketListenerImpl.class, lookup); - MethodHandles.Lookup privateLookupInFilterMask = MethodHandles.privateLookupIn(FilterMask.class, lookup); - MethodHandles.Lookup privateLookupInChatProcessor = MethodHandles.privateLookupIn(ChatProcessor.class, lookup); - - serverGamePacketListenerImpl$chatMessageChain = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "chatMessageChain", - FutureChain.class - ); - - serverGamePacketListenerImpl$nextChatIndex = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "nextChatIndex", - int.class - ); - - serverGamePacketListenerImpl$messageSignatureCache = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "messageSignatureCache", - MessageSignatureCache.class - ); - - serverGamePacketListenerImpl$lastSeenMessages = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "lastSeenMessages", - LastSeenMessagesValidator.class - ); - - filterMask$mask = privateLookupInFilterMask.findVirtual( - FilterMask.class, - "mask", - MethodType.methodType(BitSet.class) - ); - - filterMask$constructorBitSet = privateLookupInFilterMask.findConstructor( - FilterMask.class, - MethodType.methodType(void.class, BitSet.class) - ); - - chatProcessor$PAPER_RAW = privateLookupInChatProcessor.findStaticVarHandle( - ChatProcessor.class, - "PAPER_RAW", - ResourceKey.class - ); - } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) { - throw new ExceptionInInitializerError(e); - } - } -} 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_11SurfPaperNmsCommandArgumentTypesBridgeImpl.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_11SurfPaperNmsCommandArgumentTypesBridgeImpl.kt index 8dc5afa4a..c0755f19f 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_11SurfPaperNmsCommandArgumentTypesBridgeImpl.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_11SurfPaperNmsCommandArgumentTypesBridgeImpl.kt @@ -2,26 +2,16 @@ package dev.slne.surf.api.paper.server.nms.v1_21_11.bridges import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.context.CommandContext -import com.mojang.brigadier.exceptions.CommandSyntaxException import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsCommandArgumentTypesBridge import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.AdventureNBT -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred -import net.bytebuddy.ByteBuddy -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy -import net.bytebuddy.dynamic.scaffold.TypeValidation -import net.bytebuddy.implementation.InvocationHandlerAdapter -import net.bytebuddy.implementation.bind.annotation.RuntimeType -import net.bytebuddy.matcher.ElementMatchers import net.kyori.adventure.chat.SignedMessage import net.kyori.adventure.nbt.CompoundBinaryTag import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.arguments.CompoundTagArgument import net.minecraft.commands.arguments.MessageArgument -import java.lang.reflect.InvocationHandler -import java.util.concurrent.ConcurrentHashMap @NmsUseWithCaution class V1_21_11SurfPaperNmsCommandArgumentTypesBridgeImpl : SurfPaperNmsCommandArgumentTypesBridge { @@ -51,79 +41,4 @@ class V1_21_11SurfPaperNmsCommandArgumentTypesBridgeImpl : SurfPaperNmsCommandAr return deferred } - - @Suppress("UNCHECKED_CAST") - private fun wrap( - base: ArgumentType, - converter: OpenedResultConverter - ): ArgumentType { - val wrappedConverter = OpenedResultConverterImpl.of(converter) - val wrapped = V1_21_11Reflection.VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY.wrap( - V1_21_11Reflection.VANILLA_ARGUMENT_PROVIDER_PROXY.provider(), - base, - wrappedConverter - ) as ArgumentType - - return wrapped - } - - - fun interface OpenedResultConverter { - @Throws(CommandSyntaxException::class) - fun convert(type: T): R - } - - object OpenedResultConverterImpl { - private val converterCache = ConcurrentHashMap() - - @Suppress("UNCHECKED_CAST") - fun of(converter: OpenedResultConverter): Any { - val cacheKey = "${converter.javaClass.name}_${System.identityHashCode(converter)}" - - return converterCache.computeIfAbsent(cacheKey) { - createConverter(converter) - } - } - - @Suppress("UNCHECKED_CAST") - private fun createConverter(converter: OpenedResultConverter): Any { - val resultConverterInterface = Class.forName( - "io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl\$ResultConverter" - ) - - val handler = InvocationHandler { _, method, args -> - when (method.name) { - "convert" -> converter.convert(args[0] as B) - else -> throw UnsupportedOperationException("Unknown method: ${method.name}") - } - } - - val dynamicType = ByteBuddy() - .with(TypeValidation.DISABLED) - .subclass(Any::class.java) - .implement(resultConverterInterface) - .name("io.papermc.paper.command.brigadier.argument.GeneratedResultConverter\$${System.nanoTime()}") - .method(ElementMatchers.any()) - .intercept(InvocationHandlerAdapter.of(handler)) - .make() - - val loadedClass = dynamicType.load( - resultConverterInterface.classLoader, - ClassLoadingStrategy.Default.INJECTION - ).loaded - - return loadedClass.getDeclaredConstructor().newInstance() - } - - - class InterceptorHolder( - private val converter: OpenedResultConverter - ) { - @RuntimeType - @Throws(Exception::class) - fun convert(@RuntimeType input: B): C { - return converter.convert(input) - } - } - } } 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_11SurfPaperNmsCommonBridgeImpl.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_11SurfPaperNmsCommonBridgeImpl.kt index 4bf87f50f..f87fadf42 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_11SurfPaperNmsCommonBridgeImpl.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_11SurfPaperNmsCommonBridgeImpl.kt @@ -6,7 +6,7 @@ import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsCommonBridge import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNms import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNmsBlock import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNmsItem -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import io.papermc.paper.configuration.GlobalConfiguration import net.kyori.adventure.text.Component import net.minecraft.network.protocol.common.ClientboundClearDialogPacket @@ -79,8 +79,7 @@ class V1_21_11SurfPaperNmsCommonBridgeImpl : SurfPaperNmsCommonBridge { } override fun getServerIp(): InetSocketAddress { - val channels = - V1_21_11Reflection.SERVER_CONNECTION_LISTENER_PROXY.getChannels(MinecraftServer.getServer().connection) + val channels = V1_21_11NmsReflections.getConnectionChannelFutures(MinecraftServer.getServer().connection) val channel = channels.firstOrNull() ?: error("No channels found in server connection listener proxy") 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_11SurfPaperNmsGlowingBridgeImpl.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_11SurfPaperNmsGlowingBridgeImpl.kt index b8af4a206..902fb99f6 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_11SurfPaperNmsGlowingBridgeImpl.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_11SurfPaperNmsGlowingBridgeImpl.kt @@ -7,7 +7,7 @@ import dev.slne.surf.api.paper.server.nms.v1_21_11.bridges.packets.V1_21_11Packe import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNms import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.V1_21_11TeamData import dev.slne.surf.api.paper.server.nms.v1_21_11.packet.listener.V1_21_11GlowingPacketListener -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket import net.minecraft.network.syncher.SynchedEntityData.DataValue @@ -40,7 +40,7 @@ object V1_21_11SurfPaperNmsGlowingBridgeImpl : SurfPaperNmsGlowingBridge { fun setEntityFlags(entityId: Int, flags: Byte, ignorePacket: Boolean = false): PacketOperation = V1_21_11PacketOperationImpl.simple { - val dataAccessor = V1_21_11Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataAccessor = V1_21_11NmsReflections.getEntityDataFlagsSharedId() val data = DataValue(dataAccessor.id(), dataAccessor.serializer, flags) ClientboundSetEntityDataPacket(entityId, listOf(data)).also { if (ignorePacket) { @@ -50,7 +50,7 @@ object V1_21_11SurfPaperNmsGlowingBridgeImpl : SurfPaperNmsGlowingBridge { } override fun getCurrentFlags(entity: Entity): Byte { - val dataAccessor = V1_21_11Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataAccessor = V1_21_11NmsReflections.getEntityDataFlagsSharedId() return entity.toNms().entityData.get(dataAccessor) } } 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 ff71652cb..12a2b35a4 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 @@ -11,7 +11,7 @@ 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.common.dummy.DummyEntityEquipment import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNms -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.NmsReflections +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import io.papermc.paper.adventure.PaperAdventure import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -67,7 +67,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { override fun runOnChatMessageChain(player: Player, scope: CoroutineScope, block: suspend () -> Unit) { val nmsPlayer = player.toNms() val connection = nmsPlayer.connection - val chatMessageChain = NmsReflections.getChatMessageChain(connection) + val chatMessageChain = V1_21_11NmsReflections.getChatMessageChain(connection) val done = CompletableFuture() synchronized(chatMessageChain) { @@ -113,7 +113,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { @Suppress("USELESS_ELVIS") override fun increaseNextChatIndex(player: Player): Int? { val connection = player.toNms().connection ?: return null - return NmsReflections.getAndIncreaseNextChatIndex(connection) + return V1_21_11NmsReflections.getAndIncreaseNextChatIndex(connection) } override fun createPlayerChatMessageMirrorFromAdventure( @@ -140,7 +140,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val filterMask = when (val mask = nms.filterMask()) { FilterMask.FULLY_FILTERED -> PlayerChatMessageMirror.FilterMask.FULLY_FILTERED FilterMask.PASS_THROUGH -> PlayerChatMessageMirror.FilterMask.PASS_THROUGH - else -> PlayerChatMessageMirror.FilterMask(NmsReflections.getMask(mask)) + else -> PlayerChatMessageMirror.FilterMask(V1_21_11NmsReflections.getFilterMask(mask)) } return PlayerChatMessageMirror( @@ -164,7 +164,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val filterMask = when (mirror.filterMask) { PlayerChatMessageMirror.FilterMask.FULLY_FILTERED -> FilterMask.FULLY_FILTERED PlayerChatMessageMirror.FilterMask.PASS_THROUGH -> FilterMask.PASS_THROUGH - else -> NmsReflections.createFilterMask(mirror.filterMask.mask) + else -> V1_21_11NmsReflections.createFilterMask(mirror.filterMask.mask) } return PlayerChatMessage( @@ -182,12 +182,12 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val nmsMessage = message.playerChatMessage() val nmsPlayer = receiver.toNms() val connection = nmsPlayer.connection ?: return - val messageSignatureCache = NmsReflections.getMessageSignatureCache(connection) + val messageSignatureCache = V1_21_11NmsReflections.getMessageSignatureCache(connection) synchronized(messageSignatureCache) { connection.send( ClientboundPlayerChatPacket( - NmsReflections.getAndIncreaseNextChatIndex(connection), + V1_21_11NmsReflections.getAndIncreaseNextChatIndex(connection), nmsMessage.link().sender(), nmsMessage.link().index(), nmsMessage.signature(), @@ -201,7 +201,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val signature = nmsMessage.signature() if (signature != null) { messageSignatureCache.push(nmsMessage.signedBody(), signature) - val lastSeenMessages = NmsReflections.getLastSeenMessages(connection) + val lastSeenMessages = V1_21_11NmsReflections.getLastSeenMessages(connection) synchronized(lastSeenMessages) { lastSeenMessages.addPending(signature) @@ -211,7 +211,7 @@ class V1_21_11SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { } override fun getPaperRawChatType(): ChatType { - return ChatType.chatType(PaperAdventure.asAdventureKey(NmsReflections.getPaperRaw())) + return ChatType.chatType(PaperAdventure.asAdventureKey(V1_21_11NmsReflections.getPaperRawChatTypeKey())) } override suspend fun editOfflineInventory( 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_11SurfPaperNmsStatsBridgeImpl.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_11SurfPaperNmsStatsBridgeImpl.kt index a18b4536e..24557bbe8 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_11SurfPaperNmsStatsBridgeImpl.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_11SurfPaperNmsStatsBridgeImpl.kt @@ -3,15 +3,15 @@ package dev.slne.surf.api.paper.server.nms.v1_21_11.bridges import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsStatsBridge import dev.slne.surf.api.paper.server.nms.v1_21_11.extensions.toNms -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import org.bukkit.entity.Player @NmsUseWithCaution class V1_21_11SurfPaperNmsStatsBridgeImpl : SurfPaperNmsStatsBridge { override fun getPlayerStatsAsJson(player: Player): String { - val gson = V1_21_11Reflection.SERVER_STATS_COUNTER_PROXY.getGson() - val jsonElement = V1_21_11Reflection.SERVER_STATS_COUNTER_PROXY.toJson(player.toNms().stats) + val gson = V1_21_11NmsReflections.getServerStatsCounterGson() + val jsonElement = V1_21_11NmsReflections.convertServerStatsCounterToJson(player.toNms().stats) return gson.toJson(jsonElement) } 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/glow/V1_21_11SurfGlowingApiImpl.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/glow/V1_21_11SurfGlowingApiImpl.kt index 344f82e33..ea9d1d3bf 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/glow/V1_21_11SurfGlowingApiImpl.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/glow/V1_21_11SurfGlowingApiImpl.kt @@ -12,7 +12,7 @@ import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.block.BlockGlowingData import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.block.BlockPlayerData import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.entity.EntityGlowingData import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.entity.EntityPlayerData -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import dev.slne.surf.api.paper.util.isChunkVisible import io.papermc.paper.adventure.PaperAdventure import net.kyori.adventure.text.format.NamedTextColor @@ -28,7 +28,7 @@ object V1_21_11SurfGlowingApiImpl : SurfGlowingApi { private val entityPlayerData = ConcurrentHashMap() private val blockPlayerData = ConcurrentHashMap() - val glowingFlag = 1 shl V1_21_11Reflection.ENTITY_PROXY.getFlagGlowing() + val glowingFlag = 1 shl V1_21_11NmsReflections.getEntityFlagGlowing() override fun makeGlowing( target: Entity, 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/glow/block/BlockGlowingData.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/glow/block/BlockGlowingData.kt index 04fa1ec90..dd43ddce4 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/glow/block/BlockGlowingData.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/glow/block/BlockGlowingData.kt @@ -7,7 +7,7 @@ import dev.slne.surf.api.paper.nms.bridges.packets.entity.SurfPaperNmsSpawnPacke import dev.slne.surf.api.paper.server.nms.v1_21_11.bridges.V1_21_11SurfPaperNmsGlowingBridgeImpl import dev.slne.surf.api.paper.server.nms.v1_21_11.bridges.packets.V1_21_11PacketOperationImpl import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.V1_21_11SurfGlowingApiImpl -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import glm_.shl import net.kyori.adventure.text.format.NamedTextColor import net.minecraft.network.protocol.game.ClientboundAddEntityPacket @@ -74,6 +74,6 @@ class BlockGlowingData( } companion object { - val invisibleFlag = 1.toByte() shl V1_21_11Reflection.ENTITY_PROXY.getFlagInvisible() + val invisibleFlag = 1.toByte() shl V1_21_11NmsReflections.getEntityFlagInvisible() } } 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/packet/listener/V1_21_11GlowingPacketListener.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/packet/listener/V1_21_11GlowingPacketListener.kt index 28d1dc2b6..fb5cfc8f3 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/packet/listener/V1_21_11GlowingPacketListener.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/packet/listener/V1_21_11GlowingPacketListener.kt @@ -8,7 +8,7 @@ import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.packet.listener.listener.PacketListener import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener import dev.slne.surf.api.paper.server.nms.v1_21_11.glow.V1_21_11SurfGlowingApiImpl -import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11Reflection +import dev.slne.surf.api.paper.server.nms.v1_21_11.reflection.V1_21_11NmsReflections import glm_.or import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.ClientboundBundlePacket @@ -66,7 +66,7 @@ object V1_21_11GlowingPacketListener : PacketListener { var flagsFound = false var edited = false val newItems = mutableObjectListOf>(incoming.size + 1) - val dataFlagsShared = V1_21_11Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataFlagsShared = V1_21_11NmsReflections.getEntityDataFlagsSharedId() val dataFlagsSharedId = dataFlagsShared.id for (dataValue in incoming) { 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_11EntityProxy.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_11EntityProxy.kt deleted file mode 100644 index 79e25a57d..000000000 --- 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_11EntityProxy.kt +++ /dev/null @@ -1,23 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection - -import net.minecraft.network.syncher.EntityDataAccessor -import net.minecraft.world.entity.Entity -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies -import xyz.jpenilla.reflectionremapper.proxy.annotation.Static - -@Proxies(Entity::class) -interface V1_21_11EntityProxy { - - @FieldGetter("FLAG_GLOWING") - @Static - fun getFlagGlowing(): Int - - @FieldGetter("FLAG_INVISIBLE") - @Static - fun getFlagInvisible(): Int - - @FieldGetter("DATA_SHARED_FLAGS_ID") - @Static - fun getDataFlagsSharedId(): EntityDataAccessor -} 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 new file mode 100644 index 000000000..934cfd2c3 --- /dev/null +++ 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 @@ -0,0 +1,89 @@ +package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection + +import com.google.gson.Gson +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.syncher.EntityDataAccessor +import net.minecraft.resources.ResourceKey +import net.minecraft.server.network.ServerConnectionListener +import net.minecraft.server.network.ServerGamePacketListenerImpl +import net.minecraft.stats.ServerStatsCounter +import net.minecraft.util.FutureChain +import net.minecraft.world.entity.Entity +import java.lang.invoke.VarHandle +import java.util.* + +@GenerateReflection +interface V1_21_11NmsReflections { + + @ReflectedField("chatMessageChain") + fun getChatMessageChain(instance: ServerGamePacketListenerImpl): FutureChain + + @ReflectedVarHandle( + name = "nextChatIndex", + mode = VarHandle.AccessMode.GET_AND_ADD, + ) + @ConstantIntArgument(1) + fun getAndIncreaseNextChatIndex(instance: ServerGamePacketListenerImpl): Int + + @ReflectedField("messageSignatureCache") + fun getMessageSignatureCache(instance: ServerGamePacketListenerImpl): MessageSignatureCache + + @ReflectedField(name = "lastSeenMessages") + fun getLastSeenMessages(instance: ServerGamePacketListenerImpl): LastSeenMessagesValidator + + @ReflectedConstructor + fun createFilterMask(bitSet: BitSet): FilterMask + + @ReflectedMethod("mask") + fun getFilterMask(instance: FilterMask): BitSet + + @ReflectedField( + name = "PAPER_RAW", + isStatic = true, + target = ChatProcessor::class + ) + fun getPaperRawChatTypeKey(): ResourceKey + + @ReflectedField( + name = "FLAG_GLOWING", + isStatic = true, + target = Entity::class + ) + fun getEntityFlagGlowing(): Int + + @ReflectedField( + name = "FLAG_INVISIBLE", + isStatic = true, + target = Entity::class + ) + fun getEntityFlagInvisible(): Int + + @ReflectedField( + name = "DATA_SHARED_FLAGS_ID", + isStatic = true, + target = Entity::class + ) + fun getEntityDataFlagsSharedId(): EntityDataAccessor + + @ReflectedField("channels") + fun getConnectionChannelFutures(instance: ServerConnectionListener): List + + @ReflectedMethod("toJson") + fun convertServerStatsCounterToJson(statsCounter: ServerStatsCounter): JsonElement + + @ReflectedField( + name = "GSON", + isStatic = true, + target = ServerStatsCounter::class + ) + fun getServerStatsCounterGson(): Gson + + 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-v1-21-11/src/main/kotlin/dev/slne/surf/api/paper/server/nms/v1_21_11/reflection/V1_21_11Reflection.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_11Reflection.kt index 4c46e55c8..9567e6416 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_11Reflection.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_11Reflection.kt @@ -1,39 +1,19 @@ package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection -import dev.slne.surf.api.core.reflection.SurfReflection -import dev.slne.surf.api.core.reflection.createProxy import dev.slne.surf.api.paper.util.reflectionProxy import xyz.jpenilla.reflectionremapper.ReflectionRemapper import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory object V1_21_11Reflection { - lateinit var SERVER_STATS_COUNTER_PROXY: V1_21_11ServerStatsCounterProxy - private set - lateinit var ENTITY_PROXY: V1_21_11EntityProxy - private set lateinit var ITEM_PROXY: V1_21_11ItemProxy private set - lateinit var SERVER_CONNECTION_LISTENER_PROXY: V1_21_11ServerConnectionListenerProxy - private set - lateinit var VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY: V1_21_11VanillaArgumentProviderImplProxy - private set - lateinit var VANILLA_ARGUMENT_PROVIDER_PROXY: V1_21_11VanillaArgumentProviderProxy - private set fun initialize() { val remapper = ReflectionRemapper.forReobfMappingsInPaperJar() val proxyFactory = ReflectionProxyFactory.create(remapper, V1_21_11Reflection::class.java.classLoader) - SERVER_STATS_COUNTER_PROXY = proxyFactory.reflectionProxy() - ENTITY_PROXY = proxyFactory.reflectionProxy() ITEM_PROXY = proxyFactory.reflectionProxy() - SERVER_CONNECTION_LISTENER_PROXY = - proxyFactory.reflectionProxy() - VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY = - SurfReflection.createProxy() - VANILLA_ARGUMENT_PROVIDER_PROXY = - SurfReflection.createProxy() // gc the remapper System.gc() 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_11ServerConnectionListenerProxy.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_11ServerConnectionListenerProxy.kt deleted file mode 100644 index a8e0a39c9..000000000 --- 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_11ServerConnectionListenerProxy.kt +++ /dev/null @@ -1,12 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection - -import io.netty.channel.ChannelFuture -import net.minecraft.server.network.ServerConnectionListener -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies - -@Proxies(ServerConnectionListener::class) -interface V1_21_11ServerConnectionListenerProxy { - @FieldGetter("channels") - fun getChannels(instance: ServerConnectionListener): List -} 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_11ServerStatsCounterProxy.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_11ServerStatsCounterProxy.kt deleted file mode 100644 index a6c9dfc54..000000000 --- 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_11ServerStatsCounterProxy.kt +++ /dev/null @@ -1,20 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection - -import com.google.gson.Gson -import com.google.gson.JsonElement -import net.minecraft.stats.ServerStatsCounter -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.MethodName -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies -import xyz.jpenilla.reflectionremapper.proxy.annotation.Static - -@Proxies(ServerStatsCounter::class) -interface V1_21_11ServerStatsCounterProxy { - - @MethodName("toJson") - fun toJson(statsCounter: ServerStatsCounter): JsonElement - - @FieldGetter("GSON") - @Static - fun getGson(): Gson -} 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_11VanillaArgumentProviderImplProxy.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_11VanillaArgumentProviderImplProxy.kt deleted file mode 100644 index 5e9fb9d2d..000000000 --- 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_11VanillaArgumentProviderImplProxy.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection - -import com.mojang.brigadier.arguments.ArgumentType -import com.mojang.brigadier.exceptions.CommandSyntaxException -import dev.slne.surf.api.core.reflection.Name -import dev.slne.surf.api.core.reflection.SurfProxy -import io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl - -@SurfProxy(VanillaArgumentProviderImpl::class) -interface V1_21_11VanillaArgumentProviderImplProxy { - - @Name("wrap") - @Throws(CommandSyntaxException::class) - fun wrap(instance: Any, base: Any, converter: Any): ArgumentType<*> -} 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_11VanillaArgumentProviderProxy.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_11VanillaArgumentProviderProxy.kt deleted file mode 100644 index 67082b302..000000000 --- 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_11VanillaArgumentProviderProxy.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v1_21_11.reflection - -import dev.slne.surf.api.core.reflection.Name -import dev.slne.surf.api.core.reflection.Static -import dev.slne.surf.api.core.reflection.SurfProxy - - -@SurfProxy(qualifiedName = "io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider") -interface V1_21_11VanillaArgumentProviderProxy { - - @Static - @Name("provider") - fun provider(): Any -} diff --git a/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflection.kt b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflection.kt new file mode 100644 index 000000000..5d47b44bc --- /dev/null +++ b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflection.kt @@ -0,0 +1,479 @@ +package dev.slne.surf.api.shared.api.reflection + +import dev.slne.surf.api.shared.api.util.InternalSurfApi +import java.lang.invoke.VarHandle +import kotlin.reflect.KClass + +/** + * Internal sentinel type used as the default value for annotation members that accept a [KClass]. + * + * Kotlin annotations cannot use `null` as a default value for `KClass` properties. This type is + * therefore used to express "no class was specified" for properties such as + * [GenerateReflection.target], [ReflectedMethod.target], [ReflectedField.type], and similar + * members. + * + * This type is an implementation detail of the reflection generator API and should never be used + * directly by user code. + */ +@InternalSurfApi +class UnspecifiedReflectionTarget private constructor() + +/** + * Marks an interface as a compile-time reflection proxy. + * + * The KSP processor generates a Java implementation class for every interface annotated with + * [GenerateReflection]. The generated class implements the annotated interface and resolves all + * configured reflected members once in a static initializer. Reflected methods are backed by + * [java.lang.invoke.MethodHandle] fields, while reflected fields and VarHandle operations are + * backed by [java.lang.invoke.VarHandle] or derived [java.lang.invoke.MethodHandle] fields. + * + * A minimal proxy looks like this: + * + * ```kotlin + * @GenerateReflection + * interface ExampleReflection { + * @ReflectedField("somePrivateField") + * fun getSomePrivateField(instance: ExampleTarget): String + * + * companion object : ExampleReflection by generatedReflectionAccessor() + * } + * ``` + * + * The generated implementation can then be used through the companion object: + * + * ```kotlin + * val value = ExampleReflection.getSomePrivateField(target) + * ``` + * + * The generator resolves the target class for each member in the following order: + * + * 1. The member annotation, for example [ReflectedField.target] or [ReflectedMethod.target]. + * 2. The default [target] or [targetName] configured on [GenerateReflection]. + * 3. For instance members, the first function parameter. + * 4. For constructors, the function return type. + * + * Static members cannot be inferred from parameters because they do not have a receiver. They + * therefore require either a default target on this annotation or an explicit target on the member + * annotation. + * + * Prefer [target] when the reflected class is available on the compile classpath. Use [targetName] + * when the reflected class should only be resolved by name at runtime. + * + * @property target Default target class for all reflected members in the interface. This is useful + * when most or all members operate on the same class. + * @property targetName Binary name of the default target class, for example + * `"net.minecraft.server.SomeClass"`. Use this when the class is not available as a [KClass] during + * compilation. + * @property packageName Package of the generated implementation class. If blank, the interface + * package is used. + * @property className Simple name of the generated implementation class. If blank, the generated + * name is `Impl`. + * @property singleton Whether the generated implementation should expose a public static + * `INSTANCE` field and use a private constructor. This should normally stay enabled because + * reflection handles are immutable after initialization. + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class GenerateReflection( + val target: KClass<*> = UnspecifiedReflectionTarget::class, + val targetName: String = "", + val packageName: String = "", + val className: String = "", + val singleton: Boolean = true, +) + + +/** + * Controls how the generated Java implementation invokes a [java.lang.invoke.MethodHandle]. + * + * The default mode is [EXACT], which emits `invokeExact(...)`. This is the fastest and strictest + * mode, but it requires the generated Java callsite to match the handle type exactly. + * + * Use [AS_TYPE_EXACT] when the raw reflected member is resolved dynamically, for example through a + * JVM descriptor or runtime class name, but the public proxy method still has a stable signature. + * In that mode, the handle is adapted once in the generated static initializer and then invoked via + * `invokeExact(...)`. + * + * Use [INVOKE] only as a fallback for intentionally dynamic cases where method-handle adaptation + * should happen at the callsite. + */ +enum class ReflectionInvocationMode { + /** + * Emits a direct `invokeExact(...)` call. + * + * Use this when all involved types are available at compile time and the generated method + * signature exactly matches the reflected member after JVM erasure. + * + * Example: + * + * ```kotlin + * @ReflectedMethod("mask") + * fun getMask(instance: FilterMask): BitSet + * ``` + */ + EXACT, + + /** + * Emits `asType(...)` once during static initialization and then calls `invokeExact(...)`. + * + * This is useful when the target class or method descriptor is only known as a string, but the + * proxy API itself should still be strongly typed. + * + * Example: + * + * ```kotlin + * @ReflectedMethod( + * name = "mask", + * targetName = "net.minecraft.network.chat.FilterMask", + * descriptor = "()Ljava/util/BitSet;", + * invocation = ReflectionInvocationMode.AS_TYPE_EXACT, + * ) + * fun getMask(instance: Any): BitSet + * ``` + */ + AS_TYPE_EXACT, + + /** + * Emits plain `invoke(...)`. + * + * This mode allows the JVM to perform method-handle conversions at the callsite. It is more + * permissive than [EXACT], but should only be used when strict callsite typing is not possible + * or not desired. + */ + INVOKE, +} + +/** + * Describes a reflected method. + * + * Use this annotation on an interface function to call a private, package-private, protected, or + * otherwise inaccessible method through a generated [java.lang.invoke.MethodHandle]. + * + * For instance methods, the first proxy function parameter is treated as the receiver: + * + * ```kotlin + * @ReflectedMethod("mask") + * fun getMask(instance: FilterMask): BitSet + * ``` + * + * This resolves a method similar to: + * + * ```java + * BitSet FilterMask.mask() + * ``` + * + * For static methods, set [isStatic] to `true`. Static methods do not use a receiver parameter: + * + * ```kotlin + * @ReflectedMethod(name = "create", isStatic = true, target = SomeFactory::class) + * fun create(value: String): SomeObject + * ``` + * + * If [descriptor] is blank, the generator infers the JVM method type from the proxy function + * signature. If [descriptor] is specified, the descriptor is used for lookup instead. This is useful + * for overloaded methods or for classes that are only available by [targetName]. + * + * Target resolution follows the common generator rules: + * + * 1. [target] or [targetName] on this annotation. + * 2. [GenerateReflection.target] or [GenerateReflection.targetName]. + * 3. For instance methods, the first proxy parameter. + * + * @property name Name of the reflected method. If blank, the proxy function name is used. + * @property isStatic Whether the reflected method is static. + * @property target Compile-time target class override for this method. + * @property targetName Runtime target class name override for this method. + * @property descriptor JVM method descriptor used for lookup. If blank, the descriptor is inferred + * from the proxy function signature. + * @property invocation Method-handle invocation strategy used by the generated wrapper. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +annotation class ReflectedMethod( + val name: String = "", + val isStatic: Boolean = false, + val target: KClass<*> = UnspecifiedReflectionTarget::class, + val targetName: String = "", + val descriptor: String = "", + val invocation: ReflectionInvocationMode = ReflectionInvocationMode.EXACT, +) + +/** + * Describes a reflected constructor. + * + * Use this annotation on an interface function to call a constructor through a generated + * [java.lang.invoke.MethodHandle]. + * + * Constructor proxy functions do not have a receiver parameter. The proxy function parameters are + * treated as constructor arguments, and the return type is treated as the constructed type: + * + * ```kotlin + * @ReflectedConstructor + * fun createFilterMask(bitSet: BitSet): FilterMask + * ``` + * + * If [target] and [targetName] are not specified, the generator attempts to infer the target class + * from the proxy function return type. This works for normal strongly typed constructors. If the + * return type is `Any`, or if the target class is not available at compile time, specify [targetName] + * and usually also [descriptor]. + * + * For constructors, [descriptor] must be a JVM constructor descriptor whose return type is `V`, + * for example: + * + * ```kotlin + * @ReflectedConstructor( + * targetName = "net.minecraft.network.chat.FilterMask", + * descriptor = "(Ljava/util/BitSet;)V", + * invocation = ReflectionInvocationMode.AS_TYPE_EXACT, + * ) + * fun createFilterMask(bitSet: BitSet): Any + * ``` + * + * @property target Compile-time constructor owner class. + * @property targetName Runtime constructor owner class name. + * @property descriptor JVM constructor descriptor. If blank, the descriptor is inferred from the + * proxy function parameters. + * @property invocation Method-handle invocation strategy used by the generated wrapper. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +annotation class ReflectedConstructor( + val target: KClass<*> = UnspecifiedReflectionTarget::class, + val targetName: String = "", + val descriptor: String = "", + val invocation: ReflectionInvocationMode = ReflectionInvocationMode.EXACT, +) + +/** + * Defines the supported simple field operations for [ReflectedField]. + * + * For atomic operations, volatile operations, compare-and-set, get-and-add, and related access + * modes, use [ReflectedVarHandle] instead. + */ +enum class ReflectedFieldAccess { + /** + * Reads the field via [java.lang.invoke.VarHandle.get]. + * + * Instance fields require the proxy function to declare the receiver as the first parameter. + * Static fields do not require a receiver parameter. + */ + GET, + + /** + * Writes the field via [java.lang.invoke.VarHandle.set]. + * + * The proxy function should return `Unit` and provide the new field value as its last argument. + * Instance fields require the receiver as the first parameter; static fields do not. + * + * Final fields are intentionally not supported. + */ + SET, +} + +/** + * Describes a reflected field get or set operation. + * + * The generated implementation resolves the field as a [java.lang.invoke.VarHandle] and then emits + * a simple access operation such as `get(...)` or `set(...)`. + * + * Instance field getter example: + * + * ```kotlin + * @ReflectedField("chatMessageChain") + * fun getChatMessageChain(instance: ServerGamePacketListenerImpl): FutureChain + * ``` + * + * Instance field setter example: + * + * ```kotlin + * @ReflectedField(name = "someField", access = ReflectedFieldAccess.SET) + * fun setSomeField(instance: SomeTarget, value: String) + * ``` + * + * Static field getter example: + * + * ```kotlin + * @ReflectedField( + * name = "PAPER_RAW", + * isStatic = true, + * target = ChatProcessor::class, + * type = ResourceKey::class, + * ) + * fun getPaperRawChatTypeKey(): ResourceKey + * ``` + * + * If [type], [typeName], and [typeDescriptor] are blank, the generator infers the field type from + * the proxy function: + * + * - For [ReflectedFieldAccess.GET], the return type is used. + * - For [ReflectedFieldAccess.SET], the value parameter type is used. + * + * Specify [type], [typeName], or [typeDescriptor] when the proxy type is generic, erased, dynamic, + * or otherwise not suitable for the actual field lookup. For example, a field declared as raw + * `ResourceKey` can be exposed as `ResourceKey` while using `type = ResourceKey::class` + * for lookup. + * + * Target resolution follows the common generator rules: + * + * 1. [target] or [targetName] on this annotation. + * 2. [GenerateReflection.target] or [GenerateReflection.targetName]. + * 3. For instance fields, the first proxy parameter. + * + * Static fields cannot be inferred from a receiver and therefore need an explicit target unless the + * interface has a default target. + * + * @property name Name of the reflected field. If blank, the proxy function name is used. + * @property isStatic Whether the reflected field is static. + * @property access Field operation emitted by the generated wrapper. + * @property target Compile-time field owner class. + * @property targetName Runtime field owner class name. + * @property type Compile-time field type used for VarHandle lookup. + * @property typeName Runtime field type name used for VarHandle lookup. + * @property typeDescriptor JVM field descriptor used for VarHandle lookup. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +annotation class ReflectedField( + val name: String = "", + val isStatic: Boolean = false, + val access: ReflectedFieldAccess = ReflectedFieldAccess.GET, + val target: KClass<*> = UnspecifiedReflectionTarget::class, + val targetName: String = "", + val type: KClass<*> = UnspecifiedReflectionTarget::class, + val typeName: String = "", + val typeDescriptor: String = "", +) + +/** + * Describes a VarHandle access-mode operation. + * + * Use this annotation when a simple [ReflectedField] get or set is not enough. This includes + * volatile reads/writes, acquire/release operations, compare-and-set operations, compare-and-exchange + * operations, atomic get-and-set, atomic get-and-add, and bitwise atomic operations. + * + * Example for an atomic increment: + * + * ```kotlin + * @ReflectedVarHandle( + * name = "nextChatIndex", + * mode = VarHandle.AccessMode.GET_AND_ADD, + * ) + * @ConstantIntArgument(1) + * fun getAndIncreaseNextChatIndex(instance: ServerGamePacketListenerImpl): Int + * ``` + * + * This generates a wrapper equivalent to: + * + * ```java + * return (int) nextChatIndex.getAndAdd(instance, 1); + * ``` + * + * For instance fields, the first proxy function parameter is treated as the receiver. For static + * fields, set [isStatic] to `true` and omit the receiver parameter. + * + * If [invocation] is [ReflectionInvocationMode.EXACT], the generated Java code directly calls the + * corresponding VarHandle access-mode method, for example `getAndAdd(...)` or + * `compareAndSet(...)`. + * + * If [invocation] is [ReflectionInvocationMode.AS_TYPE_EXACT] or [ReflectionInvocationMode.INVOKE], + * the generated implementation first converts the VarHandle access mode to a + * [java.lang.invoke.MethodHandle] using `toMethodHandle(...)`. This is useful for more dynamic + * signatures where the exact VarHandle callsite cannot be emitted directly. + * + * If [type], [typeName], and [typeDescriptor] are blank, the generator tries to infer the field type + * from the proxy signature or constant arguments. Specify an explicit field type when inference + * would be ambiguous, especially for `compareAndSet`, erased generic fields, or dynamically loaded + * target classes. + * + * Target resolution follows the common generator rules: + * + * 1. [target] or [targetName] on this annotation. + * 2. [GenerateReflection.target] or [GenerateReflection.targetName]. + * 3. For instance fields, the first proxy parameter. + * + * @property name Name of the reflected field. If blank, the proxy function name is used. + * @property isStatic Whether the reflected field is static. + * @property mode [VarHandle.AccessMode] to invoke. + * @property target Compile-time field owner class. + * @property targetName Runtime field owner class name. + * @property type Compile-time field type used for VarHandle lookup. + * @property typeName Runtime field type name used for VarHandle lookup. + * @property typeDescriptor JVM field descriptor used for VarHandle lookup. + * @property invocation Invocation strategy. [ReflectionInvocationMode.EXACT] emits direct VarHandle + * access-mode calls. Other modes convert the access mode to a MethodHandle first. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +annotation class ReflectedVarHandle( + val name: String = "", + val isStatic: Boolean = false, + val mode: VarHandle.AccessMode, + val target: KClass<*> = UnspecifiedReflectionTarget::class, + val targetName: String = "", + val type: KClass<*> = UnspecifiedReflectionTarget::class, + val typeName: String = "", + val typeDescriptor: String = "", + val invocation: ReflectionInvocationMode = ReflectionInvocationMode.EXACT, +) + +/** + * Appends a constant `int` argument to the generated reflective call. + * + * Constant arguments are appended after all proxy function parameters. They are useful when the + * public proxy API should hide a fixed argument that is always passed to the reflected member. + * + * Example: + * + * ```kotlin + * @ReflectedVarHandle( + * name = "nextChatIndex", + * mode = VarHandle.AccessMode.GET_AND_ADD, + * ) + * @ConstantIntArgument(1) + * fun getAndIncreaseNextChatIndex(instance: ServerGamePacketListenerImpl): Int + * ``` + * + * The generated call receives both the receiver and the constant: + * + * ```java + * nextChatIndex.getAndAdd(instance, 1) + * ``` + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +@Repeatable +annotation class ConstantIntArgument(val value: Int) + +/** + * Appends a constant `long` argument to the generated reflective call. + * + * Constant arguments are appended after all proxy function parameters, in annotation processing + * order. Use this for fixed numeric arguments that should not be exposed in the proxy method + * signature. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +@Repeatable +annotation class ConstantLongArgument(val value: Long) + +/** + * Appends a constant `boolean` argument to the generated reflective call. + * + * Constant arguments are appended after all proxy function parameters. This is useful for reflected + * members that always need the same flag value but should expose a simpler proxy API. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +@Repeatable +annotation class ConstantBooleanArgument(val value: Boolean) + +/** + * Appends a constant [String] argument to the generated reflective call. + * + * Constant arguments are appended after all proxy function parameters. The generated Java source + * emits the value as a normal escaped string literal. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) +@Repeatable +annotation class ConstantStringArgument(val value: String) \ No newline at end of file diff --git a/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt new file mode 100644 index 000000000..b91e6e5a1 --- /dev/null +++ b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt @@ -0,0 +1,127 @@ +package dev.slne.surf.api.shared.api.reflection + +import java.lang.reflect.Modifier +import kotlin.reflect.KClass + + +/** + * Returns the generated reflection implementation for [T]. + * + * This function is intended for companion-object delegation inside interfaces annotated with + * [GenerateReflection]: * + * ```kotlin + * @GenerateReflection + * interface ExampleReflection { + * companion object : ExampleReflection by generatedReflectionAccessor() + * } + * ``` + * + * The generated implementation class is loaded by convention from the [GenerateReflection] + * annotation. If no custom class name is configured, the implementation name is + * `Impl` in the interface package. + * + * This function does not generate code at runtime. The implementation must still be generated by + * KSP during compilation. + * + * @throws IllegalArgumentException if [T] is not an interface or is missing [GenerateReflection]. + * @throws IllegalStateException if the generated implementation class cannot be loaded or does not + * implement [T]. + */ +inline fun generatedReflectionAccessor(): T { + return GeneratedReflectionAccessors.getTyped(T::class.java) +} + +/** + * Returns the generated reflection implementation for [type]. + * + * Prefer the reified [generatedReflectionAccessor] overload from Kotlin code. + */ +fun generatedReflectionAccessor(type: KClass): T { + return GeneratedReflectionAccessors.getTyped(type.java) +} + +/** + * Returns the generated reflection implementation for [type]. + * + * Prefer the reified [generatedReflectionAccessor] overload from Kotlin code. + */ +fun generatedReflectionAccessor(type: Class): T { + return GeneratedReflectionAccessors.getTyped(type) +} + +/** + * Runtime loader and cache for generated reflection implementations. + */ +@PublishedApi +internal object GeneratedReflectionAccessors : ClassValue() { + + override fun computeValue(type: Class<*>): Any? { + return type.cast(createAccessor(type)) + } + + fun getTyped(type: Class): T { + return type.cast(get(type)) + } + + private fun createAccessor(type: Class<*>): Any { + require(type.isInterface) { + "Generated reflection accessors can only be loaded for interfaces: ${type.name}" + } + + val annotation = type.getAnnotation(GenerateReflection::class.java) + ?: throw IllegalArgumentException( + "Missing @${GenerateReflection::class.java.simpleName}: on ${type.name}" + ) + + val implementationClassName = annotation.implementationClassName(type) + + val implementationClass = try { + Class.forName(implementationClassName, true, type.classLoader) + } catch (exception: ClassNotFoundException) { + throw IllegalStateException( + "Generated reflection implementation '$implementationClassName' was not found for '${type.name}'. " + + "Run the KSP task for the module that declares this interface.", + exception, + ) + } + + check(type.isAssignableFrom(implementationClass)) { + "Generated reflection implementation '${implementationClass.name}' does not implement '${type.name}'" + } + + return implementationClass.readSingletonInstance() ?: implementationClass.createNewInstance() + } + + private fun GenerateReflection.implementationClassName(interfaceClass: Class<*>): String { + val implementationPackage = packageName.ifBlank { interfaceClass.packageName } + val implementationSimpleName = className.ifBlank { "${interfaceClass.simpleName}Impl" } + + return if (implementationPackage.isBlank()) { + implementationSimpleName + } else { + "$implementationPackage.$implementationSimpleName" + } + } + + private fun Class<*>.readSingletonInstance(): Any? { + val field = runCatching { getField("INSTANCE") }.getOrNull() ?: return null + + if (!Modifier.isStatic(field.modifiers)) { + return null + } + + return field.get(null) + } + + private fun Class<*>.createNewInstance(): Any { + val constructor = runCatching { getDeclaredConstructor() }.getOrElse { exception -> + throw IllegalStateException( + "Generated reflection implementation '$name' has no INSTANCE field and no no-args constructor.", + exception, + ) + } + + constructor.trySetAccessible() + return constructor.newInstance() + } +} \ No newline at end of file From 103e3ebcf0a78cbef143dbd443ec853942bf8384 Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 19:25:27 +0200 Subject: [PATCH 2/6] =?UTF-8?q?=E2=9C=A8=20feat(reflection):=20introduce?= =?UTF-8?q?=20V26=5F1NmsReflections=20for=20improved=20reflection=20handli?= =?UTF-8?q?ng?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace V26_1Reflection references with V26_1NmsReflections in multiple files - add new V26_1NmsReflections interface with reflection methods for entity flags and chat handling - update glowing flag calculations to use new reflection methods --- .../reflection/ReflectionSymbolProcessor.kt | 3 +- .../v1_21_11/reflection/V1_21_11ItemProxy.kt | 1 + .../reflection/V1_21_11NmsReflections.kt | 1 + .../v1_21_11/reflection/V1_21_11Reflection.kt | 1 + .../nms/v26_1/reflection/NmsReflections.java | 114 ------------------ ...fPaperNmsCommandArgumentTypesBridgeImpl.kt | 85 ------------- .../V26_1SurfPaperNmsCommonBridgeImpl.kt | 8 +- .../V26_1SurfPaperNmsGlowingBridgeImpl.kt | 6 +- .../V26_1SurfPaperNmsPlayerBridgeImpl.kt | 18 +-- .../V26_1SurfPaperNmsStatsBridgeImpl.kt | 6 +- .../nms/v26_1/glow/V26_1SurfGlowingApiImpl.kt | 4 +- .../nms/v26_1/glow/block/BlockGlowingData.kt | 4 +- .../listener/V26_1GlowingPacketListener.kt | 4 +- .../nms/v26_1/reflection/V26_1EntityProxy.kt | 24 ---- .../v26_1/reflection/V26_1NmsReflections.kt | 89 ++++++++++++++ .../nms/v26_1/reflection/V26_1Reflection.kt | 27 +---- .../V26_1ServerConnectionListenerProxy.kt | 13 -- .../V26_1ServerStatsCounterProxy.kt | 21 ---- .../V26_1VanillaArgumentProviderImplProxy.kt | 16 --- .../V26_1VanillaArgumentProviderProxy.kt | 14 --- 20 files changed, 119 insertions(+), 340 deletions(-) delete mode 100644 surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/java/dev/slne/surf/api/paper/server/nms/v26_1/reflection/NmsReflections.java delete mode 100644 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_1EntityProxy.kt create mode 100644 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 delete mode 100644 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_1ServerConnectionListenerProxy.kt delete mode 100644 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_1ServerStatsCounterProxy.kt delete mode 100644 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_1VanillaArgumentProviderImplProxy.kt delete mode 100644 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_1VanillaArgumentProviderProxy.kt 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 54755bc0f..07f7329cd 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 @@ -193,7 +193,7 @@ class ReflectionSymbolProcessor(environment: SymbolProcessorEnvironment) : Symbo val mode = try { VarHandle.AccessMode.valueOf(modeString) - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { throw ReflectionProcessorException("Invalid VarHandle mode: $modeString") } @@ -389,7 +389,6 @@ class ReflectionSymbolProcessor(environment: SymbolProcessorEnvironment) : Symbo val declaration = type.declaration as? KSClassDeclaration ?: return null val qualifiedName = declaration.qualifiedName?.asString() ?: return null - println("typeArg: $qualifiedName") if (qualifiedName == ClassNames.UNSPECIFIED_REFLECTION_TARGET) { return null } 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_11ItemProxy.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_11ItemProxy.kt index 28a91789e..809e28aab 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_11ItemProxy.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_11ItemProxy.kt @@ -5,6 +5,7 @@ import net.minecraft.world.item.Item import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies +@Suppress("ClassName") @Proxies(Item::class) interface V1_21_11ItemProxy { @FieldSetter("components") 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 934cfd2c3..3a7c5e3a4 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 @@ -19,6 +19,7 @@ import net.minecraft.world.entity.Entity import java.lang.invoke.VarHandle import java.util.* +@Suppress("ClassName") @GenerateReflection interface V1_21_11NmsReflections { 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_11Reflection.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_11Reflection.kt index 9567e6416..3a0394eb2 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_11Reflection.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_11Reflection.kt @@ -4,6 +4,7 @@ import dev.slne.surf.api.paper.util.reflectionProxy import xyz.jpenilla.reflectionremapper.ReflectionRemapper import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory +@Suppress("ClassName") object V1_21_11Reflection { lateinit var ITEM_PROXY: V1_21_11ItemProxy private set diff --git a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/java/dev/slne/surf/api/paper/server/nms/v26_1/reflection/NmsReflections.java b/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/java/dev/slne/surf/api/paper/server/nms/v26_1/reflection/NmsReflections.java deleted file mode 100644 index 43c207316..000000000 --- a/surf-api-paper/surf-api-paper-nms/surf-api-paper-nms-v26-1/src/main/java/dev/slne/surf/api/paper/server/nms/v26_1/reflection/NmsReflections.java +++ /dev/null @@ -1,114 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection; - -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.resources.ResourceKey; -import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.util.FutureChain; -import org.jspecify.annotations.NullMarked; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.lang.invoke.VarHandle; -import java.util.BitSet; - -@NullMarked -public final class NmsReflections { - private NmsReflections() { - throw new UnsupportedOperationException(); - } - - private static final VarHandle serverGamePacketListenerImpl$chatMessageChain; - private static final VarHandle serverGamePacketListenerImpl$nextChatIndex; - private static final VarHandle serverGamePacketListenerImpl$messageSignatureCache; - private static final VarHandle serverGamePacketListenerImpl$lastSeenMessages; - private static final VarHandle chatProcessor$PAPER_RAW; - - private static final MethodHandle filterMask$mask; - private static final MethodHandle filterMask$constructorBitSet; - - public static FutureChain getChatMessageChain(ServerGamePacketListenerImpl instance) { - return (FutureChain) serverGamePacketListenerImpl$chatMessageChain.get(instance); - } - - public static int getAndIncreaseNextChatIndex(ServerGamePacketListenerImpl instance) { - return (int) serverGamePacketListenerImpl$nextChatIndex.getAndAdd(instance, 1); - } - - public static MessageSignatureCache getMessageSignatureCache(ServerGamePacketListenerImpl instance) { - return (MessageSignatureCache) serverGamePacketListenerImpl$messageSignatureCache.get(instance); - } - - public static LastSeenMessagesValidator getLastSeenMessages(ServerGamePacketListenerImpl instance) { - return (LastSeenMessagesValidator) serverGamePacketListenerImpl$lastSeenMessages.get(instance); - } - - public static FilterMask createFilterMask(BitSet bitSet) throws Throwable { - return (FilterMask) filterMask$constructorBitSet.invokeExact(bitSet); - } - - public static BitSet getMask(FilterMask filterMask) throws Throwable { - return (BitSet) filterMask$mask.invokeExact(filterMask); - } - - @SuppressWarnings("unchecked") - public static ResourceKey getPaperRaw() { - return (ResourceKey) chatProcessor$PAPER_RAW.get(); - } - - static { - MethodHandles.Lookup lookup = MethodHandles.lookup(); - try { - MethodHandles.Lookup privateLookupInServerGamePacketListener = MethodHandles.privateLookupIn(ServerGamePacketListenerImpl.class, lookup); - MethodHandles.Lookup privateLookupInFilterMask = MethodHandles.privateLookupIn(FilterMask.class, lookup); - MethodHandles.Lookup privateLookupInChatProcessor = MethodHandles.privateLookupIn(ChatProcessor.class, lookup); - - serverGamePacketListenerImpl$chatMessageChain = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "chatMessageChain", - FutureChain.class - ); - - serverGamePacketListenerImpl$nextChatIndex = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "nextChatIndex", - int.class - ); - - serverGamePacketListenerImpl$messageSignatureCache = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "messageSignatureCache", - MessageSignatureCache.class - ); - - serverGamePacketListenerImpl$lastSeenMessages = privateLookupInServerGamePacketListener.findVarHandle( - ServerGamePacketListenerImpl.class, - "lastSeenMessages", - LastSeenMessagesValidator.class - ); - - filterMask$mask = privateLookupInFilterMask.findVirtual( - FilterMask.class, - "mask", - MethodType.methodType(BitSet.class) - ); - - filterMask$constructorBitSet = privateLookupInFilterMask.findConstructor( - FilterMask.class, - MethodType.methodType(void.class, BitSet.class) - ); - - chatProcessor$PAPER_RAW = privateLookupInChatProcessor.findStaticVarHandle( - ChatProcessor.class, - "PAPER_RAW", - ResourceKey.class - ); - } catch (IllegalAccessException | NoSuchFieldException | NoSuchMethodException e) { - throw new ExceptionInInitializerError(e); - } - } -} 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_1SurfPaperNmsCommandArgumentTypesBridgeImpl.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_1SurfPaperNmsCommandArgumentTypesBridgeImpl.kt index 7891582a6..88bc7400f 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_1SurfPaperNmsCommandArgumentTypesBridgeImpl.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_1SurfPaperNmsCommandArgumentTypesBridgeImpl.kt @@ -2,26 +2,16 @@ package dev.slne.surf.api.paper.server.nms.v26_1.bridges import com.mojang.brigadier.arguments.ArgumentType import com.mojang.brigadier.context.CommandContext -import com.mojang.brigadier.exceptions.CommandSyntaxException import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsCommandArgumentTypesBridge import dev.slne.surf.api.paper.server.nms.v26_1.extensions.AdventureNBT -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Deferred -import net.bytebuddy.ByteBuddy -import net.bytebuddy.dynamic.loading.ClassLoadingStrategy -import net.bytebuddy.dynamic.scaffold.TypeValidation -import net.bytebuddy.implementation.InvocationHandlerAdapter -import net.bytebuddy.implementation.bind.annotation.RuntimeType -import net.bytebuddy.matcher.ElementMatchers import net.kyori.adventure.chat.SignedMessage import net.kyori.adventure.nbt.CompoundBinaryTag import net.minecraft.commands.CommandSourceStack import net.minecraft.commands.arguments.CompoundTagArgument import net.minecraft.commands.arguments.MessageArgument -import java.lang.reflect.InvocationHandler -import java.util.concurrent.ConcurrentHashMap @NmsUseWithCaution @Suppress("ClassName") @@ -52,79 +42,4 @@ class V26_1SurfPaperNmsCommandArgumentTypesBridgeImpl : SurfPaperNmsCommandArgum return deferred } - - @Suppress("UNCHECKED_CAST") - private fun wrap( - base: ArgumentType, - converter: OpenedResultConverter - ): ArgumentType { - val wrappedConverter = OpenedResultConverterImpl.of(converter) - val wrapped = V26_1Reflection.VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY.wrap( - V26_1Reflection.VANILLA_ARGUMENT_PROVIDER_PROXY.provider(), - base, - wrappedConverter - ) as ArgumentType - - return wrapped - } - - - fun interface OpenedResultConverter { - @Throws(CommandSyntaxException::class) - fun convert(type: T): R - } - - object OpenedResultConverterImpl { - private val converterCache = ConcurrentHashMap() - - @Suppress("UNCHECKED_CAST") - fun of(converter: OpenedResultConverter): Any { - val cacheKey = "${converter.javaClass.name}_${System.identityHashCode(converter)}" - - return converterCache.computeIfAbsent(cacheKey) { - createConverter(converter) - } - } - - @Suppress("UNCHECKED_CAST") - private fun createConverter(converter: OpenedResultConverter): Any { - val resultConverterInterface = Class.forName( - "io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl\$ResultConverter" - ) - - val handler = InvocationHandler { _, method, args -> - when (method.name) { - "convert" -> converter.convert(args[0] as B) - else -> throw UnsupportedOperationException("Unknown method: ${method.name}") - } - } - - val dynamicType = ByteBuddy() - .with(TypeValidation.DISABLED) - .subclass(Any::class.java) - .implement(resultConverterInterface) - .name("io.papermc.paper.command.brigadier.argument.GeneratedResultConverter\$${System.nanoTime()}") - .method(ElementMatchers.any()) - .intercept(InvocationHandlerAdapter.of(handler)) - .make() - - val loadedClass = dynamicType.load( - resultConverterInterface.classLoader, - ClassLoadingStrategy.Default.INJECTION - ).loaded - - return loadedClass.getDeclaredConstructor().newInstance() - } - - - class InterceptorHolder( - private val converter: OpenedResultConverter - ) { - @RuntimeType - @Throws(Exception::class) - fun convert(@RuntimeType input: B): C { - return converter.convert(input) - } - } - } } 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_1SurfPaperNmsCommonBridgeImpl.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_1SurfPaperNmsCommonBridgeImpl.kt index c00cba923..863053efe 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_1SurfPaperNmsCommonBridgeImpl.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_1SurfPaperNmsCommonBridgeImpl.kt @@ -6,7 +6,7 @@ import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsCommonBridge import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNms import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNmsBlock import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNmsItem -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import io.papermc.paper.configuration.GlobalConfiguration import net.kyori.adventure.text.Component import net.minecraft.network.protocol.common.ClientboundClearDialogPacket @@ -80,10 +80,8 @@ class V26_1SurfPaperNmsCommonBridgeImpl : SurfPaperNmsCommonBridge { } override fun getServerIp(): InetSocketAddress { - val channels = - V26_1Reflection.SERVER_CONNECTION_LISTENER_PROXY.getChannels(MinecraftServer.getServer().connection) - val channel = - channels.firstOrNull() ?: error("No channels found in server connection listener proxy") + val channels = V26_1NmsReflections.getConnectionChannelFutures(MinecraftServer.getServer().connection) + val channel = channels.firstOrNull() ?: error("No channels found in server connection listener proxy") return channel.channel().localAddress() as? InetSocketAddress ?: error("Local address is not an instance of InetSocketAddress") 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_1SurfPaperNmsGlowingBridgeImpl.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_1SurfPaperNmsGlowingBridgeImpl.kt index 904bcbd17..9b66fe7bd 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_1SurfPaperNmsGlowingBridgeImpl.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_1SurfPaperNmsGlowingBridgeImpl.kt @@ -7,7 +7,7 @@ import dev.slne.surf.api.paper.server.nms.v26_1.bridges.packets.V26_1PacketOpera import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNms import dev.slne.surf.api.paper.server.nms.v26_1.glow.V26_1TeamData import dev.slne.surf.api.paper.server.nms.v26_1.packet.listener.V26_1GlowingPacketListener -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket import net.minecraft.network.syncher.SynchedEntityData.DataValue @@ -41,7 +41,7 @@ object V26_1SurfPaperNmsGlowingBridgeImpl : SurfPaperNmsGlowingBridge { fun setEntityFlags(entityId: Int, flags: Byte, ignorePacket: Boolean = false): PacketOperation = V26_1PacketOperationImpl.simple { - val dataAccessor = V26_1Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataAccessor = V26_1NmsReflections.getEntityDataFlagsSharedId() val data = DataValue(dataAccessor.id(), dataAccessor.serializer, flags) ClientboundSetEntityDataPacket(entityId, listOf(data)).also { if (ignorePacket) { @@ -51,7 +51,7 @@ object V26_1SurfPaperNmsGlowingBridgeImpl : SurfPaperNmsGlowingBridge { } override fun getCurrentFlags(entity: Entity): Byte { - val dataAccessor = V26_1Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataAccessor = V26_1NmsReflections.getEntityDataFlagsSharedId() return entity.toNms().entityData.get(dataAccessor) } } 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 3cfcfb6d7..0e54a00bc 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 @@ -11,7 +11,7 @@ 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.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.NmsReflections +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import io.papermc.paper.adventure.PaperAdventure import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -66,7 +66,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { override fun runOnChatMessageChain(player: Player, scope: CoroutineScope, block: suspend () -> Unit) { val nmsPlayer = player.toNms() val connection = nmsPlayer.connection - val chatMessageChain = NmsReflections.getChatMessageChain(connection) + val chatMessageChain = V26_1NmsReflections.getChatMessageChain(connection) val done = CompletableFuture() synchronized(chatMessageChain) { @@ -112,7 +112,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { @Suppress("USELESS_ELVIS") override fun increaseNextChatIndex(player: Player): Int? { val connection = player.toNms().connection ?: return null - return NmsReflections.getAndIncreaseNextChatIndex(connection) + return V26_1NmsReflections.getAndIncreaseNextChatIndex(connection) } override fun createPlayerChatMessageMirrorFromAdventure( @@ -139,7 +139,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val filterMask = when (val mask = nms.filterMask()) { FilterMask.FULLY_FILTERED -> PlayerChatMessageMirror.FilterMask.FULLY_FILTERED FilterMask.PASS_THROUGH -> PlayerChatMessageMirror.FilterMask.PASS_THROUGH - else -> PlayerChatMessageMirror.FilterMask(NmsReflections.getMask(mask)) + else -> PlayerChatMessageMirror.FilterMask(V26_1NmsReflections.getFilterMask(mask)) } return PlayerChatMessageMirror( @@ -163,7 +163,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val filterMask = when (mirror.filterMask) { PlayerChatMessageMirror.FilterMask.FULLY_FILTERED -> FilterMask.FULLY_FILTERED PlayerChatMessageMirror.FilterMask.PASS_THROUGH -> FilterMask.PASS_THROUGH - else -> NmsReflections.createFilterMask(mirror.filterMask.mask) + else -> V26_1NmsReflections.createFilterMask(mirror.filterMask.mask) } return PlayerChatMessage( @@ -181,12 +181,12 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val nmsMessage = message.playerChatMessage() val nmsPlayer = receiver.toNms() val connection = nmsPlayer.connection ?: return - val messageSignatureCache = NmsReflections.getMessageSignatureCache(connection) + val messageSignatureCache = V26_1NmsReflections.getMessageSignatureCache(connection) synchronized(messageSignatureCache) { connection.send( ClientboundPlayerChatPacket( - NmsReflections.getAndIncreaseNextChatIndex(connection), + V26_1NmsReflections.getAndIncreaseNextChatIndex(connection), nmsMessage.link().sender(), nmsMessage.link().index(), nmsMessage.signature(), @@ -200,7 +200,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { val signature = nmsMessage.signature() if (signature != null) { messageSignatureCache.push(nmsMessage.signedBody(), signature) - val lastSeenMessages = NmsReflections.getLastSeenMessages(connection) + val lastSeenMessages = V26_1NmsReflections.getLastSeenMessages(connection) synchronized(lastSeenMessages) { lastSeenMessages.addPending(signature) @@ -210,7 +210,7 @@ class V26_1SurfPaperNmsPlayerBridgeImpl : SurfPaperNmsPlayerBridge { } override fun getPaperRawChatType(): ChatType { - return ChatType.chatType(PaperAdventure.asAdventureKey(NmsReflections.getPaperRaw())) + return ChatType.chatType(PaperAdventure.asAdventureKey(V26_1NmsReflections.getPaperRawChatTypeKey())) } override suspend fun editOfflineInventory( 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_1SurfPaperNmsStatsBridgeImpl.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_1SurfPaperNmsStatsBridgeImpl.kt index c00489e6e..205aeb5db 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_1SurfPaperNmsStatsBridgeImpl.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_1SurfPaperNmsStatsBridgeImpl.kt @@ -3,7 +3,7 @@ package dev.slne.surf.api.paper.server.nms.v26_1.bridges import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.nms.bridges.SurfPaperNmsStatsBridge import dev.slne.surf.api.paper.server.nms.v26_1.extensions.toNms -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import org.bukkit.entity.Player @NmsUseWithCaution @@ -11,8 +11,8 @@ import org.bukkit.entity.Player class V26_1SurfPaperNmsStatsBridgeImpl : SurfPaperNmsStatsBridge { override fun getPlayerStatsAsJson(player: Player): String { - val gson = V26_1Reflection.SERVER_STATS_COUNTER_PROXY.getGson() - val jsonElement = V26_1Reflection.SERVER_STATS_COUNTER_PROXY.toJson(player.toNms().stats) + val gson = V26_1NmsReflections.getServerStatsCounterGson() + val jsonElement = V26_1NmsReflections.convertServerStatsCounterToJson(player.toNms().stats) return gson.toJson(jsonElement) } 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/glow/V26_1SurfGlowingApiImpl.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/glow/V26_1SurfGlowingApiImpl.kt index 4a73bcfa8..2759d39c2 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/glow/V26_1SurfGlowingApiImpl.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/glow/V26_1SurfGlowingApiImpl.kt @@ -12,7 +12,7 @@ import dev.slne.surf.api.paper.server.nms.v26_1.glow.block.BlockGlowingData import dev.slne.surf.api.paper.server.nms.v26_1.glow.block.BlockPlayerData import dev.slne.surf.api.paper.server.nms.v26_1.glow.entity.EntityGlowingData import dev.slne.surf.api.paper.server.nms.v26_1.glow.entity.EntityPlayerData -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import dev.slne.surf.api.paper.util.isChunkVisible import io.papermc.paper.adventure.PaperAdventure import net.kyori.adventure.text.format.NamedTextColor @@ -29,7 +29,7 @@ object V26_1SurfGlowingApiImpl : SurfGlowingApi { private val entityPlayerData = ConcurrentHashMap() private val blockPlayerData = ConcurrentHashMap() - val glowingFlag = 1 shl V26_1Reflection.ENTITY_PROXY.getFlagGlowing() + val glowingFlag = 1 shl V26_1NmsReflections.getEntityFlagGlowing() override fun makeGlowing( target: Entity, 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/glow/block/BlockGlowingData.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/glow/block/BlockGlowingData.kt index 403bf294f..986f7eb73 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/glow/block/BlockGlowingData.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/glow/block/BlockGlowingData.kt @@ -7,7 +7,7 @@ import dev.slne.surf.api.paper.nms.bridges.packets.entity.SurfPaperNmsSpawnPacke import dev.slne.surf.api.paper.server.nms.v26_1.bridges.V26_1SurfPaperNmsGlowingBridgeImpl import dev.slne.surf.api.paper.server.nms.v26_1.bridges.packets.V26_1PacketOperationImpl import dev.slne.surf.api.paper.server.nms.v26_1.glow.V26_1SurfGlowingApiImpl -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import glm_.shl import net.kyori.adventure.text.format.NamedTextColor import net.minecraft.network.protocol.game.ClientboundAddEntityPacket @@ -74,6 +74,6 @@ class BlockGlowingData( } companion object { - val invisibleFlag = 1.toByte() shl V26_1Reflection.ENTITY_PROXY.getFlagInvisible() + val invisibleFlag = 1.toByte() shl V26_1NmsReflections.getEntityFlagInvisible() } } 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/packet/listener/V26_1GlowingPacketListener.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/packet/listener/V26_1GlowingPacketListener.kt index 3e0fafef1..a07a2e416 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/packet/listener/V26_1GlowingPacketListener.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/packet/listener/V26_1GlowingPacketListener.kt @@ -8,7 +8,7 @@ import dev.slne.surf.api.paper.nms.NmsUseWithCaution import dev.slne.surf.api.paper.packet.listener.listener.PacketListener import dev.slne.surf.api.paper.packet.listener.listener.annotation.ClientboundListener import dev.slne.surf.api.paper.server.nms.v26_1.glow.V26_1SurfGlowingApiImpl -import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1Reflection +import dev.slne.surf.api.paper.server.nms.v26_1.reflection.V26_1NmsReflections import glm_.or import net.minecraft.network.protocol.Packet import net.minecraft.network.protocol.game.ClientboundBundlePacket @@ -67,7 +67,7 @@ object V26_1GlowingPacketListener : PacketListener { var flagsFound = false var edited = false val newItems = mutableObjectListOf>(incoming.size + 1) - val dataFlagsShared = V26_1Reflection.ENTITY_PROXY.getDataFlagsSharedId() + val dataFlagsShared = V26_1NmsReflections.getEntityDataFlagsSharedId() val dataFlagsSharedId = dataFlagsShared.id for (dataValue in incoming) { 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_1EntityProxy.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_1EntityProxy.kt deleted file mode 100644 index d71202c80..000000000 --- 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_1EntityProxy.kt +++ /dev/null @@ -1,24 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection - -import net.minecraft.network.syncher.EntityDataAccessor -import net.minecraft.world.entity.Entity -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies -import xyz.jpenilla.reflectionremapper.proxy.annotation.Static - -@Proxies(Entity::class) -@Suppress("ClassName") -interface V26_1EntityProxy { - - @FieldGetter("FLAG_GLOWING") - @Static - fun getFlagGlowing(): Int - - @FieldGetter("FLAG_INVISIBLE") - @Static - fun getFlagInvisible(): Int - - @FieldGetter("DATA_SHARED_FLAGS_ID") - @Static - fun getDataFlagsSharedId(): EntityDataAccessor -} 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 new file mode 100644 index 000000000..2a2539d6f --- /dev/null +++ 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 @@ -0,0 +1,89 @@ +package dev.slne.surf.api.paper.server.nms.v26_1.reflection + +import com.google.gson.Gson +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.syncher.EntityDataAccessor +import net.minecraft.resources.ResourceKey +import net.minecraft.server.network.ServerConnectionListener +import net.minecraft.server.network.ServerGamePacketListenerImpl +import net.minecraft.stats.ServerStatsCounter +import net.minecraft.util.FutureChain +import net.minecraft.world.entity.Entity +import java.lang.invoke.VarHandle +import java.util.* + +@Suppress("ClassName") +@GenerateReflection +interface V26_1NmsReflections { + @ReflectedField("chatMessageChain") + fun getChatMessageChain(instance: ServerGamePacketListenerImpl): FutureChain + + @ReflectedVarHandle( + name = "nextChatIndex", + mode = VarHandle.AccessMode.GET_AND_ADD, + ) + @ConstantIntArgument(1) + fun getAndIncreaseNextChatIndex(instance: ServerGamePacketListenerImpl): Int + + @ReflectedField("messageSignatureCache") + fun getMessageSignatureCache(instance: ServerGamePacketListenerImpl): MessageSignatureCache + + @ReflectedField(name = "lastSeenMessages") + fun getLastSeenMessages(instance: ServerGamePacketListenerImpl): LastSeenMessagesValidator + + @ReflectedConstructor + fun createFilterMask(bitSet: BitSet): FilterMask + + @ReflectedMethod("mask") + fun getFilterMask(instance: FilterMask): BitSet + + @ReflectedField( + name = "PAPER_RAW", + isStatic = true, + target = ChatProcessor::class + ) + fun getPaperRawChatTypeKey(): ResourceKey + + @ReflectedField( + name = "FLAG_GLOWING", + isStatic = true, + target = Entity::class + ) + fun getEntityFlagGlowing(): Int + + @ReflectedField( + name = "FLAG_INVISIBLE", + isStatic = true, + target = Entity::class + ) + fun getEntityFlagInvisible(): Int + + @ReflectedField( + name = "DATA_SHARED_FLAGS_ID", + isStatic = true, + target = Entity::class + ) + fun getEntityDataFlagsSharedId(): EntityDataAccessor + + @ReflectedField("channels") + fun getConnectionChannelFutures(instance: ServerConnectionListener): List + + @ReflectedMethod("toJson") + fun convertServerStatsCounterToJson(statsCounter: ServerStatsCounter): JsonElement + + @ReflectedField( + name = "GSON", + isStatic = true, + target = ServerStatsCounter::class + ) + fun getServerStatsCounterGson(): Gson + + companion object : V26_1NmsReflections 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/reflection/V26_1Reflection.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_1Reflection.kt index f1580f600..4cfe16132 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_1Reflection.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_1Reflection.kt @@ -1,39 +1,16 @@ package dev.slne.surf.api.paper.server.nms.v26_1.reflection -import dev.slne.surf.api.core.reflection.SurfReflection -import dev.slne.surf.api.core.reflection.createProxy -import dev.slne.surf.api.paper.util.reflectionProxy -import xyz.jpenilla.reflectionremapper.ReflectionRemapper -import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory - @Suppress("ClassName") object V26_1Reflection { - lateinit var SERVER_STATS_COUNTER_PROXY: V26_1ServerStatsCounterProxy - private set - lateinit var ENTITY_PROXY: V26_1EntityProxy - private set - lateinit var SERVER_CONNECTION_LISTENER_PROXY: V26_1ServerConnectionListenerProxy - private set - lateinit var VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY: V26_1VanillaArgumentProviderImplProxy - private set - lateinit var VANILLA_ARGUMENT_PROVIDER_PROXY: V26_1VanillaArgumentProviderProxy - private set fun initialize() { + /* val remapper = ReflectionRemapper.forReobfMappingsInPaperJar() val proxyFactory = ReflectionProxyFactory.create(remapper, V26_1Reflection::class.java.classLoader) - SERVER_STATS_COUNTER_PROXY = proxyFactory.reflectionProxy() - ENTITY_PROXY = proxyFactory.reflectionProxy() - SERVER_CONNECTION_LISTENER_PROXY = - proxyFactory.reflectionProxy() - VANILLA_ARGUMENT_PROVIDER_IMPL_PROXY = - SurfReflection.createProxy() - VANILLA_ARGUMENT_PROVIDER_PROXY = - SurfReflection.createProxy() - // gc the remapper System.gc() + */ } } 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_1ServerConnectionListenerProxy.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_1ServerConnectionListenerProxy.kt deleted file mode 100644 index a2ab7a92f..000000000 --- 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_1ServerConnectionListenerProxy.kt +++ /dev/null @@ -1,13 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection - -import io.netty.channel.ChannelFuture -import net.minecraft.server.network.ServerConnectionListener -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies - -@Proxies(ServerConnectionListener::class) -@Suppress("ClassName") -interface V26_1ServerConnectionListenerProxy { - @FieldGetter("channels") - fun getChannels(instance: ServerConnectionListener): List -} 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_1ServerStatsCounterProxy.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_1ServerStatsCounterProxy.kt deleted file mode 100644 index 28f64682a..000000000 --- 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_1ServerStatsCounterProxy.kt +++ /dev/null @@ -1,21 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection - -import com.google.gson.Gson -import com.google.gson.JsonElement -import net.minecraft.stats.ServerStatsCounter -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter -import xyz.jpenilla.reflectionremapper.proxy.annotation.MethodName -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies -import xyz.jpenilla.reflectionremapper.proxy.annotation.Static - -@Proxies(ServerStatsCounter::class) -@Suppress("ClassName") -interface V26_1ServerStatsCounterProxy { - - @MethodName("toJson") - fun toJson(statsCounter: ServerStatsCounter): JsonElement - - @FieldGetter("GSON") - @Static - fun getGson(): Gson -} 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_1VanillaArgumentProviderImplProxy.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_1VanillaArgumentProviderImplProxy.kt deleted file mode 100644 index 802b7f9f8..000000000 --- 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_1VanillaArgumentProviderImplProxy.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection - -import com.mojang.brigadier.arguments.ArgumentType -import com.mojang.brigadier.exceptions.CommandSyntaxException -import dev.slne.surf.api.core.reflection.Name -import dev.slne.surf.api.core.reflection.SurfProxy -import io.papermc.paper.command.brigadier.argument.VanillaArgumentProviderImpl - -@SurfProxy(VanillaArgumentProviderImpl::class) -@Suppress("ClassName") -interface V26_1VanillaArgumentProviderImplProxy { - - @Name("wrap") - @Throws(CommandSyntaxException::class) - fun wrap(instance: Any, base: Any, converter: Any): ArgumentType<*> -} 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_1VanillaArgumentProviderProxy.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_1VanillaArgumentProviderProxy.kt deleted file mode 100644 index 2a7ed7d0e..000000000 --- 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_1VanillaArgumentProviderProxy.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.slne.surf.api.paper.server.nms.v26_1.reflection - -import dev.slne.surf.api.core.reflection.Name -import dev.slne.surf.api.core.reflection.Static -import dev.slne.surf.api.core.reflection.SurfProxy - -@SurfProxy(qualifiedName = "io.papermc.paper.command.brigadier.argument.VanillaArgumentProvider") -@Suppress("ClassName") -interface V26_1VanillaArgumentProviderProxy { - - @Static - @Name("provider") - fun provider(): Any -} From 557d4503a762146a39216b69d514ba84ee0a141d Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 20:16:33 +0200 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=94=A7=20chore(abi):=20update=20api?= =?UTF-8?q?=20dump?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/surf-api-shared-public.api | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/surf-api-shared/surf-api-shared-public/api/surf-api-shared-public.api b/surf-api-shared/surf-api-shared-public/api/surf-api-shared-public.api index 0b781ac49..201b8c217 100644 --- a/surf-api-shared/surf-api-shared-public/api/surf-api-shared-public.api +++ b/surf-api-shared/surf-api-shared-public/api/surf-api-shared-public.api @@ -178,6 +178,115 @@ public abstract interface annotation class dev/slne/surf/api/shared/api/componen public abstract interface annotation class dev/slne/surf/api/shared/api/component/types/Service : java/lang/annotation/Annotation { } +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantBooleanArgument : java/lang/annotation/Annotation { + public abstract fun value ()Z +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantBooleanArgument$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Ldev/slne/surf/api/shared/api/reflection/ConstantBooleanArgument; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantIntArgument : java/lang/annotation/Annotation { + public abstract fun value ()I +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantIntArgument$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Ldev/slne/surf/api/shared/api/reflection/ConstantIntArgument; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantLongArgument : java/lang/annotation/Annotation { + public abstract fun value ()J +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantLongArgument$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Ldev/slne/surf/api/shared/api/reflection/ConstantLongArgument; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantStringArgument : java/lang/annotation/Annotation { + public abstract fun value ()Ljava/lang/String; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ConstantStringArgument$Container : java/lang/annotation/Annotation { + public abstract fun value ()[Ldev/slne/surf/api/shared/api/reflection/ConstantStringArgument; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/GenerateReflection : java/lang/annotation/Annotation { + public abstract fun className ()Ljava/lang/String; + public abstract fun packageName ()Ljava/lang/String; + public abstract fun singleton ()Z + public abstract fun target ()Ljava/lang/Class; + public abstract fun targetName ()Ljava/lang/String; +} + +public final class dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessorKt { + public static final fun generatedReflectionAccessor (Ljava/lang/Class;)Ljava/lang/Object; + public static final fun generatedReflectionAccessor (Lkotlin/reflect/KClass;)Ljava/lang/Object; +} + +public final class dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessors : java/lang/ClassValue { + public static final field INSTANCE Ldev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessors; + public final fun getTyped (Ljava/lang/Class;)Ljava/lang/Object; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ReflectedConstructor : java/lang/annotation/Annotation { + public abstract fun descriptor ()Ljava/lang/String; + public abstract fun invocation ()Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public abstract fun target ()Ljava/lang/Class; + public abstract fun targetName ()Ljava/lang/String; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ReflectedField : java/lang/annotation/Annotation { + public abstract fun access ()Ldev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess; + public abstract fun isStatic ()Z + public abstract fun name ()Ljava/lang/String; + public abstract fun target ()Ljava/lang/Class; + public abstract fun targetName ()Ljava/lang/String; + public abstract fun type ()Ljava/lang/Class; + public abstract fun typeDescriptor ()Ljava/lang/String; + public abstract fun typeName ()Ljava/lang/String; +} + +public final class dev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess : java/lang/Enum { + public static final field GET Ldev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess; + public static final field SET Ldev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess; + public static fun values ()[Ldev/slne/surf/api/shared/api/reflection/ReflectedFieldAccess; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ReflectedMethod : java/lang/annotation/Annotation { + public abstract fun descriptor ()Ljava/lang/String; + public abstract fun invocation ()Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public abstract fun isStatic ()Z + public abstract fun name ()Ljava/lang/String; + public abstract fun target ()Ljava/lang/Class; + public abstract fun targetName ()Ljava/lang/String; +} + +public abstract interface annotation class dev/slne/surf/api/shared/api/reflection/ReflectedVarHandle : java/lang/annotation/Annotation { + public abstract fun invocation ()Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public abstract fun isStatic ()Z + public abstract fun mode ()Ljava/lang/invoke/VarHandle$AccessMode; + public abstract fun name ()Ljava/lang/String; + public abstract fun target ()Ljava/lang/Class; + public abstract fun targetName ()Ljava/lang/String; + public abstract fun type ()Ljava/lang/Class; + public abstract fun typeDescriptor ()Ljava/lang/String; + public abstract fun typeName ()Ljava/lang/String; +} + +public final class dev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode : java/lang/Enum { + public static final field AS_TYPE_EXACT Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public static final field EXACT Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public static final field INVOKE Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; + public static fun values ()[Ldev/slne/surf/api/shared/api/reflection/ReflectionInvocationMode; +} + +public final class dev/slne/surf/api/shared/api/reflection/UnspecifiedReflectionTarget { +} + public abstract interface annotation class dev/slne/surf/api/shared/api/util/InternalInvokerApi : java/lang/annotation/Annotation { } From d5c358196a3d36a4ec0854ab2042ebc122df4e0c Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 20:16:42 +0200 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=94=A7=20chore:=20update=20version=20?= =?UTF-8?q?to=203.13.0=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 f1e646c9f..f96891f30 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.12.0 +version=3.13.0 relocationPrefix=dev.slne.surf.api.libs snapshot=false From d6756de4a71c01e34b05e4538ad3f55ac2374c4a Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 21:12:30 +0200 Subject: [PATCH 5/6] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(reflection):?= =?UTF-8?q?=20simplify=20constant=20argument=20handling=20in=20ReflectionS?= =?UTF-8?q?ymbolProcessor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace multiple filter and forEach calls with a single for loop and when expression - improve readability and maintainability of constant argument processing logic --- .../reflection/ReflectionSymbolProcessor.kt | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) 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 07f7329cd..186805631 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 @@ -13,9 +13,14 @@ import com.palantir.javapoet.TypeName import com.palantir.javapoet.WildcardTypeName import dev.slne.surf.api.processor.ClassNames import dev.slne.surf.api.processor.ShortClassNames +import dev.slne.surf.api.processor.ShortClassNames.CONSTANT_BOOLEAN_ARGUMENT +import dev.slne.surf.api.processor.ShortClassNames.CONSTANT_INT_ARGUMENT +import dev.slne.surf.api.processor.ShortClassNames.CONSTANT_LONG_ARGUMENT +import dev.slne.surf.api.processor.ShortClassNames.CONSTANT_STRING_ARGUMENT import dev.slne.surf.api.processor.reflection.emitter.ReflectionJavaEmitter import dev.slne.surf.api.processor.reflection.exception.ReflectionProcessorException import dev.slne.surf.api.processor.reflection.model.* +import dev.slne.surf.api.processor.reflection.model.ConstantArgument.* import dev.slne.surf.api.processor.util.* import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.name.FqNameUnsafe @@ -342,21 +347,14 @@ class ReflectionSymbolProcessor(environment: SymbolProcessorEnvironment) : Symbo private fun KSFunctionDeclaration.constantArguments(): List { val result = mutableListOf() - annotations - .filter { it.shortName.asString() == ShortClassNames.CONSTANT_INT_ARGUMENT } - .forEach { result += ConstantArgument.IntValue(it.intArg("value", 0)) } - - annotations - .filter { it.shortName.asString() == ShortClassNames.CONSTANT_LONG_ARGUMENT } - .forEach { result += ConstantArgument.LongValue(it.longArg("value", 0L)) } - - annotations - .filter { it.shortName.asString() == ShortClassNames.CONSTANT_BOOLEAN_ARGUMENT } - .forEach { result += ConstantArgument.BooleanValue(it.booleanArg("value", false)) } - - annotations - .filter { it.shortName.asString() == ShortClassNames.CONSTANT_STRING_ARGUMENT } - .forEach { result += ConstantArgument.StringValue(it.stringArg("value")) } + for (annotation in annotations) { + when (annotation.shortName.asString()) { + CONSTANT_INT_ARGUMENT -> result += IntValue(annotation.intArg("value", 0)) + CONSTANT_LONG_ARGUMENT -> result += LongValue(annotation.longArg("value", 0L)) + CONSTANT_BOOLEAN_ARGUMENT -> result += BooleanValue(annotation.booleanArg("value", false)) + CONSTANT_STRING_ARGUMENT -> result += StringValue(annotation.stringArg("value")) + } + } return result } From b361948fa662e86698d875de0cf953968bf334ee Mon Sep 17 00:00:00 2001 From: twisti <76837088+twisti-dev@users.noreply.github.com> Date: Fri, 22 May 2026 21:13:36 +0200 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=93=9D=20docs(reflection):=20fix=20co?= =?UTF-8?q?mment=20formatting=20in=20GeneratedReflectionAccessor.kt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/shared/api/reflection/GeneratedReflectionAccessor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt index b91e6e5a1..a69f65703 100644 --- a/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt +++ b/surf-api-shared/surf-api-shared-public/src/main/kotlin/dev/slne/surf/api/shared/api/reflection/GeneratedReflectionAccessor.kt @@ -8,7 +8,7 @@ import kotlin.reflect.KClass * Returns the generated reflection implementation for [T]. * * This function is intended for companion-object delegation inside interfaces annotated with - * [GenerateReflection]: * + * [GenerateReflection]: * ```kotlin * @GenerateReflection * interface ExampleReflection {