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
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ buildscript {
}
dependencies {
classpath("dev.slne.surf:surf-api-gradle-plugin:1.21.11+")
classpath("dev.slne.surf.microservice:surf-microservice-gradle-plugin:1.21.11+")
}
}

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kotlin.code.style=official
kotlin.stdlib.default.dependency=false
org.gradle.parallel=true
version=1.21.11-1.1.0-SNAPSHOT
version=1.21.11-2.0.1-SNAPSHOT
2 changes: 0 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
[versions]
surf-database = "1.3.0"
kotlinx-serialization = "1.10.0"
kotlinx-coroutines = "1.9.0"
junit = "5.11.3"
surf-clan-api = "1.21.11+"

[libraries]
surf-database-r2dbc = { module = "dev.slne.surf:surf-database-r2dbc", version.ref = "surf-database" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
Expand Down
7 changes: 5 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ plugins {
}

include("surf-stats-api")
include("surf-stats-core")
include("surf-stats-paper")
include("surf-stats-core:surf-stats-core-common")
include("surf-stats-core:surf-stats-core-client")

include("surf-stats-paper")
include("surf-stats-microservice")
2 changes: 1 addition & 1 deletion surf-stats-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
plugins {
id("dev.slne.surf.surfapi.gradle.paper-raw")
id("dev.slne.surf.surfapi.gradle.core")
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,80 +1,21 @@
package dev.slne.surf.stats.api

import dev.slne.surf.stats.api.model.PlayerStats
import dev.slne.surf.stats.api.model.PlayerStatsBatch
import dev.slne.surf.surfapi.core.api.util.requiredService
import java.util.*

private val api = requiredService<SurfStatsApi>()

/**
* Main API interface for the Surf Stats system.
* Provides high-level operations for statistics management.
*
* Obtain an instance via Bukkit's ServicesManager:
* ```kotlin
* val api = server.servicesManager.getRegistration(SurfStatsApi::class.java)?.provider
* ```
*/
interface SurfStatsApi {
/**
* Processes statistics for a player by computing diffs since the last snapshot
* and saving them to the database.
*
* @param playerUuid The player's UUID
* @param playerName The player's name
* @return Result containing the batch of diff entries saved
*/
suspend fun processPlayerStats(playerUuid: UUID, playerName: String): Result<PlayerStatsBatch>

/**
* Processes statistics for multiple players (e.g., on world save).
* Computes diffs for all tracked players and saves them to the database.
*
* @param players Map of UUID to player name
* @return List of batches saved
*/
suspend fun processAllPlayerStats(players: Map<UUID, String>): List<PlayerStatsBatch>

/**
* Loads player statistics without processing.
*
* @param playerUuid The player's UUID
* @param playerName The player's name
* @return The player's statistics, or null if not found
*/
suspend fun getPlayerStats(playerUuid: UUID, playerName: String): PlayerStats?
interface SurfStatsApi {

/**
* Saves a single custom statistic under the `minecraft:custom` category.
*
* @param playerUuid The player's UUID
* @param playerName The player's name
* @param key The stat key (e.g. `"my_plugin:kills"`)
* @param value The stat value
*/
suspend fun saveCustomStat(playerUuid: UUID, playerName: String, key: String, value: Long)
suspend fun processPlayerStats(playerUuid: UUID)

/**
* Saves multiple custom statistics under the `minecraft:custom` category in a single transaction.
*
* @param playerUuid The player's UUID
* @param playerName The player's name
* @param stats Map of stat key to value
*/
suspend fun saveCustomStats(playerUuid: UUID, playerName: String, stats: Map<String, Long>)
suspend fun processAllPlayerStats(uuids: Set<UUID>)

/**
* Extracts all unique category names from a player's stats.
* Useful for populating the stat_categories table.
*/
fun extractCategories(stats: PlayerStats): Set<String>
suspend fun getPlayerStats(playerUuid: UUID): PlayerStats

/**
* Extracts all unique stat key names from a player's stats.
* Useful for populating the stat_keys table.
*/
fun extractStatKeys(stats: PlayerStats): Set<String>
suspend fun saveStats(playerUuid: UUID, stats: PlayerStats)

companion object : SurfStatsApi by api {
val INSTANCE get() = api
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
package dev.slne.surf.stats.api.model

import java.util.UUID
import dev.slne.surf.surfapi.core.api.serializer.java.uuid.SerializableUUID
import kotlinx.serialization.Serializable

/**
* Represents player statistics loaded from the filesystem.
* Maps to: players table + player_stats entries
*/
@Serializable
data class PlayerStats(
val uuid: UUID,
val name: String,
val dataVersion: Int,
val stats: List<StatEntry>
) {
/**
* Gets all stats for a specific category.
*/
fun getByCategory(category: String): List<StatEntry> =
stats.filter { it.category == category }

/**
* Gets a specific stat value.
*/
fun getStat(category: String, key: String): Long? =
stats.find { it.category == category && it.key == key }?.value
val playerUuid: SerializableUUID,
val serverName: String,
val stats: List<StatEntry> = emptyList()
) : Collection<StatEntry> {
override fun contains(element: StatEntry): Boolean {
return stats.contains(element)
}

/**
* Gets all unique categories in this player's stats.
*/
fun categories(): Set<String> =
stats.map { it.category }.toSet()
override fun containsAll(elements: Collection<StatEntry>): Boolean {
return stats.containsAll(elements)
}

/**
* Gets all unique stat keys across all categories.
*/
fun statKeys(): Set<String> =
stats.map { it.key }.toSet()
override fun isEmpty(): Boolean {
return stats.isEmpty()
}

companion object {
fun empty(uuid: UUID, name: String = "Unknown", dataVersion: Int = 0): PlayerStats =
PlayerStats(uuid, name, dataVersion, emptyList())
override fun iterator(): Iterator<StatEntry> {
return stats.iterator()
}
}

override val size: Int get() = stats.size
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package dev.slne.surf.stats.api.model

import java.util.UUID
import dev.slne.surf.surfapi.core.api.serializer.java.uuid.SerializableUUID
import kotlinx.serialization.Serializable

/**
* Represents a batch of player stats ready for database insertion.
* Includes server context and clan assignment for the player_stats table.
*/
@Serializable
data class PlayerStatsBatch(
val player: PlayerStats,
val playerUuid: SerializableUUID,
val stats: PlayerStats,
val serverName: String,
val clanUuid: UUID? = null
val clanUuid: SerializableUUID? = null,
val dataVersion: Int = 0
)
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package dev.slne.surf.stats.api.model

import dev.slne.surf.surfapi.core.api.serializer.adventure.key.SerializableKey
import kotlinx.serialization.Serializable

/**
* Represents a single statistic entry matching the database schema.
* Maps to: player_stats table (categoryName, statKeyName, value)
*/
@Serializable
data class StatEntry(
val category: String, // e.g., "minecraft:mined"
val key: String, // e.g., "minecraft:stone"
val category: SerializableKey, // e.g., "minecraft:mined"
val key: SerializableKey, // e.g., "minecraft:stone"
val value: Long
)

This file was deleted.

This file was deleted.

Loading