Skip to content
Open
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
42,928 changes: 201 additions & 42,727 deletions surf-api-bukkit/surf-api-bukkit-api/api/surf-api-bukkit-api.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,24 @@ abstract class GameRules<CTX : Any> {
return Key(id, description, lastGameRuleIndex.getAndIncrement())
}

/**
* Creates a new [RuleSet] populated with the default values of every
* registered rule type. After the first invocation the entire registry
* becomes frozen and no further rule types may be registered.
*/
fun createRuleSet(): RuleSet = RuleSet().also { frozen = true }

/**
* Deserializes a [RuleSet] from the provided [CompoundBinaryTag]. The
* resulting instance is fully initialised and the registry is frozen on
* first creation.
*/
fun createRuleSet(values: CompoundBinaryTag): RuleSet = RuleSet(values).also { frozen = true }

/**
* Loads a [RuleSet] from the given NBT [file]. This behaves the same as
* [createRuleSet] but reads the serialized values from disk.
*/
fun loadRuleSet(file: Path): RuleSet = RuleSet(file).also { frozen = true }

/**
Expand Down Expand Up @@ -279,18 +295,34 @@ abstract class GameRules<CTX : Any> {
.freeze()
)

/**
* Retrieves the [Value] instance backing the specified [key]. The
* returned object can be used to read or modify the current setting in
* a type-safe manner.
*/
fun <T : Value<T, CTX, V>, V> getRule(key: Key<T, CTX, V>): T {
return gameruleArray[key.gameRuleIndex] as T
}

/**
* Shortcut for obtaining the primitive value of a rule without accessing
* the full [Value] wrapper.
*/
fun <T : Value<T, CTX, V>, V> getValue(key: Key<T, CTX, V>): V {
return getRule(key).get()
}

/**
* Operator alias for [getRule] allowing array-like access to values.
*/
operator fun <T : Value<T, CTX, V>, V> get(key: Key<T, CTX, V>): T {
return getRule(key)
}

