Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions Box3JS-NeoForge-1.21.1/docs/api/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@
/box3script create mygame
```

::: warning modId 命名规范
项目名(也是编译为 JAR 时的 modId)必须符合 NeoForge 命名规范:`^[a-z][a-z0-9_]{1,63}$`

- 首字符必须是**小写字母**
- 后续字符只能用**小写字母、数字、下划线**
- 长度 **2–64** 个字符
- 不能用大写、连字符 `-`、点号 `.` 或其他特殊符号

✅ 有效:`mygame`、`arena_battle`、`cq`;❌ 无效:`MyGame`、`my-game`、`c`
:::

创建后需要:

```bash
Expand Down Expand Up @@ -134,6 +145,10 @@ npm install && npm run build

编译时**从 `package.json` 读取以下字段**写入 `neoforge.mods.toml`:

::: warning modId 验证
`name` 字段的 modId 会在编译时校验,必须符合 `^[a-z][a-z0-9_]{1,63}$`(小写字母开头,仅含小写字母/数字/下划线,2-64 字符)。不符合的 modId 会导致编译失败并提示具体原因。
:::

| package.json | mods.toml 字段 | 说明 |
|-------------|---------------|------|
| `name` | `modId` | 模组 ID |
Expand Down
22 changes: 12 additions & 10 deletions Box3JS-NeoForge-1.21.1/docs/api/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Box3JS 通过全局 `db` 对象提供 SQLite 数据库能力,无需手动管理连接。

::: info 运行环境
服务端和客户端都可用。服务端数据库位于 `config/box3/data/<project>.db`;客户端数据库位于本地游戏目录的 `box3/client-db/<project>.db`。两端数据库互不共享,需要同步数据时请使用 `remoteChannel`。
服务端和客户端都可用。服务端数据库位于 `config/box3/data/<project>.db`;客户端数据库位于本地游戏目录的 `config/box3/client-db/<project>.db`。两端数据库互不共享,需要同步数据时请使用 `remoteChannel`。
:::

## 依赖与降级行为
Expand All @@ -19,9 +19,10 @@ db API requires SQLite JDBC driver. Install the minecraft-sqlite-jdbc mod, then
安装 `minecraft-sqlite-jdbc` 并重启服务器后,`db` API 即可恢复可用。

::: warning NeoForge 开发环境

- 请将 `minecraft-sqlite-jdbc` 放到 `run/mods/`。
- 模组文件必须是 `.jar`(例如 `xxx.jar`),不要使用 `.zip`,否则不会被 NeoForge 加载。
:::
:::

## `db.isAvailable()`

