{"id":31500281,"url":"https://github.com/spruce-mc/spruce","last_synced_at":"2026-05-05T04:03:25.970Z","repository":{"id":307402973,"uuid":"1029388429","full_name":"Spruce-MC/spruce","owner":"Spruce-MC","description":"A modular, event-driven framework for Minecraft plugins and microservices with gRPC, Redis, and codegen.","archived":false,"fork":false,"pushed_at":"2025-07-31T03:04:48.000Z","size":106,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-07-31T04:25:46.978Z","etag":null,"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"],"latest_commit_sha":null,"homepage":"https://sprucemc.tech","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Spruce-MC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-31T01:25:21.000Z","updated_at":"2025-07-31T03:04:51.000Z","dependencies_parsed_at":"2025-07-31T04:26:13.807Z","dependency_job_id":"929b75fb-b68e-4e6e-b90f-67f0a61929dc","html_url":"https://github.com/Spruce-MC/spruce","commit_stats":null,"previous_names":["spruce-mc/spruce"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Spruce-MC/spruce","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spruce-MC%2Fspruce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spruce-MC%2Fspruce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spruce-MC%2Fspruce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spruce-MC%2Fspruce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Spruce-MC","download_url":"https://codeload.github.com/Spruce-MC/spruce/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Spruce-MC%2Fspruce/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278038304,"owners_count":25919562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-02T02:00:08.890Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2025-10-02T17:10:13.820Z","updated_at":"2025-10-02T17:10:16.484Z","avatar_url":"https://github.com/Spruce-MC.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🌲 Spruce Framework\n\n\u003e **Revolutionary platform for Minecraft plugin development with microservice architecture, gRPC integration and lightning-fast performance**\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![GitHub Stars](https://img.shields.io/github/stars/Spruce-MC/spruce?style=social)](https://github.com/Spruce-MC/spruce)\n[![GitHub Issues](https://img.shields.io/github/issues/Spruce-MC/spruce)](https://github.com/Spruce-MC/spruce/issues)\n[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/Spruce-MC/spruce)\n\n**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**.\n\n---\n\n## ✨ Key Features\n\n### 🏗️ **Modern Architecture**\n- **Microservice-Ready**: Built from ground up for distributed systems\n- **Zero-Reflection**: Compile-time dependency injection with KSP\n- **Event-Driven**: Global event handling with Redis Pub/Sub\n- **Cloud-Native**: Docker \u0026 Kubernetes ready with health checks\n\n### ⚡ **Performance First**\n- **50,000+ RPS**: Ultra-fast gRPC communication\n- **0.1ms Latency**: Redis-powered event streaming\n- **Memory Efficient**: No runtime reflection overhead\n\n### 🧩 **Developer Experience**\n- **Annotation-Based**: Clean, declarative code style\n- **Type-Safe**: Full Kotlin type safety across services\n- **Auto-Config**: YAML configuration with validation\n- **Cross-Platform**: Unified API for Spigot \u0026 Velocity\n\n---\n\n## 📦 Installation\n\nTo use **Spruce** in your Minecraft plugin or microservice, add the following to your `build.gradle.kts`:\n\n### 🔗 Repository\n\n```kotlin\nrepositories {\n    maven(\"https://repo.sprucemc.tech/repository/maven-releases/\")\n}\n```\n\n### 📦 Dependencies\n\n```kotlin\ndependencies {\n    implementation(\"org.spruce:spruce-api:1.0.1\")\n}\n```\n\nIf you are using annotation-based features (e.g. `@SprucePlugin`, `@FileConfig`, etc.), add the plugin and the appropriate processor:\n\n```kotlin\nplugins {\n    id(\"com.google.devtools.ksp\") version \"1.9.23-1.0.20\"\n}\n\ndependencies {\n    // For Spigot plugins\n    ksp(\"org.spruce:spruce-processor-spigot:1.0.1\")\n\n    // Or for Velocity plugins\n    ksp(\"org.spruce:spruce-processor-velocity:1.0.1\")\n}\n```\n\n\n **Minimum Java version**: 17\n\n**Recommended Java version**: 21\n\n---\n\n## 🚀 Your First Plugin\n\n```kotlin\n@SprucePlugin(\n    name = \"MyPlugin\",\n    version = \"1.0.0\",\n    author = \"YourName\"\n)\nclass MyPlugin {\n    @Inject lateinit var server: Server\n    @Inject lateinit var gateway: SpruceGatewayClient\n    \n    @PostConstruct\n    fun onEnable() {\n        server.consoleSender.sendMessage(\"§a[MyPlugin] Started successfully!\")\n    }\n    \n    @Command(\"hello\")\n    fun helloCommand(sender: CommandSender, args: Array\u003cString\u003e) {\n        sender.sendMessage(\"§eHello from Spruce!\")\n    }\n    \n    @EventListener\n    fun onPlayerJoin(event: PlayerJoinEvent) {\n        gateway.emitGlobal(PlayerJoinedNetworkEvent(event.player.name))\n    }\n}\n```\n\n### Configuration\n\n```kotlin\n@FileConfig(\"config.yml\")\nclass MyConfig {\n    lateinit var welcomeMessage: String\n    lateinit var features: Features\n\n    class Features {\n        lateinit var broadcastJoin: List\u003cString\u003e\n        lateinit var customCommand: String\n    }\n}\n```\n\n### Microservice Integration\n\n```kotlin\nclass PlayerStatsService : SpruceServiceBase(\"player-stats\") {\n    \n    @Action(\"get-stats\")\n    fun getPlayerStats(request: PlayerStatsRequest): PlayerStatsResponse {\n        // Your business logic here\n        return PlayerStatsResponse(\n            playerId = request.playerId,\n            level = 42,\n            experience = 15750\n        )\n    }\n}\n```\n\n---\n\n## 🏗️ Architecture Overview\n\n```\n                    🌐 DISTRIBUTED MINECRAFT NETWORK\n    \n┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐\n│   Spigot Server │    │  Velocity Proxy │    │  Spigot Server  │\n│                 │    │                 │    │                 │\n│  ┌─────────────┐│    │  ┌─────────────┐│    │  ┌─────────────┐│\n│  │   Spruce    ││    │  │   Spruce    ││    │  │   Spruce    ││\n│  │   Plugin    ││    │  │   Plugin    ││    │  │   Plugin    ││\n│  └─────────────┘│    │  └─────────────┘│    │  └─────────────┘│\n└──────┬──────────┘    └──────┬──────────┘    └──────┬──────────┘\n       │                      │                      │\n       │                      │                      │\n       └──────────────────────┼──────────────────────┘\n                              │\n                    ┌─────────┴─────────┐\n                    │  Spruce Gateway   │\n                    │    (gRPC Server)  │\n                    └─────────┬─────────┘\n                              │\n    ┌─────────────────────────┼─────────────────────────┐\n    │                         │                         │\n┌───▼────┐              ┌────▼────┐              ┌────▼────┐\n│Service │              │ Service │              │ Service │\n│   A    │◄─────────────┤ Gateway ├─────────────►│   B     │\n│        │              │         │              │         │\n└────────┘              └─────────┘              └─────────┘\n                              │\n                        ┌─────▼─────┐\n                        │   Redis   │\n                        │  Pub/Sub  │ ◄── Global Events\n                        │  Streams  │ ◄── Service Discovery\n                        └───────────┘ ◄── Caching \u0026 State\n```\n\n### Component Roles\n\n- **🎮 Minecraft Servers**: Run Spruce plugins with local event handling\n- **🌐 Spruce Gateway**: Central gRPC hub for service communication\n- **🔧 Microservices**: Independent services handling specific business logic\n- **📡 Redis**: Global event bus, caching, and service coordination\n\n---\n\n## 📁 Advanced Examples\n\n### Event-Driven Communication\n\n```kotlin\n// Define your events\n@GatewayEventType(\"player.banned\")\ndata class PlayerBannedEvent(\n    val playerId: UUID,\n    val reason: String,\n    val bannedBy: String,\n    val expiresAt: Instant?\n): GatewayEvent\n\n// Plugin A: Ban system\n@Component\nclass BanManager {\n    @Inject lateinit var gateway: SpruceGatewayClient\n    \n    @Command(\"ban\")\n    fun banPlayer(sender: CommandSender, args: Array\u003cString\u003e) {\n        val event = PlayerBannedEvent(\n            playerId = UUID.fromString(args[0]),\n            reason = args.drop(1).joinToString(\" \"),\n            bannedBy = sender.name,\n            expiresAt = null\n        )\n        \n        gateway.emitGlobal(event)\n    }\n}\n\n// Plugin B: Discord integration\n@Component  \nclass DiscordNotifier {\n    @GlobalEventListener\n    fun onPlayerBanned(event: PlayerBannedEvent) {\n        discordWebhook.send(\n            \"🔨 Player banned: ${event.playerId} by ${event.bannedBy}\\n\" +\n            \"Reason: ${event.reason}\"\n        )\n    }\n}\n```\n\n### Scheduled Tasks \u0026 Async Operations\n\n```kotlin\n@Component\nclass PerformanceMonitor {\n    @Inject lateinit var logger: Logger\n    @Inject lateinit var gateway: SpruceGatewayClient\n    @Inject lateinit var server: Server\n    \n    @Scheduled(period = 20 * 30) // Every 30 seconds\n    fun monitorTPS() {\n        val tps = server.getTPS()\n        if (tps \u003c 18.0) {\n            logger.warning(\"Low TPS detected: $tps\")\n        }\n    }\n    \n    @Scheduled(delay = 20 * 10, async = true) // Async task after 10 seconds\n    fun cleanupExpiredData() {\n        val cleanupStats = databaseService.cleanupExpiredEntries()\n        logger.info(\"Cleaned up ${cleanupStats.deletedEntries} expired entries\")\n    }\n}\n```\n\n### Cross-Service Communication\n\n```kotlin\n// Service 1: Economy Service\nclass EconomyService : SpruceService(\"economy\") {\n    \n    @Action(\"transfer\")\n    fun transferMoney(request: TransferRequest): TransferResponse {\n        // Validate and process transfer\n        return TransferResponse(success = true, newBalance = 1500.0)\n    }\n}\n\n// Service 2: Shop Plugin\n@Component\nclass ShopManager {\n    @Inject lateinit var gateway: SpruceGatewayClient\n\n    @Command(\"buy\")\n    fun buyItem(player: Player, args: Array\u003cString\u003e) {\n        val call = GatewayCall.of(\n            \"economy\",\n            \"transfer\",\n            TransferRequest(\n                from = player.uniqueId,\n                to = SHOP_ACCOUNT,\n                amount = 100.0\n            ),\n            TransferResponse::class.java\n        )\n\n        gateway.call(call).whenComplete { response, error -\u003e\n            if (error != null) {\n                player.sendMessage(\"§cAn error occurred: ${error.message}\")\n                return@whenComplete\n            }\n\n            if (response.success) {\n                player.sendMessage(\"§aPurchase successful!\")\n                // Give item to player here\n            } else {\n                player.sendMessage(\"§cPurchase failed!\")\n            }\n        }\n    }\n}\n```\n\n---\n\n# ❤️ Contributing\n## We're open to ideas and improvements!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspruce-mc%2Fspruce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspruce-mc%2Fspruce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspruce-mc%2Fspruce/lists"}