From 6181ffe5e50dd21609b89544c98cfee49be24250 Mon Sep 17 00:00:00 2001 From: viyrs <2991883280@qq.com> Date: Mon, 18 May 2026 13:13:04 +0800 Subject: [PATCH 1/4] =?UTF-8?q?docs(database):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=B8=AD=E7=9A=84=E5=AE=A2=E6=88=B7=E7=AB=AF?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将中英文文档中的客户端数据库路径从 `box3/client-db/.db` 更新为 `config/box3/client-db/.db` feat(database): 将客户端数据库移至 config 目录 将客户端数据库文件从 `/box3/client-db/` 移至 `/config/box3/client-db/`,以优化组织结构并与其他配置文件保持一致 docs(storage): 更新文档中的客户端存储路径 将中英文文档中的客户端存储路径从 `box3/client-storage//` 更新为 `config/box3/client-storage//` refactor(storage): 将客户端存储移至 config 目录 将客户端存储文件从 `/box3/client-storage/` 移至 `/config/box3/client-storage/`,与新的数据库位置保持一致 style(database): 改进文档中的表格格式 调整数据库 API 文档中的表格列宽和对齐方式,优化表格格式,提升可读性 style(storage): 修复对比章节中的表格格式 修复数据库与存储对比章节中的表格格式问题,优化视觉呈现效果 --- Box3JS-NeoForge-1.21.1/docs/api/database.md | 22 +- Box3JS-NeoForge-1.21.1/docs/api/storage.md | 2 +- .../docs/en/api/database.md | 35 +- Box3JS-NeoForge-1.21.1/docs/en/api/storage.md | 2 +- .../box3js/client/Box3JSClientDatabase.java | 21 +- .../box3js/client/Box3JSClientStorage.java | 80 ++- .../box3js/standalone/Box3JSRegistryGen.java | 535 ++++++++++-------- .../box3js/standalone/Box3ScriptCompiler.java | 280 ++++++--- .../box3js/template/assets/lang/en_us.json | 6 +- .../box3js/template/assets/lang/zh_cn.json | 6 +- .../assets/box3js/template/types/shared.d.ts | 15 +- .../tools/box3js-api-manifest.json | 36 +- 12 files changed, 639 insertions(+), 401 deletions(-) diff --git a/Box3JS-NeoForge-1.21.1/docs/api/database.md b/Box3JS-NeoForge-1.21.1/docs/api/database.md index 6bb703c..78d9672 100644 --- a/Box3JS-NeoForge-1.21.1/docs/api/database.md +++ b/Box3JS-NeoForge-1.21.1/docs/api/database.md @@ -3,7 +3,7 @@ Box3JS 通过全局 `db` 对象提供 SQLite 数据库能力,无需手动管理连接。 ::: info 运行环境 -服务端和客户端都可用。服务端数据库位于 `config/box3/data/.db`;客户端数据库位于本地游戏目录的 `box3/client-db/.db`。两端数据库互不共享,需要同步数据时请使用 `remoteChannel`。 +服务端和客户端都可用。服务端数据库位于 `config/box3/data/.db`;客户端数据库位于本地游戏目录的 `config/box3/client-db/.db`。两端数据库互不共享,需要同步数据时请使用 `remoteChannel`。 ::: ## 依赖与降级行为 @@ -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()` @@ -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 脚本注意事项:** diff --git a/Box3JS-NeoForge-1.21.1/docs/api/storage.md b/Box3JS-NeoForge-1.21.1/docs/api/storage.md index 10df6bf..0f9b432 100644 --- a/Box3JS-NeoForge-1.21.1/docs/api/storage.md +++ b/Box3JS-NeoForge-1.21.1/docs/api/storage.md @@ -3,7 +3,7 @@ `storage` 提供 JSON 文件持久化存储,带内存缓存加速读写。 ::: info 运行环境 -服务端和客户端都可用。服务端数据保存在 `config/box3/storage/<项目名>/`;客户端数据保存在本地游戏目录的 `box3/client-storage/<项目名>/`。每个项目自动拥有独立命名空间。 +服务端和客户端都可用。服务端数据保存在 `config/box3/storage/<项目名>/`;客户端数据保存在本地游戏目录的 `config/box3/client-storage/<项目名>/`。每个项目自动拥有独立命名空间。 ::: ## 获取存储实例 diff --git a/Box3JS-NeoForge-1.21.1/docs/en/api/database.md b/Box3JS-NeoForge-1.21.1/docs/en/api/database.md index f7cb4d6..45493b6 100644 --- a/Box3JS-NeoForge-1.21.1/docs/en/api/database.md +++ b/Box3JS-NeoForge-1.21.1/docs/en/api/database.md @@ -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/.db`; client databases live under the local game directory at `box3/client-db/.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/.db`; client databases live under the local game directory at `config/box3/client-db/.db`. The two sides do not share database files; use `remoteChannel` when data must be synchronized. ::: ## Dependency & Graceful Fallback @@ -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()` @@ -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/.db` | `storage//.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/.db` | `storage//.json` | +| Concurrency | Naturally safe (WAL mode) | Serial per-project is adequate | ## Notes @@ -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:** diff --git a/Box3JS-NeoForge-1.21.1/docs/en/api/storage.md b/Box3JS-NeoForge-1.21.1/docs/en/api/storage.md index e33d556..d0716f0 100644 --- a/Box3JS-NeoForge-1.21.1/docs/en/api/storage.md +++ b/Box3JS-NeoForge-1.21.1/docs/en/api/storage.md @@ -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//`; client data is saved under the local game directory at `box3/client-storage//`. Each project automatically gets an independent namespace. +Available on both server and client. Server data is saved under `config/box3/storage//`; client data is saved under the local game directory at `config/box3/client-storage//`. Each project automatically gets an independent namespace. ::: ## Getting a Storage Instance diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientDatabase.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientDatabase.java index 6cfd972..e9492d6 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientDatabase.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientDatabase.java @@ -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. * - *

Database files are stored at {@code /box3/client-db/.db}. + *