Expand Down Expand Up @@ -297,20 +298,21 @@ db.sql`SELECT * FROM players WHERE name = ${name}`;
// ❌ 错误
db.sql`SELECT * FROM ${table} WHERE name = ${name}`;
```

:::

## Rhino 兼容性注意事项

Box3JS 使用 Rhino 1.9.1 引擎。**TypeScript 项目用 `npm run build` 编译后,以下特性均可直接使用**(Babel 插件自动转为 Rhino 兼容代码):

| 特性 | 编译方式 |
|------|---------|
| 箭头函数 `(x) => x + 1` | Babel `@babel/preset-env` |
| 模板字面量 `` `Hello ${name}` `` | `rhinoTemplatePlugin` |
| `for...of` (JS 数组 + Java ArrayList) | `rhinoForOfPlugin` → 索引 for 循环 + `.toArray()` |
| `.map()` `.filter()` `.forEach()` `.find()` `.some()` `.every()` | `rhinoArrayMethodsPlugin` → IIFE + for 循环 |
| `const` / `let` | Babel `@babel/preset-env` |
| 解构 `const { x, y } = obj` | Babel `@babel/preset-env` |
| 特性 | 编译方式 |
| ---------------------------------------------------------------- | ------------------------------------------------- |
| 箭头函数 `(x) => x + 1` | Babel `@babel/preset-env` |
| 模板字面量 `` `Hello ${name}` `` | `rhinoTemplatePlugin` |
| `for...of` (JS 数组 + Java ArrayList) | `rhinoForOfPlugin` → 索引 for 循环 + `.toArray()` |
| `.map()` `.filter()` `.forEach()` `.find()` `.some()` `.every()` | `rhinoArrayMethodsPlugin` → IIFE + for 循环 |
| `const` / `let` | Babel `@babel/preset-env` |
| 解构 `const { x, y } = obj` | Babel `@babel/preset-env` |

**纯 JS 脚本注意事项:**

Expand Down
2 changes: 2 additions & 0 deletions Box3JS-NeoForge-1.21.1/docs/api/registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mygame/
| `noCollision` | bool | `false` | 无碰撞(可穿过) |
| `requiresTool` | bool | `false` | 需要正确工具才掉落 |
| `instabreak` | bool | `false` | 瞬间破坏 |
| `renderType` | string | `"solid"` | 渲染类型:`"solid"`(默认)、`"cutout"`(带透明像素,如星形灯)、`"translucent"`(半透明,如染色玻璃) |
| `creativeTab` | string | `""` | 所属创造标签页 ID(与 creativeTabs.json 中的 key 对应) |

### mapColor 可选值
Expand All @@ -67,6 +68,7 @@ mygame/
"resistance": 0.3,
"sound": "glass",
"noOcclusion": true,
"renderType": "cutout",
"creativeTab": "my_blocks"
}
}
Expand Down
2 changes: 1 addition & 1 deletion Box3JS-NeoForge-1.21.1/docs/api/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
`storage` 提供 JSON 文件持久化存储,带内存缓存加速读写。

::: info 运行环境
服务端和客户端都可用。服务端数据保存在 `config/box3/storage/<项目名>/`;客户端数据保存在本地游戏目录的 `box3/client-storage/<项目名>/`。每个项目自动拥有独立命名空间。
服务端和客户端都可用。服务端数据保存在 `config/box3/storage/<项目名>/`;客户端数据保存在本地游戏目录的 `config/box3/client-storage/<项目名>/`。每个项目自动拥有独立命名空间。
:::

## 获取存储实例
Expand Down
35 changes: 18 additions & 17 deletions Box3JS-NeoForge-1.21.1/docs/en/api/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Box3JS exposes SQLite capabilities through the global `db` object. Connections are managed automatically.

::: info Runtime
Available on both server and client. Server databases live at `config/box3/data/<project>.db`; client databases live under the local game directory at `box3/client-db/<project>.db`. The two sides do not share database files; use `remoteChannel` when data must be synchronized.
Available on both server and client. Server databases live at `config/box3/data/<project>.db`; client databases live under the local game directory at `config/box3/client-db/<project>.db`. The two sides do not share database files; use `remoteChannel` when data must be synchronized.
:::

## Dependency & Graceful Fallback
Expand All @@ -22,9 +22,10 @@ db API requires SQLite JDBC driver. Install the minecraft-sqlite-jdbc mod, then
After installing `minecraft-sqlite-jdbc` and restarting the server, the `db` API becomes available.

::: warning NeoForge dev environment

- Put `minecraft-sqlite-jdbc` under `run/mods/`.
- The file must be a `.jar` (for example, `xxx.jar`), not `.zip`, otherwise NeoForge will not load it.
:::
:::

## `db.isAvailable()`

Expand Down Expand Up @@ -220,13 +221,13 @@ world.onPlayerLeave(function (entity) {

## Comparison with storage

| | `db` (SQLite) | `storage` (JSON) |
| ---- | ----------------------------- | ------------------------------- |
| Query | SQL WHERE/JOIN/ORDER BY/LIMIT | Read all, filter in JS |
| Write | Single-row atomic | Full overwrite |
| Best for | Leaderboards, economy, logs, relational data | Config, flags, simple key-value |
| File | `data/<project>.db` | `storage/<project>/<name>.json` |
| Concurrency | Naturally safe (WAL mode) | Serial per-project is adequate |
| | `db` (SQLite) | `storage` (JSON) |
| ----------- | -------------------------------------------- | ------------------------------- |
| Query | SQL WHERE/JOIN/ORDER BY/LIMIT | Read all, filter in JS |
| Write | Single-row atomic | Full overwrite |
| Best for | Leaderboards, economy, logs, relational data | Config, flags, simple key-value |
| File | `data/<project>.db` | `storage/<project>/<name>.json` |
| Concurrency | Naturally safe (WAL mode) | Serial per-project is adequate |

## Notes

Expand All @@ -240,14 +241,14 @@ world.onPlayerLeave(function (entity) {

Box3JS uses the Rhino 1.9.1 engine. **TypeScript projects compiled with `npm run build` can use all modern syntax** — Babel plugins convert it to Rhino-compatible code:

| Feature | Compilation |
|---------|------------|
| Arrow functions `(x) => x + 1` | Babel `@babel/preset-env` |
| Template literals `` `Hello ${name}` `` | `rhinoTemplatePlugin` |
| `for...of` (JS arrays + Java ArrayList) | `rhinoForOfPlugin` → indexed for + `.toArray()` |
| `.map()` `.filter()` `.forEach()` `.find()` `.some()` `.every()` | `rhinoArrayMethodsPlugin` → IIFE + for loop |
| `const` / `let` | Babel `@babel/preset-env` |
| Destructuring `const { x, y } = obj` | Babel `@babel/preset-env` |
| Feature | Compilation |
| ---------------------------------------------------------------- | ----------------------------------------------- |
| Arrow functions `(x) => x + 1` | Babel `@babel/preset-env` |
| Template literals `` `Hello ${name}` `` | `rhinoTemplatePlugin` |
| `for...of` (JS arrays + Java ArrayList) | `rhinoForOfPlugin` → indexed for + `.toArray()` |
| `.map()` `.filter()` `.forEach()` `.find()` `.some()` `.every()` | `rhinoArrayMethodsPlugin` → IIFE + for loop |
| `const` / `let` | Babel `@babel/preset-env` |
| Destructuring `const { x, y } = obj` | Babel `@babel/preset-env` |

**Plain JS notes:**

Expand Down
2 changes: 1 addition & 1 deletion Box3JS-NeoForge-1.21.1/docs/en/api/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
`storage` provides JSON file persistence with in-memory caching for fast reads/writes.

::: info Runtime
Available on both server and client. Server data is saved under `config/box3/storage/<project>/`; client data is saved under the local game directory at `box3/client-storage/<project>/`. Each project automatically gets an independent namespace.
Available on both server and client. Server data is saved under `config/box3/storage/<project>/`; client data is saved under the local game directory at `config/box3/client-storage/<project>/`. Each project automatically gets an independent namespace.
:::

## Getting a Storage Instance
Expand Down
15 changes: 15 additions & 0 deletions Box3JS-NeoForge-1.21.1/docs/guide/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,21 @@ db.sql("SELECT * FROM t WHERE name = '" + userInput + "'");

生成 `<项目名>-<版本>.jar`,放入 `mods/` 即可。接收方也需要安装 Box3JS 模组(作为运行时依赖)。自定义方块/物品需要客户端也安装 JAR。

### Q: 编译时报 "is not a valid mod file" 或 "Invalid modId"

这是由于项目名(modId)不符合 NeoForge 命名规范。modId 必须匹配:`^[a-z][a-z0-9_]{1,63}$`

- ✅ 首字符必须是**小写字母**
- ✅ 后续字符只能用**小写字母、数字、下划线**
- ✅ 长度 **2–64** 个字符
- ❌ 不能用大写、连字符 `-`、点号 `.` 或其他特殊符号

**修复方法:**

1. 检查 `package.json` 中的 `"name"` 字段
2. 将项目名改为合法的 modId(如 `mygame`、`arena_battle`)
3. 或在编译时指定:`/box3script compile mygame --modId mygame`

### Q: 编译的 JAR 和解释模式有什么区别?

| 特性 | 解释模式 | 编译 JAR |
Expand Down
11 changes: 11 additions & 0 deletions Box3JS-NeoForge-1.21.1/docs/guide/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,17 @@ Box3JS 的 API 设计继承自**[神奇代码岛](https://dao3.fun)(Box3)**

这会在 `config/box3/script/mygame/` 生成完整的 TypeScript 项目。

::: warning modId 命名规范
项目名会作为 NeoForge 的 **modId** 使用,必须符合命名规则:
- 正则:`^[a-z][a-z0-9_]{1,63}$`
- 首字符必须是**小写字母** `[a-z]`
- 后续字符只能用**小写字母、数字、下划线** `[a-z0-9_]`
- 长度:**2–64** 个字符

✅ 合法:`mygame`、`colorzone`、`arena_battle`、`sky_parkour_2`
❌ 非法:`c`(太短)、`MyGame`(含大写)、`my-game`(含连字符)
:::

### 理解项目结构

```text
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.box3lab.box3js;

