https://github.com/spruce-mc/spruce
A modular, event-driven framework for Minecraft plugins and microservices with gRPC, Redis, and codegen.
https://github.com/spruce-mc/spruce
annotation-processing cloud code-generation dependency-injection event-driven framework gaming grpc kotlin ksp microservices minecraft modular paper plugin-framework redis scalable spigot spring-like velocity
Last synced: 6 months ago
JSON representation
A modular, event-driven framework for Minecraft plugins and microservices with gRPC, Redis, and codegen.
- Host: GitHub
- URL: https://github.com/spruce-mc/spruce
- Owner: Spruce-MC
- License: mit
- Created: 2025-07-31T01:25:21.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2025-07-31T03:04:48.000Z (8 months ago)
- Last Synced: 2025-07-31T04:25:46.978Z (8 months ago)
- Topics: annotation-processing, cloud, code-generation, dependency-injection, event-driven, framework, gaming, grpc, kotlin, ksp, microservices, minecraft, modular, paper, plugin-framework, redis, scalable, spigot, spring-like, velocity
- Language: Kotlin
- Homepage: https://sprucemc.tech
- Size: 104 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ๐ฒ Spruce Framework
> **Revolutionary platform for Minecraft plugin development with microservice architecture, gRPC integration and lightning-fast performance**
[](https://opensource.org/licenses/MIT)
[](https://github.com/Spruce-MC/spruce)
[](https://github.com/Spruce-MC/spruce/issues)
[](https://github.com/Spruce-MC/spruce)
**Spruce** is a next-generation framework that brings modern software architecture to Minecraft plugin development. Built with **Kotlin**, powered by **gRPC**, **Redis**, and **compile-time code generation**.
---
## โจ Key Features
### ๐๏ธ **Modern Architecture**
- **Microservice-Ready**: Built from ground up for distributed systems
- **Zero-Reflection**: Compile-time dependency injection with KSP
- **Event-Driven**: Global event handling with Redis Pub/Sub
- **Cloud-Native**: Docker & Kubernetes ready with health checks
### โก **Performance First**
- **50,000+ RPS**: Ultra-fast gRPC communication
- **0.1ms Latency**: Redis-powered event streaming
- **Memory Efficient**: No runtime reflection overhead
### ๐งฉ **Developer Experience**
- **Annotation-Based**: Clean, declarative code style
- **Type-Safe**: Full Kotlin type safety across services
- **Auto-Config**: YAML configuration with validation
- **Cross-Platform**: Unified API for Spigot & Velocity
---
## ๐ฆ Installation
To use **Spruce** in your Minecraft plugin or microservice, add the following to your `build.gradle.kts`:
### ๐ Repository
```kotlin
repositories {
maven("https://repo.sprucemc.tech/repository/maven-releases/")
}
```
### ๐ฆ Dependencies
```kotlin
dependencies {
implementation("org.spruce:spruce-api:1.0.1")
}
```
If you are using annotation-based features (e.g. `@SprucePlugin`, `@FileConfig`, etc.), add the plugin and the appropriate processor:
```kotlin
plugins {
id("com.google.devtools.ksp") version "1.9.23-1.0.20"
}
dependencies {
// For Spigot plugins
ksp("org.spruce:spruce-processor-spigot:1.0.1")
// Or for Velocity plugins
ksp("org.spruce:spruce-processor-velocity:1.0.1")
}
```
**Minimum Java version**: 17
**Recommended Java version**: 21
---
## ๐ Your First Plugin
```kotlin
@SprucePlugin(
name = "MyPlugin",
version = "1.0.0",
author = "YourName"
)
class MyPlugin {
@Inject lateinit var server: Server
@Inject lateinit var gateway: SpruceGatewayClient
@PostConstruct
fun onEnable() {
server.consoleSender.sendMessage("ยงa[MyPlugin] Started successfully!")
}
@Command("hello")
fun helloCommand(sender: CommandSender, args: Array) {
sender.sendMessage("ยงeHello from Spruce!")
}
@EventListener
fun onPlayerJoin(event: PlayerJoinEvent) {
gateway.emitGlobal(PlayerJoinedNetworkEvent(event.player.name))
}
}
```
### Configuration
```kotlin
@FileConfig("config.yml")
class MyConfig {
lateinit var welcomeMessage: String
lateinit var features: Features
class Features {
lateinit var broadcastJoin: List
lateinit var customCommand: String
}
}
```
### Microservice Integration
```kotlin
class PlayerStatsService : SpruceServiceBase("player-stats") {
@Action("get-stats")
fun getPlayerStats(request: PlayerStatsRequest): PlayerStatsResponse {
// Your business logic here
return PlayerStatsResponse(
playerId = request.playerId,
level = 42,
experience = 15750
)
}
}
```
---
## ๐๏ธ Architecture Overview
```
๐ DISTRIBUTED MINECRAFT NETWORK
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ
โ Spigot Server โ โ Velocity Proxy โ โ Spigot Server โ
โ โ โ โ โ โ
โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ
โ โ Spruce โโ โ โ Spruce โโ โ โ Spruce โโ
โ โ Plugin โโ โ โ Plugin โโ โ โ Plugin โโ
โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโ
โโโโโโโโฌโโโโโโโโโโโ โโโโโโโโฌโโโโโโโโโโโ โโโโโโโโฌโโโโโโโโโโโ
โ โ โ
โ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโดโโโโโโโโโโ
โ Spruce Gateway โ
โ (gRPC Server) โ
โโโโโโโโโโโฌโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โโโโโผโโโโโ โโโโโโผโโโโโ โโโโโโผโโโโโ
โService โ โ Service โ โ Service โ
โ A โโโโโโโโโโโโโโโโค Gateway โโโโโโโโโโโโโโโบโ B โ
โ โ โ โ โ โ
โโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ
โ
โโโโโโโผโโโโโโ
โ Redis โ
โ Pub/Sub โ โโโ Global Events
โ Streams โ โโโ Service Discovery
โโโโโโโโโโโโโ โโโ Caching & State
```
### Component Roles
- **๐ฎ Minecraft Servers**: Run Spruce plugins with local event handling
- **๐ Spruce Gateway**: Central gRPC hub for service communication
- **๐ง Microservices**: Independent services handling specific business logic
- **๐ก Redis**: Global event bus, caching, and service coordination
---
## ๐ Advanced Examples
### Event-Driven Communication
```kotlin
// Define your events
@GatewayEventType("player.banned")
data class PlayerBannedEvent(
val playerId: UUID,
val reason: String,
val bannedBy: String,
val expiresAt: Instant?
): GatewayEvent
// Plugin A: Ban system
@Component
class BanManager {
@Inject lateinit var gateway: SpruceGatewayClient
@Command("ban")
fun banPlayer(sender: CommandSender, args: Array) {
val event = PlayerBannedEvent(
playerId = UUID.fromString(args[0]),
reason = args.drop(1).joinToString(" "),
bannedBy = sender.name,
expiresAt = null
)
gateway.emitGlobal(event)
}
}
// Plugin B: Discord integration
@Component
class DiscordNotifier {
@GlobalEventListener
fun onPlayerBanned(event: PlayerBannedEvent) {
discordWebhook.send(
"๐จ Player banned: ${event.playerId} by ${event.bannedBy}\n" +
"Reason: ${event.reason}"
)
}
}
```
### Scheduled Tasks & Async Operations
```kotlin
@Component
class PerformanceMonitor {
@Inject lateinit var logger: Logger
@Inject lateinit var gateway: SpruceGatewayClient
@Inject lateinit var server: Server
@Scheduled(period = 20 * 30) // Every 30 seconds
fun monitorTPS() {
val tps = server.getTPS()
if (tps < 18.0) {
logger.warning("Low TPS detected: $tps")
}
}
@Scheduled(delay = 20 * 10, async = true) // Async task after 10 seconds
fun cleanupExpiredData() {
val cleanupStats = databaseService.cleanupExpiredEntries()
logger.info("Cleaned up ${cleanupStats.deletedEntries} expired entries")
}
}
```
### Cross-Service Communication
```kotlin
// Service 1: Economy Service
class EconomyService : SpruceService("economy") {
@Action("transfer")
fun transferMoney(request: TransferRequest): TransferResponse {
// Validate and process transfer
return TransferResponse(success = true, newBalance = 1500.0)
}
}
// Service 2: Shop Plugin
@Component
class ShopManager {
@Inject lateinit var gateway: SpruceGatewayClient
@Command("buy")
fun buyItem(player: Player, args: Array) {
val call = GatewayCall.of(
"economy",
"transfer",
TransferRequest(
from = player.uniqueId,
to = SHOP_ACCOUNT,
amount = 100.0
),
TransferResponse::class.java
)
gateway.call(call).whenComplete { response, error ->
if (error != null) {
player.sendMessage("ยงcAn error occurred: ${error.message}")
return@whenComplete
}
if (response.success) {
player.sendMessage("ยงaPurchase successful!")
// Give item to player here
} else {
player.sendMessage("ยงcPurchase failed!")
}
}
}
}
```
---
# โค๏ธ Contributing
## We're open to ideas and improvements!