+ * Database files are stored at + * {@code /config/box3/client-db/.db}. */ public class Box3JSClientDatabase extends Box3DatabaseBase { @@ -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) { diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientStorage.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientStorage.java index 57766c2..7962248 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientStorage.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/client/Box3JSClientStorage.java @@ -1,22 +1,28 @@ package com.box3lab.box3js.client; -import com.box3lab.box3js.script.Box3StorageTypes; -import com.box3lab.box3js.script.Box3StorageSupport; -import com.mojang.logging.LogUtils; -import org.mozilla.javascript.Context; -import org.mozilla.javascript.Function; -import org.slf4j.Logger; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.slf4j.Logger; + +import com.box3lab.box3js.script.Box3StorageSupport; +import com.box3lab.box3js.script.Box3StorageTypes; +import com.mojang.logging.LogUtils; + /** - * Client-side persistent key-value storage with ValueEntry/ReturnValue/QueryList support. + * Client-side persistent key-value storage with + * ValueEntry/ReturnValue/QueryList support. * - *

Data is stored per-project under {@code /box3/client-storage/}. + *

+ * Data is stored per-project under + * {@code /config/box3/client-storage/}. */ public class Box3JSClientStorage { @@ -27,7 +33,7 @@ public class Box3JSClientStorage { private final Map> cache = new ConcurrentHashMap<>(); public Box3JSClientStorage(java.io.File gameDir, String projectName) { - this.baseDir = gameDir.toPath().resolve("box3").resolve("client-storage"); + this.baseDir = gameDir.toPath().resolve("config").resolve("box3").resolve("client-storage"); this.projectName = projectName; try { Files.createDirectories(baseDir); @@ -36,7 +42,9 @@ public Box3JSClientStorage(java.io.File gameDir, String projectName) { } } - public String getKey() { return ""; } + public String getKey() { + return ""; + } public GameDataStorage getDataStorage(String name) { return new GameDataStorage(resolveName(name)); @@ -60,7 +68,9 @@ public class GameDataStorage { this.data = cache.computeIfAbsent(path, p -> Box3StorageSupport.readData(p, "client")); } - public String getKey() { return name; } + public String getKey() { + return name; + } private void persist() { Box3StorageSupport.writeData(path, data, "client"); @@ -69,7 +79,8 @@ private void persist() { // ── Basic API ── public void set(String key, Object value) { - if (key == null) return; + if (key == null) + return; long now = System.currentTimeMillis(); synchronized (data) { Box3StorageTypes.ValueEntry existing = data.get(key); @@ -85,7 +96,8 @@ public void set(String key, Object value) { } public Object get(String key) { - if (key == null) return null; + if (key == null) + return null; synchronized (data) { Box3StorageTypes.ValueEntry entry = data.get(key); return entry != null ? entry.value : null; @@ -99,14 +111,16 @@ public String[] keys() { } public void update(String key, Function handler) { - if (key == null || handler == null) return; + if (key == null || handler == null) + return; synchronized (data) { Box3StorageTypes.ValueEntry entry = data.get(key); - if (entry == null) return; + if (entry == null) + return; long now = System.currentTimeMillis(); Context cx = Context.enter(); try { - entry.value = handler.call(cx, handler, handler, new Object[]{entry.value}); + entry.value = handler.call(cx, handler, handler, new Object[] { entry.value }); } finally { Context.exit(); } @@ -117,7 +131,8 @@ public void update(String key, Function handler) { } public Object remove(String key) { - if (key == null) return null; + if (key == null) + return null; synchronized (data) { Box3StorageTypes.ValueEntry entry = data.remove(key); if (entry != null) { @@ -129,7 +144,8 @@ public Object remove(String key) { } public double increment(String key, double value) { - if (key == null) return 0; + if (key == null) + return 0; double delta = Double.isNaN(value) ? 1.0 : value; long now = System.currentTimeMillis(); synchronized (data) { @@ -172,7 +188,8 @@ public Box3StorageTypes.QueryList list(Map options) { String constraintTarget = null; if (options != null) { - if (options.get("cursor") instanceof Number n) cursor = n.intValue(); + if (options.get("cursor") instanceof Number n) + cursor = n.intValue(); if (options.get("pageSize") instanceof Number n) { pageSize = Math.max(1, Math.min(100, n.intValue())); } @@ -180,9 +197,12 @@ public Box3StorageTypes.QueryList list(Map options) { doSort = true; ascending = Boolean.TRUE.equals(options.get("ascending")); } - if (options.get("max") instanceof Number n) max = n.doubleValue(); - if (options.get("min") instanceof Number n) min = n.doubleValue(); - if (options.get("constraintTarget") instanceof String s) constraintTarget = s; + if (options.get("max") instanceof Number n) + max = n.doubleValue(); + if (options.get("min") instanceof Number n) + min = n.doubleValue(); + if (options.get("constraintTarget") instanceof String s) + constraintTarget = s; } final String target = constraintTarget; @@ -201,8 +221,10 @@ public Box3StorageTypes.QueryList list(Map options) { List filtered = new ArrayList<>(); for (Box3StorageTypes.ReturnValue rv : results) { double v = extractSortValue(rv.value, target); - if (min != null && v < min) continue; - if (max != null && v > max) continue; + if (min != null && v < min) + continue; + if (max != null && v > max) + continue; filtered.add(rv); } results = filtered; @@ -213,7 +235,8 @@ public Box3StorageTypes.QueryList list(Map options) { private double extractSortValue(Object value, String target) { if (target == null || target.isEmpty()) { - if (value instanceof Number n) return n.doubleValue(); + if (value instanceof Number n) + return n.doubleValue(); return 0; } Object current = value; @@ -224,7 +247,8 @@ private double extractSortValue(Object value, String target) { return 0; } } - if (current instanceof Number n) return n.doubleValue(); + if (current instanceof Number n) + return n.doubleValue(); return 0; } diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java index 4531402..507cbcb 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java @@ -13,7 +13,8 @@ * from the project directory and generates Java DeferredRegister source code * that gets injected into the generated {@code @Mod} class. * - *

Only used during {@code /box3script compile} — never loaded at mod runtime. + *

+ * Only used during {@code /box3script compile} — never loaded at mod runtime. */ public class Box3JSRegistryGen { @@ -21,99 +22,103 @@ public class Box3JSRegistryGen { // ── Sound type mapping ── private static final Map SOUND_TYPE_MAP = Map.ofEntries( - Map.entry("wood", "WOOD"), Map.entry("stone", "STONE"), - Map.entry("metal", "METAL"), Map.entry("glass", "GLASS"), - Map.entry("wool", "WOOL"), Map.entry("sand", "SAND"), - Map.entry("snow", "SNOW"), Map.entry("slime", "SLIME_BLOCK"), - Map.entry("anvil", "ANVIL"), Map.entry("gravel", "GRAVEL"), - Map.entry("grass", "GRASS"), Map.entry("bamboo", "BAMBOO"), - Map.entry("netherite", "NETHERITE_BLOCK"), Map.entry("empty", "EMPTY"), - Map.entry("powder_snow", "POWDER_SNOW"), Map.entry("sculk", "SCULK"), - Map.entry("vine", "VINE"), Map.entry("ladder", "LADDER"), - Map.entry("lantern", "LANTERN"), Map.entry("chain", "CHAIN") - ); + Map.entry("wood", "WOOD"), Map.entry("stone", "STONE"), + Map.entry("metal", "METAL"), Map.entry("glass", "GLASS"), + Map.entry("wool", "WOOL"), Map.entry("sand", "SAND"), + Map.entry("snow", "SNOW"), Map.entry("slime", "SLIME_BLOCK"), + Map.entry("anvil", "ANVIL"), Map.entry("gravel", "GRAVEL"), + Map.entry("grass", "GRASS"), Map.entry("bamboo", "BAMBOO"), + Map.entry("netherite", "NETHERITE_BLOCK"), Map.entry("empty", "EMPTY"), + Map.entry("powder_snow", "POWDER_SNOW"), Map.entry("sculk", "SCULK"), + Map.entry("vine", "VINE"), Map.entry("ladder", "LADDER"), + Map.entry("lantern", "LANTERN"), Map.entry("chain", "CHAIN")); // ── MapColor mapping ── private static final Map MAP_COLOR_MAP = Map.ofEntries( - Map.entry("none", "NONE"), Map.entry("grass", "GRASS"), - Map.entry("sand", "SAND"), Map.entry("wool", "WOOL"), - Map.entry("fire", "FIRE"), Map.entry("ice", "ICE"), - Map.entry("metal", "METAL"), Map.entry("plant", "PLANT"), - Map.entry("snow", "SNOW"), Map.entry("clay", "CLAY"), - Map.entry("dirt", "DIRT"), Map.entry("stone", "STONE"), - Map.entry("water", "WATER"), Map.entry("wood", "WOOD"), - Map.entry("quartz", "QUARTZ"), Map.entry("color_orange", "COLOR_ORANGE"), - Map.entry("color_magenta", "COLOR_MAGENTA"), Map.entry("color_light_blue", "COLOR_LIGHT_BLUE"), - Map.entry("color_yellow", "COLOR_YELLOW"), Map.entry("color_light_green", "COLOR_LIGHT_GREEN"), - Map.entry("color_pink", "COLOR_PINK"), Map.entry("color_gray", "COLOR_GRAY"), - Map.entry("color_light_gray", "COLOR_LIGHT_GRAY"), Map.entry("color_cyan", "COLOR_CYAN"), - Map.entry("color_purple", "COLOR_PURPLE"), Map.entry("color_blue", "COLOR_BLUE"), - Map.entry("color_brown", "COLOR_BROWN"), Map.entry("color_green", "COLOR_GREEN"), - Map.entry("color_red", "COLOR_RED"), Map.entry("color_black", "COLOR_BLACK"), - Map.entry("gold", "GOLD"), Map.entry("diamond", "DIAMOND"), - Map.entry("lapis", "LAPIS"), Map.entry("emerald", "EMERALD"), - Map.entry("podzol", "PODZOL"), Map.entry("nether", "NETHER"), - Map.entry("terracotta_white", "TERRACOTTA_WHITE"), Map.entry("terracotta_orange", "TERRACOTTA_ORANGE"), - Map.entry("terracotta_magenta", "TERRACOTTA_MAGENTA"), Map.entry("terracotta_light_blue", "TERRACOTTA_LIGHT_BLUE"), - Map.entry("terracotta_yellow", "TERRACOTTA_YELLOW"), Map.entry("terracotta_light_green", "TERRACOTTA_LIGHT_GREEN"), - Map.entry("terracotta_pink", "TERRACOTTA_PINK"), Map.entry("terracotta_gray", "TERRACOTTA_GRAY"), - Map.entry("terracotta_light_gray", "TERRACOTTA_LIGHT_GRAY"), Map.entry("terracotta_cyan", "TERRACOTTA_CYAN"), - Map.entry("terracotta_purple", "TERRACOTTA_PURPLE"), Map.entry("terracotta_blue", "TERRACOTTA_BLUE"), - Map.entry("terracotta_brown", "TERRACOTTA_BROWN"), Map.entry("terracotta_green", "TERRACOTTA_GREEN"), - Map.entry("terracotta_red", "TERRACOTTA_RED"), Map.entry("terracotta_black", "TERRACOTTA_BLACK"), - Map.entry("crimson_nylium", "CRIMSON_NYLIUM"), Map.entry("crimson_stem", "CRIMSON_STEM"), - Map.entry("crimson_hyphae", "CRIMSON_HYPHAE"), Map.entry("warped_nylium", "WARPED_NYLIUM"), - Map.entry("warped_stem", "WARPED_STEM"), Map.entry("warped_hyphae", "WARPED_HYPHAE"), - Map.entry("warped_wart_block", "WARPED_WART_BLOCK") - ); + Map.entry("none", "NONE"), Map.entry("grass", "GRASS"), + Map.entry("sand", "SAND"), Map.entry("wool", "WOOL"), + Map.entry("fire", "FIRE"), Map.entry("ice", "ICE"), + Map.entry("metal", "METAL"), Map.entry("plant", "PLANT"), + Map.entry("snow", "SNOW"), Map.entry("clay", "CLAY"), + Map.entry("dirt", "DIRT"), Map.entry("stone", "STONE"), + Map.entry("water", "WATER"), Map.entry("wood", "WOOD"), + Map.entry("quartz", "QUARTZ"), Map.entry("color_orange", "COLOR_ORANGE"), + Map.entry("color_magenta", "COLOR_MAGENTA"), Map.entry("color_light_blue", "COLOR_LIGHT_BLUE"), + Map.entry("color_yellow", "COLOR_YELLOW"), Map.entry("color_light_green", "COLOR_LIGHT_GREEN"), + Map.entry("color_pink", "COLOR_PINK"), Map.entry("color_gray", "COLOR_GRAY"), + Map.entry("color_light_gray", "COLOR_LIGHT_GRAY"), Map.entry("color_cyan", "COLOR_CYAN"), + Map.entry("color_purple", "COLOR_PURPLE"), Map.entry("color_blue", "COLOR_BLUE"), + Map.entry("color_brown", "COLOR_BROWN"), Map.entry("color_green", "COLOR_GREEN"), + Map.entry("color_red", "COLOR_RED"), Map.entry("color_black", "COLOR_BLACK"), + Map.entry("gold", "GOLD"), Map.entry("diamond", "DIAMOND"), + Map.entry("lapis", "LAPIS"), Map.entry("emerald", "EMERALD"), + Map.entry("podzol", "PODZOL"), Map.entry("nether", "NETHER"), + Map.entry("terracotta_white", "TERRACOTTA_WHITE"), Map.entry("terracotta_orange", "TERRACOTTA_ORANGE"), + Map.entry("terracotta_magenta", "TERRACOTTA_MAGENTA"), + Map.entry("terracotta_light_blue", "TERRACOTTA_LIGHT_BLUE"), + Map.entry("terracotta_yellow", "TERRACOTTA_YELLOW"), + Map.entry("terracotta_light_green", "TERRACOTTA_LIGHT_GREEN"), + Map.entry("terracotta_pink", "TERRACOTTA_PINK"), Map.entry("terracotta_gray", "TERRACOTTA_GRAY"), + Map.entry("terracotta_light_gray", "TERRACOTTA_LIGHT_GRAY"), + Map.entry("terracotta_cyan", "TERRACOTTA_CYAN"), + Map.entry("terracotta_purple", "TERRACOTTA_PURPLE"), Map.entry("terracotta_blue", "TERRACOTTA_BLUE"), + Map.entry("terracotta_brown", "TERRACOTTA_BROWN"), Map.entry("terracotta_green", "TERRACOTTA_GREEN"), + Map.entry("terracotta_red", "TERRACOTTA_RED"), Map.entry("terracotta_black", "TERRACOTTA_BLACK"), + Map.entry("crimson_nylium", "CRIMSON_NYLIUM"), Map.entry("crimson_stem", "CRIMSON_STEM"), + Map.entry("crimson_hyphae", "CRIMSON_HYPHAE"), Map.entry("warped_nylium", "WARPED_NYLIUM"), + Map.entry("warped_stem", "WARPED_STEM"), Map.entry("warped_hyphae", "WARPED_HYPHAE"), + Map.entry("warped_wart_block", "WARPED_WART_BLOCK")); /** * Parsed block definition. */ public record BlockDef( - String id, double hardness, double resistance, String sound, - int lightLevel, String mapColor, double friction, - double speedFactor, double jumpFactor, - boolean noOcclusion, boolean noCollision, - boolean requiresTool, boolean instabreak, - String creativeTab - ) {} + String id, double hardness, double resistance, String sound, + int lightLevel, String mapColor, double friction, + double speedFactor, double jumpFactor, + boolean noOcclusion, boolean noCollision, + boolean requiresTool, boolean instabreak, + String creativeTab) { + } /** * Parsed creative tab definition. */ public record CreativeTabDef( - String id, String title, String icon, - boolean searchBar, boolean rightAligned - ) {} + String id, String title, String icon, + boolean searchBar, boolean rightAligned) { + } /** * Parsed item definition (food, decorative, tool, or armor). */ public record ItemDef( - String id, String type, String displayName, - String rarity, int maxStackSize, boolean glint, - String creativeTab, - int nutrition, double saturation, boolean alwaysEdible, - String tier, String armorTexture - ) { + String id, String type, String displayName, + String rarity, int maxStackSize, boolean glint, + String creativeTab, + int nutrition, double saturation, boolean alwaysEdible, + String tier, String armorTexture) { public boolean isTool() { return "sword".equals(type) || "pickaxe".equals(type) || "axe".equals(type) - || "shovel".equals(type) || "hoe".equals(type); + || "shovel".equals(type) || "hoe".equals(type); } + public boolean isArmor() { return "helmet".equals(type) || "chestplate".equals(type) - || "leggings".equals(type) || "boots".equals(type); + || "leggings".equals(type) || "boots".equals(type); + } + + public boolean isEquipment() { + return isTool() || isArmor(); } - public boolean isEquipment() { return isTool() || isArmor(); } } /** * Parsed sound definition. */ public record SoundDef( - String id, String subtitle, boolean stream - ) {} + String id, String subtitle, boolean stream) { + } // ── JSON parsing ── @@ -123,20 +128,25 @@ public record SoundDef( */ public static List readBlocks(Path projectDir) { Path file = projectDir.resolve("registries/blocks.json"); - if (!Files.exists(file)) return List.of(); + if (!Files.exists(file)) + return List.of(); String raw = readFile(file); - if (raw.isEmpty()) return List.of(); + if (raw.isEmpty()) + return List.of(); List blocks = new ArrayList<>(); int pos = 0; while ((pos = raw.indexOf('"', pos)) != -1) { int keyStart = pos + 1; int keyEnd = raw.indexOf('"', keyStart); - if (keyEnd == -1) break; + if (keyEnd == -1) + break; String blockId = raw.substring(keyStart, keyEnd); pos = raw.indexOf('{', keyEnd); - if (pos == -1) break; + if (pos == -1) + break; int blockEnd = findMatchingBrace(raw, pos); - if (blockEnd == -1) break; + if (blockEnd == -1) + break; String body = raw.substring(pos + 1, blockEnd); blocks.add(parseBlockDef(blockId, body)); pos = blockEnd + 1; @@ -149,20 +159,25 @@ public static List readBlocks(Path projectDir) { */ public static List readCreativeTabs(Path projectDir) { Path file = projectDir.resolve("registries/creativeTabs.json"); - if (!Files.exists(file)) return List.of(); + if (!Files.exists(file)) + return List.of(); String raw = readFile(file); - if (raw.isEmpty()) return List.of(); + if (raw.isEmpty()) + return List.of(); List tabs = new ArrayList<>(); int pos = 0; while ((pos = raw.indexOf('"', pos)) != -1) { int keyStart = pos + 1; int keyEnd = raw.indexOf('"', keyStart); - if (keyEnd == -1) break; + if (keyEnd == -1) + break; String tabId = raw.substring(keyStart, keyEnd); pos = raw.indexOf('{', keyEnd); - if (pos == -1) break; + if (pos == -1) + break; int tabEnd = findMatchingBrace(raw, pos); - if (tabEnd == -1) break; + if (tabEnd == -1) + break; String body = raw.substring(pos + 1, tabEnd); tabs.add(parseCreativeTabDef(tabId, body)); pos = tabEnd + 1; @@ -175,20 +190,25 @@ public static List readCreativeTabs(Path projectDir) { */ public static List readItems(Path projectDir) { Path file = projectDir.resolve("registries/items.json"); - if (!Files.exists(file)) return List.of(); + if (!Files.exists(file)) + return List.of(); String raw = readFile(file); - if (raw.isEmpty()) return List.of(); + if (raw.isEmpty()) + return List.of(); List items = new ArrayList<>(); int pos = 0; while ((pos = raw.indexOf('"', pos)) != -1) { int keyStart = pos + 1; int keyEnd = raw.indexOf('"', keyStart); - if (keyEnd == -1) break; + if (keyEnd == -1) + break; String itemId = raw.substring(keyStart, keyEnd); pos = raw.indexOf('{', keyEnd); - if (pos == -1) break; + if (pos == -1) + break; int itemEnd = findMatchingBrace(raw, pos); - if (itemEnd == -1) break; + if (itemEnd == -1) + break; String body = raw.substring(pos + 1, itemEnd); items.add(parseItemDef(itemId, body)); pos = itemEnd + 1; @@ -201,20 +221,25 @@ public static List readItems(Path projectDir) { */ public static List readSounds(Path projectDir) { Path file = projectDir.resolve("registries/sounds.json"); - if (!Files.exists(file)) return List.of(); + if (!Files.exists(file)) + return List.of(); String raw = readFile(file); - if (raw.isEmpty()) return List.of(); + if (raw.isEmpty()) + return List.of(); List sounds = new ArrayList<>(); int pos = 0; while ((pos = raw.indexOf('"', pos)) != -1) { int keyStart = pos + 1; int keyEnd = raw.indexOf('"', keyStart); - if (keyEnd == -1) break; + if (keyEnd == -1) + break; String soundId = raw.substring(keyStart, keyEnd); pos = raw.indexOf('{', keyEnd); - if (pos == -1) break; + if (pos == -1) + break; int soundEnd = findMatchingBrace(raw, pos); - if (soundEnd == -1) break; + if (soundEnd == -1) + break; String body = raw.substring(pos + 1, soundEnd); sounds.add(parseSoundDef(soundId, body)); pos = soundEnd + 1; @@ -224,29 +249,25 @@ public static List readSounds(Path projectDir) { // ── Tier / ArmorMaterial mappings ── private static final Map TIER_MAP = Map.ofEntries( - Map.entry("wood", "Tiers.WOOD"), Map.entry("stone", "Tiers.STONE"), - Map.entry("iron", "Tiers.IRON"), Map.entry("gold", "Tiers.GOLD"), - Map.entry("diamond", "Tiers.DIAMOND"), Map.entry("netherite", "Tiers.NETHERITE") - ); + Map.entry("wood", "Tiers.WOOD"), Map.entry("stone", "Tiers.STONE"), + Map.entry("iron", "Tiers.IRON"), Map.entry("gold", "Tiers.GOLD"), + Map.entry("diamond", "Tiers.DIAMOND"), Map.entry("netherite", "Tiers.NETHERITE")); private static final Map ARMOR_MATERIAL_MAP = Map.ofEntries( - Map.entry("leather", "ArmorMaterials.LEATHER"), Map.entry("chain", "ArmorMaterials.CHAIN"), - Map.entry("iron", "ArmorMaterials.IRON"), Map.entry("gold", "ArmorMaterials.GOLD"), - Map.entry("diamond", "ArmorMaterials.DIAMOND"), Map.entry("netherite", "ArmorMaterials.NETHERITE"), - Map.entry("turtle", "ArmorMaterials.TURTLE") - ); + Map.entry("leather", "ArmorMaterials.LEATHER"), Map.entry("chain", "ArmorMaterials.CHAIN"), + Map.entry("iron", "ArmorMaterials.IRON"), Map.entry("gold", "ArmorMaterials.GOLD"), + Map.entry("diamond", "ArmorMaterials.DIAMOND"), Map.entry("netherite", "ArmorMaterials.NETHERITE"), + Map.entry("turtle", "ArmorMaterials.TURTLE")); private static final Map ARMOR_TYPE_MAP = Map.of( - "helmet", "ArmorItem.Type.HELMET", - "chestplate", "ArmorItem.Type.CHESTPLATE", - "leggings", "ArmorItem.Type.LEGGINGS", - "boots", "ArmorItem.Type.BOOTS" - ); + "helmet", "ArmorItem.Type.HELMET", + "chestplate", "ArmorItem.Type.CHESTPLATE", + "leggings", "ArmorItem.Type.LEGGINGS", + "boots", "ArmorItem.Type.BOOTS"); private static final Map TOOL_CLASS_MAP = Map.of( - "sword", "SwordItem", "pickaxe", "PickaxeItem", - "axe", "AxeItem", "shovel", "ShovelItem", "hoe", "HoeItem" - ); + "sword", "SwordItem", "pickaxe", "PickaxeItem", + "axe", "AxeItem", "shovel", "ShovelItem", "hoe", "HoeItem"); private static BlockDef parseBlockDef(String id, String body) { double hardness = 1.0, resistance = 1.0, friction = 0.6; @@ -264,32 +285,32 @@ private static BlockDef parseBlockDef(String id, String body) { lastEnd = valStart; switch (key) { case "hardness" -> { - double[] v = {hardness}; + double[] v = { hardness }; parseDouble(body, valStart, v); hardness = v[0]; } case "resistance" -> { - double[] v = {resistance}; + double[] v = { resistance }; parseDouble(body, valStart, v); resistance = v[0]; } case "friction" -> { - double[] v = {friction}; + double[] v = { friction }; parseDouble(body, valStart, v); friction = v[0]; } case "speedFactor" -> { - double[] v = {speedFactor}; + double[] v = { speedFactor }; parseDouble(body, valStart, v); speedFactor = v[0]; } case "jumpFactor" -> { - double[] v = {jumpFactor}; + double[] v = { jumpFactor }; parseDouble(body, valStart, v); jumpFactor = v[0]; } case "lightLevel" -> { - int[] v = {lightLevel}; + int[] v = { lightLevel }; parseInt(body, valStart, v); lightLevel = Math.max(0, Math.min(15, v[0])); } @@ -303,8 +324,8 @@ private static BlockDef parseBlockDef(String id, String body) { } } return new BlockDef(id, hardness, resistance, sound, lightLevel, mapColor, - friction, speedFactor, jumpFactor, noOcclusion, noCollision, - requiresTool, instabreak, creativeTab); + friction, speedFactor, jumpFactor, noOcclusion, noCollision, + requiresTool, instabreak, creativeTab); } private static CreativeTabDef parseCreativeTabDef(String id, String body) { @@ -350,18 +371,18 @@ private static ItemDef parseItemDef(String id, String body) { case "tier" -> tier = parseString(body, valStart, "iron"); case "armorTexture" -> armorTexture = parseString(body, valStart, ""); case "maxStackSize" -> { - int[] v = {maxStackSize}; + int[] v = { maxStackSize }; parseInt(body, valStart, v); maxStackSize = Math.max(1, Math.min(64, v[0])); } case "glint" -> glint = parseBool(body, valStart); case "nutrition" -> { - int[] v = {nutrition}; + int[] v = { nutrition }; parseInt(body, valStart, v); nutrition = Math.max(1, Math.min(20, v[0])); } case "saturation" -> { - double[] v = {saturation}; + double[] v = { saturation }; parseDouble(body, valStart, v); saturation = v[0]; } @@ -369,7 +390,7 @@ private static ItemDef parseItemDef(String id, String body) { } } return new ItemDef(id, type, displayName, rarity, maxStackSize, glint, - creativeTab, nutrition, saturation, alwaysEdible, tier, armorTexture); + creativeTab, nutrition, saturation, alwaysEdible, tier, armorTexture); } private static SoundDef parseSoundDef(String id, String body) { @@ -395,11 +416,11 @@ private static SoundDef parseSoundDef(String id, String body) { * Generates the Java source code for DeferredRegister fields and * registration calls that get injected into the generated @Mod class. * - * @param modId the modId for DeferredRegister.createBlocks/createItems - * @param blocks parsed block definitions - * @param tabs parsed creative tab definitions - * @param items parsed item definitions - * @param sounds parsed sound definitions + * @param modId the modId for DeferredRegister.createBlocks/createItems + * @param blocks parsed block definitions + * @param tabs parsed creative tab definitions + * @param items parsed item definitions + * @param sounds parsed sound definitions * @return array of [fieldDeclarations, constructorRegistrations] */ public static String[] generateJavaCode(String modId, List blocks, @@ -407,17 +428,18 @@ public static String[] generateJavaCode(String modId, List blocks, StringBuilder fields = new StringBuilder(); StringBuilder regs = new StringBuilder(); - if (blocks.isEmpty() && tabs.isEmpty() && items.isEmpty() && sounds.isEmpty()) return new String[]{"", ""}; + if (blocks.isEmpty() && tabs.isEmpty() && items.isEmpty() && sounds.isEmpty()) + return new String[] { "", "" }; boolean needItemsRegister = !blocks.isEmpty() || !items.isEmpty(); if (!blocks.isEmpty()) { fields.append(""" - \n private static final DeferredRegister.Blocks BLOCKS = - DeferredRegister.createBlocks("%s"); - private static final DeferredRegister.Items ITEMS = - DeferredRegister.createItems("%s"); - """.formatted(modId, modId)); + \n private static final DeferredRegister.Blocks BLOCKS = + DeferredRegister.createBlocks("%s"); + private static final DeferredRegister.Items ITEMS = + DeferredRegister.createItems("%s"); + """.formatted(modId, modId)); for (BlockDef b : blocks) { String field = b.id().toUpperCase(); @@ -425,7 +447,7 @@ public static String[] generateJavaCode(String modId, List blocks, StringBuilder props = new StringBuilder(); props.append("BlockBehaviour.Properties.of()"); props.append(".strength(").append(blockFloat(b.hardness())) - .append("f, ").append(blockFloat(b.resistance())).append("f)"); + .append("f, ").append(blockFloat(b.resistance())).append("f)"); String soundType = SOUND_TYPE_MAP.getOrDefault(b.sound(), "STONE"); props.append(".sound(SoundType.").append(soundType).append(")"); if (b.lightLevel() > 0) @@ -436,20 +458,24 @@ public static String[] generateJavaCode(String modId, List blocks, props.append(".speedFactor(").append(blockFloat(b.speedFactor())).append("f)"); if (Math.abs(b.jumpFactor() - 1.0) > 0.001) props.append(".jumpFactor(").append(blockFloat(b.jumpFactor())).append("f)"); - if (b.noOcclusion()) props.append(".noOcclusion()"); - if (b.noCollision()) props.append(".noCollision()"); - if (b.requiresTool()) props.append(".requiresCorrectToolForDrops()"); - if (b.instabreak()) props.append(".instabreak()"); + if (b.noOcclusion()) + props.append(".noOcclusion()"); + if (b.noCollision()) + props.append(".noCollision()"); + if (b.requiresTool()) + props.append(".requiresCorrectToolForDrops()"); + if (b.instabreak()) + props.append(".instabreak()"); String mc = MAP_COLOR_MAP.getOrDefault(b.mapColor(), "STONE"); if (!"STONE".equals(mc)) props.append(".mapColor(MapColor.").append(mc).append(")"); fields.append(""" - \n public static final DeferredBlock %s = - BLOCKS.registerSimpleBlock("%s", %s); - public static final DeferredItem %s_ITEM = - ITEMS.registerSimpleBlockItem(%s); - """.formatted(field, b.id(), props, field, field)); + \n public static final DeferredBlock %s = + BLOCKS.registerSimpleBlock("%s", %s); + public static final DeferredItem %s_ITEM = + ITEMS.registerSimpleBlockItem(%s); + """.formatted(field, b.id(), props, field, field)); } regs.append(" BLOCKS.register(modEventBus);\n"); @@ -457,9 +483,9 @@ public static String[] generateJavaCode(String modId, List blocks, } else if (!items.isEmpty()) { // Items-only project: need ITEMS register without BLOCKS fields.append(""" - \n private static final DeferredRegister.Items ITEMS = - DeferredRegister.createItems("%s"); - """.formatted(modId)); + \n private static final DeferredRegister.Items ITEMS = + DeferredRegister.createItems("%s"); + """.formatted(modId)); } // Standalone item registrations (food, decorative, tools, armor) @@ -473,21 +499,21 @@ public static String[] generateJavaCode(String modId, List blocks, } // Generate custom ArmorMaterial Holder fields for (var entry : armorTexTier.entrySet()) { - String texName = entry.getKey(); // e.g. "star" - String baseTier = entry.getValue(); // e.g. "diamond" + String texName = entry.getKey(); // e.g. "star" + String baseTier = entry.getValue(); // e.g. "diamond" String fieldName = texName.toUpperCase() + "_ARMOR"; fields.append(""" - \n private static final Holder %1$s = - Holder.direct(new ArmorMaterial( - ArmorMaterials.%6$s.value().defense(), - ArmorMaterials.%6$s.value().enchantmentValue(), - ArmorMaterials.%6$s.value().equipSound(), - ArmorMaterials.%6$s.value().repairIngredient(), - java.util.List.of(new ArmorMaterial.Layer( - ResourceLocation.fromNamespaceAndPath("%2$s", "%3$s"))), - ArmorMaterials.%6$s.value().toughness(), - ArmorMaterials.%6$s.value().knockbackResistance())); - """.formatted(fieldName, modId, texName, "", "", baseTier.toUpperCase())); + \n private static final Holder %1$s = + Holder.direct(new ArmorMaterial( + ArmorMaterials.%6$s.value().defense(), + ArmorMaterials.%6$s.value().enchantmentValue(), + ArmorMaterials.%6$s.value().equipSound(), + ArmorMaterials.%6$s.value().repairIngredient(), + java.util.List.of(new ArmorMaterial.Layer( + ResourceLocation.fromNamespaceAndPath("%2$s", "%3$s"))), + ArmorMaterials.%6$s.value().toughness(), + ArmorMaterials.%6$s.value().knockbackResistance())); + """.formatted(fieldName, modId, texName, "", "", baseTier.toUpperCase())); } for (ItemDef it : items) { @@ -497,11 +523,11 @@ public static String[] generateJavaCode(String modId, List blocks, String tierExpr = TIER_MAP.getOrDefault(it.tier(), "Tiers.IRON"); var props = buildItemProperties(it); fields.append(""" - \n public static final DeferredItem<%1$s> %2$s = - ITEMS.register("%3$s", () -> new %1$s( - %4$s, - %5$s)); - """.formatted(toolClass, field, it.id(), tierExpr, props)); + \n public static final DeferredItem<%1$s> %2$s = + ITEMS.register("%3$s", () -> new %1$s( + %4$s, + %5$s)); + """.formatted(toolClass, field, it.id(), tierExpr, props)); } else if (it.isArmor()) { String armorMaterial; if (!it.armorTexture().isEmpty()) { @@ -512,17 +538,17 @@ public static String[] generateJavaCode(String modId, List blocks, String armorType = ARMOR_TYPE_MAP.get(it.type()); var props = buildItemProperties(it); fields.append(""" - \n public static final DeferredItem %1$s = - ITEMS.register("%2$s", () -> new ArmorItem( - %3$s, %4$s, - %5$s)); - """.formatted(field, it.id(), armorMaterial, armorType, props)); + \n public static final DeferredItem %1$s = + ITEMS.register("%2$s", () -> new ArmorItem( + %3$s, %4$s, + %5$s)); + """.formatted(field, it.id(), armorMaterial, armorType, props)); } else { var props = buildItemProperties(it); fields.append(""" - \n public static final DeferredItem %s = - ITEMS.register("%s", () -> new Item(%s)); - """.formatted(field, it.id(), props)); + \n public static final DeferredItem %s = + ITEMS.register("%s", () -> new Item(%s)); + """.formatted(field, it.id(), props)); } } if (blocks.isEmpty()) { @@ -533,18 +559,18 @@ public static String[] generateJavaCode(String modId, List blocks, // Sound registrations if (!sounds.isEmpty()) { fields.append(""" - \n private static final DeferredRegister SOUNDS = - DeferredRegister.create(Registries.SOUND_EVENT, "%s"); - """.formatted(modId)); + \n private static final DeferredRegister SOUNDS = + DeferredRegister.create(Registries.SOUND_EVENT, "%s"); + """.formatted(modId)); for (SoundDef s : sounds) { String field = s.id().toUpperCase(); fields.append(""" - \n public static final DeferredHolder %s = - SOUNDS.register("%s", - () -> SoundEvent.createVariableRangeEvent( - ResourceLocation.fromNamespaceAndPath("%s", "%s"))); - """.formatted(field, s.id(), modId, s.id())); + \n public static final DeferredHolder %s = + SOUNDS.register("%s", + () -> SoundEvent.createVariableRangeEvent( + ResourceLocation.fromNamespaceAndPath("%s", "%s"))); + """.formatted(field, s.id(), modId, s.id())); } regs.append(" SOUNDS.register(modEventBus);\n"); @@ -554,15 +580,16 @@ public static String[] generateJavaCode(String modId, List blocks, if (!needItemsRegister && sounds.isEmpty()) { // Tabs need an items register for icons even without blocks/items fields.append(""" - \n private static final DeferredRegister.Items ITEMS = - DeferredRegister.createItems("%s"); - """.formatted(modId)); + \n private static final DeferredRegister.Items ITEMS = + DeferredRegister.createItems("%s"); + """.formatted(modId)); } fields.append(""" - \n private static final DeferredRegister TABS = - DeferredRegister.create(Registries.CREATIVE_MODE_TAB, "%s"); - """.formatted(modId)); + \n private static final DeferredRegister TABS = + DeferredRegister.create(Registries.CREATIVE_MODE_TAB, "%s"); + """.formatted(modId)); + int tabIndex = 0; for (CreativeTabDef t : tabs) { String tabField = t.id().toUpperCase() + "_TAB"; // Collect block items for this tab @@ -599,29 +626,33 @@ public static String[] generateJavaCode(String modId, List blocks, } } StringBuilder tabOpts = new StringBuilder(); - if (t.searchBar()) tabOpts.append(".withSearchBar()"); - if (t.rightAligned()) tabOpts.append(".alignedRight()"); + if (t.searchBar()) + tabOpts.append(".withSearchBar()"); + if (t.rightAligned()) + tabOpts.append(".alignedRight()"); String titleText = escapeJavaString(t.title()); fields.append(""" - \n public static final DeferredHolder %s = - TABS.register("%s", () -> CreativeModeTab.builder() - .title(Component.literal("%s")) - .icon(() -> new ItemStack(%s)) - .displayItems((params, output) -> { - %s }) - %s.build()); - """.formatted(tabField, t.id(), + \n public static final DeferredHolder %s = + TABS.register("%s", () -> CreativeModeTab.builder(CreativeModeTab.Row.TOP, %d) + .title(Component.literal("%s")) + .icon(() -> new ItemStack(%s)) + .displayItems((params, output) -> { + %s }) + %s.build()); + """.formatted(tabField, t.id(), + tabIndex, titleText, iconExpr != null ? iconExpr : "Items.STONE", itemsCode.toString().indent(2).stripTrailing(), tabOpts.toString())); + tabIndex++; } regs.append(" TABS.register(modEventBus);\n"); } - return new String[]{fields.toString(), regs.toString()}; + return new String[] { fields.toString(), regs.toString() }; } private static String buildItemProperties(ItemDef it) { @@ -653,70 +684,70 @@ public static String generateImports(boolean hasBlocks, boolean hasTabs, StringBuilder sb = new StringBuilder(); if (hasBlocks) { sb.append(""" - import net.neoforged.neoforge.registries.DeferredRegister; - import net.neoforged.neoforge.registries.DeferredBlock; - import net.neoforged.neoforge.registries.DeferredItem; - import net.minecraft.world.level.block.Block; - import net.minecraft.world.level.block.state.BlockBehaviour; - import net.minecraft.world.level.block.SoundType; - import net.minecraft.world.item.BlockItem; - import net.minecraft.world.level.material.MapColor; - """); + import net.neoforged.neoforge.registries.DeferredRegister; + import net.neoforged.neoforge.registries.DeferredBlock; + import net.neoforged.neoforge.registries.DeferredItem; + import net.minecraft.world.level.block.Block; + import net.minecraft.world.level.block.state.BlockBehaviour; + import net.minecraft.world.level.block.SoundType; + import net.minecraft.world.item.BlockItem; + import net.minecraft.world.level.material.MapColor; + """); } else if (hasItems || hasTabs) { sb.append(""" - import net.neoforged.neoforge.registries.DeferredRegister; - import net.neoforged.neoforge.registries.DeferredItem; - """); + import net.neoforged.neoforge.registries.DeferredRegister; + import net.neoforged.neoforge.registries.DeferredItem; + """); } if (hasSounds) { sb.append(""" - import net.neoforged.neoforge.registries.DeferredRegister; - import net.minecraft.core.registries.Registries; - import net.minecraft.sounds.SoundEvent; - import net.minecraft.resources.ResourceLocation; - import net.neoforged.neoforge.registries.DeferredHolder; - """); + import net.neoforged.neoforge.registries.DeferredRegister; + import net.minecraft.core.registries.Registries; + import net.minecraft.sounds.SoundEvent; + import net.minecraft.resources.ResourceLocation; + import net.neoforged.neoforge.registries.DeferredHolder; + """); } if (hasItems || hasTools || hasArmor) { sb.append("import net.minecraft.world.item.Rarity;\n"); } if (hasItems) { sb.append(""" - import net.minecraft.world.item.Item; - import net.minecraft.world.food.FoodProperties; - """); + import net.minecraft.world.item.Item; + import net.minecraft.world.food.FoodProperties; + """); } if (hasTools) { sb.append(""" - import net.minecraft.world.item.SwordItem; - import net.minecraft.world.item.PickaxeItem; - import net.minecraft.world.item.AxeItem; - import net.minecraft.world.item.ShovelItem; - import net.minecraft.world.item.HoeItem; - import net.minecraft.world.item.Tiers; - """); + import net.minecraft.world.item.SwordItem; + import net.minecraft.world.item.PickaxeItem; + import net.minecraft.world.item.AxeItem; + import net.minecraft.world.item.ShovelItem; + import net.minecraft.world.item.HoeItem; + import net.minecraft.world.item.Tiers; + """); } if (hasArmor) { sb.append(""" - import net.minecraft.world.item.ArmorItem; - import net.minecraft.world.item.ArmorMaterials; - import net.minecraft.world.item.ArmorMaterial; - import net.minecraft.resources.ResourceLocation; - import net.minecraft.core.Holder; - """); + import net.minecraft.world.item.ArmorItem; + import net.minecraft.world.item.ArmorMaterials; + import net.minecraft.world.item.ArmorMaterial; + import net.minecraft.resources.ResourceLocation; + import net.minecraft.core.Holder; + """); } if (hasTabs) { if (!hasBlocks && !hasSounds) { sb.append("import net.neoforged.neoforge.registries.DeferredRegister;\n"); } sb.append(""" - import net.minecraft.core.registries.Registries; - import net.minecraft.world.item.CreativeModeTab; - import net.minecraft.world.item.ItemStack; - import net.minecraft.network.chat.Component; - import net.minecraft.world.item.Items; - import net.neoforged.neoforge.registries.DeferredHolder; - """); + import net.minecraft.core.registries.Registries; + import net.minecraft.world.item.CreativeModeTab; + import net.minecraft.world.item.ItemStack; + import net.minecraft.network.chat.Component; + import net.minecraft.world.item.Items; + import net.neoforged.neoforge.registries.DeferredHolder; + """); } return sb.toString(); } @@ -724,18 +755,23 @@ public static String generateImports(boolean hasBlocks, boolean hasTabs, // ── Helpers ── private static String readFile(Path path) { - try { return Files.readString(path, StandardCharsets.UTF_8); } - catch (IOException e) { return ""; } + try { + return Files.readString(path, StandardCharsets.UTF_8); + } catch (IOException e) { + return ""; + } } private static int findMatchingBrace(String s, int openPos) { int depth = 0; for (int i = openPos; i < s.length(); i++) { char c = s.charAt(i); - if (c == '{') depth++; + if (c == '{') + depth++; else if (c == '}') { depth--; - if (depth == 0) return i; + if (depth == 0) + return i; } } return -1; @@ -743,28 +779,36 @@ else if (c == '}') { private static String parseString(String body, int start, String def) { int q1 = body.indexOf('"', start); - if (q1 == -1) return def; + if (q1 == -1) + return def; int q2 = body.indexOf('"', q1 + 1); - if (q2 == -1) return def; + if (q2 == -1) + return def; return body.substring(q1 + 1, q2); } private static void parseDouble(String body, int start, double[] out) { int end = start; while (end < body.length() && (Character.isDigit(body.charAt(end)) - || body.charAt(end) == '.' || body.charAt(end) == '-')) end++; + || body.charAt(end) == '.' || body.charAt(end) == '-')) + end++; if (end > start) { - try { out[0] = Double.parseDouble(body.substring(start, end)); } - catch (NumberFormatException ignored) {} + try { + out[0] = Double.parseDouble(body.substring(start, end)); + } catch (NumberFormatException ignored) { + } } } private static void parseInt(String body, int start, int[] out) { int end = start; - while (end < body.length() && Character.isDigit(body.charAt(end))) end++; + while (end < body.length() && Character.isDigit(body.charAt(end))) + end++; if (end > start) { - try { out[0] = Integer.parseInt(body.substring(start, end)); } - catch (NumberFormatException ignored) {} + try { + out[0] = Integer.parseInt(body.substring(start, end)); + } catch (NumberFormatException ignored) { + } } } @@ -774,7 +818,8 @@ private static boolean parseBool(String body, int start) { } private static String blockFloat(double v) { - if (v == (long) v) return String.valueOf((long) v); + if (v == (long) v) + return String.valueOf((long) v); return String.valueOf(v); } @@ -799,11 +844,13 @@ private static String escapeJsonString(String s) { } public static String generateSoundsJson(String modId, List sounds) { - if (sounds.isEmpty()) return null; + if (sounds.isEmpty()) + return null; StringBuilder sb = new StringBuilder(); sb.append("{\n"); for (int i = 0; i < sounds.size(); i++) { - if (i > 0) sb.append(",\n"); + if (i > 0) + sb.append(",\n"); SoundDef s = sounds.get(i); sb.append(" \"").append(s.id()).append("\": {\n"); sb.append(" \"sounds\": [\"").append(modId).append(":").append(s.id()).append("\"]"); diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java index 12acfa5..566251f 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java @@ -1,26 +1,41 @@ package com.box3lab.box3js.standalone; -import javax.tools.JavaCompiler; -import javax.tools.JavaFileObject; -import javax.tools.StandardJavaFileManager; -import javax.tools.ToolProvider; -import java.io.*; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UncheckedIOException; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; +import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.util.*; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.regex.Pattern; +import javax.tools.JavaCompiler; +import javax.tools.JavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.ToolProvider; + /** * Compiles a Box3JS TypeScript project into a lightweight NeoForge mod JAR. * - *

Script JARs (~50KB) contain only the generated {@code @Mod} entry point + *

+ * Script JARs (~50KB) contain only the generated {@code @Mod} entry point * and bundled JS source. They depend on the Box3JS mod ({@code box3js}) for * the Rhino runtime and API bindings — no classes are bundled. * @@ -156,7 +171,8 @@ private void bundleLogo(Path workDir) throws IOException { private void bundleAssets(Path workDir) throws IOException { Path src = projectDir.resolve("assets"); - if (!Files.isDirectory(src)) return; + if (!Files.isDirectory(src)) + return; Path dest = workDir.resolve("assets/" + modId); copyDir(src, dest); @@ -166,7 +182,8 @@ private void bundleAssets(Path workDir) throws IOException { private void generateSoundsFile(Path workDir, java.util.List sounds) throws IOException { String soundsJson = Box3JSRegistryGen.generateSoundsJson(modId, sounds); - if (soundsJson == null) return; + if (soundsJson == null) + return; Path assetsDir = workDir.resolve("assets/" + modId); Files.createDirectories(assetsDir); Files.writeString(assetsDir.resolve("sounds.json"), soundsJson); @@ -195,32 +212,34 @@ private void generateModClass(Path genSrcDir, String[] registryCode = Box3JSRegistryGen.generateJavaCode(modId, blocks, tabs, items, sounds); String fieldDecls = registryCode[0]; String constructorRegs = registryCode[1]; - String extraImports = Box3JSRegistryGen.generateImports(hasBlocks, hasTabs, hasItems, hasSounds, hasTools, hasArmor); + String extraImports = Box3JSRegistryGen.generateImports(hasBlocks, hasTabs, hasItems, hasSounds, hasTools, + hasArmor); // Generate supplier map builder methods StringBuilder mapMethods = new StringBuilder(); if (hasBlocks) { mapMethods.append(""" - private static java.util.Map> buildBlockMap() { - java.util.Map> map = new java.util.HashMap<>(); - """); + private static java.util.Map> buildBlockMap() { + java.util.Map> map = new java.util.HashMap<>(); + """); for (var b : blocks) { String field = b.id().toUpperCase(); mapMethods.append(" map.put(\"").append(b.id()) - .append("\", () -> ").append(field).append(".get());\n"); + .append("\", () -> ").append(field).append(".get());\n"); } mapMethods.append(" return map;\n }\n"); - mapMethods.append(""" + mapMethods + .append(""" - private static java.util.Map> buildBlockItemMap() { - java.util.Map> map = new java.util.HashMap<>(); - """); + private static java.util.Map> buildBlockItemMap() { + java.util.Map> map = new java.util.HashMap<>(); + """); for (var b : blocks) { String field = b.id().toUpperCase(); mapMethods.append(" map.put(\"").append(b.id()) - .append("\", () -> ").append(field).append("_ITEM.get());\n"); + .append("\", () -> ").append(field).append("_ITEM.get());\n"); } mapMethods.append(" return map;\n }\n"); } @@ -228,27 +247,28 @@ private static java.util.Map> bui if (hasItems) { mapMethods.append(""" - private static java.util.Map> buildItemMap() { - java.util.Map> map = new java.util.HashMap<>(); - """); + private static java.util.Map> buildItemMap() { + java.util.Map> map = new java.util.HashMap<>(); + """); for (var it : items) { String field = it.id().toUpperCase(); mapMethods.append(" map.put(\"").append(it.id()) - .append("\", () -> ").append(field).append(".get());\n"); + .append("\", () -> ").append(field).append(".get());\n"); } mapMethods.append(" return map;\n }\n"); } if (hasSounds) { - mapMethods.append(""" + mapMethods + .append(""" - private static java.util.Map> buildSoundMap() { - java.util.Map> map = new java.util.HashMap<>(); - """); + private static java.util.Map> buildSoundMap() { + java.util.Map> map = new java.util.HashMap<>(); + """); for (var s : sounds) { String field = s.id().toUpperCase(); mapMethods.append(" map.put(\"").append(s.id()) - .append("\", () -> ").append(field).append(".get());\n"); + .append("\", () -> ").append(field).append(".get());\n"); } mapMethods.append(" return map;\n }\n"); } @@ -261,8 +281,10 @@ private static java.util.Map> bu superArgs.append(", ").append(hasSounds ? "buildSoundMap()" : "null"); var hardcodedImports = new StringBuilder(); - if (hasItems) hardcodedImports.append("import net.minecraft.world.item.Item;\n"); - if (hasSounds) hardcodedImports.append("import net.minecraft.sounds.SoundEvent;\n"); + if (hasItems) + hardcodedImports.append("import net.minecraft.world.item.Item;\n"); + if (hasSounds) + hardcodedImports.append("import net.minecraft.sounds.SoundEvent;\n"); String src = String.format(""" package %s; @@ -293,23 +315,20 @@ public class %s extends Box3StandaloneBootstrap { } private void compileJava(Path genSrcDir, Path classesDir) throws Exception { - List cpEntries = new ArrayList<>(); - ClassLoader cl = getClass().getClassLoader(); - if (cl instanceof URLClassLoader ucl) { - for (URL url : ucl.getURLs()) { - try { - cpEntries.add(Path.of(url.toURI())); - } catch (Exception ignored) { - } - } - } + LinkedHashSet cpEntries = new LinkedHashSet<>(); + + collectFromClassLoader(cpEntries, getClass().getClassLoader()); + collectFromJavaPathProperty(cpEntries, "java.class.path", File.pathSeparator); + collectFromJavaPathProperty(cpEntries, "jdk.module.path", File.pathSeparator); + collectFromLegacyClasspathFile(cpEntries); + collectFromCodeSource(cpEntries, Box3ScriptCompiler.class); + collectFromCodeSource(cpEntries, Box3StandaloneBootstrap.class); + collectFromRuntimeAnchors(cpEntries); + if (cpEntries.isEmpty()) { - String cp = System.getProperty("java.class.path"); - for (String entry : cp.split(File.pathSeparator)) { - Path p = Path.of(entry); - if (Files.exists(p)) - cpEntries.add(p); - } + throw new RuntimeException( + "No classpath entries resolved for generated @Mod compilation. " + + "Expected java.class.path or legacyClassPath.file to be available."); } List sourceFiles = new ArrayList<>(); @@ -330,11 +349,9 @@ private void compileJava(Path genSrcDir, Path classesDir) throws Exception { options.add("-d"); options.add(classesDir.toString()); options.add("-proc:none"); - if (!cpEntries.isEmpty()) { - options.add("-classpath"); - options.add(String.join(File.pathSeparator, - cpEntries.stream().map(Path::toString).toList())); - } + options.add("-classpath"); + options.add(String.join(File.pathSeparator, + cpEntries.stream().map(Path::toString).toList())); JavaCompiler.CompilationTask task = jc.getTask( null, fm, null, options, null, units); @@ -345,6 +362,143 @@ private void compileJava(Path genSrcDir, Path classesDir) throws Exception { System.out.println(" Compiled " + sourceFiles.size() + " generated source(s)"); } + private static void collectFromClassLoader(Set out, ClassLoader cl) { + if (!(cl instanceof URLClassLoader ucl)) { + return; + } + for (URL url : ucl.getURLs()) { + addPathFromLocation(out, url.toString()); + } + } + + private static void collectFromJavaPathProperty(Set out, String prop, String separator) { + String raw = System.getProperty(prop); + if (raw == null || raw.isBlank()) { + return; + } + for (String entry : raw.split(Pattern.quote(separator))) { + if (entry == null || entry.isBlank()) { + continue; + } + try { + Path p = Path.of(entry.trim()); + if (Files.exists(p)) { + out.add(p); + } + } catch (Exception ignored) { + } + } + } + + private static void collectFromLegacyClasspathFile(Set out) { + String legacyFile = System.getProperty("legacyClassPath.file"); + if (legacyFile == null || legacyFile.isBlank()) { + return; + } + Path file = Path.of(legacyFile.trim()); + if (!Files.isRegularFile(file)) { + return; + } + + try { + String raw = Files.readString(file, StandardCharsets.UTF_8); + for (String token : raw.split("\\s+")) { + if (token == null || token.isBlank()) { + continue; + } + try { + Path p = Path.of(token.trim()); + if (Files.exists(p)) { + out.add(p); + } + } catch (Exception ignored) { + } + } + } catch (IOException ignored) { + } + } + + private static void collectFromCodeSource(Set out, Class type) { + try { + CodeSource cs = type.getProtectionDomain().getCodeSource(); + if (cs == null || cs.getLocation() == null) { + return; + } + addPathFromLocation(out, cs.getLocation().toString()); + + URL classRes = type.getResource(type.getSimpleName() + ".class"); + if (classRes != null) { + addPathFromLocation(out, classRes.toString()); + } + } catch (Exception ignored) { + } + } + + private static void addPathFromLocation(Set out, String location) { + if (location == null || location.isBlank()) { + return; + } + Path p = pathFromLocation(location); + if (p != null && Files.exists(p)) { + out.add(p); + } + } + + private static Path pathFromLocation(String location) { + String s = location.trim(); + try { + if (s.startsWith("jar:")) { + s = s.substring("jar:".length()); + } + if (s.startsWith("union:")) { + s = s.substring("union:".length()); + } + + int bang = s.indexOf("!/"); + if (bang >= 0) { + s = s.substring(0, bang); + } + + int marker = s.indexOf("%23"); + if (marker >= 0) { + s = s.substring(0, marker); + } + + s = URLDecoder.decode(s, StandardCharsets.UTF_8); + + if (s.startsWith("file:")) { + return Path.of(URI.create(s)); + } + if (s.startsWith("/")) { + return Path.of(s); + } + + URI uri = URI.create(s); + if ("file".equalsIgnoreCase(uri.getScheme())) { + return Path.of(uri); + } + } catch (Exception ignored) { + } + return null; + } + + private static void collectFromRuntimeAnchors(Set out) { + String[] anchors = new String[] { + "net.minecraft.world.level.block.Block", + "net.minecraft.world.item.Item", + "net.neoforged.fml.common.Mod", + "net.neoforged.neoforge.registries.DeferredRegister", + "net.neoforged.bus.api.IEventBus" + }; + for (String name : anchors) { + try { + Class c = Class.forName(name); + collectFromCodeSource(out, c); + } catch (Throwable ignored) { + } + } + } + // ── Step 4: Metadata + packaging ── private void createMetadata(Path workDir) throws IOException { @@ -435,17 +589,17 @@ private void packageJar(Path workDir, Path outputJar) throws IOException { private void addDirToJar(JarOutputStream jos, Path root, Path dir) throws IOException { try (var stream = Files.walk(dir)) { stream.filter(Files::isRegularFile) - .filter(f -> !f.getFileName().toString().equals(".DS_Store")) - .forEach(file -> { - try { - String entryName = root.relativize(file).toString().replace('\\', '/'); - jos.putNextEntry(new JarEntry(entryName)); - Files.copy(file, jos); - jos.closeEntry(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - }); + .filter(f -> !f.getFileName().toString().equals(".DS_Store")) + .forEach(file -> { + try { + String entryName = root.relativize(file).toString().replace('\\', '/'); + jos.putNextEntry(new JarEntry(entryName)); + Files.copy(file, jos); + jos.closeEntry(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }); } catch (UncheckedIOException e) { throw e.getCause(); } diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json index e1a9c3b..00f43ff 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json @@ -1,5 +1,5 @@ { - "block.mymod.ruby_block": "Ruby Block", - "item.mymod.chocolate": "Chocolate Bar", - "itemGroup.mymod.my_blocks": "My Blocks" + "block.mygame.ruby_block": "Ruby Block", + "item.mygame.chocolate": "Chocolate Bar", + "itemGroup.mygame.my_blocks": "My Blocks" } diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json index b9d2a1c..64bb808 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json @@ -1,5 +1,5 @@ { - "block.mymod.ruby_block": "红宝石块", - "item.mymod.chocolate": "巧克力棒", - "itemGroup.mymod.my_blocks": "我的方块" + "block.mygame.ruby_block": "红宝石块", + "item.mygame.chocolate": "巧克力棒", + "itemGroup.mygame.my_blocks": "我的方块" } diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/types/shared.d.ts b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/types/shared.d.ts index 5ccda11..f51df92 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/types/shared.d.ts +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/types/shared.d.ts @@ -831,11 +831,11 @@ interface GameQueryResult> { /** * @zh SQLite 数据库,服务端存储在 `config/box3/data/.db`, - * 客户端存储在 `/box3/client-db/.db`。 + * 客户端存储在 `/config/box3/client-db/.db`。 * 通过全局 `db` 访问,支持 `?` 占位符和 tagged template 两种调用约定。 * * @en SQLite database — server stores at `config/box3/data/.db`, - * client stores at `/box3/client-db/.db`. + * client stores at `/config/box3/client-db/.db`. * Access via global `db`, supports both `?` placeholder and tagged template calling conventions. */ interface GameDatabase { @@ -1031,7 +1031,10 @@ declare const http: GameHttpAPI; * }, 60); * // token.cancel(); // cancel before it fires */ -declare function setTimeout(handler: () => void, ticks: number): GameEventHandlerToken; +declare function setTimeout( + handler: () => void, + ticks: number, +): GameEventHandlerToken; /** * @zh 设置循环定时回调。Rhino 引擎不提供浏览器内置的 setInterval,由 Box3JS 提供。 @@ -1046,5 +1049,7 @@ declare function setTimeout(handler: () => void, ticks: number): GameEventHandle * }, 100); * // token.cancel(); // stop the interval */ -declare function setInterval(handler: () => void, ticks: number): GameEventHandlerToken; - +declare function setInterval( + handler: () => void, + ticks: number, +): GameEventHandlerToken; diff --git a/Box3JS-NeoForge-1.21.1/tools/box3js-api-manifest.json b/Box3JS-NeoForge-1.21.1/tools/box3js-api-manifest.json index cbbd474..4b55146 100644 --- a/Box3JS-NeoForge-1.21.1/tools/box3js-api-manifest.json +++ b/Box3JS-NeoForge-1.21.1/tools/box3js-api-manifest.json @@ -32,7 +32,7 @@ "accessorProperties": ["serverId", "rainDensity", "thunderDensity", "time", "timeScale", "difficulty", "spawnPoint", "ambientSound", "playerJoinSound", "playerLeaveSound", "placeVoxelSound", "breakVoxelSound", "borderSize"], "keepMethodNames": ["setTime"], "ignoreJava": ["setProjectName", "removeProject", "resetAll", "getProjectName", "getCurrentTick"], - "docs": ["docs/api/world.md", "docs/api/world_en.md"] + "docs": ["docs/api/world.md", "docs/en/api/world.md"] }, { "name": "GameVector3", @@ -85,7 +85,7 @@ "ignoreJava": ["getEntity"], "accessorProperties": ["id", "entityType", "player", "position", "velocity", "bounds", "meshInvisible", "glowing", "nameTag", "onGround", "eyePosition", "destroyed", "hp", "maxHp", "collides", "fixed", "gravity", "friction", "mass", "restitution", "invulnerable"], "keepMethodNames": ["isPlayer", "setNameTag"], - "docs": ["docs/api/entity.md", "docs/api/entity_en.md"] + "docs": ["docs/api/entity.md", "docs/en/api/entity.md"] }, { "name": "player", @@ -95,7 +95,7 @@ "iface": "GamePlayer", "ignoreJava": ["getPlayer"], "accessorProperties": ["position", "velocity", "bounds", "onGround", "name", "userId", "opLevel", "invisible", "scale", "walkSpeed", "runSpeed", "jumpPower", "moveState", "walkState", "enableJump", "crouchSpeed", "swimSpeed", "canFly", "flying", "collision", "spectator", "flySpeed", "gameMode", "dimension", "disableFly", "cameraMode", "cameraEntity", "cameraPitch", "cameraYaw", "facingDirection", "cameraTarget", "dead", "spawnPoint", "hp", "maxHp", "xp", "food", "saturation"], - "docs": ["docs/api/player.md", "docs/api/player_en.md"] + "docs": ["docs/api/player.md", "docs/en/api/player.md"] }, { "name": "voxels", @@ -104,7 +104,7 @@ "dts": "src/main/resources/assets/box3js/template/types/server/voxels.d.ts", "iface": "GameVoxels", "ignoreJava": ["getId"], - "docs": ["docs/api/voxels.md", "docs/api/voxels_en.md"] + "docs": ["docs/api/voxels.md", "docs/en/api/voxels.md"] }, { "name": "server storage", @@ -113,7 +113,7 @@ "dts": ["src/main/resources/assets/box3js/template/types/shared.d.ts", "src/main/resources/assets/box3js/template/types/server/server.d.ts"], "iface": "GameStorage", "accessorProperties": ["key"], - "docs": ["docs/api/storage.md", "docs/api/storage_en.md"] + "docs": ["docs/api/storage.md", "docs/en/api/storage.md"] }, { "name": "server data storage", @@ -123,7 +123,7 @@ "iface": "GameDataStorage", "nestedClass": "GameDataStorage", "accessorProperties": ["key"], - "docs": ["docs/api/storage.md", "docs/api/storage_en.md"] + "docs": ["docs/api/storage.md", "docs/en/api/storage.md"] }, { "name": "client storage", @@ -146,7 +146,7 @@ "java": "src/main/java/com/box3lab/box3js/script/Box3JSRemoteChannel.java", "dts": "src/main/resources/assets/box3js/template/types/server/server.d.ts", "iface": "RemoteChannel", - "docs": ["docs/api/server.md", "docs/api/server_en.md"] + "docs": ["docs/api/server.md", "docs/en/api/server.md"] }, { "name": "http", @@ -154,7 +154,7 @@ "java": "src/main/java/com/box3lab/box3js/script/Box3JSHttp.java", "dts": "src/main/resources/assets/box3js/template/types/shared.d.ts", "iface": "GameHttpAPI", - "docs": ["docs/api/http.md", "docs/api/http_en.md"] + "docs": ["docs/api/http.md", "docs/en/api/http.md"] }, { "name": "client http", @@ -168,7 +168,7 @@ "java": "src/main/java/com/box3lab/box3js/script/Box3DatabaseBase.java", "dts": "src/main/resources/assets/box3js/template/types/shared.d.ts", "iface": "GameDatabase", - "docs": ["docs/api/database.md", "docs/api/database_en.md"] + "docs": ["docs/api/database.md", "docs/en/api/database.md"] }, { "name": "query result", @@ -194,7 +194,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/standalone/Box3StandaloneBootstrap.java", "variable": "registriesObj" }, "dts": "src/main/resources/assets/box3js/template/types/server/server.d.ts", "iface": "GameRegistries", - "docs": ["docs/api/registries.md", "docs/api/registries_en.md"] + "docs": ["docs/api/registries.md", "docs/en/api/registries.md"] }, { "name": "client", @@ -202,7 +202,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "clientObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/client.d.ts", "iface": "GameClient", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/client.md"] }, { "name": "audio", @@ -210,7 +210,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "audioObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/audio.d.ts", "iface": "GameAudio", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/audio.md"] }, { "name": "input", @@ -218,7 +218,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "inputObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/input.d.ts", "iface": "GameInput", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/input.md"] }, { "name": "ui", @@ -226,7 +226,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "uiObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/ui.d.ts", "iface": "GameUI", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/ui.md"] }, { "name": "chat", @@ -234,7 +234,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "chatObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/chat.d.ts", "iface": "GameChat", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/chat.md"] }, { "name": "client remoteChannel", @@ -242,7 +242,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "rcObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/client.d.ts", "iface": "RemoteChannel", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/remote-channel.md"] }, { "name": "client database", @@ -262,7 +262,7 @@ "javaObject": { "path": "src/main/java/com/box3lab/box3js/client/Box3JSClientEngine.java", "variable": "guiObj" }, "dts": "src/main/resources/assets/box3js/template/types/client/gui.d.ts", "iface": "GameGUI", - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/gui.md"] }, { "name": "gui controller", @@ -271,7 +271,7 @@ "dts": "src/main/resources/assets/box3js/template/types/client/gui.d.ts", "iface": "GuiController", "ignoreJava": ["fireSlotClick", "fireClose"], - "docs": ["docs/api/client.md", "docs/api/client_en.md"] + "docs": ["docs/api/client.md", "docs/en/api/gui.md"] } ] } From d51fbc4bdeb2b93d4935f956a5ccac5b3030e26a Mon Sep 17 00:00:00 2001 From: viyrs <2991883280@qq.com> Date: Mon, 18 May 2026 15:47:46 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(template):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=A4=8D=E5=88=B6=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E6=96=87=E4=BB=B6=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将静态文件列表替换为基于资源的动态枚举 - 添加从 JAR 和目录两种来源复制模板的支持 - 使用 JarFile 和 Files.walk 实现递归文件遍历 - 为资源解析添加适当的错误处理 refactor(registries): 更新方块定义,增加渲染类型支持 - 为 BlockDef 记录添加 renderType 属性,支持 cutout/translucent 类型方块 - 在模板中添加具有相应属性的新示例方块 - 将 chocolate item 重命名为 chocolate_bar 以保持一致性 chore(template): 更新示例内容,支持占位符替换 - 将旧示例方块替换为新的 star_lamp、rainbow_cube 等 - 更新语言文件,使用 PROJECT_NAME 占位符 - 修改工具验证逻辑,检查递归枚举实现情况 --- .../box3js/script/Box3ScriptTemplate.java | 121 +++++++++++------- .../box3js/standalone/Box3JSRegistryGen.java | 14 +- .../template/assets/blockstates/candy.json | 5 + .../assets/blockstates/rainbow_cube.json | 5 + .../assets/blockstates/snowflake_lamp.json | 5 + .../assets/blockstates/star_lamp.json | 5 + .../assets/blockstates/treasure_chest.json | 5 + .../box3js/template/assets/lang/en_us.json | 10 +- .../box3js/template/assets/lang/zh_cn.json | 10 +- .../template/assets/models/block/candy.json | 12 ++ .../assets/models/block/rainbow_cube.json | 13 ++ .../assets/models/block/snowflake_lamp.json | 13 ++ .../assets/models/block/star_lamp.json | 13 ++ .../assets/models/block/treasure_chest.json | 12 ++ .../template/assets/models/item/candy.json | 3 + .../assets/models/item/chocolate_bar.json | 6 + .../assets/models/item/rainbow_cube.json | 3 + .../assets/models/item/snowflake_lamp.json | 3 + .../assets/models/item/star_lamp.json | 3 + .../assets/models/item/treasure_chest.json | 3 + .../assets/textures/block/candy_back.png | Bin 0 -> 147 bytes .../assets/textures/block/candy_bottom.png | Bin 0 -> 147 bytes .../assets/textures/block/candy_front.png | Bin 0 -> 147 bytes .../assets/textures/block/candy_left.png | Bin 0 -> 147 bytes .../assets/textures/block/candy_right.png | Bin 0 -> 147 bytes .../assets/textures/block/candy_top.png | Bin 0 -> 147 bytes .../textures/block/rainbow_cube_back.png | Bin 0 -> 330 bytes .../block/rainbow_cube_back.png.mcmeta | 1 + .../textures/block/rainbow_cube_bottom.png | Bin 0 -> 330 bytes .../block/rainbow_cube_bottom.png.mcmeta | 1 + .../textures/block/rainbow_cube_front.png | Bin 0 -> 330 bytes .../block/rainbow_cube_front.png.mcmeta | 1 + .../textures/block/rainbow_cube_left.png | Bin 0 -> 330 bytes .../block/rainbow_cube_left.png.mcmeta | 1 + .../textures/block/rainbow_cube_right.png | Bin 0 -> 330 bytes .../block/rainbow_cube_right.png.mcmeta | 1 + .../textures/block/rainbow_cube_top.png | Bin 0 -> 330 bytes .../block/rainbow_cube_top.png.mcmeta | 1 + .../textures/block/snowflake_lamp_back.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_back.png.mcmeta | 1 + .../textures/block/snowflake_lamp_bottom.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_bottom.png.mcmeta | 1 + .../textures/block/snowflake_lamp_front.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_front.png.mcmeta | 1 + .../textures/block/snowflake_lamp_left.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_left.png.mcmeta | 1 + .../textures/block/snowflake_lamp_right.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_right.png.mcmeta | 1 + .../textures/block/snowflake_lamp_top.png | Bin 0 -> 356 bytes .../block/snowflake_lamp_top.png.mcmeta | 1 + .../assets/textures/block/star_lamp_back.png | Bin 0 -> 269 bytes .../textures/block/star_lamp_back.png.mcmeta | 1 + .../textures/block/star_lamp_bottom.png | Bin 0 -> 269 bytes .../block/star_lamp_bottom.png.mcmeta | 1 + .../assets/textures/block/star_lamp_front.png | Bin 0 -> 269 bytes .../textures/block/star_lamp_front.png.mcmeta | 1 + .../assets/textures/block/star_lamp_left.png | Bin 0 -> 269 bytes .../textures/block/star_lamp_left.png.mcmeta | 1 + .../assets/textures/block/star_lamp_right.png | Bin 0 -> 269 bytes .../textures/block/star_lamp_right.png.mcmeta | 1 + .../assets/textures/block/star_lamp_top.png | Bin 0 -> 269 bytes .../textures/block/star_lamp_top.png.mcmeta | 1 + .../textures/block/treasure_chest_back.png | Bin 0 -> 239 bytes .../textures/block/treasure_chest_bottom.png | Bin 0 -> 206 bytes .../textures/block/treasure_chest_front.png | Bin 0 -> 260 bytes .../textures/block/treasure_chest_left.png | Bin 0 -> 219 bytes .../textures/block/treasure_chest_right.png | Bin 0 -> 219 bytes .../textures/block/treasure_chest_top.png | Bin 0 -> 214 bytes .../assets/textures/item/chocolate_bar.png | Bin 0 -> 147 bytes .../box3js/template/registries/blocks.json | 41 +++++- .../box3js/template/registries/items.json | 2 +- .../tools/verify-box3js-project.mjs | 34 +---- 72 files changed, 272 insertions(+), 87 deletions(-) create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/candy.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/rainbow_cube.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/snowflake_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/star_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/treasure_chest.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/candy.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/treasure_chest.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/candy.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/chocolate_bar.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/rainbow_cube.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/snowflake_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/star_lamp.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/treasure_chest.json create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_back.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_bottom.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_front.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_left.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_right.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_top.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_back.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_back.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png.mcmeta create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_back.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_bottom.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_front.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_left.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_right.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_top.png create mode 100644 Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/item/chocolate_bar.png diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptTemplate.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptTemplate.java index 3b166cc..67d6ee0 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptTemplate.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptTemplate.java @@ -2,60 +2,95 @@ import java.io.IOException; import java.io.InputStream; +import java.net.URI; +import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.util.jar.JarFile; public class Box3ScriptTemplate { - private static final String[] FILES = { - "gitignore.template", - "package.json", - "build.mjs", - "tsconfig.base.json", - "tsconfig.server.json", - "tsconfig.client.json", - "eslint.config.mjs", - "src/server/app.ts", - "src/client/app.ts", - "types/shared.d.ts", - "types/server/index.d.ts", - "types/server/server.d.ts", - "types/server/entity.d.ts", - "types/server/player.d.ts", - "types/server/world.d.ts", - "types/server/voxels.d.ts", - "types/client/index.d.ts", - "types/client/client.d.ts", - "types/client/audio.d.ts", - "types/client/input.d.ts", - "types/client/ui.d.ts", - "types/client/chat.d.ts", - "types/client/gui.d.ts", - "registries/blocks.json", - "registries/items.json", - "registries/sounds.json", - "registries/creativeTabs.json", - "assets/lang/en_us.json", - "assets/lang/zh_cn.json", - }; + private static final String RESOURCE_PREFIX = "/assets/box3js/template/"; + private static final String JAR_ENTRY_PREFIX = "assets/box3js/template/"; public static void copyTo(Path projectDir, String projectName) throws IOException { Files.createDirectories(projectDir); - for (String relPath : FILES) { - String destName = relPath.equals("gitignore.template") ? ".gitignore" : relPath; - Path dest = projectDir.resolve(destName); - Files.createDirectories(dest.getParent()); - String resourcePath = "/assets/box3js/template/" + relPath; - try (InputStream in = Box3ScriptTemplate.class.getResourceAsStream(resourcePath)) { - if (in == null) - throw new IOException("Template file not found: " + resourcePath); - Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING); + URL known = Box3ScriptTemplate.class.getResource(RESOURCE_PREFIX + "package.json"); + if (known == null) + throw new IOException("Template resource not found"); + + if (known.getProtocol().equals("jar")) { + copyFromJar(projectDir, projectName, known); + } else { + try { + Path templateRoot = Path.of(known.toURI()).getParent(); + copyFromDir(projectDir, projectName, templateRoot); + } catch (Exception e) { + throw new IOException("Cannot resolve template directory from: " + known, e); } - if (relPath.endsWith(".json") || relPath.endsWith(".ts") || relPath.endsWith(".mjs")) { - String content = Files.readString(dest); - Files.writeString(dest, content.replace("PROJECT_NAME", projectName)); + } + } + + private static void copyFromDir(Path projectDir, String projectName, Path templateRoot) throws IOException { + try (var stream = Files.walk(templateRoot)) { + for (Path file : stream.filter(Files::isRegularFile).toList()) { + Path rel = templateRoot.relativize(file); + copyOne(projectDir, projectName, file, rel.toString()); } } } + + private static void copyFromJar(Path projectDir, String projectName, URL known) throws IOException { + String s = known.toString(); + int bang = s.lastIndexOf('!'); + if (bang < 0) + throw new IOException("Malformed jar URL: " + s); + String jarUri = s.substring(4, bang); + Path jarPath; + try { + jarPath = Path.of(new URI(jarUri)); + } catch (Exception e) { + throw new IOException("Cannot resolve jar path from: " + jarUri, e); + } + + try (JarFile jar = new JarFile(jarPath.toFile())) { + var entries = jar.entries(); + while (entries.hasMoreElements()) { + var entry = entries.nextElement(); + String name = entry.getName(); + if (!name.startsWith(JAR_ENTRY_PREFIX) || name.endsWith("/")) + continue; + String rel = name.substring(JAR_ENTRY_PREFIX.length()); + Path dest = destPath(projectDir, rel); + Files.createDirectories(dest.getParent()); + try (InputStream in = jar.getInputStream(entry)) { + Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING); + } + replacePlaceholders(dest, projectName); + } + } + } + + private static void copyOne(Path projectDir, String projectName, Path source, String relPath) throws IOException { + Path dest = destPath(projectDir, relPath); + Files.createDirectories(dest.getParent()); + Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING); + replacePlaceholders(dest, projectName); + } + + private static Path destPath(Path projectDir, String relPath) { + String normalized = relPath.replace('\\', '/'); + if (normalized.equals("gitignore.template")) + return projectDir.resolve(".gitignore"); + return projectDir.resolve(normalized); + } + + private static void replacePlaceholders(Path dest, String projectName) throws IOException { + String name = dest.getFileName().toString(); + if (name.endsWith(".json") || name.endsWith(".ts") || name.endsWith(".mjs")) { + String content = Files.readString(dest); + Files.writeString(dest, content.replace("PROJECT_NAME", projectName)); + } + } } diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java index 507cbcb..11495ef 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java @@ -78,7 +78,14 @@ public record BlockDef( double speedFactor, double jumpFactor, boolean noOcclusion, boolean noCollision, boolean requiresTool, boolean instabreak, - String creativeTab) { + String creativeTab, String renderType) { + public boolean isCutout() { + return "cutout".equals(renderType); + } + + public boolean isTranslucent() { + return "translucent".equals(renderType); + } } /** @@ -273,7 +280,7 @@ private static BlockDef parseBlockDef(String id, String body) { double hardness = 1.0, resistance = 1.0, friction = 0.6; double speedFactor = 1.0, jumpFactor = 1.0; int lightLevel = 0; - String sound = "stone", mapColor = "stone", creativeTab = ""; + String sound = "stone", mapColor = "stone", creativeTab = "", renderType = "solid"; boolean noOcclusion = false, noCollision = false; boolean requiresTool = false, instabreak = false; @@ -321,11 +328,12 @@ private static BlockDef parseBlockDef(String id, String body) { case "noCollision" -> noCollision = parseBool(body, valStart); case "requiresTool" -> requiresTool = parseBool(body, valStart); case "instabreak" -> instabreak = parseBool(body, valStart); + case "renderType" -> renderType = parseString(body, valStart, "solid"); } } return new BlockDef(id, hardness, resistance, sound, lightLevel, mapColor, friction, speedFactor, jumpFactor, noOcclusion, noCollision, - requiresTool, instabreak, creativeTab); + requiresTool, instabreak, creativeTab, renderType); } private static CreativeTabDef parseCreativeTabDef(String id, String body) { diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/candy.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/candy.json new file mode 100644 index 0000000..24b73b5 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/candy.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "PROJECT_NAME:block/candy" } + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/rainbow_cube.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/rainbow_cube.json new file mode 100644 index 0000000..39d2794 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/rainbow_cube.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "PROJECT_NAME:block/rainbow_cube" } + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/snowflake_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/snowflake_lamp.json new file mode 100644 index 0000000..219eac8 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/snowflake_lamp.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "PROJECT_NAME:block/snowflake_lamp" } + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/star_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/star_lamp.json new file mode 100644 index 0000000..36be002 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/star_lamp.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "PROJECT_NAME:block/star_lamp" } + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/treasure_chest.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/treasure_chest.json new file mode 100644 index 0000000..f40302e --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/blockstates/treasure_chest.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "PROJECT_NAME:block/treasure_chest" } + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json index 00f43ff..db9e1ce 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/en_us.json @@ -1,5 +1,9 @@ { - "block.mygame.ruby_block": "Ruby Block", - "item.mygame.chocolate": "Chocolate Bar", - "itemGroup.mygame.my_blocks": "My Blocks" + "block.PROJECT_NAME.star_lamp": "Star Lamp", + "block.PROJECT_NAME.rainbow_cube": "Rainbow Cube", + "block.PROJECT_NAME.snowflake_lamp": "Snowflake Lamp", + "block.PROJECT_NAME.treasure_chest": "Treasure Chest", + "block.PROJECT_NAME.candy": "Candy", + "item.PROJECT_NAME.chocolate_bar": "Chocolate Bar", + "itemGroup.PROJECT_NAME.my_blocks": "My Blocks" } diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json index 64bb808..4b9ccbf 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/lang/zh_cn.json @@ -1,5 +1,9 @@ { - "block.mygame.ruby_block": "红宝石块", - "item.mygame.chocolate": "巧克力棒", - "itemGroup.mygame.my_blocks": "我的方块" + "block.PROJECT_NAME.star_lamp": "星光灯", + "block.PROJECT_NAME.rainbow_cube": "彩虹方块", + "block.PROJECT_NAME.snowflake_lamp": "雪花灯", + "block.PROJECT_NAME.treasure_chest": "宝箱", + "block.PROJECT_NAME.candy": "糖果", + "item.PROJECT_NAME.chocolate_bar": "巧克力棒", + "itemGroup.PROJECT_NAME.my_blocks": "我的方块" } diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/candy.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/candy.json new file mode 100644 index 0000000..52e98cd --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/candy.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "PROJECT_NAME:block/candy_front", + "north": "PROJECT_NAME:block/candy_front", + "south": "PROJECT_NAME:block/candy_back", + "east": "PROJECT_NAME:block/candy_left", + "west": "PROJECT_NAME:block/candy_right", + "up": "PROJECT_NAME:block/candy_top", + "down": "PROJECT_NAME:block/candy_bottom" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json new file mode 100644 index 0000000..679363b --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json @@ -0,0 +1,13 @@ +{ + "render_type": "minecraft:cutout", + "parent": "minecraft:block/cube", + "textures": { + "particle": "PROJECT_NAME:block/rainbow_cube_front", + "north": "PROJECT_NAME:block/rainbow_cube_front", + "south": "PROJECT_NAME:block/rainbow_cube_back", + "east": "PROJECT_NAME:block/rainbow_cube_left", + "west": "PROJECT_NAME:block/rainbow_cube_right", + "up": "PROJECT_NAME:block/rainbow_cube_top", + "down": "PROJECT_NAME:block/rainbow_cube_bottom" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json new file mode 100644 index 0000000..249ed00 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json @@ -0,0 +1,13 @@ +{ + "render_type": "minecraft:cutout", + "parent": "minecraft:block/cube", + "textures": { + "particle": "PROJECT_NAME:block/snowflake_lamp_front", + "north": "PROJECT_NAME:block/snowflake_lamp_front", + "south": "PROJECT_NAME:block/snowflake_lamp_back", + "east": "PROJECT_NAME:block/snowflake_lamp_left", + "west": "PROJECT_NAME:block/snowflake_lamp_right", + "up": "PROJECT_NAME:block/snowflake_lamp_top", + "down": "PROJECT_NAME:block/snowflake_lamp_bottom" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json new file mode 100644 index 0000000..6e2c259 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json @@ -0,0 +1,13 @@ +{ + "render_type": "minecraft:cutout", + "parent": "minecraft:block/cube", + "textures": { + "particle": "PROJECT_NAME:block/star_lamp_front", + "north": "PROJECT_NAME:block/star_lamp_front", + "south": "PROJECT_NAME:block/star_lamp_back", + "east": "PROJECT_NAME:block/star_lamp_left", + "west": "PROJECT_NAME:block/star_lamp_right", + "up": "PROJECT_NAME:block/star_lamp_top", + "down": "PROJECT_NAME:block/star_lamp_bottom" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/treasure_chest.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/treasure_chest.json new file mode 100644 index 0000000..7ae462c --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/treasure_chest.json @@ -0,0 +1,12 @@ +{ + "parent": "minecraft:block/cube", + "textures": { + "particle": "PROJECT_NAME:block/treasure_chest_front", + "north": "PROJECT_NAME:block/treasure_chest_front", + "south": "PROJECT_NAME:block/treasure_chest_back", + "east": "PROJECT_NAME:block/treasure_chest_left", + "west": "PROJECT_NAME:block/treasure_chest_right", + "up": "PROJECT_NAME:block/treasure_chest_top", + "down": "PROJECT_NAME:block/treasure_chest_bottom" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/candy.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/candy.json new file mode 100644 index 0000000..4f907b9 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/candy.json @@ -0,0 +1,3 @@ +{ + "parent": "PROJECT_NAME:block/candy" +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/chocolate_bar.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/chocolate_bar.json new file mode 100644 index 0000000..e4e8f0d --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/chocolate_bar.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "PROJECT_NAME:item/chocolate_bar" + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/rainbow_cube.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/rainbow_cube.json new file mode 100644 index 0000000..3cd413d --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/rainbow_cube.json @@ -0,0 +1,3 @@ +{ + "parent": "PROJECT_NAME:block/rainbow_cube" +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/snowflake_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/snowflake_lamp.json new file mode 100644 index 0000000..aa6b137 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/snowflake_lamp.json @@ -0,0 +1,3 @@ +{ + "parent": "PROJECT_NAME:block/snowflake_lamp" +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/star_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/star_lamp.json new file mode 100644 index 0000000..9aa86f5 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/star_lamp.json @@ -0,0 +1,3 @@ +{ + "parent": "PROJECT_NAME:block/star_lamp" +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/treasure_chest.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/treasure_chest.json new file mode 100644 index 0000000..eb799d4 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/item/treasure_chest.json @@ -0,0 +1,3 @@ +{ + "parent": "PROJECT_NAME:block/treasure_chest" +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_back.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/candy_back.png new file mode 100644 index 0000000000000000000000000000000000000000..a9579b39b9bae98bd2ec0223419b565eb1758a39 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP|(%W z#WAE}PI86v(GT)%7a2Fn=vA_LnH=U-2sggb@Lwe3n85?y&RLEPjY4M&nD0$kDVcLX qn#&|vqJhutG9zC9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_back.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_back.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_back.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbba84c4aef185108d5c57a60261887e9b2cbca GIT binary patch literal 330 zcmV-Q0k!^#P)C9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_bottom.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbba84c4aef185108d5c57a60261887e9b2cbca GIT binary patch literal 330 zcmV-Q0k!^#P)C9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_front.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbba84c4aef185108d5c57a60261887e9b2cbca GIT binary patch literal 330 zcmV-Q0k!^#P)C9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_left.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbba84c4aef185108d5c57a60261887e9b2cbca GIT binary patch literal 330 zcmV-Q0k!^#P)C9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_right.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbba84c4aef185108d5c57a60261887e9b2cbca GIT binary patch literal 330 zcmV-Q0k!^#P)C9|4Uv!|AUasv%K!iX07*qoM6N<$f>)7&J^%m! literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/rainbow_cube_top.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_back.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_bottom.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_front.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_left.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_right.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png new file mode 100644 index 0000000000000000000000000000000000000000..cb0ce70ab26a214f65793ce8902b048c3fd16e8b GIT binary patch literal 356 zcmV-q0h|7bP)a2V%a5t))v6x5ux@ohSovyt)m?K{y#wB5%<*t zWCx`RXxx(?V4+Mc&Z}Jl=tmE*W{`H(gH2?rrSk`V%7{OeS94AP0000|w literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/snowflake_lamp_top.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_back.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_bottom.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_front.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_left.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_right.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png new file mode 100644 index 0000000000000000000000000000000000000000..74c78e7d0a362f3c32dc61ed9e4bcc84ac1193a3 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^0zmA*!3HFSYrjteQk(@Ik;M!Q%r8Kgacgx@Hc)WC zr;B4q#hka(8#!AHcw9HnTYSf>ukiC7J%28_6S23J4EdsD>@XOn6%+|{e3Xuvtv`aokXHr!h@d*isdIY?uqWPNUgiUu<_HKE$1^` R3xQr?@O1TaS?83{1OU_)ZSVj9 literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png.mcmeta b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png.mcmeta new file mode 100644 index 0000000..caf5e57 --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/star_lamp_top.png.mcmeta @@ -0,0 +1 @@ +{"animation":{"frametime":4}} \ No newline at end of file diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_back.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_back.png new file mode 100644 index 0000000000000000000000000000000000000000..abf9469f2ed5118d64aa45f7384648a18551910d GIT binary patch literal 239 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;jBA zi(^Q|oa7zbls?#7)$Q0X>@z=eBXe?!Vab_>H3DspU5QeOp9~CBdJ+#z3*z39l%sbb z6T}kfIn6MG)u1$iSuV+NW`usj5eWyDT`Gob2fi2{n8SW(`tunLhCTg#7oR?itKj%5 zZ)RhBPi(bC7x%UY7koGlCnu)u(G8S%rX+Dps@J&CLUOjnSDqQ2&wNUjKRRR)tfYAT g!B?h8wtyza%FRmdKI;Vst0O+?>n*aa+ literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_bottom.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..757c1ea40d2888f717f0d93de501dc010840cfd5 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_V(% z#WAE}PIASL{h#eQ;^!qh3AMdUZZLGH@LtIpW@CI$thIklJ7b!ZS?dm-21%w%rx}zS zxHcrtNQ+4-VBRK>scp4v9;0z#MWev6*G;-EJ6bfDw*_40j=qq@x|%y}(gd*;=?8pF z(v!R$Dv}w)?GA`De^}C^Z~V}RLDe{L)?7w4fs*X-Z=Y@f-NNAM>gTe~DWM4f9gIPP literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_front.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_front.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe521edb5b119c3c905332a7fb39e2ed817aa3f GIT binary patch literal 260 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP;i^4 zi(^Q|oa7zbls?#7)$Q0X>@z=eBXe?!Vab_>H3DspU5QeOp9~CBdJ+#z3*z39l%sbb z6T}kfIn6MG)u1$iSuV+NW`usj5eWyDT`Gob2fi2{n8SW(`tunLhI-5so+jQ)X(+sK zn4v{-_8WHCYlHSd}=MbC@!xdCcTYOAco4Z=LyZn}mez!G-d`mi|ysukN`` zDS1L1n?U1P0VlQtUkn@OFg}i%m+U0cwz9qY;lqD^a~>KP-xFJH(Z#*(!37_V!^w$h zdvpUOo+(Kjlj=1tw2+)_@s(#r=QE#@<&O@r1S{!XfAEzlk}Y7;T*eE#JN&xSGk*ge O#^CAd=d#Wzp$PzB|4nKD literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_right.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_right.png new file mode 100644 index 0000000000000000000000000000000000000000..c4e416b6206fe4034b72423f79fbb8c79b81bf00 GIT binary patch literal 219 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_Wn2 z#WAE}PV$azN+0Z1XE3_V$=rCjd;65A1kvLL3EX_GEW9^@vKmDXn>-d`mi|ysukN`` zDS1L1n?U1P0VlQtUkn@OFg}i%m+U0cwz9qY;lqD^a~>KP-xFJH(Z#*(!37_V!^w$h zdvpUOo+(Kjlj=1tw2+)_@s(#r=QE#@<&O@r1S{!XfAEzlk}Y7;T*eE#JN&xSGk*ge O#^CAd=d#Wzp$PzB|4nKD literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_top.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/block/treasure_chest_top.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d1b70abaf2b2d060a5f8dd53313b3b130545a1 GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP_V<( z#WAE}PV$azN+0a4>UQiG_L-l#kvTcVu;fg`8i6*)u0*NCPX>l5J&6aV1#$04%F#QJ z31W%#oMxE8YEXLR`yt69-WyFvxo_||gtuR3z0Sd+bd+t5;&JZu2}umgxwi=zG$iQc zC?0Rn^*DD(oGql6bqBZJc|%2O#l|m9ybi~A#5DNoGW<D{BNFH)22WQ%mvv4F FO#pdvM=SsU literal 0 HcmV?d00001 diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/item/chocolate_bar.png b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/textures/item/chocolate_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..a9579b39b9bae98bd2ec0223419b565eb1758a39 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP|(%W z#WAE}PI86v(GT)%7a2Fn=vA_LnH=U-2sggb@Lwe3n85?y&RLEPjY4M&nD0$kDVcLX qn#&|vqJhutG9z m[1]).sort(); -} - -function normalizeTemplatePath(path) { - return path === "gitignore.template" ? "gitignore.template" : path; -} - -function verifyTemplateFiles() { - const declared = extractTemplateFiles().map(normalizeTemplatePath); - const actual = listFiles("src/main/resources/assets/box3js/template") - .filter((path) => !path.startsWith("dist/")) - .filter((path) => !path.endsWith(".map")); - - for (const path of declared) { - if (!actual.includes(path)) { - fail(`Template file declared in Box3ScriptTemplate but missing from resources: ${path}`); - } - } - - for (const path of actual) { - if (!declared.includes(path)) { - fail(`Template resource is not copied by Box3ScriptTemplate: ${path}`); - } + if (!source.includes("JarFile") || !source.includes("Files.walk")) { + fail("Box3ScriptTemplate must use recursive enumeration (JarFile + Files.walk) to copy all template files"); } } @@ -532,7 +506,7 @@ function verifyEntrypoints() { } } -verifyTemplateFiles(); +verifyTemplateRecursive(); verifyRuntimeTypeSplit(); verifyEventTokens(); verifyGlobalDeclarations(); From e6f1b578ee08f26845d3523e3344b33ee5846eb1 Mon Sep 17 00:00:00 2001 From: viyrs <2991883280@qq.com> Date: Mon, 18 May 2026 17:09:00 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20modId=20?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E4=B8=8E=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=B1=BB=E5=9E=8B=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加针对 NeoForge 命名规范的 modId 验证(正则:^[a-z][a-z0-9_]{1,63}$) - 在 create 和 compile 命令中验证项目名称 - 当 modId 无效时显示详细的错误信息 - 在常见问题(FAQ)和入门指南中添加 modId 命名规范说明 - 实现客户端渲染类型注册,支持 cutout/translucent 类型的方块 - 为具有透明纹理的自定义方块生成正确的渲染层调用 - 移除模板模型中硬编码的 render_type,以便正确注册渲染类型 这些变更确保项目遵循 NeoForge 的 modId 标准,并正确处理灯饰和彩虹方块等透明材质的方块渲染。 修复了因渲染层处理不当导致自定义方块显示为黑色或偏暗的问题。 --- Box3JS-NeoForge-1.21.1/docs/guide/faq.md | 15 ++++++ .../docs/guide/getting-started.md | 11 ++++ .../box3lab/box3js/Box3JSClientEvents.java | 48 +++++++++++++++++ .../box3js/script/Box3ScriptCommand.java | 16 ++++++ .../box3js/standalone/Box3JSRegistryGen.java | 29 ++++++++++ .../box3js/standalone/Box3ScriptCompiler.java | 53 +++++++++++++++++-- .../standalone/Box3StandaloneBootstrap.java | 34 ++++++++++++ .../assets/models/block/rainbow_cube.json | 1 - .../assets/models/block/snowflake_lamp.json | 1 - .../assets/models/block/star_lamp.json | 1 - 10 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/Box3JSClientEvents.java diff --git a/Box3JS-NeoForge-1.21.1/docs/guide/faq.md b/Box3JS-NeoForge-1.21.1/docs/guide/faq.md index 412a983..77dc02c 100644 --- a/Box3JS-NeoForge-1.21.1/docs/guide/faq.md +++ b/Box3JS-NeoForge-1.21.1/docs/guide/faq.md @@ -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 | diff --git a/Box3JS-NeoForge-1.21.1/docs/guide/getting-started.md b/Box3JS-NeoForge-1.21.1/docs/guide/getting-started.md index 8511dce..3062e19 100644 --- a/Box3JS-NeoForge-1.21.1/docs/guide/getting-started.md +++ b/Box3JS-NeoForge-1.21.1/docs/guide/getting-started.md @@ -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 diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/Box3JSClientEvents.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/Box3JSClientEvents.java new file mode 100644 index 0000000..8ff191e --- /dev/null +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/Box3JSClientEvents.java @@ -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}. + * + *

+ * 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()); + }); + } +} diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptCommand.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptCommand.java index 27764ef..ab2409a 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptCommand.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/script/Box3ScriptCommand.java @@ -138,6 +138,14 @@ private static LiteralArgumentBuilder createCommand() { .then(argument("name", StringArgumentType.word()) .executes(ctx -> { String name = StringArgumentType.getString(ctx, "name"); + // Validate modId against NeoForge naming rules + String validationError = Box3ScriptCompiler.validateModId(name); + if (validationError != null) { + ctx.getSource().sendFailure( + Component.literal("§cInvalid project name: " + validationError + + "\n§7NeoForge modId: [a-z][a-z0-9_]{1,63} (2-64 chars)")); + return 0; + } Path projectDir = scriptDir(ctx.getSource().getServer()) .resolve(name).normalize(); if (Files.exists(projectDir)) { @@ -373,6 +381,14 @@ private static LiteralArgumentBuilder compileCommand() { // Read package.json for metadata String[] info = Box3ScriptCompiler.readPackageInfo(projectDir); String modId = info[0]; + String validationError = Box3ScriptCompiler.validateModId(modId); + if (validationError != null) { + ctx.getSource().sendFailure( + Component.literal("§cInvalid modId: " + validationError + + "\n§7NeoForge modId: [a-z][a-z0-9_]{1,63} (2-64 chars)" + + "\n§7Fix: rename your project or set '--modId' in package.json")); + return 0; + } String displayName = info[1]; String modVersion = info[2]; String description = info[3]; diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java index 11495ef..36da088 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3JSRegistryGen.java @@ -683,6 +683,35 @@ private static String buildItemProperties(ItemDef it) { return props.toString(); } + /** + * Generates constructor-body calls to {@code Box3StandaloneBootstrap.addBlockRenderType()} + * for blocks that need non-solid render types. Returns empty string if none. + * + *

The generated code passes a method reference to the DeferredBlock + * (e.g. {@code STAR_LAMP::get}) as a {@code Supplier}. The actual + * {@code ItemBlockRenderTypes.setRenderLayer()} calls happen later in the + * main Box3JS mod's client setup — no client imports needed here.

+ */ + public static String generateClientRenderCalls(String modId, List blocks) { + var cutout = blocks.stream().filter(BlockDef::isCutout).toList(); + var translucent = blocks.stream().filter(BlockDef::isTranslucent).toList(); + if (cutout.isEmpty() && translucent.isEmpty()) + return ""; + + StringBuilder sb = new StringBuilder(); + for (var b : cutout) { + sb.append(" Box3StandaloneBootstrap.addBlockRenderType(\"") + .append(modId).append("\", ").append(b.id().toUpperCase()) + .append("::get, \"cutout\");\n"); + } + for (var b : translucent) { + sb.append(" Box3StandaloneBootstrap.addBlockRenderType(\"") + .append(modId).append("\", ").append(b.id().toUpperCase()) + .append("::get, \"translucent\");\n"); + } + return sb.toString(); + } + /** * Generates the additional import statements needed for the generated code. */ diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java index 566251f..7e96914 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3ScriptCompiler.java @@ -63,6 +63,33 @@ public class Box3ScriptCompiler { /** ModId of the main Box3JS mod that script JARs depend on. */ public static final String BOX3JS_MOD_ID = "box3js"; + /** NeoForge modId regex: lowercase letter + 1-63 alphanumeric/underscores. */ + private static final Pattern MODID_PATTERN = Pattern.compile("^[a-z][a-z0-9_]{1,63}$"); + + /** + * Validates a modId against NeoForge naming rules. + * @return null if valid, or an error message string if invalid + */ + public static String validateModId(String modId) { + if (modId == null || modId.isEmpty()) { + return "modId cannot be empty"; + } + if (!MODID_PATTERN.matcher(modId).matches()) { + if (modId.length() < 2) { + return "modId '" + modId + "' is too short (min 2 chars)"; + } + if (modId.length() > 64) { + return "modId '" + modId + "' is too long (max 64 chars)"; + } + if (!Character.isLowerCase(modId.charAt(0))) { + return "modId '" + modId + "' must start with a lowercase letter [a-z]"; + } + return "modId '" + modId + "' contains invalid characters. " + + "Use only lowercase letters, digits, and underscores: [a-z][a-z0-9_]{1,63}"; + } + return null; + } + private final Path projectDir; private final Path outputJar; private final String modId; @@ -96,6 +123,11 @@ public Box3ScriptCompiler(Path projectDir, Path outputJar, } public void compile() throws Exception { + String validationError = validateModId(modId); + if (validationError != null) { + throw new IllegalArgumentException("Invalid modId: " + validationError); + } + Path serverJs = projectDir.resolve("dist/server.js"); if (!Files.exists(serverJs)) { throw new FileNotFoundException("dist/server.js not found in " + projectDir @@ -215,6 +247,9 @@ private void generateModClass(Path genSrcDir, String extraImports = Box3JSRegistryGen.generateImports(hasBlocks, hasTabs, hasItems, hasSounds, hasTools, hasArmor); + // Client render type setup for cutout / translucent blocks + String clientRenderCalls = Box3JSRegistryGen.generateClientRenderCalls(modId, blocks); + // Generate supplier map builder methods StringBuilder mapMethods = new StringBuilder(); if (hasBlocks) { @@ -304,10 +339,12 @@ public class %s extends Box3StandaloneBootstrap { public %s(IEventBus modEventBus, ModContainer modContainer) { super(modEventBus, modContainer, "%s", "%s"%s); %s - } + %s } } - """, pkg, hardcodedImports, extraImports, modId, className, fieldDecls, mapMethods, - className, resourcePath, modId, superArgs.toString(), constructorRegs); + """, pkg, hardcodedImports, extraImports, modId, className, + fieldDecls, mapMethods, + className, resourcePath, modId, superArgs.toString(), constructorRegs, + clientRenderCalls); Path out = genSrcDir.resolve(pkg.replace('.', '/')).resolve(className + ".java"); Files.createDirectories(out.getParent()); @@ -713,6 +750,16 @@ public static void main(String[] args) throws Exception { String logoFile = info[8]; String modId = opts.getOrDefault("modId", name); + String validationError = validateModId(modId); + if (validationError != null) { + System.err.println("Error: " + validationError); + System.err.println("NeoForge modId must match: ^[a-z][a-z0-9_]{1,63}$"); + System.err.println(" - Must start with a lowercase letter"); + System.err.println(" - Use only lowercase letters, digits, underscores"); + System.err.println(" - Length: 2-64 characters"); + System.err.println(" - Fix: rename your project or use --modId "); + System.exit(1); + } String modName = opts.getOrDefault("name", displayName); String modVersion = opts.getOrDefault("version", version); Path output = Path.of(opts.getOrDefault("output", diff --git a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3StandaloneBootstrap.java b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3StandaloneBootstrap.java index 4b3eb5c..5a8c5de 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3StandaloneBootstrap.java +++ b/Box3JS-NeoForge-1.21.1/src/main/java/com/box3lab/box3js/standalone/Box3StandaloneBootstrap.java @@ -29,7 +29,10 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; /** @@ -51,6 +54,37 @@ public class Box3StandaloneBootstrap { private static final Logger LOGGER = LogUtils.getLogger(); + /** Record stored by addBlockRenderType(), consumed by Box3JSClientEvents on the client. */ + public record BlockRenderEntry(Supplier blockSupplier, String renderType) {} + + /** modId → list of blocks needing non-solid render types (cutout, translucent). */ + private static final Map> pendingRenderTypes = new ConcurrentHashMap<>(); + + /** + * Called by generated @Mod constructors to register a block that needs a + * non-solid render type. The actual {@code ItemBlockRenderTypes.setRenderLayer()} + * call happens later during FMLClientSetupEvent via {@code Box3JSClientEvents}. + * + * @param modId the mod that owns the block + * @param blockSupplier deferred supplier (the DeferredBlock itself, e.g. {@code STAR_LAMP}) + * @param renderType "cutout" or "translucent" + */ + public static void addBlockRenderType(String modId, Supplier blockSupplier, String renderType) { + pendingRenderTypes.computeIfAbsent(modId, k -> new ArrayList<>()) + .add(new BlockRenderEntry(blockSupplier, renderType)); + LOGGER.debug("Registered render type {} for block in mod {}", renderType, modId); + } + + /** + * Returns all pending render type registrations, then clears the store. + * Called by Box3JSClientEvents during FMLClientSetupEvent. + */ + public static Map> drainPendingRenderTypes() { + Map> result = Map.copyOf(pendingRenderTypes); + pendingRenderTypes.clear(); + return result; + } + private final String scriptResource; private final String projectName; private final Map> blockSuppliers; diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json index 679363b..a620c60 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/rainbow_cube.json @@ -1,5 +1,4 @@ { - "render_type": "minecraft:cutout", "parent": "minecraft:block/cube", "textures": { "particle": "PROJECT_NAME:block/rainbow_cube_front", diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json index 249ed00..b827f23 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/snowflake_lamp.json @@ -1,5 +1,4 @@ { - "render_type": "minecraft:cutout", "parent": "minecraft:block/cube", "textures": { "particle": "PROJECT_NAME:block/snowflake_lamp_front", diff --git a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json index 6e2c259..8ce9606 100644 --- a/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json +++ b/Box3JS-NeoForge-1.21.1/src/main/resources/assets/box3js/template/assets/models/block/star_lamp.json @@ -1,5 +1,4 @@ { - "render_type": "minecraft:cutout", "parent": "minecraft:block/cube", "textures": { "particle": "PROJECT_NAME:block/star_lamp_front", From 5dba38ff90e6f7ec3dabfc60c97930bf4a710066 Mon Sep 17 00:00:00 2001 From: viyrs <2991883280@qq.com> Date: Mon, 18 May 2026 17:16:43 +0800 Subject: [PATCH 4/4] =?UTF-8?q?docs(commands):=20=E6=B7=BB=E5=8A=A0=20modI?= =?UTF-8?q?d=20=E5=91=BD=E5=90=8D=E8=A7=84=E8=8C=83=E8=AD=A6=E5=91=8A?= =?UTF-8?q?=E4=B8=8E=E9=AA=8C=E8=AF=81=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 modId 命名规范警告章节,正则表达式为 ^[a-z][a-z0-9_]{1,63}$ - 包含验证要求:首字符必须小写字母,仅允许小写字母/数字/下划线 - 明确长度限制为 2-64 个字符,并说明禁止使用的特殊字符 - 添加有效与无效 modId 格式的示例 - 为 package.json 中的 modId 字段增加编译时验证警告 --- Box3JS-NeoForge-1.21.1/docs/api/commands.md | 15 +++++++++++++++ Box3JS-NeoForge-1.21.1/docs/api/registries.md | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Box3JS-NeoForge-1.21.1/docs/api/commands.md b/Box3JS-NeoForge-1.21.1/docs/api/commands.md index 8403f50..4fc14fc 100644 --- a/Box3JS-NeoForge-1.21.1/docs/api/commands.md +++ b/Box3JS-NeoForge-1.21.1/docs/api/commands.md @@ -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 @@ -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 | diff --git a/Box3JS-NeoForge-1.21.1/docs/api/registries.md b/Box3JS-NeoForge-1.21.1/docs/api/registries.md index 899dc2f..40de6a2 100644 --- a/Box3JS-NeoForge-1.21.1/docs/api/registries.md +++ b/Box3JS-NeoForge-1.21.1/docs/api/registries.md @@ -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 可选值 @@ -67,6 +68,7 @@ mygame/ "resistance": 0.3, "sound": "glass", "noOcclusion": true, + "renderType": "cutout", "creativeTab": "my_blocks" } }