|
1 | | -[](https://github.com/SLNE-Development/surf-api/actions/workflows/publish.yml) |
| 1 | +# surf-api |
2 | 2 |
|
3 | | -# SURF-API |
| 3 | +**Multi-platform plugin API framework** for Minecraft servers — providing modular service |
| 4 | +discovery, lifecycle-managed components, NMS bridge abstractions, and rich DSLs across Paper, |
| 5 | +Velocity, and Standalone platforms. |
| 6 | + |
| 7 | +[](https://github.com/SLNE-Development/surf-api/releases/latest) |
| 8 | +[](LICENSE) |
| 9 | +[](https://kotlinlang.org/) |
| 10 | +[](https://openjdk.org/) |
| 11 | +[](https://docs.slne.dev/surf-api) |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Overview |
| 16 | + |
| 17 | +surf-api is the shared foundation for SLNE's server plugin ecosystem. It provides: |
| 18 | + |
| 19 | +- **Platform-abstracted APIs** for Paper (Bukkit/NMS), Velocity, and Standalone environments |
| 20 | +- **Service discovery** via `ServiceLoader` (`requiredService<T>()`) for singleton bridges |
| 21 | +- **Component system** with compile-time KSP discovery, topological lifecycle ordering, and |
| 22 | + conditional activation |
| 23 | +- **NMS bridge pattern** to safely abstract version-specific internals behind stable interfaces |
| 24 | +- **Inventory framework DSLs**, command helpers, scoreboard utilities, and extension-rich APIs |
| 25 | +- **Gradle plugin** (`surf-api-gradle-plugin`) that configures downstream projects, handles |
| 26 | + dependency shading, and runs the KSP processor |
| 27 | + |
| 28 | +--- |
| 29 | + |
| 30 | +## Module Structure |
| 31 | + |
| 32 | +``` |
| 33 | +surf-api |
| 34 | +├── surf-api-core # Platform-agnostic core API & implementation |
| 35 | +│ ├── surf-api-core # Core API interfaces |
| 36 | +│ └── surf-api-core-server # Core implementations |
| 37 | +│ |
| 38 | +├── surf-api-paper # Paper/Bukkit platform |
| 39 | +│ ├── surf-api-paper # Public API (extensions, bridges, DSLs) |
| 40 | +│ ├── surf-api-paper-server # Implementations (shaded into final JAR) |
| 41 | +│ ├── surf-api-paper-nms |
| 42 | +│ │ ├── surf-api-paper-nms-common # Shared NMS bridge interfaces |
| 43 | +│ │ ├── surf-api-paper-nms-v1-21-11 # NMS impl for 1.21.11 |
| 44 | +│ │ └── surf-api-paper-nms-v26-1 # NMS impl for 26.1 |
| 45 | +│ └── surf-api-paper-plugin-test # Local test server (excluded from CI) |
| 46 | +│ |
| 47 | +├── surf-api-velocity # Velocity proxy platform |
| 48 | +│ ├── surf-api-velocity # Public API |
| 49 | +│ └── surf-api-velocity-server # Implementations |
| 50 | +│ |
| 51 | +├── surf-api-standalone # Standalone (non-server) platform |
| 52 | +│ |
| 53 | +├── surf-api-shared # Cross-platform annotations & component system |
| 54 | +│ ├── surf-api-shared-public # Public shared annotations (@ComponentMeta, etc.) |
| 55 | +│ └── surf-api-shared-internal # Internal shared utilities |
| 56 | +│ |
| 57 | +├── surf-api-gradle-plugin # Gradle convention plugins & KSP processor |
| 58 | +│ └── surf-api-processor # KSP processor — discovers @ComponentMeta at compile time |
| 59 | +│ |
| 60 | +└── surf-api-generator # NMS module scaffolding generator (local only) |
| 61 | +``` |
| 62 | + |
| 63 | +> **API/Implementation split:** `-api` modules define public interfaces consumed by downstream |
| 64 | +> projects. `-server` modules contain implementations that are shaded into the final JAR and never |
| 65 | +> exposed as a public API dependency. |
| 66 | +
|
| 67 | +--- |
| 68 | + |
| 69 | +## Key Concepts |
| 70 | + |
| 71 | +### Service Discovery |
| 72 | + |
| 73 | +Two mechanisms are available depending on the use case: |
| 74 | + |
| 75 | +#### 1. `requiredService<T>()` — Singleton bridges |
| 76 | + |
| 77 | +For singleton platform bridges and NMS abstractions. Backed by Java `ServiceLoader`; implementations |
| 78 | +register themselves with `@AutoService`. |
| 79 | + |
| 80 | +```kotlin |
| 81 | +// In -api: declare the bridge interface with companion delegation |
| 82 | +@NmsUseWithCaution |
| 83 | +interface SurfBukkitNmsCommonBridge { |
| 84 | + fun nextEntityId(): Int |
| 85 | + |
| 86 | + companion object : SurfBukkitNmsCommonBridge by requiredService() |
| 87 | +} |
| 88 | + |
| 89 | +// In -server: provide the implementation |
| 90 | +@AutoService(SurfBukkitNmsCommonBridge::class) |
| 91 | +class SurfBukkitNmsCommonBridgeImpl : SurfBukkitNmsCommonBridge { |
| 92 | + init { checkInstantiationByServiceLoader() } |
| 93 | + |
| 94 | + override fun nextEntityId(): Int = TODO() |
| 95 | +} |
| 96 | + |
| 97 | +// Usage — callers go through the companion, unaware of the implementation |
| 98 | +val id = SurfBukkitNmsCommonBridge.nextEntityId() |
| 99 | +``` |
| 100 | + |
| 101 | +#### 2. Component System — Lifecycle-managed components |
| 102 | + |
| 103 | +Classes annotated with `@ComponentMeta` (or the meta-annotations `@Service` / `@Repository`) are |
| 104 | +discovered at compile-time by the KSP processor, which writes JSON metadata to |
| 105 | +`META-INF/surfapi/components/`. At runtime, `ComponentService`: |
| 106 | + |
| 107 | +1. Loads all component metadata |
| 108 | +2. Topologically sorts by `@Priority` and `@DependsOnComponent` |
| 109 | +3. Checks conditions (`@ConditionalOnProperty`, `@DependsOnPlugin`, `@DependsOnClass`, …) |
| 110 | +4. Calls `suspend fun load()` → `enable()` → `disable()` in order |
| 111 | + |
| 112 | +```kotlin |
| 113 | +@Service |
| 114 | +class MyFeatureService : SurfComponent { |
| 115 | + override suspend fun enable() { |
| 116 | + // runs after all dependencies are enabled |
| 117 | + } |
| 118 | + |
| 119 | + override suspend fun disable() { |
| 120 | + // runs in reverse order on shutdown |
| 121 | + } |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## Getting Started |
| 128 | + |
| 129 | +### Prerequisites |
| 130 | + |
| 131 | +- Java 25 (GraalVM recommended for CI) |
| 132 | +- Gradle 8+ |
| 133 | + |
| 134 | +### Build |
| 135 | + |
| 136 | +```bash |
| 137 | +# Full build (produces shadow JARs with relocated dependencies) |
| 138 | +./gradlew build shadowJar |
| 139 | + |
| 140 | +# Build a single module |
| 141 | +./gradlew :surf-api-paper:surf-api-paper-server:build |
| 142 | + |
| 143 | +# Publish to local Maven repository |
| 144 | +./gradlew publishToMavenLocal |
| 145 | + |
| 146 | +# Run the Paper test server (local only, not available in CI) |
| 147 | +./gradlew :surf-api-paper:surf-api-paper-plugin-test:runServer |
| 148 | +``` |
| 149 | + |
| 150 | +--- |
| 151 | + |
| 152 | +## Conventions |
| 153 | + |
| 154 | +### Kotlin |
| 155 | + |
| 156 | +| Convention | Details | |
| 157 | +|---|---| |
| 158 | +| **Context parameters** | Enabled globally (`-Xcontext-parameters`). Used in inventory DSLs and other scoped builders. | |
| 159 | +| **`@InternalSurfApi`** | `@RequiresOptIn` — mark new internal APIs with this annotation. All subprojects opt-in via compiler args. | |
| 160 | +| **Coroutines** | All async work uses Kotlin coroutines. Component lifecycle methods are `suspend`. Bukkit uses MCCoroutine (Folia-aware). | |
| 161 | +| **Extension functions** | Primary API enrichment pattern; organized in files named `*-extension.kt` or `*-extensions.kt`. | |
| 162 | +| **DSL markers** | `@InventoryFrameworkDSL`, `@ItemDsl`, `@PaneMarker` restrict scope in builder DSLs. | |
| 163 | + |
| 164 | +### Logging |
| 165 | + |
| 166 | +Use Google Flogger via the `logger()` helper: |
| 167 | + |
| 168 | +```kotlin |
| 169 | +private val log = logger() // FluentLogger.forEnclosingClass() |
| 170 | + |
| 171 | +log.atInfo().log("Server started") |
| 172 | +log.atWarning().withCause(e).log("Failed to load component %s", name) |
| 173 | +``` |
| 174 | + |
| 175 | +### Package Layout |
| 176 | + |
| 177 | +``` |
| 178 | +dev.slne.surf.api # Root |
| 179 | +dev.slne.surf.api.core.api # Core API interfaces |
| 180 | +dev.slne.surf.api.core.server # Core implementations |
| 181 | +dev.slne.surf.api.paper.api # Paper API (extensions, bridges, DSLs) |
| 182 | +dev.slne.surf.api.paper.server # Paper implementations |
| 183 | +dev.slne.surf.api.velocity.api # Velocity API |
| 184 | +dev.slne.surf.api.velocity.server # Velocity implementations |
| 185 | +dev.slne.surf.api.shared.api # Shared annotations & component system |
| 186 | +dev.slne.surf.api.libs.* # Relocation target for all shaded dependencies |
| 187 | +``` |
| 188 | + |
| 189 | +### Dependency Shading |
| 190 | + |
| 191 | +All non-platform dependencies are shaded via Shadow and relocated to `dev.slne.surf.api.libs.*` |
| 192 | +(configured via `relocationPrefix` in `gradle.properties`). Platform APIs (Paper, Velocity) remain |
| 193 | +`compileOnly`. Relocations are declared in `CommonSurfPlugin` using an infix DSL: |
| 194 | + |
| 195 | +```kotlin |
| 196 | +"me.devnatan.inventoryframework" relocatesTo "devnatan.inventoryframework" |
| 197 | +``` |
| 198 | + |
| 199 | +--- |
| 200 | + |
| 201 | +## Notable Dependencies |
| 202 | + |
| 203 | +| Library | Purpose | |
| 204 | +|---|---| |
| 205 | +| [PacketEvents](https://github.com/retrooper/packetevents) | Packet-level networking abstraction | |
| 206 | +| [CommandAPI](https://commandapi.jorel.dev/) | Brigadier-based command framework | |
| 207 | +| [MCCoroutine](https://github.com/Shynixn/MCCoroutine) (SLNE fork) | Folia-aware coroutine dispatching | |
| 208 | +| [Inventory Framework](https://github.com/devnatan/inventory-framework) | GUI/inventory DSL | |
| 209 | +| [Adventure](https://docs.advntr.net/) | Text & component API | |
| 210 | +| [LuckPerms](https://luckperms.net/) | Permissions API | |
| 211 | +| [PlaceholderAPI](https://github.com/PlaceholderAPI/PlaceholderAPI) | Placeholder support | |
| 212 | +| [Caffeine](https://github.com/ben-manes/caffeine) | High-performance caching | |
| 213 | +| [Flogger](https://google.github.io/flogger/) | Fluent logging | |
| 214 | +| [Ktor](https://ktor.io/) | HTTP client | |
| 215 | +| [Configurate](https://github.com/SpongePowered/Configurate) | Configuration loading | |
| 216 | +| [KSP](https://github.com/google/ksp) | Compile-time component discovery | |
| 217 | +| [AutoService](https://github.com/google/auto/tree/main/service) | `ServiceLoader` registration | |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## Contributing |
| 222 | + |
| 223 | +When making changes: |
| 224 | + |
| 225 | +- **API changes** go in `-api` modules. **Never** expose `-server` types in the public API. |
| 226 | +- **Implementations** go in `-server` modules. |
| 227 | +- **New components**: annotate with `@ComponentMeta` / `@Service` / `@Repository` and implement the |
| 228 | + appropriate lifecycle methods. |
| 229 | +- **New NMS bridges**: declare the interface in `-api`, implement in `-server`, register via |
| 230 | + `@AutoService`, and call `checkInstantiationByServiceLoader()` in the `init` block. |
| 231 | +- Publishing is automated — push to a `version/*` branch to trigger a release. |
| 232 | + |
| 233 | +--- |
| 234 | + |
| 235 | +## License |
| 236 | + |
| 237 | +This project is licensed under the [GNU General Public License v3.0](LICENSE). |
| 238 | + |
| 239 | +--- |
| 240 | + |
| 241 | +## Links |
| 242 | + |
| 243 | +- 📖 [Documentation](https://docs.slne.dev/surf-api) |
| 244 | +- 📦 [Releases](https://github.com/SLNE-Development/surf-api/releases) |
| 245 | +- 🐛 [Issues](https://github.com/SLNE-Development/surf-api/issues) |
| 246 | +- 💬 [Discussions](https://github.com/SLNE-Development/surf-api/discussions) |
0 commit comments