# PaperTS
PaperTS 是一个 Paper Minecraft 插件,它允许您使用 TypeScript 编写 Minecraft 服务器插件,通过 [Javet](https://github.com/caoccao/Javet) 在 Node.js JavaScript 引擎上运行。它为 TypeScript 模块提供运行时环境,让您可以使用 JavaScript/TypeScript 的强大功能和灵活性进行 Minecraft 服务器开发。**Node.JS 运行时在运行时下载**,插件从 [Javet 官方依赖](https://www.caoccao.com/Javet/tutorial/basic/installation.html) 安装它,您可以验证您的平台是否受支持。
## 特性
- **TypeScript/JavaScript 插件支持**: 使用 TypeScript 或 JavaScript 编写 Minecraft 插件,并在您的 Paper 服务器上直接运行它们。
- **自动模块加载**: 扫描插件的数据文件夹中寻找模块(包含 `package.json` 的目录),自动初始化并启动它们。
- **Node.js 环境**: 使用 Javet 的 NodeRuntime 提供类似于 Node.js 的环境。
- **事件和命令注册**: 暴露 API,用于从 TypeScript 注册/取消注册 Bukkit 事件和命令。
- **热重载**: 模块可以发布和重新加载,而无需重启服务器。
## 工作原理
1. 服务器启动时,PaperTS 扫描其数据文件夹,查找包含 `package.json` 文件的子目录。
2. 对于每个模块,它读取 `package.json` 中的 `main` 字段以找到入口脚本。
3. 入口脚本在 Node.js 运行时中执行,并可以使用 PaperTS API 与服务器进行交互。
4. 模块可以使用提供的全局 `PaperTS` 对象注册事件处理程序和命令。
## 快速开始
1. **安装 PaperTS**: 将 PaperTS 插件 JAR 文件放置在服务器的 `plugins` 文件夹中。
2. **创建模块**:
- 在 `plugins/PaperTS` 文件夹中,为您的模块创建一个新目录(例如 `my-plugin`)。
- 添加一个 `package.json` 文件,其中包含指向您的入口脚本的 `main` 字段(例如 `index.js`)。
- 编写您的 TypeScript/JavaScript 代码,如果需要,将其编译为 JavaScript。
3. **示例 `package.json`**:
```json
{
"name": "my-plugin",
"main": "index.js"
}
```
4. **示例 `index.js`**:
```js
class ServerModule {
constructor() {
console.log("我的插件已加载!");
}
public handlePlayerJoin(event) {
// 处理玩家加入事件
event.player.sendMessage(event.player.name, "欢迎来到服务器!");
}
public helloCommand(sender, args) {
sender.sendMessage("来自 PaperTS 的问候!");
}
}
const server = new ServerModule();
PaperTS.registerEvent(
org.bukkit.event.player.PlayerJoinEvent,
server.handlePlayerJoin.bind(server),
);
PaperTS.registerCommand(
"hello",
"向玩家问候",
"/hello",
"",
[],
server.helloCommand.bind(server),
);
```
5. 启动您的 Paper 服务器。PaperTS 将自动加载您的模块。您可以使用 `/paperts reload` 命令随时重新加载模块。
### PaperTS 全局 API
以下全局 API 在您的 TypeScript/JavaScript 代码中可用:
```ts
declare namespace PaperTS {
export function registerEvent(
eventClass: { new (...args: any[]): T },
listener: (event: T) => void,
): void;
export function registerCommand(
name: string,
description: string,
usageMessage: string,
permission: string,
aliases: string[],
executor: (sender: CommandSender, args: string[]) => void,
): void;
export function getPersistentContainerString(
key: string,
container: PersistentDataContainer
): string;
export function hasKeyPersistentContainer(
key: string,
container: PersistentDataContainer
): boolean;
export function setPersistentContainerString(
key: string,
value: string,
container: PersistentDataContainer
): void;
}
```
#### 注册事件
要注册事件,请使用 `PaperTS.registerEvent` 方法。您可以传递事件类和将在事件触发时执行的回调函数。
```js
export class ServerModule {
public handlePlayerJoin(event) {
console.log(`玩家 ${event.player.name} 加入了服务器!`);
}
}
const server = new ServerModule();
PaperTS.registerEvent(
org.bukkit.event.PlayerJoinEvent,
server.handlePlayerJoin.bind(server),
);
```
#### 注册命令
要注册命令,请使用 `PaperTS.registerCommand` 方法。您可以指定命令名称、描述、用法消息、权限、别名以及将处理命令执行的执行器函数。
```js
class ServerModule {
public command(sender, args) {
console.log(`命令由 ${sender.displayName()} 执行,参数:${args.join(", ")}`);
}
}
const server = new ServerModule();
PaperTS.registerCommand(
"commandname",
"命令描述",
"/commandname ",
"command.permission",
["alias1", "alias2"],
server.command.bind(server),
);
```
#### 注意
使用 `bind` 是必要的,当将方法作为回调传递时,可以确保正确维护 (`this`) 上下文。
### 使用 TypeScript
要使用 TypeScript,您可以在模块目录中设置一个 `tsconfig.json` 文件:
```json
{
"compilerOptions": {
"incremental": true,
"module": "commonjs",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"typeRoots": [
"node_modules/paperts-java-ts-bind",
"node_modules/@types"
]
},
"include": [
"src/**/*"
]
}
```
在您的模块目录中安装必要的依赖项:
```sh
npm install --save github:MetlHedd/java-ts-bind#1.20-R0.1-SNAPSHOT
```
您可以替换版本,使用 `paperts-java-ts-bind` 软件包的另一个版本,如果您想使用其他版本,或者通过在此存储库中打开问题来请求另一个版本。
然后,您可以在 `src` 目录中编写您的 TypeScript 代码,并在 `dist` 目录中将其编译为 JavaScript。
您的 `package.json` 应该如下所示:
```json
{
"main": "dist/index.js",
"scripts": {
"watch": "tsc --project . --watch",
"bundle": "esbuild src/index.ts --watch --bundle --outfile=dist/index.js --platform=node --target=node22 --external:org.* --external:com.* --external:net.* --external:java.*"
},
"dependencies": {
"esbuild": "^0.25.8",
"paperts-java-ts-bind": "github:MetlHedd/java-ts-bind#1.20-R0.1-SNAPSHOT",
"typescript": "^5.8.3",
"@types/node": "^24.0.15"
}
}
```
然后,您可以使用 `npm run bundle` 将您的 TypeScript 代码捆绑到一个可以由 PaperTS 运行的单个 JavaScript 文件中。
示例 `index.ts`:
```ts
import { Event } from "org.bukkit.event";
import { PlayerJoinEvent } from "org.bukkit.event.player";
import { CommandSender } from "org.bukkit.command";
declare namespace PaperTS {
export function registerEvent(
eventClass: { new (...args: any[]): T },
listener: (event: T) => void,
): void;
export function registerCommand(
name: string,
description: string,
usageMessage: string,
permission: string,
aliases: string[],
executor: (sender: CommandSender, args: string[]) => void,
): void;
}
export class ServerModule {
constructor() {
// 此构造函数在模块加载时调用
console.log("我的插件已加载!");
}
public handlePlayerJoin(event: PlayerJoinEvent) {
// 处理玩家加入事件
event.player.sendMessage(event.player.name, "欢迎来到服务器!");
}
public helloCommand(sender: CommandSender, args: string[]) {
sender.sendMessage("来自 PaperTS 的问候!");
}
}
const server = new ServerModule();
PaperTS.registerEvent(
PlayerJoinEvent,
server.handlePlayerJoin.bind(server),
);
PaperTS.registerCommand(
"hello",
"向玩家问候",
"/hello",
"",
[],
server.helloCommand.bind(server),
);
```
### 国际化 (i18n)
PaperTS 通过 Node.js 运行时支持国际化 (i18n),从而能够使用一些 Node.js 模块实现更兼容的行为。要启用 i18n,请在插件的数据文件夹中创建一个名为 `node-icu` 的文件夹(通常为 `plugins/PaperTS/node-icu`),并将 ICU 数据文件放置在那里。您可以从 [Javet 存储库](https://github.com/caoccao/Javet) 的任何操作中下载 ICU 数据文件。插件将自动检测 `node-icu` 文件夹是否存在,并启用 i18n 支持。
## 插件命令
您可以使用以下命令来管理您的 PaperTS 模块:
- `/paperts reload`: 重新加载所有模块。
- `/paperts reload `: 重新加载一个特定的模块。
- `/paperts list`: 列出所有已加载的模块。
- `/paperts unload `: 卸载一个特定的模块。
- `/paperts load `: 加载一个特定的模块。
## 开发
PaperTS 使用 [Javet](https://github.com/caoccao/Javet) 嵌入 V8 引擎和 Node.js 运行时。该插件管理引擎池、工作目录,并暴露一个 `Globals` API 来管理事件和命令。
### 构建
使用 Gradle 构建:
```sh
./gradlew build
```
输出 JAR 将在 `app/build/libs/` 中。
## 贡献
欢迎贡献!请为错误修复、功能或文档改进打开问题或拉取请求。
## 灵感
一些激发 PaperTS 的项目:
- [Grakkit](https://github.com/grakkit/grakkit)
- [Javet](https://github.com/caoccao/Javet)
- [Custom Realms](https://github.com/customrealms)
- [CraftJS](https://github.com/Dysfold/craftjs)