An open API service indexing awesome lists of open source software.

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.

Awesome Lists containing this project

README

          

# ๐ŸŒฒ Spruce Framework

> **Revolutionary platform for Minecraft plugin development with microservice architecture, gRPC integration and lightning-fast performance**

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
[![GitHub Stars](https://img.shields.io/github/stars/Spruce-MC/spruce?style=social)](https://github.com/Spruce-MC/spruce)
[![GitHub Issues](https://img.shields.io/github/issues/Spruce-MC/spruce)](https://github.com/Spruce-MC/spruce/issues)
[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](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!