papermc-skill 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/bin/cli.js +307 -0
- package/docs/01-modern-paper-architecture.md +172 -0
- package/docs/02-adventure-and-minimessage.md +160 -0
- package/docs/03-persistent-data-container.md +179 -0
- package/docs/04-commands-and-brigadier.md +157 -0
- package/docs/05-concurrency-and-folia.md +150 -0
- package/docs/06-events-and-listeners.md +163 -0
- package/docs/07-best-practices-and-perf.md +118 -0
- package/docs/08-gui-and-menus.md +198 -0
- package/docs/09-custom-items-and-combat.md +171 -0
- package/docs/10-database-and-storage.md +187 -0
- package/package.json +44 -0
- package/skills/paper-plugin-dev/SKILL.md +167 -0
- package/templates/paper-modern-template/.editorconfig +8 -0
- package/templates/paper-modern-template/.gradle/9.4.1/checksums/checksums.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/checksums/md5-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/checksums/sha1-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/executionHistory/executionHistory.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/executionHistory/executionHistory.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/fileChanges/last-build.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/fileHashes/fileHashes.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/fileHashes/fileHashes.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/fileHashes/resourceHashesCache.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.4.1/gc.properties +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
- package/templates/paper-modern-template/.gradle/file-system.probe +0 -0
- package/templates/paper-modern-template/.gradle/vcs-1/gc.properties +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/ExamplePaperPlugin.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/PaperPluginBootstrap.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/command/HelloCommand.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/data/PluginDataKeys.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/listener/PlayerEventListener.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/util/SchedulerAdapter.class +0 -0
- package/templates/paper-modern-template/build/tmp/compileJava/previous-compilation-data.bin +0 -0
- package/templates/paper-modern-template/build.gradle.kts +54 -0
- package/templates/paper-modern-template/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/templates/paper-modern-template/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/templates/paper-modern-template/gradlew +248 -0
- package/templates/paper-modern-template/gradlew.bat +93 -0
- package/templates/paper-modern-template/settings.gradle.kts +5 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/ExamplePaperPlugin.java +35 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/PaperPluginBootstrap.java +30 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/command/HelloCommand.java +55 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/data/PluginDataKeys.java +22 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/listener/PlayerEventListener.java +61 -0
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/util/SchedulerAdapter.java +77 -0
- package/templates/paper-modern-template/src/main/resources/config.yml +10 -0
- package/templates/paper-modern-template/src/main/resources/paper-plugin.yml +9 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-09-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Interactive NPX CLI Tool (`papermc-skill`)**:
|
|
12
|
+
- `npx papermc-skill create [name]`: Interactive project scaffolding for modern Paper & Folia plugins with customizable package name, author, and Minecraft target version.
|
|
13
|
+
- `npx papermc-skill install-skill [--global]`: Automated installation of the `paper-plugin-dev` AI agent skill for workspace (`.agents/skills/`) or global (`~/.gemini/config/skills/`).
|
|
14
|
+
- `npx papermc-skill docs [topic]`: Instant in-terminal viewer for architectural guides.
|
|
15
|
+
- **Modern Starter Template (`templates/paper-modern-template`)**:
|
|
16
|
+
- Gradle Kotlin DSL build setup (`build.gradle.kts`) with Java 21 toolchain and Foojay resolver convention.
|
|
17
|
+
- Paper 1.21.4 API dependency and instant testing tasks (`./gradlew runServer`, `./gradlew runFoliaServer`).
|
|
18
|
+
- Bundled official Gradle Wrapper (`gradlew`, `gradlew.bat`).
|
|
19
|
+
- Native Brigadier command registration via `LifecycleEvents.COMMANDS`.
|
|
20
|
+
- Kyori Adventure & MiniMessage integration without deprecated formatting codes.
|
|
21
|
+
- PersistentDataContainer (PDC) usage for player join tracking.
|
|
22
|
+
- Transparent Folia multi-threaded scheduler adapter (`SchedulerAdapter.java`).
|
|
23
|
+
- **Comprehensive Architectural & Technical Guides (`docs/`)**:
|
|
24
|
+
- `01-modern-paper-architecture.md`: Paperweight userdev, `paper-plugin.yml`, and `PluginBootstrap`.
|
|
25
|
+
- `02-adventure-and-minimessage.md`: Kyori Adventure Component, gradient tags, Title, Sound, and BossBar.
|
|
26
|
+
- `03-persistent-data-container.md`: PDC on Item, Entity, and Chunk with custom `PersistentDataType`.
|
|
27
|
+
- `04-commands-and-brigadier.md`: Paper native Brigadier commands and Incendo Cloud framework.
|
|
28
|
+
- `05-concurrency-and-folia.md`: Threaded Regions, Folia Schedulers, and thread safety.
|
|
29
|
+
- `06-events-and-listeners.md`: Paper-exclusive events, event priority, and custom events.
|
|
30
|
+
- `07-best-practices-and-perf.md`: Memory leak prevention, async chunk loading, and anti-patterns.
|
|
31
|
+
- `08-gui-and-menus.md`: Safe custom `InventoryHolder` menus with PDC click action buttons.
|
|
32
|
+
- `09-custom-items-and-combat.md`: Raycasting spells, particles, vanilla cooldowns, and combat mechanics.
|
|
33
|
+
- `10-database-and-storage.md`: Asynchronous database handling with SQLite and MySQL HikariCP pool.
|
|
34
|
+
- **AI Agent Skill Specification (`skills/paper-plugin-dev/SKILL.md`)**:
|
|
35
|
+
- Step-by-step workflow, dependency matrix, and ready-to-use code recipes for coding assistants.
|
|
36
|
+
- **Nine Cloned Open-Source Reference Plugins (`references/`)**:
|
|
37
|
+
- `squaremap`, `chunky`, `luckperms`, `miniplaceholders`, `vault-api`, `run-paper`, `decentholograms`, `bstats`, and `paperweight-test-plugin`.
|
|
38
|
+
- **Open-Source Governance & Security Suite**:
|
|
39
|
+
- MIT License (`LICENSE`), Code of Conduct (`CODE_OF_CONDUCT.md`), Contributing Guide (`CONTRIBUTING.md`), and Security Policy (`SECURITY.md`).
|
|
40
|
+
- GitHub Issue and Pull Request templates (`.github/ISSUE_TEMPLATE/`).
|
|
41
|
+
- Automated CI and NPM Publish workflows with provenance (`.github/workflows/`).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paper Minecraft Plugin Skill Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🎮 Paper Minecraft Plugin Skill & Knowledge Base
|
|
4
|
+
|
|
5
|
+
[](https://papermc.io)
|
|
6
|
+
[](https://adoptium.net)
|
|
7
|
+
[](https://github.com/PaperMC/Folia)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](CONTRIBUTING.md)
|
|
10
|
+
|
|
11
|
+
**A comprehensive, production-grade knowledge base, architectural reference collection, starter template, and AI engineering skill for modern Minecraft Java Server plugin development on [PaperMC](https://papermc.io) and [Folia](https://github.com/PaperMC/Folia).**
|
|
12
|
+
|
|
13
|
+
[Features](#-features) • [Quickstart](#-quickstart) • [Architecture Guide](#-documentation-suite) • [Reference Plugins](#-cloned-reference-plugins) • [AI Skill](#-ai-agent-integration) • [Contributing](#-contributing)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
## 🌟 Overview
|
|
20
|
+
|
|
21
|
+
The Minecraft server development ecosystem has undergone massive architectural shifts with Minecraft 1.20.5+ and 1.21+. Legacy Bukkit/Spigot habits—such as section sign color formatting (`§`), `plugin.yml` command registration, third-party NBT hacks, and blocking main-thread schedulers—lead to performance degradation, memory leaks, and fatal crashes on modern servers.
|
|
22
|
+
|
|
23
|
+
This repository serves as an **authoritative, single-source knowledge base** and **developer skill** designed to empower both human software engineers and AI pair-programming assistants to build performant, thread-safe, and idiomatic Paper plugins.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## ⚡ Key Architecture Highlights
|
|
28
|
+
|
|
29
|
+
| Modern Paper Standard | Legacy Spigot / Bukkit | Why Modern Matters |
|
|
30
|
+
| :--- | :--- | :--- |
|
|
31
|
+
| **Kyori Adventure & MiniMessage** | `ChatColor` & `§` color codes | Type-safe, component-based, 24-bit TrueColor/Hex gradients, interactive click/hover events, and zero string-injection risks. |
|
|
32
|
+
| **PersistentDataContainer (PDC)** | External NBT libraries / Lore parsing | Official native NBT storage on Items, Entities, Chunks, and Worlds. Serializes Java records seamlessly. |
|
|
33
|
+
| **Paper Native Brigadier** | `plugin.yml` & `setExecutor(...)` | Client-side syntax validation, auto tab-completion, and command routing directly inside Mojang's Brigadier engine. |
|
|
34
|
+
| **Regionized Threading (Folia)** | Single Main Thread `BukkitScheduler` | Scales across CPU cores via Threaded Regions. Uses `RegionScheduler`, `EntityScheduler`, and `AsyncScheduler`. |
|
|
35
|
+
| **Mojang Mappings (Paperweight)** | Spigot remapped / Obfuscated NMS | Zero-remapping runtime on Paper 1.20.5+; direct, clean access to Mojang server internals with `paperweight-userdev`. |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 📚 Documentation Suite
|
|
40
|
+
|
|
41
|
+
Our documentation suite includes 10 comprehensive, in-depth architectural guides:
|
|
42
|
+
|
|
43
|
+
1. **[01. Modern Paper Architecture](https://github.com/ardianryan/papermc-skill/blob/main/docs/01-modern-paper-architecture.md)**
|
|
44
|
+
*Deep dive into `paper-plugin.yml`, `PluginBootstrap` vs `JavaPlugin`, Gradle `paperweight-userdev`, and runtime Mojang mappings.*
|
|
45
|
+
2. **[02. Adventure API & MiniMessage](https://github.com/ardianryan/papermc-skill/blob/main/docs/02-adventure-and-minimessage.md)**
|
|
46
|
+
*Hex colors, gradient tags, dynamic `TagResolver` placeholders, `ComponentLogger`, Titles, BossBars, and Sound playback.*
|
|
47
|
+
3. **[03. PersistentDataContainer (PDC)](https://github.com/ardianryan/papermc-skill/blob/main/docs/03-persistent-data-container.md)**
|
|
48
|
+
*Storing custom data on ItemStacks, Entities, and Chunks; implementing custom `PersistentDataType<T, Z>` for POJO/record serialization.*
|
|
49
|
+
4. **[04. Modern Command Systems: Brigadier & Cloud](https://github.com/ardianryan/papermc-skill/blob/main/docs/04-commands-and-brigadier.md)**
|
|
50
|
+
*Paper's native Brigadier command registration via `LifecycleEvents.COMMANDS`, argument parsing, suggestions, and Incendo Cloud.*
|
|
51
|
+
5. **[05. Concurrency, Threading & Folia Compatibility](https://github.com/ardianryan/papermc-skill/blob/main/docs/05-concurrency-and-folia.md)**
|
|
52
|
+
*Threaded regions, region schedulers, entity schedulers, async schedulers, and universal cross-platform scheduling abstractions.*
|
|
53
|
+
6. **[06. Event System & Listeners](https://github.com/ardianryan/papermc-skill/blob/main/docs/06-events-and-listeners.md)**
|
|
54
|
+
*Paper-exclusive events (`AsyncChatEvent`, `PrePlayerAttackEntityEvent`), event priority lifecycle, and custom event authoring.*
|
|
55
|
+
7. **[07. Best Practices & Performance Optimization](https://github.com/ardianryan/papermc-skill/blob/main/docs/07-best-practices-and-perf.md)**
|
|
56
|
+
*Preventing GC memory leaks (UUID vs entity references), asynchronous chunk loading (`world.getChunkAtAsync`), and anti-patterns.*
|
|
57
|
+
8. **[08. Modern GUI & Inventory Menus](https://github.com/ardianryan/papermc-skill/blob/main/docs/08-gui-and-menus.md)**
|
|
58
|
+
*Custom `InventoryHolder` verification, PDC-tagged interactive buttons, and bulletproof duplication-exploit prevention.*
|
|
59
|
+
9. **[09. Custom Items, Abilities & Combat](https://github.com/ardianryan/papermc-skill/blob/main/docs/09-custom-items-and-combat.md)**
|
|
60
|
+
*Raycasting hitscan spells (`rayTraceEntities`), particle beam effects, vanilla visual cooldowns, and Adventure sound effects.*
|
|
61
|
+
10. **[10. Asynchronous Database Storage](https://github.com/ardianryan/papermc-skill/blob/main/docs/10-database-and-storage.md)**
|
|
62
|
+
*SQLite and MySQL/PostgreSQL connection pooling via HikariCP, thread-safe asynchronous DAO pattern with `CompletableFuture`.*
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 🔍 Cloned Reference Plugins
|
|
67
|
+
|
|
68
|
+
Nine industry-standard open-source repositories have been cloned into [`references/`](references/) as real-world architectural blueprints:
|
|
69
|
+
|
|
70
|
+
| Repository | Source Origin | Core Concepts Demonstrated |
|
|
71
|
+
| :--- | :--- | :--- |
|
|
72
|
+
| **[`squaremap`](references/squaremap)** | `jpenilla/squaremap` | Developed by a PaperMC core developer. World rendering, `paperweight-userdev`, Incendo Cloud commands, and Folia multi-threading. |
|
|
73
|
+
| **[`chunky`](references/chunky)** | `pop4959/Chunky` | High-throughput async chunk loading (`world.getChunkAtAsync`), mathematical optimizations, and multi-platform platform adapters. |
|
|
74
|
+
| **[`luckperms`](references/luckperms)** | `LuckPerms/LuckPerms` | Enterprise-grade Java architecture: HikariCP connection pooling, custom internal event bus, caching hierarchy, and async I/O. |
|
|
75
|
+
| **[`miniplaceholders`](references/miniplaceholders)** | `MiniPlaceholders/MiniPlaceholders` | Advanced Kyori Adventure API usage, custom MiniMessage tag resolvers, and modern `paper-plugin.yml` bootstrapping. |
|
|
76
|
+
| **[`vault-api`](references/vault-api)** | `MilkBowl/VaultAPI` | The standard ecosystem bridge API for economy and permissions in Minecraft Java. |
|
|
77
|
+
| **[`run-paper`](references/run-paper)** | `jpenilla/run-paper` | The Gradle plugin source code for running instant local Paper and Folia test environments. |
|
|
78
|
+
| **[`decentholograms`](references/decentholograms)** | `DecentSoftware-eu/DecentHolograms` | Display entity manipulation, protocol packet handling, and dynamic per-player text rendering. |
|
|
79
|
+
| **[`bstats`](references/bstats)** | `Bastian/bStats-Metrics` | Industry-standard telemetry and metrics integration for Minecraft server plugins. |
|
|
80
|
+
| **[`paperweight-test-plugin`](references/paperweight-test-plugin)** | `PaperMC/paperweight-test-plugin` | Official PaperMC test project for `paperweight-userdev` Gradle configurations. |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 🚀 Quickstart
|
|
85
|
+
|
|
86
|
+
### Method 1: Instant Scaffolding via NPX (Recommended)
|
|
87
|
+
|
|
88
|
+
You can scaffold a new Paper plugin or install the AI skill instantly anywhere using `npx`:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# 1. Interactively create a new modern Paper & Folia plugin
|
|
92
|
+
npx papermc-skill create MyAwesomePlugin
|
|
93
|
+
|
|
94
|
+
# 2. Or install the AI skill into your workspace (.agents/skills) or globally
|
|
95
|
+
npx papermc-skill install-skill --global
|
|
96
|
+
|
|
97
|
+
# 3. Read architectural guides right in your terminal
|
|
98
|
+
npx papermc-skill docs
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Method 2: Manual Clone & Local Server Test
|
|
102
|
+
|
|
103
|
+
A production-ready Gradle starter template is available in [`templates/paper-modern-template/`](templates/paper-modern-template).
|
|
104
|
+
|
|
105
|
+
#### Prerequisites
|
|
106
|
+
- **Java 21 SDK** installed ([Adoptium Temurin 21](https://adoptium.net/)):
|
|
107
|
+
- macOS: `brew install openjdk@21`
|
|
108
|
+
- Linux: `sudo apt install openjdk-21-jdk`
|
|
109
|
+
- Gradle is bundled inside the template via `./gradlew` (no manual Gradle install required).
|
|
110
|
+
|
|
111
|
+
#### Running a Local Test Server in 60 Seconds
|
|
112
|
+
```bash
|
|
113
|
+
# 1. Navigate to the template project
|
|
114
|
+
cd templates/paper-modern-template
|
|
115
|
+
|
|
116
|
+
# 2. Launch an automated Paper test server
|
|
117
|
+
./gradlew runServer
|
|
118
|
+
|
|
119
|
+
# 3. Or launch a multi-threaded Folia test server
|
|
120
|
+
./gradlew runFoliaServer
|
|
121
|
+
```
|
|
122
|
+
The task will automatically download the Paper/Folia runtime, accept the Mojang EULA, compile your code, and launch the server with your plugin enabled.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 🤖 AI Agent Integration
|
|
127
|
+
|
|
128
|
+
This repository includes a standardized AI Assistant Skill in [`skills/paper-plugin-dev/SKILL.md`](skills/paper-plugin-dev/SKILL.md).
|
|
129
|
+
|
|
130
|
+
When working with AI coding assistants (such as Antigravity, GitHub Copilot, or Cursor):
|
|
131
|
+
- Point your assistant to `skills/paper-plugin-dev/SKILL.md`.
|
|
132
|
+
- The assistant will automatically adopt modern Paper 1.21+ standards, avoid deprecated Spigot methods, enforce Folia thread-safety, and use Adventure/PDC patterns out of the box.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 📁 Repository Structure
|
|
137
|
+
|
|
138
|
+
```text
|
|
139
|
+
minecraft-plugin-skill/
|
|
140
|
+
├── .github/ # Issue & Pull Request templates
|
|
141
|
+
│ ├── ISSUE_TEMPLATE/
|
|
142
|
+
│ │ ├── bug_report.yml
|
|
143
|
+
│ │ └── feature_request.yml
|
|
144
|
+
│ └── pull_request_template.md
|
|
145
|
+
├── docs/ # 10 In-depth architectural guides
|
|
146
|
+
├── references/ # 9 Cloned open-source reference plugins
|
|
147
|
+
├── skills/
|
|
148
|
+
│ └── paper-plugin-dev/ # AI Assistant Skill definition (SKILL.md)
|
|
149
|
+
├── templates/
|
|
150
|
+
│ └── paper-modern-template/ # Standalone, runnable starter plugin project
|
|
151
|
+
├── CODE_OF_CONDUCT.md # Contributor Covenant v2.1
|
|
152
|
+
├── CONTRIBUTING.md # Comprehensive contribution guide
|
|
153
|
+
├── LICENSE # MIT License
|
|
154
|
+
├── README.md # Repository documentation
|
|
155
|
+
└── SECURITY.md # Security policy & reporting guidelines
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## 🤝 Contributing
|
|
161
|
+
|
|
162
|
+
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) and [Code of Conduct](CODE_OF_CONDUCT.md) before submitting pull requests or opening issues.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 📄 License & Trademarks
|
|
167
|
+
|
|
168
|
+
- This project is licensed under the **[MIT License](LICENSE)**.
|
|
169
|
+
- *Minecraft* is a registered trademark of Mojang Synergies AB. This project is not affiliated with or endorsed by Mojang Synergies AB, Microsoft Corporation, or PaperMC.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import readline from 'node:readline';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const ROOT_DIR = path.resolve(__dirname, '..');
|
|
11
|
+
|
|
12
|
+
// ANSI Color Helpers (Pure zero-dependency, safe for all terminals)
|
|
13
|
+
const c = {
|
|
14
|
+
reset: '\x1b[0m',
|
|
15
|
+
bold: '\x1b[1m',
|
|
16
|
+
dim: '\x1b[2m',
|
|
17
|
+
cyan: '\x1b[36m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
yellow: '\x1b[33m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
gray: '\x1b[90m'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function printBanner() {
|
|
25
|
+
console.log(`
|
|
26
|
+
${c.cyan}${c.bold} ____ __ __ ____
|
|
27
|
+
| _ \\ __ _ _ __ ___ _ __ | \\/ |/ ___| ____ _ _ _ _
|
|
28
|
+
| |_) / _\` | '_ \\ / _ \\ '__|____| |\\/| | | / ___|| | __(_) | |
|
|
29
|
+
| __/ (_| | |_) | __/ | |_____| | | | |___ \\___ \\| |/ / | | |
|
|
30
|
+
|_| \\__,_| .__/ \\___|_| |_| |_|\\____| |____/|_|\\_\\_|_|_|
|
|
31
|
+
|_| ${c.reset}${c.dim}Modern Paper & Folia Plugin Toolchain${c.reset}
|
|
32
|
+
`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function prompt(question, defaultValue) {
|
|
36
|
+
const rl = readline.createInterface({
|
|
37
|
+
input: process.stdin,
|
|
38
|
+
output: process.stdout
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
const q = defaultValue
|
|
43
|
+
? `${c.cyan}?${c.reset} ${c.bold}${question}${c.reset} ${c.dim}(${defaultValue})${c.reset}: `
|
|
44
|
+
: `${c.cyan}?${c.reset} ${c.bold}${question}${c.reset}: `;
|
|
45
|
+
|
|
46
|
+
rl.question(q, (answer) => {
|
|
47
|
+
rl.close();
|
|
48
|
+
resolve(answer.trim() || defaultValue);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Strict Input Sanitization (Prevents path traversal, code injection & illegal characters)
|
|
54
|
+
function sanitizePluginName(name) {
|
|
55
|
+
const sanitized = String(name || '').trim().replace(/[^a-zA-Z0-9_-]/g, '');
|
|
56
|
+
if (!sanitized) {
|
|
57
|
+
throw new Error('Plugin name must contain at least one alphanumeric character.');
|
|
58
|
+
}
|
|
59
|
+
return sanitized;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function sanitizePackageName(pkg) {
|
|
63
|
+
const sanitized = String(pkg || '').trim().toLowerCase().replace(/[^a-z0-9_.]/g, '');
|
|
64
|
+
if (!sanitized || sanitized.startsWith('.') || sanitized.endsWith('.') || sanitized.includes('..')) {
|
|
65
|
+
throw new Error('Invalid package name format. Example: com.example.myplugin');
|
|
66
|
+
}
|
|
67
|
+
return sanitized;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sanitizeSafeDir(baseDir, targetName) {
|
|
71
|
+
const safeName = path.basename(targetName).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
72
|
+
const resolved = path.resolve(baseDir, safeName);
|
|
73
|
+
if (!resolved.startsWith(path.resolve(baseDir))) {
|
|
74
|
+
throw new Error('Security Error: Path traversal attempt detected.');
|
|
75
|
+
}
|
|
76
|
+
return resolved;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function copyDirRecursive(src, dest, replacements = {}) {
|
|
80
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
81
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
82
|
+
|
|
83
|
+
for (const entry of entries) {
|
|
84
|
+
const srcPath = path.join(src, entry.name);
|
|
85
|
+
const destPath = path.join(dest, entry.name);
|
|
86
|
+
|
|
87
|
+
if (entry.isDirectory()) {
|
|
88
|
+
copyDirRecursive(srcPath, destPath, replacements);
|
|
89
|
+
} else {
|
|
90
|
+
const content = fs.readFileSync(srcPath);
|
|
91
|
+
|
|
92
|
+
// Text replacement only on safe, recognized source file types
|
|
93
|
+
if (
|
|
94
|
+
entry.name.endsWith('.gradle.kts') ||
|
|
95
|
+
entry.name.endsWith('.yml') ||
|
|
96
|
+
entry.name.endsWith('.yaml') ||
|
|
97
|
+
entry.name.endsWith('.java') ||
|
|
98
|
+
entry.name.endsWith('.md')
|
|
99
|
+
) {
|
|
100
|
+
let text = content.toString('utf-8');
|
|
101
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
102
|
+
text = text.replaceAll(key, value);
|
|
103
|
+
}
|
|
104
|
+
fs.writeFileSync(destPath, text, 'utf-8');
|
|
105
|
+
} else {
|
|
106
|
+
fs.copyFileSync(srcPath, destPath);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Preserve executable permissions for gradlew wrapper
|
|
110
|
+
if (entry.name === 'gradlew') {
|
|
111
|
+
try {
|
|
112
|
+
fs.chmodSync(destPath, 0o755);
|
|
113
|
+
} catch (_) {}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function handleCreate(targetDirArg) {
|
|
120
|
+
printBanner();
|
|
121
|
+
console.log(`${c.bold}Scaffolding a new Modern Paper Plugin...${c.reset}\n`);
|
|
122
|
+
|
|
123
|
+
const rawPluginName = await prompt('Plugin Name', targetDirArg || 'MyPaperPlugin');
|
|
124
|
+
const pluginName = sanitizePluginName(rawPluginName);
|
|
125
|
+
const targetDir = sanitizeSafeDir(process.cwd(), targetDirArg || pluginName.toLowerCase());
|
|
126
|
+
|
|
127
|
+
if (fs.existsSync(targetDir) && fs.readdirSync(targetDir).length > 0) {
|
|
128
|
+
console.error(`\n${c.red}Error:${c.reset} Target directory '${targetDir}' already exists and is not empty.\n`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const rawPackage = await prompt('Package Group (e.g. com.example.plugin)', `com.example.${pluginName.toLowerCase().replace(/[^a-z0-9]/g, '')}`);
|
|
133
|
+
const packageName = sanitizePackageName(rawPackage);
|
|
134
|
+
|
|
135
|
+
const rawAuthor = await prompt('Author Name', 'Developer');
|
|
136
|
+
const authorName = String(rawAuthor || 'Developer').replace(/[^a-zA-Z0-9_\s-]/g, '').trim() || 'Developer';
|
|
137
|
+
|
|
138
|
+
const mcVersion = await prompt('Paper Minecraft Version', '1.21.4');
|
|
139
|
+
const cleanMcVersion = String(mcVersion || '1.21.4').replace(/[^0-9.]/g, '') || '1.21.4';
|
|
140
|
+
|
|
141
|
+
console.log(`\n${c.yellow}Creating project at:${c.reset} ${c.dim}${targetDir}${c.reset}`);
|
|
142
|
+
|
|
143
|
+
const templateDir = path.join(ROOT_DIR, 'templates', 'paper-modern-template');
|
|
144
|
+
if (!fs.existsSync(templateDir)) {
|
|
145
|
+
console.error(`${c.red}Error:${c.reset} Starter template directory not found at ${templateDir}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 1. Copy base template with sanitized replacements
|
|
150
|
+
copyDirRecursive(templateDir, targetDir, {
|
|
151
|
+
'ModernPaperTemplate': pluginName,
|
|
152
|
+
'paper-modern-template': pluginName.toLowerCase().replace(/[^a-z0-9_-]/gi, '-'),
|
|
153
|
+
'com.example.paperplugin': packageName,
|
|
154
|
+
'1.21.4': cleanMcVersion,
|
|
155
|
+
'Developer': authorName
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 2. Refactor Java package directory structure if package changed from com.example.paperplugin
|
|
159
|
+
const defaultPackagePath = path.join(targetDir, 'src', 'main', 'java', 'com', 'example', 'paperplugin');
|
|
160
|
+
const targetPackagePath = path.join(targetDir, 'src', 'main', 'java', ...packageName.split('.'));
|
|
161
|
+
|
|
162
|
+
if (defaultPackagePath !== targetPackagePath && fs.existsSync(defaultPackagePath)) {
|
|
163
|
+
fs.mkdirSync(targetPackagePath, { recursive: true });
|
|
164
|
+
const files = fs.readdirSync(defaultPackagePath);
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
fs.renameSync(path.join(defaultPackagePath, file), path.join(targetPackagePath, file));
|
|
167
|
+
}
|
|
168
|
+
// Cleanup empty old dirs safely
|
|
169
|
+
try {
|
|
170
|
+
fs.rmSync(path.join(targetDir, 'src', 'main', 'java', 'com', 'example'), { recursive: true, force: true });
|
|
171
|
+
} catch (_) {}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
console.log(`\n${c.green}${c.bold}✔ Project created successfully!${c.reset}\n`);
|
|
175
|
+
console.log(`${c.bold}To start developing and run your test server:${c.reset}`);
|
|
176
|
+
console.log(` ${c.cyan}cd${c.reset} ${path.relative(process.cwd(), targetDir) || '.'}`);
|
|
177
|
+
console.log(` ${c.cyan}./gradlew runServer${c.reset} ${c.gray}# Launches Paper test server${c.reset}`);
|
|
178
|
+
console.log(` ${c.cyan}./gradlew runFoliaServer${c.reset} # Launches Folia multi-threaded test server${c.reset}\n`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async function handleInstallSkill(options) {
|
|
182
|
+
printBanner();
|
|
183
|
+
console.log(`${c.bold}Installing 'paper-plugin-dev' Agent Skill...${c.reset}\n`);
|
|
184
|
+
|
|
185
|
+
const isGlobal = options.includes('--global') || options.includes('-g');
|
|
186
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
187
|
+
|
|
188
|
+
let targetSkillDir;
|
|
189
|
+
if (isGlobal) {
|
|
190
|
+
targetSkillDir = path.join(homeDir, '.gemini', 'config', 'skills', 'paper-plugin-dev');
|
|
191
|
+
} else {
|
|
192
|
+
// Current workspace customization root
|
|
193
|
+
targetSkillDir = path.resolve(process.cwd(), '.agents', 'skills', 'paper-plugin-dev');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const srcSkillDir = path.join(ROOT_DIR, 'skills', 'paper-plugin-dev');
|
|
197
|
+
if (!fs.existsSync(srcSkillDir)) {
|
|
198
|
+
console.error(`${c.red}Error:${c.reset} Source skill directory not found at ${srcSkillDir}`);
|
|
199
|
+
process.exit(1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
copyDirRecursive(srcSkillDir, targetSkillDir);
|
|
203
|
+
|
|
204
|
+
console.log(`${c.green}${c.bold}✔ Skill successfully installed!${c.reset}`);
|
|
205
|
+
console.log(` Target: ${c.dim}${targetSkillDir}${c.reset}\n`);
|
|
206
|
+
console.log(`Your AI coding agent can now automatically discover and load the ${c.bold}paper-plugin-dev${c.reset} skill.`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function handleDocs(topic) {
|
|
210
|
+
const docsDir = path.join(ROOT_DIR, 'docs');
|
|
211
|
+
if (!fs.existsSync(docsDir)) {
|
|
212
|
+
console.error(`${c.red}Error:${c.reset} Docs directory not found.`);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const files = fs.readdirSync(docsDir).filter(f => f.endsWith('.md')).sort();
|
|
217
|
+
|
|
218
|
+
if (!topic) {
|
|
219
|
+
printBanner();
|
|
220
|
+
console.log(`${c.bold}Available Architectural & Technical Guides:${c.reset}\n`);
|
|
221
|
+
files.forEach((file, index) => {
|
|
222
|
+
console.log(` ${c.cyan}${String(index + 1).padStart(2, '0')}.${c.reset} ${c.bold}${file}${c.reset}`);
|
|
223
|
+
});
|
|
224
|
+
console.log(`\nTo view a specific guide, run: ${c.cyan}npx papermc-skill docs <number|keyword>${c.reset}\n`);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const cleanTopic = String(topic).replace(/[^a-zA-Z0-9_-]/g, '');
|
|
229
|
+
const matched = files.find(f => f.toLowerCase().includes(cleanTopic.toLowerCase()) || f.startsWith(cleanTopic.padStart(2, '0')));
|
|
230
|
+
if (!matched) {
|
|
231
|
+
console.error(`${c.red}No guide found matching '${topic}'. Run 'npx papermc-skill docs' for a list.${c.reset}`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const content = fs.readFileSync(path.join(docsDir, matched), 'utf-8');
|
|
236
|
+
console.log(`\n${c.green}${c.bold}--- ${matched} ---${c.reset}\n`);
|
|
237
|
+
console.log(content);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function printHelp() {
|
|
241
|
+
printBanner();
|
|
242
|
+
console.log(`${c.bold}Usage:${c.reset}
|
|
243
|
+
npx papermc-skill <command> [options]
|
|
244
|
+
|
|
245
|
+
${c.bold}Commands:${c.reset}
|
|
246
|
+
${c.cyan}create [name]${c.reset} / ${c.cyan}init [name]${c.reset} Scaffold a modern Paper & Folia plugin project
|
|
247
|
+
${c.cyan}install-skill [--global]${c.reset} Install the AI skill into workspace (.agents) or global (~/.gemini)
|
|
248
|
+
${c.cyan}docs [number|keyword]${c.reset} Browse or print documentation guides
|
|
249
|
+
${c.cyan}help${c.reset} / ${c.cyan}--help${c.reset} Display this help screen
|
|
250
|
+
${c.cyan}--version${c.reset} Show version information
|
|
251
|
+
|
|
252
|
+
${c.bold}Examples:${c.reset}
|
|
253
|
+
${c.dim}# Create a new Paper plugin in current directory${c.reset}
|
|
254
|
+
npx papermc-skill create MyAwesomePlugin
|
|
255
|
+
|
|
256
|
+
${c.dim}# Install AI skill globally for your coding agent${c.reset}
|
|
257
|
+
npx papermc-skill install-skill --global
|
|
258
|
+
|
|
259
|
+
${c.dim}# View the Adventure API and MiniMessage guide${c.reset}
|
|
260
|
+
npx papermc-skill docs adventure
|
|
261
|
+
`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// CLI Router
|
|
265
|
+
async function main() {
|
|
266
|
+
const args = process.argv.slice(2);
|
|
267
|
+
const command = args[0] || 'create';
|
|
268
|
+
|
|
269
|
+
switch (command) {
|
|
270
|
+
case 'create':
|
|
271
|
+
case 'init':
|
|
272
|
+
case 'new':
|
|
273
|
+
await handleCreate(args[1]);
|
|
274
|
+
break;
|
|
275
|
+
case 'install-skill':
|
|
276
|
+
case 'install':
|
|
277
|
+
case 'skill':
|
|
278
|
+
await handleInstallSkill(args.slice(1));
|
|
279
|
+
break;
|
|
280
|
+
case 'docs':
|
|
281
|
+
case 'doc':
|
|
282
|
+
case 'guide':
|
|
283
|
+
handleDocs(args[1]);
|
|
284
|
+
break;
|
|
285
|
+
case '--version':
|
|
286
|
+
case '-v': {
|
|
287
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf-8'));
|
|
288
|
+
console.log(`papermc-skill v${pkg.version}`);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
case 'help':
|
|
292
|
+
case '--help':
|
|
293
|
+
case '-h':
|
|
294
|
+
default:
|
|
295
|
+
if (command.startsWith('-')) {
|
|
296
|
+
printHelp();
|
|
297
|
+
} else {
|
|
298
|
+
await handleCreate(command);
|
|
299
|
+
}
|
|
300
|
+
break;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
main().catch((err) => {
|
|
305
|
+
console.error(`\n${c.red}Error:${c.reset}`, err.message);
|
|
306
|
+
process.exit(1);
|
|
307
|
+
});
|