Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
14cf430
Add V26_1NmsProvider implementation for Minecraft 26.1
Copilot Apr 13, 2026
66a034d
Add V26_1 NMS implementation files for multi-version architecture
Copilot Apr 13, 2026
064a344
feat: add multiversion NMS support architecture for MC 1.21.11 + 26.1
Copilot Apr 13, 2026
c9e05f7
feat: wire multiversion NMS support through server module
Copilot Apr 13, 2026
30bd574
feat: remove @AutoService from old impls, fix proxy delegation conflicts
Copilot Apr 13, 2026
c7e6336
feat: implement 1.21.11 NMS module and add NMS template generator
Copilot Apr 14, 2026
420c9ad
feat: add multiversion support for NMS 1.21.11 and 26.1, update depen…
TheBjoRedCraft Apr 14, 2026
64eb38b
feat: enhance logging for NmsProvider and NmsVersion detection
TheBjoRedCraft Apr 14, 2026
dce266e
fix: add manual META-INF/services files for NmsProvider ServiceLoader…
Copilot Apr 14, 2026
efb23fd
feat: enhance logging for NmsProvider and NmsVersion detection
TheBjoRedCraft Apr 14, 2026
a64dde7
feat: add multiversion support for NMS 1.21.11 and 26.1, update NmsPr…
TheBjoRedCraft Apr 14, 2026
7d44ce9
feat: exclude Kotlin dependencies from shadowJar configuration
TheBjoRedCraft Apr 14, 2026
995d583
feat: remove Kotlin dependencies exclusion from shadowJar configuration
TheBjoRedCraft Apr 14, 2026
4684673
fix: update NmsProvider to correctly load multiple providers from Ser…
TheBjoRedCraft Apr 14, 2026
ddd2a48
fix: resolve ClassCastException in NMS INSTANCE companions and remove…
Copilot Apr 14, 2026
6771b66
fix: ensure glowing bridge INSTANCE is initialized before glowing API…
Copilot Apr 15, 2026
9ff57de
♻️ refactor(nms): remove unused file transformers for NMS version han…
twisti-dev Apr 15, 2026
72e23a3
✨ feat(nms): add multiversion support with NMS module generator
twisti-dev Apr 15, 2026
36baa59
✨ feat(nms): unify BlockGlowingData class for multiple NMS versions
twisti-dev Apr 15, 2026
84a22af
✨ feat(nms): enhance NMS support with new internal bridge and refacto…
twisti-dev Apr 15, 2026
dac546d
feat: add LuckPerms integration for player permissions handling
TheBjoRedCraft Apr 15, 2026
5cf0fd8
feat: add getPrefixedName method for Player to include LuckPerms prefix
TheBjoRedCraft Apr 15, 2026
8cd4aca
chore: remove gitlab-ci.yml
TheBjoRedCraft Apr 15, 2026
857ccdd
✨ feat(nms): add multiversion support with new channel injector and p…
twisti-dev Apr 15, 2026
bf0dfd3
✨ feat(nms): implement LibLoaderBridge for multiversion support
twisti-dev Apr 15, 2026
a3f2c3f
✨ feat(nms): add NMS provider support for multiple versions
twisti-dev Apr 16, 2026
1383bfa
✨ feat(nms): update NmsBridge implementation for multiversion support
twisti-dev Apr 16, 2026
a7d1c1f
✨ feat(nms): update SurfPaperNmsBridgeProxy to use InternalNmsBridge
twisti-dev Apr 16, 2026
55e7a47
✨ feat(nms): update packet listener API to use InternalPacketListener…
twisti-dev Apr 16, 2026
5c15de7
chore: update ABI dumps for new LuckPerms and utility APIs
Copilot Apr 18, 2026
ccc16eb
Update surf-api-core/surf-api-core/src/main/kotlin/dev/slne/surf/api/…
twisti-dev Apr 18, 2026
e353f92
✨ feat(nms): call provider.initialize before creating packet listeners
twisti-dev Apr 18, 2026
5782ae9
✨ feat(nms): move provider.shutdown to after listener unregistration …
twisti-dev Apr 18, 2026
584ad65
✨ feat(luckperms): update luckperms property to use getter with direc…
twisti-dev Apr 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 0 additions & 36 deletions .gitlab-ci.yml

