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
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=21
mcVersion=1.21.10
group=dev.slne.surf
version=1.21.10-2.42.4
version=1.21.10-2.43.0
relocationPrefix=dev.slne.surf.surfapi.libs
snapshot=false
27 changes: 27 additions & 0 deletions surf-api-bukkit/surf-api-bukkit-api/api/surf-api-bukkit-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -5974,11 +5974,36 @@ public final class dev/slne/surf/surfapi/core/api/command/exception/WrapperComma
public abstract interface annotation class dev/slne/surf/surfapi/core/api/config/JsonConfigFileNamePattern : java/lang/annotation/Annotation {
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public synthetic fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun edit (ZLkotlin/jvm/functions/Function1;)V
public static synthetic fun edit$default (Ldev/slne/surf/surfapi/core/api/config/SpongeConfigClass;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun getConfig ()Ljava/lang/Object;
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;
public final fun init ()V
public final fun reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeJsonConfigClass : dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)V
public fun getManager ()Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeYmlConfigClass : dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)V
public fun getManager ()Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public abstract interface class dev/slne/surf/surfapi/core/api/config/SurfConfigApi {
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 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 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 Down Expand Up @@ -6034,6 +6059,8 @@ public final class dev/slne/surf/surfapi/core/api/config/manager/SpongeConfigMan
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 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 reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dev.slne.surf.surfapi.bukkit.test.command.SurfApiTestCommand;
import dev.slne.surf.surfapi.bukkit.test.command.subcommands.inventory.TestInventoryView;
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.listener.ChatListener;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
Expand All @@ -19,6 +20,9 @@ public class BukkitPluginMain extends JavaPlugin {

@Override
public void onLoad() {
ModernTestConfig.Companion.init();
ModernTestConfig.Companion.randomise();

SurfBukkitPacketListenerApi.Companion.getInstance().registerListeners(new ChatListener());
InventoryFrameworkExtensions.register(TestInventoryView.INSTANCE);
// TestConfig config = SurfCore#Api.getCore()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.slne.surf.surfapi.bukkit.test.config

import dev.slne.surf.surfapi.bukkit.test.BukkitPluginMain
import dev.slne.surf.surfapi.core.api.config.SpongeYmlConfigClass
import org.spongepowered.configurate.objectmapping.ConfigSerializable

@ConfigSerializable
data class ModernTestConfig(
var message: String = "Hello from Modern Config!",
var number: Int = 42,
var enabled: Boolean = true
) {
companion object : SpongeYmlConfigClass<ModernTestConfig>(
ModernTestConfig::class.java,
BukkitPluginMain.getInstance().dataPath,
"modern-test-config.yml"
) {
fun randomise() = edit {
message = "Random Message ${Math.random()}"
number = (Math.random() * 100).toInt()
enabled = Math.random() > 0.5
}
}
}
27 changes: 27 additions & 0 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 @@ -601,11 +601,36 @@ public final class dev/slne/surf/surfapi/core/api/command/exception/WrapperComma
public abstract interface annotation class dev/slne/surf/surfapi/core/api/config/JsonConfigFileNamePattern : java/lang/annotation/Annotation {
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public synthetic fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun edit (ZLkotlin/jvm/functions/Function1;)V
public static synthetic fun edit$default (Ldev/slne/surf/surfapi/core/api/config/SpongeConfigClass;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public final fun getConfig ()Ljava/lang/Object;
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;
public final fun init ()V
public final fun reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeJsonConfigClass : dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)V
public fun getManager ()Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public abstract class dev/slne/surf/surfapi/core/api/config/SpongeYmlConfigClass : dev/slne/surf/surfapi/core/api/config/SpongeConfigClass {
public fun <init> (Ljava/lang/Class;Ljava/nio/file/Path;Ljava/lang/String;)V
public fun getManager ()Ldev/slne/surf/surfapi/core/api/config/manager/SpongeConfigManager;
}

public abstract interface class dev/slne/surf/surfapi/core/api/config/SurfConfigApi {
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 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 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 Down Expand Up @@ -661,6 +686,8 @@ public final class dev/slne/surf/surfapi/core/api/config/manager/SpongeConfigMan
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 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 reloadFromFile ()Ljava/lang/Object;
public final fun save ()V
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package dev.slne.surf.surfapi.core.api.config

import dev.slne.surf.surfapi.core.api.config.manager.SpongeConfigManager
import java.nio.file.Path

/**
* Base convenience wrapper around a [SpongeConfigManager] for a specific config type [C].
*
* This class is intended to be extended by companion objects of config data classes.
* It provides a simple, type-safe API to:
* - access the current config instance via [getConfig]
* - persist changes via [save]
* - reload the config from disk via [reloadFromFile]
* - perform in-place mutations via [edit]
*
* The actual manager instance is provided by subclasses and typically created by
* a central configuration API (e.g. [surfConfigApi]).
*
* @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.
*/
sealed class SpongeConfigClass<C>(
configClass: Class<C>,
/**
* Folder where the configuration file is stored.
*
* Implementations should point this to a plugin- or module-specific config directory.
*/
protected val configFolder: Path,
/**
* The name of the configuration file, including its extension
* (for example `settings.yml` or `settings.json`).
*/
protected val fileName: String
) {

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

/**
* Persists the current configuration to disk.
*
* Delegates to [SpongeConfigManager.save].
*/
fun save() = manager.save()

/**
* Reloads the configuration from disk and replaces the current in-memory instance.
*
* Delegates to [SpongeConfigManager.reloadFromFile].
* Use this when external changes to the config file should be picked up at runtime.
*/
fun reloadFromFile() = manager.reloadFromFile()

/**
* Applies mutations to the current config instance in a safe way.
*
* The [block] receives the current config instance as receiver and can freely
* modify its properties. After the block completes, the config will be saved
* automatically if [save] is `true`.
*
* Example:
* ```kotlin
* MyConfig.edit {
* someField = "new value"
* }
* ```
*
* @param save whether to persist the config to disk after applying [block]. Defaults to `true`.
* @param block mutation block executed on the current config instance.
*/
inline fun edit(save: Boolean = true, block: C.() -> Unit) = manager.edit(save, block)

/**
* Returns the current in-memory configuration instance.
*
* The returned object is managed by the underlying [SpongeConfigManager] and
* will be replaced when [reloadFromFile] is called.
*/
fun getConfig(): C = manager.config

/**
* Dummy initializer to force class loading and companion object initialization.
*
* This is useful in plugin entry points (e.g. `onLoad`) to ensure that:
* - the config manager is constructed
* - the configuration is loaded before first access via [getConfig]
*
* This method is a no-op and can be safely called multiple times.
*/
fun init() = Unit
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.slne.surf.surfapi.core.api.config

import java.nio.file.Path

/**
* Convenience base class for JSON-backed Sponge configuration classes.
*
* This class wires the common configuration metadata ([configFolder], [fileName])
* to a JSON-based [dev.slne.surf.surfapi.core.api.config.manager.SpongeConfigManager] instance created by [surfConfigApi].
*
* Typical usage is via a companion object on a `@ConfigSerializable` data class:
* ```kotlin
* @ConfigSerializable
* data class MyJsonConfig(
* var someField: String = "value"
* ) {
* companion object : SpongeJsonConfigClass<MyJsonConfig>(
* MyJsonConfig::class.java,
* Path("config/my-plugin"),
* "my-config.json"
* ) {
* }
* }
* ```
*
* @param C the type of the configuration data object.
* @param configClass the Java class of [C], used by the underlying config framework.
*/
abstract class SpongeJsonConfigClass<C>(
configClass: Class<C>, configFolder: Path, fileName: String
) : SpongeConfigClass<C>(configClass, configFolder, fileName) {

/**
* JSON-backed configuration manager for this config type.
*
* The manager is created using [SurfConfigApi.createSpongeJsonConfigManager]
* with [configClass], [configFolder] and [fileName].
*/
override val manager =
surfConfigApi.createSpongeJsonConfigManager(configClass, configFolder, fileName)
Comment on lines +39 to +40

Copilot AI Dec 10, 2025

Copy link

Choose a reason for hiding this comment

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

Property initialization order issue: manager is initialized before subclass properties. The manager property initialization accesses configFolder and fileName, which are abstract properties that won't be initialized until after the superclass constructor completes. This will cause a runtime error.

Consider making manager a lazy property or use a different initialization pattern:

override val manager by lazy {
    surfConfigApi.createSpongeJsonConfigManager(configClass, configFolder, fileName)
}
Suggested change
override val manager =
surfConfigApi.createSpongeJsonConfigManager(configClass, configFolder, fileName)
override val manager by lazy {
surfConfigApi.createSpongeJsonConfigManager(configClass, configFolder, fileName)
}

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.slne.surf.surfapi.core.api.config

import java.nio.file.Path

/**
* Convenience base class for YAML-backed Sponge configuration classes.
*
* This class wires the common configuration metadata ([configFolder], [fileName])
* to a YAML-based [dev.slne.surf.surfapi.core.api.config.manager.SpongeConfigManager] instance created by [surfConfigApi].
*
* Typical usage is via a companion object on a `@ConfigSerializable` data class:
* ```kotlin
* @ConfigSerializable
* data class MyConfig(
* var someField: String = "value"
* ) {
* companion object : SpongeYmlConfigClass<MyConfig>(
* MyConfig::class.java,
* Path("config/my-plugin"),
* "my-config.yml"
* ) {
* }
* }
* ```
*
* @param C the type of the configuration data object.
* @param configClass the Java class of [C], used by the underlying config framework.
*/
abstract class SpongeYmlConfigClass<C>(
configClass: Class<C>, configFolder: Path, fileName: String
) : SpongeConfigClass<C>(configClass, configFolder, fileName) {

/**
* YAML-backed configuration manager for this config type.
*
* The manager is created using [SurfConfigApi.createSpongeYmlConfigManager]
* with [configClass], [configFolder] and [fileName].
*/
override val manager =
surfConfigApi.createSpongeYmlConfigManager(configClass, configFolder, fileName)
Comment on lines +39 to +40

Copilot AI Dec 10, 2025

Copy link

Choose a reason for hiding this comment

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

Property initialization order issue: manager is initialized before subclass properties. The manager property initialization accesses configFolder and fileName, which are abstract properties that won't be initialized until after the superclass constructor completes. This will cause a runtime error.

Consider making manager a lazy property or use a different initialization pattern:

override val manager by lazy {
    surfConfigApi.createSpongeYmlConfigManager(configClass, configFolder, fileName)
}
Suggested change
override val manager =
surfConfigApi.createSpongeYmlConfigManager(configClass, configFolder, fileName)
override val manager by lazy {
surfConfigApi.createSpongeYmlConfigManager(configClass, configFolder, fileName)
}

Copilot uses AI. Check for mistakes.
}
Loading