/**
* Serializes this rule-set into an NBT [CompoundBinaryTag].
* Each rule is stored as a simple `key → serializedValue` pair.
*/
fun createTag(): CompoundBinaryTag {
val builder = CompoundBinaryTag.builder()
for ((key, value) in rules.object2ObjectEntrySet()) {
Expand All @@ -299,6 +331,10 @@ abstract class GameRules<CTX : Any> {
return builder.build()
}

/**
* Persists this rule-set to the specified [file] in compressed NBT
* format.
*/
fun saveToFile(file: Path) {
val tag = createTag()
BinaryTagIO.writer()
Expand All @@ -312,6 +348,10 @@ abstract class GameRules<CTX : Any> {
}
}

/**
* Invokes the provided [visitor] for every registered rule type in this
* rule-set.
*/
fun visitGameRuleTypes(visitor: GameRuleTypeVisitor<CTX>) {
for ((key, type) in gameRuleTypes.object2ObjectEntrySet()) {
callVisitorCap<Nothing, Any?>(visitor, key, type)
Expand All @@ -328,6 +368,10 @@ abstract class GameRules<CTX : Any> {
type.callVisitor(visitor, key)
}

/**
* Copies all values from [other] into this rule-set and triggers the
* appropriate change callbacks for each modified rule.
*/
fun assignFrom(other: RuleSet, context: CTX) {
for (key in other.rules.keys) {
assignCap(key, other, context)
Expand Down Expand Up @@ -385,14 +429,25 @@ abstract class GameRules<CTX : Any> {
private val visitorCaller: VisitorCaller<T, CTX, V>,
) {

/**
* Builds the CommandAPI [Argument] used to set this rule's value via
* commands.
*/
fun createArgument(name: String): Argument<*> {
return argumentCreator(name)
}

/**
* Creates a new [Value] instance with its default value.
*/
fun createRule(): T {
return ruleFactory(this)
}

/**
* Dispatches the [visitor] to the type-specific callback supplied at
* construction time.
*/
fun callVisitor(visitor: GameRuleTypeVisitor<CTX>, key: Key<T, CTX, V>) {
visitorCaller.call(visitor, key, this)
}
Expand All @@ -414,17 +469,30 @@ abstract class GameRules<CTX : Any> {
* @see StringValue
*/
abstract class Value<SELF : Value<SELF, CTX, V>, CTX : Any, V>(protected val type: Type<SELF, CTX, V>) {
/**
* Parses the command argument identified by [name] and updates this
* value accordingly. Implementations should extract the argument from
* [args] and perform any necessary validation.
*/
protected abstract fun updateFromArgument(
sender: CommandSender,
args: CommandArguments,
name: String,
key: Key<SELF, CTX, V>,
)

/** Returns the currently stored primitive value. */
abstract fun get(): V

/** Self-type helper used by [onChanged]. */
protected abstract fun self(): SELF

operator fun getValue(thisRef: Any?, property: KProperty<*>) = get()

/**
* Updates this value from the command argument named [name] and fires
* the registered change callback.
*/
fun setFromArgument(
sender: CommandSender,
args: CommandArguments,
Expand All @@ -436,20 +504,31 @@ abstract class GameRules<CTX : Any> {
onChanged(context)
}

/** Invokes the change callback with this value. */
fun onChanged(context: CTX) {
type.changeCallback(context, self())
}

/** Restores the state from its serialized representation. */
abstract fun deserialize(value: String)

/** Serializes the current state to a string representation. */
abstract fun serialize(): String

/**
* Returns a user-friendly representation of the value. Defaults to
* [serialize].
*/
open fun displayValue(): String = serialize()

override fun toString(): String {
return serialize()
}

/** Creates a deep copy of this value object. */
protected abstract fun copy(): SELF

/** Assigns the state from [other] to this value. */
abstract fun setFrom(other: SELF, context: CTX)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,26 @@ abstract class SimpleGameRules<CTX : Any> : GameRules<CTX> {
return ruleSet.getValue(key)
}

/**
* Shortcut for [getRule] using Kotlin's index operator.
*/
operator fun <T : Value<T, CTX, V>, V> get(key: Key<T, CTX, V>): T {
return ruleSet[key]
}

/**
* Registers all commands for this rule-set under the given [tree].
*/
fun addToCommandTree(
tree: CommandTree,
getContext: (CommandSender) -> CTX,
) {
addToCommandTree(tree, { ruleSet }, getContext)
}

/**
* Continues an existing command argument chain with this rule-set.
*/
fun addToCommandTree(
argument: Argument<*>,
getContext: (CommandSender) -> CTX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,23 @@ class BooleanValue<CTX : Any>(type: GameRules.Type<BooleanValue<CTX>, CTX, Boole
this.value.set(value)
}

/** @inheritDoc */
/** @inheritDoc */
override fun get() = value.get()
override fun self() = this

/** @inheritDoc */
override fun deserialize(value: String) {
this.value.set(value.toBoolean())
}

/** @inheritDoc */
override fun serialize() = value.get().toString()

/** @inheritDoc */
override fun copy() = BooleanValue(type).also { it.value.set(this.value.get()) }

/** @inheritDoc */
override fun setFrom(
other: BooleanValue<CTX>,
context: CTX,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,21 @@ import dev.slne.surf.surfapi.core.api.util.logger
import org.bukkit.command.CommandSender
import javax.annotation.concurrent.ThreadSafe

/**
* Thread-safe game-rule value storing a [Double].
*
* Changes are propagated immediately through the associated change callback
* defined when calling [create].
*
* @param CTX context object type passed to callbacks
*/
@ThreadSafe
class DoubleValue<CTX : Any>(type: GameRules.Type<DoubleValue<CTX>, CTX, Double>) :
GameRules.Value<DoubleValue<CTX>, CTX, Double>(type) {

private val value = AtomicDouble()

/** @inheritDoc */
override fun updateFromArgument(
sender: CommandSender,
args: CommandArguments,
Expand All @@ -24,9 +33,11 @@ class DoubleValue<CTX : Any>(type: GameRules.Type<DoubleValue<CTX>, CTX, Double>
value.set(newValue)
}

/** @inheritDoc */
override fun get() = value.get()
override fun self() = this

/** @inheritDoc */
override fun deserialize(value: String) {
val deserialized = value.toDoubleOrNull()
if (deserialized != null) {
Expand All @@ -38,9 +49,13 @@ class DoubleValue<CTX : Any>(type: GameRules.Type<DoubleValue<CTX>, CTX, Double>
}
}

/** @inheritDoc */
override fun serialize() = value.get().toString()

/** @inheritDoc */
override fun copy() = DoubleValue(type).also { it.value.set(this.value.get()) }

/** @inheritDoc */
override fun setFrom(
other: DoubleValue<CTX>,
context: CTX,
Expand All @@ -52,6 +67,14 @@ class DoubleValue<CTX : Any>(type: GameRules.Type<DoubleValue<CTX>, CTX, Double>
companion object {
private val log = logger()

/**
* Factory for a [DoubleValue] rule.
*
* @param defaultValue default starting value
* @param min minimum allowed value
* @param max maximum allowed value
* @param onChange callback invoked whenever the value changes
*/
fun <CTX : Any> create(
defaultValue: Double,
min: Double = Double.MIN_VALUE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import javax.annotation.concurrent.ThreadSafe
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

/**
* Thread-safe game-rule value storing a Kotlin [Duration].
*
* Values are represented as ticks in commands and serialized using ISO-8601
* duration strings.
*/
@ThreadSafe
class DurationValue<CTX : Any>(type: GameRules.Type<DurationValue<CTX>, CTX, Duration>) :
GameRules.Value<DurationValue<CTX>, CTX, Duration>(type) {
private val value = AtomicReference(Duration.ZERO)

/** @inheritDoc */
override fun updateFromArgument(
sender: CommandSender,
args: CommandArguments,
Expand All @@ -26,9 +33,12 @@ class DurationValue<CTX : Any>(type: GameRules.Type<DurationValue<CTX>, CTX, Dur
value.set((ticks * Ticks.SINGLE_TICK_DURATION_MS).milliseconds)
}

/** @inheritDoc */
/** @inheritDoc */
override fun get(): Duration = value.get()
override fun self() = this

/** @inheritDoc */
override fun deserialize(value: String) {
try {
this.value.set(Duration.parseIsoString(value))
Expand All @@ -39,11 +49,15 @@ class DurationValue<CTX : Any>(type: GameRules.Type<DurationValue<CTX>, CTX, Dur
}
}

/** @inheritDoc */
override fun serialize() = value.get().toIsoString()
/** @inheritDoc */
override fun copy() = DurationValue(type).also { it.value.set(this.value.get()) }

/** @inheritDoc */
override fun displayValue() = value.get().toString()

/** @inheritDoc */
override fun setFrom(
other: DurationValue<CTX>,
context: CTX,
Expand All @@ -55,6 +69,12 @@ class DurationValue<CTX : Any>(type: GameRules.Type<DurationValue<CTX>, CTX, Dur
companion object {
private val log = logger()

/**
* Factory for a [DurationValue] rule.
*
* @param defaultValue default starting duration
* @param onChange callback invoked whenever the value changes
*/
fun <CTX : Any> create(
defaultValue: Duration = Duration.ZERO,
onChange: (CTX, DurationValue<CTX>) -> Unit = { _, _ -> },
Expand Down
Loading