This file was deleted.

10 changes: 9 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import dev.slne.surf.api.generator.nms.NmsVersion
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmExtension

plugins {
id("io.papermc.paperweight.userdev") version "2.0.0-beta.20" apply false
id("io.papermc.paperweight.userdev") version "2.0.0-beta.21" apply false
id("dev.slne.surf.api.generator.nms-module-generator")
}

nmsGenerator {
referenceVersion = NmsVersion.V1_21_11
targetVersion = NmsVersion.V26_1
}


allprojects {
repositories {
mavenCentral()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled
javaVersion=25
mcVersion=26.1.1
group=dev.slne.surf.api
version=3.0.10
version=3.1.0
relocationPrefix=dev.slne.surf.api.libs
snapshot=false
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ packetevents-plugin = "2.11.2"
commandapi = "11.2.0"

# LuckPerms
luckperms = "v5.5.0-bukkit"
luckperms = "5.4"

# Scoreboard Library
scoreboard-library = "2.7.3"
Expand Down Expand Up @@ -151,6 +151,7 @@ flogger-slf4j-backend = { module = "com.google.flogger:flogger-slf4j-backend", v
aide-reflection = { module = "tech.hiddenproject:aide-reflection", version.ref = "aide-reflection" }
glm = { module = "io.github.kotlin-graphics:glm", version.ref = "glm" }
datafixerupper = { module = "com.mojang:datafixerupper", version.ref = "datafixerupper" }
luckperms = { module = "net.luckperms:api", version.ref = "luckperms" }

auto-service-annotations = { module = "com.google.auto.service:auto-service-annotations", version.ref = "auto-service" }
auto-service = { module = "dev.zacsweers.autoservice:auto-service-ksp", version.ref = "auto-service-ksp" }
Expand Down
4 changes: 4 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pluginManagement {
mavenLocal()
gradlePluginPortal()
}
includeBuild("surf-api-generator/gradle-nms-module-generator")
}

plugins {
Expand All @@ -17,6 +18,9 @@ include(":surf-api-core:surf-api-core")
include(":surf-api-core:surf-api-core-server")

include(":surf-api-paper:surf-api-paper")
include(":surf-api-paper:surf-api-paper-nms:surf-api-paper-nms-common")
include(":surf-api-paper:surf-api-paper-nms:surf-api-paper-nms-v1-21-11")
include(":surf-api-paper:surf-api-paper-nms:surf-api-paper-nms-v26-1")
include(":surf-api-paper:surf-api-paper-server")

include(":surf-api-velocity:surf-api-velocity")
Expand Down
15 changes: 15 additions & 0 deletions surf-api-core/surf-api-core/api/surf-api-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -6628,6 +6628,21 @@ public final class dev/slne/surf/api/core/invoker/SuspendInvokerSupport {
public static synthetic fun invokeSuspendDirect$default (Ljava/lang/invoke/MethodHandle;Ljava/lang/Object;Lkotlin/coroutines/CoroutineContext;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class dev/slne/surf/api/core/luckperms/LuckPermsAccess {
public static final field INSTANCE Ldev/slne/surf/api/core/luckperms/LuckPermsAccess;
public final fun getLuckperms ()Lnet/luckperms/api/LuckPerms;
public final fun getUser (Ljava/util/UUID;)Lnet/luckperms/api/model/user/User;
public final fun loadUser (Ljava/util/UUID;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class dev/slne/surf/api/core/luckperms/LuckPermsAccessKt {
public static final fun getLuckPermsUser (Lnet/kyori/adventure/audience/Audience;)Lnet/luckperms/api/model/user/User;
public static final fun getLuckPermsUserOrNull (Lnet/kyori/adventure/audience/Audience;)Lnet/luckperms/api/model/user/User;
public static final fun getPrefix (Lnet/luckperms/api/model/user/User;)Ljava/lang/String;
public static final fun getSuffix (Lnet/luckperms/api/model/user/User;)Ljava/lang/String;
public static final fun getWeight (Lnet/luckperms/api/model/user/User;)Ljava/lang/Object;
}

public final class dev/slne/surf/api/core/math/VoxelLineTracer {
public static final field INSTANCE Ldev/slne/surf/api/core/math/VoxelLineTracer;
public final fun trace (Lorg/spongepowered/math/vector/Vector3d;Lorg/spongepowered/math/vector/Vector3d;)Lkotlin/sequences/Sequence;
Expand Down
1 change: 1 addition & 0 deletions surf-api-core/surf-api-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ plugins {
dependencies {
api(projects.surfApiShared.surfApiSharedPublic)
api(libs.adventure.nbt)
compileOnlyApi(libs.luckperms)
compileOnlyApi(libs.packetevents.api)
compileOnlyApi(libs.dazzleconf)
compileOnlyApi(libs.spongepowered.math)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.slne.surf.api.core.luckperms

import dev.slne.surf.api.core.messages.adventure.uuidOrNull
import kotlinx.coroutines.future.await
import net.kyori.adventure.audience.Audience
import net.luckperms.api.LuckPermsProvider
import net.luckperms.api.model.user.User
import net.luckperms.api.node.NodeType
import java.util.*

object LuckPermsAccess {
val luckperms get() = LuckPermsProvider.get()

fun getUser(uuid: UUID) = luckperms.userManager.getUser(uuid)
suspend fun loadUser(uuid: UUID): User = luckperms.userManager.loadUser(uuid).await()
}

val User.prefix: String
get() = this.cachedData.metaData.prefix ?: ""
val User.suffix: String
get() = this.cachedData.metaData.suffix ?: ""

val User.weight
get() = LuckPermsAccess.luckperms.groupManager.getGroup(this.primaryGroup)?.weight ?: 0

inline fun <reified T : Any> User.getMeta(key: String): T? {
val value = this.resolveInheritedNodes(NodeType.META, this.queryOptions)
.find { it.metaKey == key }
?.metaValue

return value as? T
}

inline fun <reified T : Any> User.getMeta(key: String, default: T): T {
val value = this.resolveInheritedNodes(NodeType.META, this.queryOptions)
.find { it.metaKey == key }
?.metaValue

return value as? T ?: default
}


fun Audience.getLuckPermsUser(): User = this.getLuckPermsUserOrNull()
?: error("Audience does not have a valid UUID or LuckPerms user could not be found.")

fun Audience.getLuckPermsUserOrNull(): User? = this.uuidOrNull()?.let { LuckPermsAccess.getUser(it) }
2 changes: 2 additions & 0 deletions surf-api-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ tasks.register<JavaExec>("generate") {
}




private fun String.downloadTo(output: Path) {
uri(this).toURL().openStream()
.use { Files.copy(it, output, StandardCopyOption.REPLACE_EXISTING) }
Expand Down
16 changes: 16 additions & 0 deletions surf-api-generator/gradle-nms-module-generator/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
`kotlin-dsl`
}

repositories {
gradlePluginPortal()
}

gradlePlugin {
plugins {
create("nms-module-generator") {
id = "dev.slne.surf.api.generator.nms-module-generator"
implementationClass = "dev.slne.surf.api.generator.nms.GenerateNmsModulePlugin"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
rootProject.name = "gradle-nms-module-generator"

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dev.slne.surf.api.generator.nms

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.register

/**
* Gradle plugin that registers the `nmsGenerator` extension and the
* `generateNmsModule` task.
*
* Apply the plugin and configure it in `build.gradle.kts`:
*
* ```kotlin
* plugins {
* id("dev.slne.surf.api.generator.nms-module-generator")
* }
*
* nmsGenerator {
* referenceVersion = NmsVersion.V26_1
* targetVersion = NmsVersion.V1_21_11
*
* transformations {
* renameClass("net.minecraft.resources.Identifier",
* "net.minecraft.resources.ResourceLocation")
* removeImport("net.minecraft.world.item.component.TypedEntityData")
* replaceCode("TypedEntityData.decodeEntity(nbt)", "CustomData.of(nbt)")
* }
* }
* ```
*
* Then run:
* ```
* ./gradlew generateNmsModule # interactive — asks before overwriting
* ./gradlew generateNmsModule -PforceOverwrite # non-interactive
* ```
*/
abstract class GenerateNmsModulePlugin : Plugin<Project> {
override fun apply(target: Project) {
target.extensions.create<NmsGeneratorExtension>("nmsGenerator")
target.tasks.register<GenerateNmsModuleTask>("generateNmsModule")
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package dev.slne.surf.api.generator.nms

import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.TaskAction
import java.nio.file.Path
import kotlin.io.path.exists

/**
* Generates a single NMS module from the configured reference version.
*
* If the target module already contains sources the task asks for
* confirmation before overwriting. Pass `-PforceOverwrite` to skip
* the prompt (useful in scripts or CI).
*/
abstract class GenerateNmsModuleTask : DefaultTask() {

init {
group = "generation"
description = "Generates an NMS module from the reference version sources"
}

@TaskAction
fun generate() {
val ext = project.extensions.getByType(NmsGeneratorExtension::class.java)

check(ext.isConfigured) {
"nmsGenerator.referenceVersion and nmsGenerator.targetVersion must be set"
}

val reference = ext.referenceVersion
val target = ext.targetVersion
val repoRoot = project.rootProject.projectDir.toPath()

confirmOverwriteIfNeeded(repoRoot, target)

logger.lifecycle("Generating NMS module: {} -> {} ...", reference.name, target.name)

val generated = NmsModuleGenerator(
reference = reference,
target = target,
transformations = ext.transformationScope.transformations,
repoRoot = repoRoot,
logger = logger,
).generate()

logger.lifecycle("")
logger.lifecycle("------------------------------------------")
logger.lifecycle("Generation complete - {} file(s) written.", generated)
logger.lifecycle("")
logger.lifecycle("Next steps:")
logger.lifecycle(" 1. Add include(\"{}\") to settings.gradle.kts (if not present).", target.gradlePath)
logger.lifecycle(" 2. Register {} in the runtime NmsVersion enum.", target.name)
logger.lifecycle(" 3. Review the generated sources and fix remaining compilation errors.")
logger.lifecycle("------------------------------------------")
}

// ------------------------------------------------------------------ //

private fun confirmOverwriteIfNeeded(repoRoot: Path, target: NmsVersion) {
val targetSources = repoRoot
.resolve(target.modulePath)
.resolve("src/main/kotlin")

if (!targetSources.exists()) return

// Allow skipping the prompt via project property
if (project.hasProperty("forceOverwrite")) return

val console = System.console() ?: throw GradleException(
"Target module ${target.name} already exists. " +
"Pass -PforceOverwrite to overwrite."
)

console.printf(
"%nTarget module '%s' already has sources at:%n %s%n",
target.name,
targetSources.toAbsolutePath(),
)
val answer = console.readLine("Overwrite? [y/N] ")?.trim()?.lowercase()

if (answer != "y" && answer != "yes") {
throw GradleException("Aborted by user.")
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package dev.slne.surf.api.generator.nms

/** Scope marker that prevents leaking receivers across nested DSL blocks. */
@DslMarker
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class NmsGeneratorDsl

Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package dev.slne.surf.api.generator.nms

/**
* Gradle extension for the `nms-module-generator` plugin.
*
* ```kotlin
* nmsGenerator {
* referenceVersion = NmsVersion.V26_1
* targetVersion = NmsVersion.V1_21_11
*
* transformations {
* renameClass("net.minecraft.resources.Identifier",
* "net.minecraft.resources.ResourceLocation")
* }
* }
* ```
*
* @see GenerateNmsModulePlugin
*/
@NmsGeneratorDsl
open class NmsGeneratorExtension {

/** The NMS version whose sources serve as the generation template. */
lateinit var referenceVersion: NmsVersion

/** The NMS version to generate. */
lateinit var targetVersion: NmsVersion

internal var transformationScope: TransformationScope = TransformationScope()
private set

/** `true` if both [referenceVersion] and [targetVersion] have been set. */
internal val isConfigured: Boolean
get() = ::referenceVersion.isInitialized && ::targetVersion.isInitialized

/** Configures the source-code transformations applied during generation. */
fun transformations(block: TransformationScope.() -> Unit) {
transformationScope = TransformationScope().apply(block)
}
}
Loading