import com.box3lab.box3js.standalone.Box3StandaloneBootstrap;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.level.block.Block;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent;

/**
* Client-side handler that applies render types for blocks registered by
* standalone script mods via
* {@link Box3StandaloneBootstrap#addBlockRenderType}.
*
* <p>
* The generated {@code @Mod} class cannot import client-only classes
* ({@code ItemBlockRenderTypes}, {@code RenderType}) because the in-game
* {@code javac} classpath is incomplete. This class lives in the main Box3JS
* mod (already compiled) and drains the pending render-type registrations
* during {@code FMLClientSetupEvent}.
*/
@EventBusSubscriber(modid = Box3JS.MODID, value = Dist.CLIENT)
public class Box3JSClientEvents {

@SubscribeEvent
@SuppressWarnings("deprecation")
public static void onClientSetup(FMLClientSetupEvent event) {
event.enqueueWork(() -> {
var pending = Box3StandaloneBootstrap.drainPendingRenderTypes();
if (pending.isEmpty()) return;

for (var modEntry : pending.entrySet()) {
for (var entry : modEntry.getValue()) {
Block block = entry.blockSupplier().get();
switch (entry.renderType()) {
case "cutout" -> ItemBlockRenderTypes.setRenderLayer(block, RenderType.cutout());
case "translucent" ->
ItemBlockRenderTypes.setRenderLayer(block, RenderType.translucent());
}
}
}

Box3JS.LOGGER.info("Applied client render types for {} standalone mod(s)", pending.size());
});
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
package com.box3lab.box3js.client;

import com.box3lab.box3js.script.Box3DatabaseBase;
import com.box3lab.box3js.script.Box3JSQueryResult;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.*;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

import org.slf4j.Logger;

import com.box3lab.box3js.script.Box3DatabaseBase;
import com.mojang.logging.LogUtils;

/**
* Client-side SQLite database exposed to JS as the {@code db} global.
*
* <p>Database files are stored at {@code <gameDir>/box3/client-db/<project>.db}.
* <p>
* Database files are stored at
* {@code <gameDir>/config/box3/client-db/<project>.db}.
*/
public class Box3JSClientDatabase extends Box3DatabaseBase {

Expand All @@ -24,7 +29,7 @@ public class Box3JSClientDatabase extends Box3DatabaseBase {
private Connection connection;

public Box3JSClientDatabase(java.io.File gameDir) {
this.dataDir = gameDir.toPath().resolve("box3").resolve("client-db");
this.dataDir = gameDir.toPath().resolve("config").resolve("box3").resolve("client-db");
try {
Files.createDirectories(dataDir);
} catch (IOException e) {
Expand Down
Loading
Loading