Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions .github/workflows/api-dump-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
id: check_api
run: |
set +e
./gradlew checkLegacyAbi
./gradlew checkKotlinAbi
CHECK_EXIT=$?
echo "check_exit=$CHECK_EXIT" >> $GITHUB_OUTPUT
set -e
Expand All @@ -46,7 +46,7 @@ jobs:
echo "✅ No API changes detected."
else
echo "api_changed=true" >> $GITHUB_OUTPUT
echo "❌ API changes detected! To update the reference, run './gradlew updateLegacyAbi' locally and commit the changes." >&2
echo "❌ API changes detected! To update the reference, run './gradlew updateKotlinAbi' locally and commit the changes." >&2
fi

- name: Comment on PR if API changed
Expand All @@ -61,7 +61,7 @@ jobs:
This PR contains changes that modified the public API. To update the reference ABI dumps:

```bash
./gradlew updateLegacyAbi
./gradlew updateKotlinAbi
git add **/api/**
git commit -m "Update ABI reference"
git push
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: ./gradlew build shadowJar --parallel --no-scan

- name: Check all modules with Gradle
run: ./gradlew check checkLegacyAbi --parallel --no-scan
run: ./gradlew check checkKotlinAbi --parallel --no-scan

- name: Publish all modules to Maven
run: ./gradlew publish --parallel --no-scan
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=1.21.11
group=dev.slne.surf
version=1.21.11-2.71.2
version=1.21.11-2.72.0
relocationPrefix=dev.slne.surf.surfapi.libs
snapshot=false
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dev.slne.surf.surfapi.bukkit.test.command.subcommands.inventory.TestInven
import dev.slne.surf.surfapi.bukkit.test.command.subcommands.inventory.testInventoryViewDsl
import dev.slne.surf.surfapi.bukkit.test.command.subcommands.reflection.Reflection
import dev.slne.surf.surfapi.bukkit.test.config.ModernTestConfig
import dev.slne.surf.surfapi.bukkit.test.config.MyPluginConfig
import dev.slne.surf.surfapi.bukkit.test.listener.ChatListener
import dev.slne.surf.surfapi.core.api.component.surfComponentApi

Expand All @@ -31,6 +32,8 @@ class BukkitPluginMain : SuspendingJavaPlugin() {
dialogTestCommand()
Reflection::class.java.getClassLoader() // initialize Reflection

MyPluginConfig.init()

surfComponentApi.enable(this)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dev.slne.surf.surfapi.bukkit.test.config

import dev.slne.surf.surfapi.bukkit.test.plugin
import dev.slne.surf.surfapi.core.api.config.SpongeYmlConfigClass
import dev.slne.surf.surfapi.core.api.config.migration.ConfigMigration
import org.spongepowered.configurate.ConfigurationNode
import org.spongepowered.configurate.objectmapping.ConfigSerializable

/**
* Example config class with migrations.
* Before migration:
* ```yaml
* server:
* version: "1-20-4"
* deprecated-field: "please-remove-me"
* max-players: 0
* ```
*/
@ConfigSerializable
data class MyPluginConfig(
var release: String = "1.0.0",
var maxPlayers: Int = 100
) {
companion object : SpongeYmlConfigClass<MyPluginConfig>(
MyPluginConfig::class.java,
plugin.dataPath,
"migration-example-config.yml"
) {
init {
migration(1, RenameServerVersionMigration)
migration(2, RemoveDeprecatedFieldMigration)
migration(3) { node ->
// inline migration: rename maxPlayers default
val mp = node.node("max-players")
if (!mp.virtual() && mp.getInt(0) == 0) {
mp.set(100)
Comment on lines +32 to +36
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The example YAML/migration uses kebab-case keys (max-players), but Configurate object mapping will use the Kotlin property name (maxPlayers) unless annotated (e.g., @Setting("max-players")). As written, this example/migration likely won't match the serialized keys; update the keys/migration paths or add @Setting so the demo reflects actual behavior.

Copilot uses AI. Check for mistakes.
}
}
}
}
}

object RenameServerVersionMigration : ConfigMigration {
override fun migrate(node: ConfigurationNode) {
val old = node.node("server", "version")
if (!old.virtual()) {
node.node("server", "release").set(old.raw())
old.raw(null)
}
}
}

object RemoveDeprecatedFieldMigration : ConfigMigration {
override fun migrate(node: ConfigurationNode) {
node.node("deprecated-field").raw(null)
}
}
63 changes: 60 additions & 3 deletions surf-api-core/surf-api-core-api/api/surf-api-core-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,13 @@ public abstract class dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
protected final fun getConfigFolder ()Ljava/nio/file/Path;
protected final fun getFileName ()Ljava/lang/String;
public abstract fun getManager ()Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
protected final fun getMigrationBuilder ()Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;
public final fun init ()V
protected final fun migration (ILdev/slne/surf/surfapi/core/api/config/migration/ConfigMigration;)V
protected final fun migration (ILkotlin/jvm/functions/Function1;)V
public final fun reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
protected final fun versionKey ([Ljava/lang/Object;)V
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeJsonConfigClass : dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
Expand All @@ -171,9 +175,11 @@ public abstract interface class dev/slne/surf/surfapi/core/api/config/SurfConfig
public static final field Companion Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi$Companion;
public abstract fun createDazzlConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public abstract fun createSpongeJsonConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public abstract fun createSpongeJsonConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun createSpongeJsonConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public abstract fun createSpongeJsonConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public abstract fun createSpongeYmlConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public abstract fun createSpongeYmlConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun createSpongeYmlConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public abstract fun createSpongeYmlConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public abstract fun getDazzlConfig (Ljava/lang/Class;)Ljava/lang/Object;
public abstract fun getSpongeConfig (Ljava/lang/Class;)Ljava/lang/Object;
public abstract fun getSpongeConfigManagerForConfig (Ljava/lang/Class;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
Expand All @@ -185,8 +191,10 @@ public final class dev/slne/surf/surfapi/core/api/config/SurfConfigApi$Companion
public fun createDazzlConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public fun createSpongeJsonConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public fun createSpongeJsonConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun createSpongeJsonConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun createSpongeYmlConfig (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ljava/lang/Object;
public fun createSpongeYmlConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun createSpongeYmlConfigManager (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public fun getDazzlConfig (Ljava/lang/Class;)Ljava/lang/Object;
public final fun getInstance ()Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;
public fun getSpongeConfig (Ljava/lang/Class;)Ljava/lang/Object;
Expand All @@ -195,7 +203,14 @@ public final class dev/slne/surf/surfapi/core/api/config/SurfConfigApi$Companion
public fun reloadSpongeConfig (Ljava/lang/Class;)Ljava/lang/Object;
}

public final class dev/slne/surf/surfapi/core/api/config/SurfConfigApi$DefaultImpls {
public static fun createSpongeJsonConfigManager (Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public static fun createSpongeYmlConfigManager (Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public final class dev/slne/surf/surfapi/core/api/config/SurfConfigApiKt {
public static final synthetic fun createSpongeJsonConfigManager (Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public static final synthetic fun createSpongeYmlConfigManager (Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public static final fun getSurfConfigApi ()Ldev/slne/surf/surfapi/core/api/config/SurfConfigApi;
}

Expand Down Expand Up @@ -243,16 +258,58 @@ public final class dev/slne/surf/surfapi/core/api/config/manager/SerializationCo
public final class dev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager {
public static final field Companion Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager$Companion;
public field config Ljava/lang/Object;
public synthetic fun <init> (Ljava/lang/Class;Ljava/lang/Object;Lorg/spongepowered/configurate/loader/ConfigurationLoader;Lorg/spongepowered/configurate/ConfigurationNode;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Ljava/lang/Class;Ljava/lang/Object;Lorg/spongepowered/configurate/loader/ConfigurationLoader;Lorg/spongepowered/configurate/ConfigurationNode;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addMigration (ILdev/slne/surf/surfapi/core/api/config/migration/ConfigMigration;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public final fun addMigration (ILkotlin/jvm/functions/Function1;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public final fun edit (ZLkotlin/jvm/functions/Function1;)V
public static synthetic fun edit$default (Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun migrations ()Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;
public final fun reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
}

public final class dev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager$Companion {
public final fun json (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public final fun json (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public static synthetic fun json$default (Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager$Companion;Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;ILjava/lang/Object;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public final fun yaml (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public final fun yaml (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
public static synthetic fun yaml$default (Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager$Companion;Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;ILjava/lang/Object;)Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public abstract interface class dev/slne/surf/surfapi/core/api/config/migration/ConfigMigration {
public abstract fun migrate (Lorg/spongepowered/configurate/ConfigurationNode;)V
}

public final class dev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder {
public static final field Companion Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder$Companion;
public static final field DEFAULT_VERSION_KEY Ljava/lang/String;
public fun <init> ()V
public final fun buildTransformation ()Lorg/spongepowered/configurate/transformation/ConfigurationTransformation$Versioned;
public final fun hasMigrations ()Z
public final fun latestVersion ()I
public final fun migrate (Lorg/spongepowered/configurate/ConfigurationNode;)Ldev/slne/surf/surfapi/core/api/config/migration/MigrationResult;
public final fun migration (ILdev/slne/surf/surfapi/core/api/config/migration/ConfigMigration;)Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;
public final fun migration (ILkotlin/jvm/functions/Function1;)Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;
public final fun versionKey ([Ljava/lang/Object;)Ldev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder;
}

public final class dev/slne/surf/surfapi/core/api/config/migration/ConfigMigrationBuilder$Companion {
}

public final class dev/slne/surf/surfapi/core/api/config/migration/MigrationResult {
public fun <init> (IIZ)V
public final fun component1 ()I
public final fun component2 ()I
public final fun component3 ()Z
public final fun copy (IIZ)Ldev/slne/surf/surfapi/core/api/config/migration/MigrationResult;
public static synthetic fun copy$default (Ldev/slne/surf/surfapi/core/api/config/migration/MigrationResult;IIZILjava/lang/Object;)Ldev/slne/surf/surfapi/core/api/config/migration/MigrationResult;
public fun equals (Ljava/lang/Object;)Z
public final fun getFromVersion ()I
public final fun getMigrated ()Z
public final fun getToVersion ()I
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class dev/slne/surf/surfapi/core/api/config/serializer/DefaultDazzlConfSerializers {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package dev.slne.surf.surfapi.core.api.config

import dev.slne.surf.surfapi.core.api.config.manager.SpongeConfigManager
import dev.slne.surf.surfapi.core.api.config.migration.ConfigMigration
import dev.slne.surf.surfapi.core.api.config.migration.ConfigMigrationBuilder
import org.spongepowered.configurate.ConfigurationNode
import java.nio.file.Path

/**
Expand All @@ -12,10 +15,36 @@ import java.nio.file.Path
* - persist changes via [save]
* - reload the config from disk via [reloadFromFile]
* - perform in-place mutations via [edit]
* - register schema migrations via [migration]
*
* The actual manager instance is provided by subclasses and typically created by
* a central configuration API (e.g. [surfConfigApi]).
*
* ## Versioned Migrations
*
* Migrations can be registered in the `init` block of companion objects:
*
* ```kotlin
* @ConfigSerializable
* data class MyConfig(
* var release: String = "1.0.0"
* ) {
* companion object : SpongeYmlConfigClass<MyConfig>(
* MyConfig::class.java,
* Path("config/my-plugin"),
* "my-config.yml"
* ) {
* init {
* migration(1, RenameServerVersionMigration)
* migration(2) { node ->
* // inline migration
* node.node("old-field").raw(null)
* }
* }
* }
* }
* ```
*
* @param C the type of the configuration data object.
* @param configClass the Java class of [C], used by underlying config frameworks
* to create and map configuration instances.
Expand All @@ -35,12 +64,63 @@ sealed class SpongeConfigClass<C>(
protected val fileName: String
) {

/**
* The migration builder that collects all registered migrations.
*
* Subclasses should use [migration] to register migrations rather than
* accessing this directly.
*/
protected val migrationBuilder = ConfigMigrationBuilder()

/**
* The underlying configuration manager responsible for loading, saving,
* and tracking the config instance of type [C].
*/
abstract val manager: SpongeConfigManager<C>

/**
* Registers a migration for the given target [version].
*
* Migrations are applied in version order. For existing configs without a
* version field, **all** registered migrations will be applied the first time.
*
* @param version the target version this migration upgrades to (must be >= 0)
* @param migration the migration to apply
*/
protected fun migration(version: Int, migration: ConfigMigration) {
migrationBuilder.migration(version, migration)
}

/**
* Registers an inline migration for the given target [version].
*
* ```kotlin
* init {
* migration(1) { node ->
* node.node("old-key").raw(null)
* }
* }
* ```
*
* @param version the target version this migration upgrades to (must be >= 0)
* @param migration the migration lambda
*/
protected inline fun migration(version: Int, crossinline migration: (ConfigurationNode) -> Unit) {
migrationBuilder.migration(version, ConfigMigration { node -> migration(node) })
}

/**
* Sets the path in the config file where the version number is stored.
*
* Defaults to `"config-version"`. Call this in the `init` block before
* any migration registrations if you need a custom key.
Comment on lines +115 to +116
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs say the version key defaults to "config-version", but ConfigMigrationBuilder currently defaults to ConfigMigrationBuilder.DEFAULT_VERSION_KEY ("_config_version"). Update the KDoc here (or change the default) so consumers know which key is actually written/read.

Suggested change
* Defaults to `"config-version"`. Call this in the `init` block before
* any migration registrations if you need a custom key.
* By default, this delegates to [ConfigMigrationBuilder.versionKey], which uses
* [ConfigMigrationBuilder.DEFAULT_VERSION_KEY] (currently `"_config_version"`) as the key.
* Call this in the `init` block before any migration registrations if you need a custom key.

Copilot uses AI. Check for mistakes.
*
* @param path the path components to the version key
*/
protected fun versionKey(vararg path: Any) {
migrationBuilder.versionKey(*path)
}

/**
* Persists the current configuration to disk.
*
Expand Down Expand Up @@ -92,5 +172,7 @@ sealed class SpongeConfigClass<C>(
*
* This method is a no-op and can be safely called multiple times.
*/
fun init() = Unit
fun init() {
manager
}
Comment on lines +175 to +177
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

init() is documented as a no-op, but it now forces manager initialization (which can load/save the config and run migrations). Consider updating the KDoc to reflect the new behavior so callers understand the side effects.

Copilot uses AI. Check for mistakes.
}
Loading