├── .gitignore ├── LICENSE ├── blockbench_convert ├── blockbench_convert.js ├── filter.json ├── package.json └── readme.md ├── bump_manifest ├── bump_manifest.py ├── data │ └── version.json ├── filter.json ├── readme.md └── test │ ├── .gitignore │ ├── config.json │ └── packs │ ├── BP │ └── manifest.json │ ├── RP │ └── manifest.json │ └── data │ └── bump_manifest │ └── version.json ├── filter_tester ├── README.md ├── filter.json ├── main.py └── test │ ├── .gitignore │ ├── config.json │ ├── data │ └── filter_tester │ │ └── BP │ │ ├── example.json │ │ ├── example.png │ │ └── nested │ │ └── example.txt │ └── packs │ └── BP │ ├── example.json │ ├── example.png │ └── nested │ └── example.txt ├── fix_emissive ├── filter.json ├── fix_emissive.py ├── readme.md └── requirements.txt ├── gametests ├── .gitignore ├── build.js ├── completion.md ├── data │ ├── .gitignore │ ├── .prettierrc.json │ ├── .vscode │ │ ├── extensions.json │ │ ├── launch.json │ │ └── settings.json │ ├── LICENSE │ ├── extra_files │ │ └── BP │ │ │ └── structures │ │ │ └── gametests │ │ │ └── test_5x5.mcstructure │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── main.ts │ └── tsconfig.json ├── filter.json ├── gametests.js ├── moveFiles.js ├── package-lock.json ├── package.json ├── readme.md ├── schema.json └── setup.js ├── json_cleaner ├── completion.md ├── filter.json ├── json_cleaner.py └── readme.md ├── json_convert ├── .gitignore ├── completion.md ├── filter.json ├── main.js ├── package-lock.json ├── package.json ├── readme.md └── schema.json ├── name_ninja ├── .gitignore ├── completion.md ├── filter.json ├── name_ninja.py ├── readme.md ├── requirements.txt ├── schema.json └── test │ ├── .gitignore │ ├── config.json │ ├── packs │ ├── BP │ │ ├── blocks │ │ │ ├── limestone.block.json │ │ │ ├── regolith.block.json │ │ │ └── shale.block.json │ │ ├── entities │ │ │ ├── house_cat.entity.json │ │ │ ├── puma.entity.json │ │ │ └── spotted_leopard.entity.json │ │ ├── items │ │ │ ├── bell_pepper.item.json │ │ │ ├── corn_flakes.item.json │ │ │ └── pecan.item.json │ │ ├── manifest.json │ │ ├── pack_icon.png │ │ └── texts │ │ │ └── en_US.lang │ ├── RP │ │ ├── blocks.json │ │ ├── entity │ │ │ ├── house_cat.entity.json │ │ │ ├── puma.entity.json │ │ │ └── spotted_leopard.entity.json │ │ ├── items │ │ │ ├── bell_pepper.item.json │ │ │ ├── corn_flakes.item.json │ │ │ └── pecan.item.json │ │ ├── manifest.json │ │ ├── pack_icon.png │ │ ├── texts │ │ │ └── en_US.lang │ │ └── textures │ │ │ ├── blocks │ │ │ ├── limestone.png │ │ │ ├── regolith.png │ │ │ └── shale.png │ │ │ ├── item_texture.json │ │ │ ├── items │ │ │ ├── bell_pepper.png │ │ │ ├── corn_flakes.png │ │ │ └── pecan.png │ │ │ └── terrain_texture.json │ └── data │ │ └── .gitkeep │ └── readme.md ├── readme.md ├── texture_convert ├── README.md ├── filter.json ├── requirements.txt ├── test │ ├── .gitignore │ ├── config.json │ ├── data │ │ └── filter_tester │ │ │ ├── BP │ │ │ └── pack_icon.png │ │ │ └── RP │ │ │ ├── pack_icon.png │ │ │ └── textures │ │ │ ├── gif.png │ │ │ ├── png.png │ │ │ ├── psd.png │ │ │ └── xcf.png │ └── packs │ │ ├── BP │ │ └── pack_icon.png │ │ └── RP │ │ ├── pack_icon.png │ │ └── textures │ │ ├── gif.gif │ │ ├── png.png │ │ ├── psd.psd │ │ └── xcf.xcf └── texture_convert.py ├── texture_list ├── completion.md ├── filter.json ├── readme.md ├── test │ ├── .gitignore │ ├── config.json │ ├── packs │ │ ├── BP │ │ │ ├── .gitkeep │ │ │ └── entities │ │ │ │ └── fish.json │ │ ├── RP │ │ │ ├── subpacks │ │ │ │ ├── pack1 │ │ │ │ │ └── textures │ │ │ │ │ │ ├── nested │ │ │ │ │ │ ├── nested_png.png │ │ │ │ │ │ └── nested_tga.tga │ │ │ │ │ │ ├── root_png.png │ │ │ │ │ │ └── root_tga.tga │ │ │ │ ├── pack2 │ │ │ │ │ └── textures │ │ │ │ │ │ └── new_file.tga │ │ │ │ └── pack3 │ │ │ │ │ └── .gitkeep │ │ │ └── textures │ │ │ │ ├── nested │ │ │ │ ├── nested_png.png │ │ │ │ └── nested_tga.tga │ │ │ │ ├── root_png.png │ │ │ │ └── root_tga.tga │ │ └── data │ │ │ └── .gitkeep │ └── readme.md └── texture_list.py └── type_gen ├── completion.md ├── filter.json ├── readme.md ├── schema.json └── type_gen.js /.gitignore: -------------------------------------------------------------------------------- 1 | /testing 2 | env/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bedrock OSS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /blockbench_convert/blockbench_convert.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const path = require("path") 3 | const fs = require("fs"); 4 | 5 | glob("RP/models/**/*.bbmodel", null, function (er, files) { 6 | files.forEach(function (file) { 7 | fs.readFile(file, "utf8", function (err, data) { 8 | let resultName = file.substr(0, file.lastIndexOf(".")) + ".json"; 9 | console.log("Converting " + file + " into " + resultName); 10 | fs.writeFileSync( 11 | resultName, 12 | exportModel(JSON.parse(data)) 13 | ); 14 | fs.unlinkSync(file); 15 | }); 16 | }); 17 | }); 18 | 19 | glob("RP/models/**/*.entity.bbmodel", null, function (er, files) { 20 | files.forEach(function (file) { 21 | fs.readFile(file, "utf8", function (err, data) { 22 | let resultName = "RP/textures/entity/" + path.basename(file).split(".entity")[0] + ".png"; 23 | console.log("Converting " + file + " into " + resultName); 24 | exportTexture(JSON.parse(data), "entity"); 25 | }); 26 | }); 27 | }); 28 | 29 | glob("RP/models/**/*.block.bbmodel", null, function (er, files) { 30 | files.forEach(function (file) { 31 | fs.readFile(file, "utf8", function (err, data) { 32 | let resultName = "RP/textures/entity/" + path.basename(file).split(".entity")[0] + ".png"; 33 | console.log("Converting " + file + " into " + resultName); 34 | exportTexture(JSON.parse(data), "blocks"); 35 | }); 36 | }); 37 | }); 38 | 39 | function exportTexture(data, mType) { 40 | try { 41 | for (let t = 0; t < data["textures"].length; t++) { 42 | let textureName = data["textures"][t]["name"]; 43 | let textureData = data["textures"][t]["source"].replace("data:image/png;base64,", ""); 44 | if (fs.existsSync("RP/textures/" + mType + "/") == false) { 45 | fs.mkdirSync("RP/textures/" + mType + "/", { recursive: true }); 46 | } 47 | fs.writeFileSync("RP/textures/" + mType + "/" + textureName + ".png", textureData, "base64"); 48 | } 49 | } catch { 50 | console.log("No textures found"); 51 | } 52 | } 53 | 54 | // From: https://github.com/bridge-core/editor/blob/main/src/components/ImportFile/BBModel.ts 55 | 56 | // From: https://github.com/JannisX11/blockbench/blob/1701f764641376414d29100c4f6c7cd74997fad8/js/io/formats/bedrock.js#L652 57 | function exportModel(data) { 58 | let entitymodel = {}; 59 | let main_tag = { 60 | format_version: "1.12.0", 61 | "minecraft:geometry": [entitymodel], 62 | }; 63 | entitymodel.description = { 64 | identifier: "geometry." + (data.model_identifier || "unknown"), 65 | texture_width: data.resolution.width || 16, 66 | texture_height: data.resolution.height || 16, 67 | }; 68 | let bones = []; 69 | 70 | let groups = getAllGroups(data); 71 | groups.forEach((group) => { 72 | let bone = compileGroup(data, group); 73 | bones.push(bone); 74 | }); 75 | 76 | if (bones.length) { 77 | let visible_box = calculateVisibleBox(data); 78 | entitymodel.description.visible_bounds_width = visible_box[0] || 0; 79 | entitymodel.description.visible_bounds_height = visible_box[1] || 0; 80 | entitymodel.description.visible_bounds_offset = [0, visible_box[2] || 0, 0]; 81 | 82 | entitymodel.bones = bones; 83 | } 84 | 85 | return compileJSON(main_tag); 86 | } 87 | 88 | //From: https://github.com/JannisX11/blockbench/blob/1701f764641376414d29100c4f6c7cd74997fad8/js/outliner/group.js#L492 89 | function getAllGroups(data) { 90 | let groups = []; 91 | 92 | function iterate(array, parent) { 93 | for (let obj of array) { 94 | if (obj instanceof Object) { 95 | obj.parent = parent; 96 | groups.push(obj); 97 | iterate(obj.children, obj.name); 98 | } 99 | } 100 | } 101 | 102 | iterate(data.outliner, undefined); 103 | return groups; 104 | } 105 | 106 | //From: https://github.com/JannisX11/blockbench/blob/1701f764641376414d29100c4f6c7cd74997fad8/js/io/formats/bedrock.js#L571 107 | function compileGroup(data, group) { 108 | let bone = {}; 109 | bone.name = group.name; 110 | bone.parent = group.parent; 111 | bone.pivot = group.origin.slice(); 112 | bone.pivot[0] *= -1; 113 | if (group.rotation) { 114 | if ( 115 | group.rotation[0] !== 0 || 116 | group.rotation[1] !== 0 || 117 | group.rotation[2] !== 0 118 | ) { 119 | bone.rotation = group.rotation.slice(); 120 | bone.rotation[0] *= -1; 121 | bone.rotation[1] *= -1; 122 | } 123 | } 124 | if (group.bedrock_binding) { 125 | bone.binding = group.bedrock_binding; 126 | } 127 | if (group.reset) { 128 | bone.reset = true; 129 | } 130 | if (group.mirror_uv && data.meta.box_uv) { 131 | bone.mirror = true; 132 | } 133 | if (group.material) { 134 | bone.material = group.material; 135 | } 136 | 137 | let cubes = []; 138 | let locators = {}; 139 | 140 | for (let child of group.children) { 141 | if (!(child instanceof Object)) { 142 | let element = data.elements.find((element) => element.uuid === child); 143 | if (element.type !== "locator") { 144 | let cube = compileCube(data, element, bone); 145 | cubes.push(cube); 146 | } else if (element.type === "locator") { 147 | let key = element.name; 148 | let offset = element.from.slice(); 149 | offset[0] *= -1; 150 | 151 | if ( 152 | element.rotation[0] !== 0 || 153 | element.rotation[1] !== 0 || 154 | element.rotation[2] !== 0 155 | ) { 156 | locators[key] = { 157 | offset, 158 | rotation: [ 159 | -element.rotation[0], 160 | -element.rotation[0], 161 | element.rotation[0], 162 | ], 163 | }; 164 | } else { 165 | locators[key] = offset; 166 | } 167 | } 168 | } 169 | } 170 | 171 | if (cubes.length) { 172 | bone.cubes = cubes; 173 | } 174 | if (Object.keys(locators).length) { 175 | bone.locators = locators; 176 | } 177 | return bone; 178 | } 179 | 180 | //From: https://github.com/JannisX11/blockbench/blob/1701f764641376414d29100c4f6c7cd74997fad8/js/io/formats/bedrock.js#L516 181 | function compileCube(data, element, bone) { 182 | let cube = { 183 | origin: element.from ? element.from.slice() : undefined, 184 | size: [ 185 | element.to[0] - element.from[0], 186 | element.to[1] - element.from[1], 187 | element.to[2] - element.from[2], 188 | ], 189 | inflate: element.inflate || undefined, 190 | }; 191 | cube.origin[0] = -(cube.origin[0] + cube.size[0]); 192 | 193 | if (element.rotation) { 194 | if ( 195 | element.rotation[0] !== 0 || 196 | element.rotation[1] !== 0 || 197 | element.rotation[2] !== 0 198 | ) { 199 | cube.pivot = element.origin.slice(); 200 | cube.pivot[0] *= -1; 201 | 202 | cube.rotation = element.rotation.slice(); 203 | cube.rotation.forEach(function (br, axis) { 204 | if (axis !== 2) cube.rotation[axis] *= -1; 205 | }); 206 | } 207 | } 208 | 209 | if (data.meta.box_uv) { 210 | cube.uv = element.uv_offset; 211 | if (element.mirror_uv === !bone.mirror) { 212 | cube.mirror = element.mirror_uv; 213 | } 214 | } else { 215 | cube.uv = {}; 216 | for (let key in element.faces) { 217 | let face = element.faces[key]; 218 | if (face.texture !== null) { 219 | cube.uv[key] = { 220 | uv: [face.uv[0], face.uv[1]], 221 | uv_size: [face.uv[2] - face.uv[0], face.uv[3] - face.uv[1]], 222 | }; 223 | if (face.material_name) { 224 | cube.uv[key].material_instance = face.material_name; 225 | } 226 | if (key === "up" || key === "down") { 227 | cube.uv[key].uv[0] += cube.uv[key].uv_size[0]; 228 | cube.uv[key].uv[1] += cube.uv[key].uv_size[1]; 229 | cube.uv[key].uv_size[0] *= -1; 230 | cube.uv[key].uv_size[1] *= -1; 231 | } 232 | } 233 | } 234 | } 235 | return cube; 236 | } 237 | 238 | //From: https://github.com/JannisX11/blockbench/blob/1701f764641376414d29100c4f6c7cd74997fad8/js/io/formats/bedrock.js#L276 239 | function calculateVisibleBox(data) { 240 | let visible_box = { 241 | max: { 242 | x: 0, 243 | y: 0, 244 | z: 0, 245 | }, 246 | min: { 247 | x: 0, 248 | y: 0, 249 | z: 0, 250 | }, 251 | }; 252 | 253 | const elements = data.elements; 254 | elements.forEach((element) => { 255 | if (!element.to || !element.from) return; 256 | 257 | visible_box.max.x = Math.max( 258 | visible_box.max.x, 259 | element.from[0], 260 | element.to[0] 261 | ); 262 | visible_box.min.x = Math.min( 263 | visible_box.min.x, 264 | element.from[0], 265 | element.to[0] 266 | ); 267 | 268 | visible_box.max.y = Math.max( 269 | visible_box.max.y, 270 | element.from[1], 271 | element.to[1] 272 | ); 273 | visible_box.min.y = Math.min( 274 | visible_box.min.y, 275 | element.from[1], 276 | element.to[1] 277 | ); 278 | 279 | visible_box.max.z = Math.max( 280 | visible_box.max.z, 281 | element.from[2], 282 | element.to[2] 283 | ); 284 | visible_box.min.z = Math.min( 285 | visible_box.min.z, 286 | element.from[2], 287 | element.to[2] 288 | ); 289 | }); 290 | 291 | visible_box.max.x += 8; 292 | visible_box.min.x += 8; 293 | visible_box.max.y += 8; 294 | visible_box.min.y += 8; 295 | visible_box.max.z += 8; 296 | visible_box.min.z += 8; 297 | 298 | //Width 299 | let radius = Math.max( 300 | visible_box.max.x, 301 | visible_box.max.z, 302 | -visible_box.min.x, 303 | -visible_box.min.z 304 | ); 305 | if (Math.abs(radius) === Infinity) { 306 | radius = 0; 307 | } 308 | let width = Math.ceil((radius * 2) / 16); 309 | width = Math.max(width, data.visible_box[0]); 310 | 311 | //Height 312 | let y_min = Math.floor(visible_box.min.y / 16); 313 | let y_max = Math.ceil(visible_box.max.y / 16); 314 | if (y_min === Infinity) y_min = 0; 315 | if (y_max === Infinity) y_max = 0; 316 | y_min = Math.min(y_min, data.visible_box[2] - data.visible_box[1] / 2); 317 | y_max = Math.max(y_max, data.visible_box[2] + data.visible_box[1] / 2); 318 | 319 | return [width, y_max - y_min, (y_max + y_min) / 2]; 320 | } 321 | 322 | // From: https://github.com/JannisX11/blockbench/blob/914e7eb1f74232c1a31ccf278c793309bb163848/js/io/io.js#L328 323 | function compileJSON(object, options) { 324 | if (typeof options !== "object") options = {}; 325 | function newLine(tabs) { 326 | if (options.small === true) { 327 | return ""; 328 | } 329 | var s = "\n"; 330 | for (var i = 0; i < tabs; i++) { 331 | s += "\t"; 332 | } 333 | return s; 334 | } 335 | function escape(string) { 336 | return string 337 | .replace(/\\/g, "\\\\") 338 | .replace(/"/g, '\\"') 339 | .replace(/\n|\r\n/g, "\\n") 340 | .replace(/\t/g, "\\t"); 341 | } 342 | function handleVar(o, tabs) { 343 | var out = ""; 344 | if (typeof o === "string") { 345 | //String 346 | out += '"' + escape(o) + '"'; 347 | } else if (typeof o === "boolean") { 348 | //Boolean 349 | out += o ? "true" : "false"; 350 | } else if (o === null || o === Infinity || o === -Infinity) { 351 | //Null 352 | out += "null"; 353 | } else if (typeof o === "number") { 354 | //Number 355 | o = (Math.round(o * 100000) / 100000).toString(); 356 | out += o; 357 | } else if (typeof o === "object" && o instanceof Array) { 358 | //Array 359 | var has_content = false; 360 | out += "["; 361 | for (var i = 0; i < o.length; i++) { 362 | var compiled = handleVar(o[i], tabs + 1); 363 | if (compiled) { 364 | var breaks = typeof o[i] === "object"; 365 | if (has_content) { 366 | out += "," + (breaks || options.small ? "" : " "); 367 | } 368 | if (breaks) { 369 | out += newLine(tabs); 370 | } 371 | out += compiled; 372 | has_content = true; 373 | } 374 | } 375 | if (typeof o[o.length - 1] === "object") { 376 | out += newLine(tabs - 1); 377 | } 378 | out += "]"; 379 | } else if (typeof o === "object") { 380 | //Object 381 | var breaks = o.constructor.name !== "oneLiner"; 382 | var has_content = false; 383 | out += "{"; 384 | for (var key in o) { 385 | if (o.hasOwnProperty(key)) { 386 | var compiled = handleVar(o[key], tabs + 1); 387 | if (compiled) { 388 | if (has_content) { 389 | out += "," + (breaks || options.small ? "" : " "); 390 | } 391 | if (breaks) { 392 | out += newLine(tabs); 393 | } 394 | out += 395 | '"' + escape(key) + '":' + (options.small === true ? "" : " "); 396 | out += compiled; 397 | has_content = true; 398 | } 399 | } 400 | } 401 | if (breaks && has_content) { 402 | out += newLine(tabs - 1); 403 | } 404 | out += "}"; 405 | } 406 | return out; 407 | } 408 | return handleVar(object, 1); 409 | } 410 | -------------------------------------------------------------------------------- /blockbench_convert/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Converts blockbench models into `.geometry.json` files.", 3 | "filters": [ 4 | { 5 | "runWith": "nodejs", 6 | "script": "./blockbench_convert.js", 7 | "name": "Converting .bbmodel (Blockbench) files into Bedrock model json files" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /blockbench_convert/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockbench_convert", 3 | "description": "Regolith filter for converting Blockbench files into Bedrock model json files", 4 | "license": "MIT", 5 | "repository": { 6 | "url": "https://github.com/Bedrock-OSS/regolith-filters.git", 7 | "type": "git" 8 | }, 9 | "version": "1.0.0", 10 | "main": "./blockbench_convert.js", 11 | "dependencies": { 12 | "glob": "^7.1.7", 13 | "path": "^0.12.7" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /blockbench_convert/readme.md: -------------------------------------------------------------------------------- 1 | # Blockbench Convert 2 | 3 | This filter converts all the `.bbmodel` files in your addon into geometry files, and images. 4 | 5 | This is useful since it allows you to store `.bbmodel` files directly in your `RP/models/` folder, and prevents you from constantly manually exporting the geometry! 6 | 7 | ## Getting the Filter 8 | 9 | Install with: `regolith install blockbench_convert`. After that, you can place the filter into one of your profiles. 10 | 11 | ```json 12 | { 13 | "filter": "blockbench_convert" 14 | } 15 | ``` 16 | 17 | ## Documentation 18 | 19 | Any `.bbmodel` files located in your resource pack will be converted in-place into a `.geo.json` file. If your file is named as `*.entity.bbmodel` or `*.block.bbmodel`, then textures will also be exported. 20 | 21 | 22 | ## Changelog 23 | 24 | ### 1.1.1 25 | 26 | Fixed incorrect identifier for models (#52) 27 | 28 | ### 1.1.0 29 | 30 | Added the ability to export images. 31 | 32 | ### 1.0.0 33 | 34 | The first release of Blockbench Convert. -------------------------------------------------------------------------------- /bump_manifest/bump_manifest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | from typing import List 4 | from pathlib import Path 5 | 6 | def create_version_file() -> None: 7 | """ 8 | Creates a version file. 9 | """ 10 | 11 | Path("./data/bump_manifest/").mkdir(parents=True, exist_ok=True) 12 | 13 | with open('./data/bump_manifest/version.json', 'a') as file: 14 | json.dump({'version': [1, 0, 0]}, file, indent=4) 15 | 16 | def get_version() -> str: 17 | """ 18 | Gets the current version number from the version.json file. 19 | 20 | Saves new version back to the file. 21 | """ 22 | 23 | path = './data/bump_manifest/version.json' 24 | 25 | # Create version file, if it doesn't exist 26 | if not os.path.exists(path): 27 | create_version_file() 28 | 29 | # Read version file 30 | with open(path, 'r') as f: 31 | data = json.load(f) 32 | 33 | # Update version file 34 | data['version'][-1] = data['version'][-1] + 1 35 | with open(path, 'w') as f: 36 | json.dump(data, f, indent=4) 37 | 38 | return data['version'] 39 | 40 | 41 | def update_manifest(manifest: dict, version: List[int] = None) -> dict: 42 | """ 43 | Takes in a manifest and updates the minor version number by one. 44 | """ 45 | 46 | manifest['header']['version'] = version 47 | 48 | for module in manifest.get("modules", []): 49 | module['version'] = version 50 | 51 | for dependency in manifest.get("dependencies", []): 52 | dependency['version'] = version 53 | 54 | return manifest 55 | 56 | def main(): 57 | """ 58 | Program execution begins here. 59 | """ 60 | 61 | # Get current version, and update the file 62 | version = get_version() 63 | 64 | print("Pack updated to version: ", str(version)) 65 | 66 | # Write new version to resource pack 67 | try: 68 | with open('./RP/manifest.json', 'r') as manifest_file: 69 | manifest = json.load(manifest_file) 70 | 71 | with open('./RP/manifest.json', 'w') as manifest_file: 72 | json.dump(update_manifest(manifest, version), manifest_file, indent=4) 73 | except FileNotFoundError: 74 | pass 75 | 76 | # Write new version to resource pack 77 | try: 78 | with open('./BP/manifest.json', 'r') as manifest_file: 79 | manifest = json.load(manifest_file) 80 | 81 | with open('./BP/manifest.json', 'w') as manifest_file: 82 | json.dump(update_manifest(manifest, version), manifest_file, indent=4) 83 | 84 | except FileNotFoundError: 85 | pass 86 | 87 | if __name__ == "__main__": 88 | main() -------------------------------------------------------------------------------- /bump_manifest/data/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": [1, 0, 0] 3 | } -------------------------------------------------------------------------------- /bump_manifest/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Bumps the manifest version in your RP and BP. Good for multiplayer testing where you need to avoid pack-caching issues.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./bump_manifest.py", 7 | "name": "Bumps manifest version" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /bump_manifest/readme.md: -------------------------------------------------------------------------------- 1 | # bump_manifest 2 | 3 | This filter bumps the minor version of your `manifest.json` file, every time Regolith runs. It sets all format versions to the same version, including: 4 | - header version (main version) 5 | - module versions 6 | - dependency versions 7 | 8 | ## Why 9 | 10 | This filter is useful for multiplayer scenarios, such as realms or multiplayer testing. The manifest version update will force the resource pack to be re-downloaded. 11 | 12 | ## Using the Filter 13 | 14 | Simply add to your filter list, like this: 15 | 16 | ```json 17 | { 18 | "filter": "bump_manifest" 19 | } 20 | ``` 21 | 22 | ## version.json 23 | 24 | This filter uses a file located in your data folder to track current version. By default the file will be located at `packs/data/bump_manifest/version.json`, and will start at `[1, 0, 0]`. 25 | 26 | You may edit this file by hand to set your packs current version, for example if you need to update the major version, or to reset the minor version. 27 | 28 | Every time the filter runs, the version will update (e.g, `[1, 0, 1`]), and the version will be copied into the manifest file. 29 | 30 | # Changelog 31 | 32 | ### 1.0.0 33 | 34 | The first release of Bump Manifest. 35 | -------------------------------------------------------------------------------- /bump_manifest/test/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.regolith -------------------------------------------------------------------------------- /bump_manifest/test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bump_manifest_test", 3 | "author": "Regolith", 4 | "packs": { 5 | "behaviorPack": "./packs/BP", 6 | "resourcePack": "./packs/RP" 7 | }, 8 | "regolith": { 9 | "profiles": { 10 | "export": { 11 | "filters": [ 12 | { 13 | "filter": "bump_manifest" 14 | } 15 | ], 16 | "export": { 17 | "target": "development", 18 | "readOnly": false 19 | }, 20 | "dataPath": "./packs/data" 21 | }, 22 | "dev": { 23 | "filters": [ 24 | { 25 | "runWith": "python", 26 | "script": "../bump_manifest.py" 27 | } 28 | ], 29 | "export": { 30 | "target": "local", 31 | "readOnly": false 32 | }, 33 | "dataPath": "./packs/data" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /bump_manifest/test/packs/BP/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "name": "pack.name", 5 | "description": "pack.description", 6 | "uuid": "6cdc71e6-aa0d-4d46-99d3-f4bb5130d0b3", 7 | "version":[ 1, 0, 0 ], 8 | "min_engine_version": [ 1, 13, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "type": "data", 13 | "uuid": "5701bab6-e89d-4900-a7a3-17974cbf4e23", 14 | "version":[ 1, 0, 0 ] 15 | } 16 | ], 17 | "dependencies": [ 18 | { 19 | "version":[ 1, 0, 0 ], 20 | "uuid": "de97cb10-d033-4c22-a4ed-318606e7c0a5" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /bump_manifest/test/packs/RP/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "name": "pack.name", 5 | "description": "pack.description", 6 | "uuid": "de97cb10-d033-4c22-a4ed-318606e7c0a5", 7 | "version":[ 1, 0, 0 ], 8 | "min_engine_version": [ 1, 13, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "type": "resources", 13 | "uuid": "63f1e815-ab2b-42ea-af5f-8367509c425f", 14 | "version":[ 1, 0, 0 ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /bump_manifest/test/packs/data/bump_manifest/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": [ 3 | 1, 4 | 0, 5 | 2 6 | ] 7 | } -------------------------------------------------------------------------------- /filter_tester/README.md: -------------------------------------------------------------------------------- 1 | # What does this filter do? 2 | This filter is meant to be used by the filter developers to test other filters. 3 | It compares the expected results with the files generated by Regolith. The 4 | result files are stored in `data/filter_tester/RP` and `data/filter_tester/BP`. 5 | 6 | # How to use it? 7 | In Regolith project run installation command: 8 | ``` 9 | regolith install filter_tester 10 | ``` 11 | 12 | Add the filter to the `filters` list of a profile in `config.json` file: 13 | ``` 14 | { 15 | "filter": "filter_tester", 16 | "settings": { 17 | "errors_stop_execution": true 18 | } 19 | }, 20 | ``` 21 | Put the expected results in `data/filter_tester/RP` and 22 | `data/filter_tester/BP` folders for your resource pack and behavior 23 | pack respectively. 24 | 25 | The filter will print messages about the differences between the expected 26 | and the actual results. You can use `errors_stop_execution` setting to make 27 | `filter_tester` stop Regolith if any differences are found. 28 | 29 | ## Configuration settings 30 | - `errors_stop_execution: bool` - If true, the filter will stop Regolith with 31 | sys.exit(1) in case of finding any errors. If false, the filter will just 32 | print the errors and continue. The default value is false. 33 | 34 | 35 | # Changelog 36 | 37 | ### 1.0.0 38 | 39 | The first release of Filter Tester. -------------------------------------------------------------------------------- /filter_tester/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Meant to be used by the filter developers to test other filters. It compares the expected results with the files generated by Regolith.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./main.py", 7 | "name": "Comparing generated files with expected results" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /filter_tester/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import sys 3 | from itertools import chain 4 | from pathlib import Path 5 | from typing import Dict, List, Type, Union 6 | 7 | REALITY_PATH = Path("") 8 | EXPECTATIONS_PATH = Path("data/filter_tester") 9 | 10 | def print_red(text): 11 | for t in text.split('\n'): 12 | print("\033[91m {}\033[00m".format(t)) 13 | 14 | class FilterTesterException(Exception): 15 | pass 16 | 17 | JSON = Union[Dict, List, str, int, float, bool, Type[None]] 18 | 19 | def json_path_to_str(json_path: List[Union[int, str]]) -> str: 20 | return f'[{"->".join(str(i) for i in json_path)}]' 21 | 22 | def assert_eq_json( 23 | reality: JSON, expectations: JSON, json_path: List[str]=None) -> None: 24 | ''' 25 | Compares the content of two JSON objects. If they are not equal, an 26 | FilterTesterException is raised. 27 | ''' 28 | if json_path is None: 29 | json_path = [] 30 | if reality == expectations: 31 | return 32 | elif type(reality) != type(expectations): 33 | raise FilterTesterException( 34 | f"Type mismatch at {json_path_to_str(json_path)}: " 35 | f"{reality} != {expectations}") 36 | elif isinstance(reality, dict): 37 | assert_eq_dict(reality, expectations, json_path) 38 | elif isinstance(reality, list): 39 | assert_eq_list(reality, expectations, json_path) 40 | elif reality != expectations: 41 | raise FilterTesterException( 42 | f"Value mismatch at {json_path_to_str(json_path)}: " 43 | f"{reality} != {expectations}") 44 | 45 | def assert_eq_dict( 46 | reality: Dict, expectations: Dict, json_path: List[str]) -> None: 47 | ''' 48 | Asserts that two JSON dictionaries are equal. 49 | ''' 50 | reality_keys = set(reality.keys()) 51 | expectations_keys = set(expectations.keys()) 52 | if reality_keys != expectations_keys: 53 | surplus_keys = reality_keys - expectations_keys 54 | missing_keys = expectations_keys - reality_keys 55 | error = [f"JSON keys mismatch at {json_path_to_str(json_path)}"] 56 | if surplus_keys: 57 | error.append(f" Unexpected keys: {', '.join(surplus_keys)}") 58 | if missing_keys: 59 | error.append(f" Missing keys: {', '.join(missing_keys)}") 60 | raise FilterTesterException("\n".join(error)) 61 | for key in reality_keys: 62 | assert_eq_json(reality[key], expectations[key], json_path + [key]) 63 | 64 | def assert_eq_list( 65 | reality: List, expectations: List, json_path: List[str]) -> None: 66 | ''' 67 | Asserts that two JSON lists are equal. 68 | ''' 69 | if len(reality) != len(expectations): 70 | raise FilterTesterException( 71 | f"JSON list length mismatch at {json_path_to_str(json_path)}, " 72 | f"expected {len(expectations)} elements, got {len(reality)}") 73 | for i in range(len(reality)): 74 | assert_eq_json(reality[i], expectations[i], json_path + [i]) 75 | 76 | def main(errors_stop_execution: bool): 77 | reality_files = set(chain( 78 | [i.relative_to(REALITY_PATH) for i in REALITY_PATH.glob("RP/**/*")], 79 | [i.relative_to(REALITY_PATH) for i in REALITY_PATH.glob("BP/**/*")])) 80 | expectations_files = set(chain( 81 | [i.relative_to(EXPECTATIONS_PATH) for i in EXPECTATIONS_PATH.glob("RP/**/*")], 82 | [i.relative_to(EXPECTATIONS_PATH) for i in EXPECTATIONS_PATH.glob("BP/**/*")]) 83 | ) 84 | errors = [] 85 | if expectations_files != reality_files: 86 | surplus_files = reality_files - expectations_files 87 | missing_files = expectations_files - reality_files 88 | error = [f"Files mismatch:"] 89 | if surplus_files: 90 | error.append( 91 | f" Unexpected files: " 92 | f"{', '.join(i.as_posix() for i in surplus_files)}") 93 | if missing_files: 94 | error.append( 95 | f" Missing files: " 96 | f"{', '.join(i.as_posix() for i in missing_files)}") 97 | errors.append(FilterTesterException("\n".join(error))) 98 | common_files = expectations_files & reality_files 99 | 100 | def compare_binary_files(file: Path) -> None: 101 | with open(REALITY_PATH / file, "rb") as f: 102 | reality = f.read() 103 | with open(EXPECTATIONS_PATH / file, "rb") as f: 104 | expectations = f.read() 105 | if reality != expectations: 106 | errors.append(FilterTesterException( 107 | f"File mismatch at {file.as_posix()}")) 108 | 109 | for file in common_files: 110 | if (REALITY_PATH / file).is_dir() ^ (EXPECTATIONS_PATH / file).is_dir(): 111 | errors.append(FilterTesterException( 112 | f"File mismatch at {file.as_posix()}")) 113 | if (REALITY_PATH / file).is_dir(): 114 | continue # Both are directories, so we don't need to compare them 115 | if file.suffix == ".json": # JSON is a special case 116 | try: 117 | with open(REALITY_PATH / file, "r") as f: 118 | reality = json.load(f) 119 | with open(EXPECTATIONS_PATH / file, "r") as f: 120 | expectations = json.load(f) 121 | try: 122 | assert_eq_json(reality, expectations) 123 | except FilterTesterException as e: 124 | errors.append(FilterTesterException( 125 | f"File mismatch at {file.as_posix()}:" + str(e))) 126 | except Exception as e: # Compare files like binary files 127 | compare_binary_files(file) 128 | else: 129 | compare_binary_files(file) 130 | if errors: 131 | for error in errors: 132 | print_red(str(error)) 133 | if errors_stop_execution: 134 | sys.exit(1) 135 | 136 | if __name__ == "__main__": 137 | try: 138 | config = json.loads(sys.argv[1]) 139 | except Exception: 140 | config = {} 141 | errors_stop_execution = config.get("errors_stop_execution", False) 142 | main(errors_stop_execution) 143 | -------------------------------------------------------------------------------- /filter_tester/test/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.regolith -------------------------------------------------------------------------------- /filter_tester/test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "filter_tester example", 3 | "author": "Bedrock-OSS", 4 | "packs": { 5 | "behaviorPack": "./packs/BP", 6 | "resourcePack": "./packs/RP" 7 | }, 8 | "regolith": { 9 | "profiles": { 10 | "dev": { 11 | "filters": [ 12 | { 13 | "filter": "filter_tester", 14 | "settings": { 15 | "errors_stop_execution": true 16 | } 17 | } 18 | ], 19 | "export": { 20 | "target": "development", 21 | "readOnly": false 22 | } 23 | } 24 | }, 25 | "filterDefinitions": { 26 | "filter_tester": { 27 | "runWith": "python", 28 | "script": "../main.py" 29 | } 30 | }, 31 | "dataPath": "./data" 32 | } 33 | } -------------------------------------------------------------------------------- /filter_tester/test/data/filter_tester/BP/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 1, 3 | "b": [true, 2.3, null], 4 | "c": { 5 | "x": [{"xx": false}, {"yy": {}}], 6 | "y": [4, 5, 6] 7 | } 8 | } -------------------------------------------------------------------------------- /filter_tester/test/data/filter_tester/BP/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/filter_tester/test/data/filter_tester/BP/example.png -------------------------------------------------------------------------------- /filter_tester/test/data/filter_tester/BP/nested/example.txt: -------------------------------------------------------------------------------- 1 | This is an example file. -------------------------------------------------------------------------------- /filter_tester/test/packs/BP/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": 1, 3 | "b": [true, 2.3, null], 4 | "c": { 5 | "x": [{"xx": false}, {"yy": {}}], 6 | "y": [4, 5, 6] 7 | } 8 | } -------------------------------------------------------------------------------- /filter_tester/test/packs/BP/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/filter_tester/test/packs/BP/example.png -------------------------------------------------------------------------------- /filter_tester/test/packs/BP/nested/example.txt: -------------------------------------------------------------------------------- 1 | This is an example file. -------------------------------------------------------------------------------- /fix_emissive/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Fixes emissive issues in your textures, by removing the color data from fully transparent pixels.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./fix_emissive.py", 7 | "name": "Removing RGB values from transparent pixels" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /fix_emissive/fix_emissive.py: -------------------------------------------------------------------------------- 1 | import glob 2 | 3 | from PIL import Image 4 | 5 | 6 | def fix(input_img, output_img): 7 | img = Image.open(input_img) 8 | pixels = img.load() 9 | 10 | for i in range(img.size[0]): 11 | for j in range(img.size[1]): 12 | if pixels[i, j][3] == 0: 13 | pixels[i, j] = (0, 0, 0, 0) 14 | 15 | img.save(output_img) 16 | 17 | 18 | for texture in glob.glob("./RP/textures/**/*.png", recursive=True): 19 | fix(texture, texture) 20 | -------------------------------------------------------------------------------- /fix_emissive/readme.md: -------------------------------------------------------------------------------- 1 | # Fix Emissive 2 | 3 | This filter fixes emissive issues in your textures, by removing the color data from fully transparent pixels. 4 | 5 | Some editors leave this color data in place, which confuses Minecraft. 6 | 7 | You only need this filter if you use an editor that leaves these artifacts, or you notice Minecraft rendering your "transparent" images oddly. 8 | 9 | ## Using the Filter 10 | 11 | ```json 12 | { 13 | "filter": "fix_emissive" 14 | } 15 | ``` 16 | 17 | # Changelog 18 | 19 | ### 1.0.0 20 | 21 | The first release of Fix Emissive filter. -------------------------------------------------------------------------------- /fix_emissive/requirements.txt: -------------------------------------------------------------------------------- 1 | Pillow~=8.3.1 -------------------------------------------------------------------------------- /gametests/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /gametests/build.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const json5 = require("json5"); 4 | 5 | // A simple esbuild plugin, that converts JSON5 to JSON before passing it to esbuild. 6 | const json5Plugin = (options) => { 7 | return { 8 | name: "json5", 9 | setup(build) { 10 | build.onResolve({ filter: /\.json$/ }, (args) => { 11 | return { 12 | path: path.resolve(args.resolveDir, args.path), 13 | namespace: "json5", 14 | }; 15 | }); 16 | build.onLoad({ filter: /.*/, namespace: "json5" }, (args) => { 17 | const result = fs.readFileSync(args.path, "utf-8"); 18 | const compiled = json5.parse(result); 19 | const stringed = JSON.stringify(compiled); 20 | return { 21 | contents: stringed, 22 | loader: "json", 23 | }; 24 | }); 25 | }, 26 | }; 27 | }; 28 | 29 | module.exports.run = function (settings) { 30 | // Return the promise so that callers can await the build's completion. 31 | return require("esbuild") 32 | .build({ ...settings.buildOptions, plugins: [json5Plugin()] }) 33 | .catch((err) => { 34 | console.error(err.message); 35 | process.exit(1); 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /gametests/completion.md: -------------------------------------------------------------------------------- 1 | # Gametests 2 | 3 | Features 4 | - Typescript support and bundling through [esbuild](https://esbuild.github.io/) 5 | - Manages manifest script dependencies 6 | - Manages manifest module 7 | 8 | This filter manages script-related manifest settings and allows separation of scripts from the main pack. 9 | 10 | Utilizing the separation ability allows you to keep unit tests and such separate from your main pack. This can be used by having one profile setup for QA/development which uses this filter to add the unit tests to the pack and have another profile which does not include this filter. 11 | -------------------------------------------------------------------------------- /gametests/data/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | scripts/ 3 | 4 | ### VisualStudioCode ### 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | *.code-workspace 11 | 12 | # Local History for Visual Studio Code 13 | .history/ 14 | 15 | ### VisualStudioCode Patch ### 16 | # Ignore all local history of files 17 | .history 18 | .ionide -------------------------------------------------------------------------------- /gametests/data/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 80 5 | } 6 | -------------------------------------------------------------------------------- /gametests/data/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | ] 5 | } -------------------------------------------------------------------------------- /gametests/data/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "typescript", 6 | "tsconfig": "tsconfig.json", 7 | "problemMatcher": ["$tsc"], 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /gametests/data/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[json]":{ 3 | "editor.defaultFormatter":"esbenp.prettier-vscode" 4 | }, 5 | "[jsonc]":{ 6 | "editor.defaultFormatter":"esbenp.prettier-vscode" 7 | }, 8 | "[typescript]":{ 9 | "editor.defaultFormatter":"esbenp.prettier-vscode" 10 | }, 11 | "editor.formatOnSave":true, 12 | "editor.tabSize":2, 13 | "editor.insertSpaces":true 14 | } -------------------------------------------------------------------------------- /gametests/data/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bedrock OSS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /gametests/data/extra_files/BP/structures/gametests/test_5x5.mcstructure: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/gametests/data/extra_files/BP/structures/gametests/test_5x5.mcstructure -------------------------------------------------------------------------------- /gametests/data/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-gametest", 3 | "version": "0.0.1", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "example-gametest", 9 | "version": "0.0.1", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@minecraft/server": "^1.1.0-beta.1.19.60-stable", 13 | "@minecraft/server-gametest": "^1.0.0-beta.1.19.60-stable" 14 | } 15 | }, 16 | "node_modules/@minecraft/server": { 17 | "version": "1.1.0-beta.1.19.60-stable", 18 | "resolved": "https://registry.npmjs.org/@minecraft/server/-/server-1.1.0-beta.1.19.60-stable.tgz", 19 | "integrity": "sha512-o7koQPeyX/R+MUdgexYMIfoZtdkWgr51s+e1f7gR2EqomTUu8/J/8N3sDEWIdeMU2zM5MzWaezCJK0+rEUpZTg==" 20 | }, 21 | "node_modules/@minecraft/server-gametest": { 22 | "version": "1.0.0-beta.1.19.60-stable", 23 | "resolved": "https://registry.npmjs.org/@minecraft/server-gametest/-/server-gametest-1.0.0-beta.1.19.60-stable.tgz", 24 | "integrity": "sha512-QMN9Vccji2N2lccbT12Xc+5oGlZMcaR3ycXTc2ane+3BJmYAMWyUeCJqwYKH1NTDU+aKlO4y9wu4zeNZG5pl/w==", 25 | "dependencies": { 26 | "@minecraft/server": "1.1.0-beta.1.19.60-stable" 27 | } 28 | } 29 | }, 30 | "dependencies": { 31 | "@minecraft/server": { 32 | "version": "1.1.0-beta.1.19.60-stable", 33 | "resolved": "https://registry.npmjs.org/@minecraft/server/-/server-1.1.0-beta.1.19.60-stable.tgz", 34 | "integrity": "sha512-o7koQPeyX/R+MUdgexYMIfoZtdkWgr51s+e1f7gR2EqomTUu8/J/8N3sDEWIdeMU2zM5MzWaezCJK0+rEUpZTg==" 35 | }, 36 | "@minecraft/server-gametest": { 37 | "version": "1.0.0-beta.1.19.60-stable", 38 | "resolved": "https://registry.npmjs.org/@minecraft/server-gametest/-/server-gametest-1.0.0-beta.1.19.60-stable.tgz", 39 | "integrity": "sha512-QMN9Vccji2N2lccbT12Xc+5oGlZMcaR3ycXTc2ane+3BJmYAMWyUeCJqwYKH1NTDU+aKlO4y9wu4zeNZG5pl/w==", 40 | "requires": { 41 | "@minecraft/server": "1.1.0-beta.1.19.60-stable" 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /gametests/data/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example-gametest", 3 | "version": "0.0.1", 4 | "description": "Example of a GameTest module", 5 | "main": "main.js", 6 | "scripts": {}, 7 | "author": "", 8 | "license": "ISC", 9 | "dependencies": { 10 | "@minecraft/server": "1.10.0", 11 | "@minecraft/server-gametest": "1.0.0-beta.1.20.80-stable" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /gametests/data/src/main.ts: -------------------------------------------------------------------------------- 1 | import { 2 | system, 3 | TicksPerSecond, 4 | world, 5 | } from '@minecraft/server'; 6 | import * as GameTest from '@minecraft/server-gametest'; 7 | 8 | system.runInterval(() => { 9 | let player_count = world.getAllPlayers().length; 10 | if (player_count > 0) { 11 | world.sendMessage(`It has been ${system.currentTick / TicksPerSecond} seconds`); 12 | } 13 | }, 100); 14 | 15 | function simpleMobTest(test: GameTest.Test) { 16 | const attackerId = 'fox'; 17 | const victimId = 'chicken'; 18 | 19 | test.spawn(attackerId, {x:3, y:2, z:3}); 20 | test.spawn(victimId, {x:2, y:2, z:2}); 21 | 22 | test.assertEntityPresentInArea(victimId, true); 23 | 24 | // Succeed when the victim dies 25 | test.succeedWhen(() => { 26 | test.assertEntityPresentInArea(victimId, false); 27 | }); 28 | } 29 | 30 | // Registration Code for our test 31 | GameTest.register('StarterTests', 'simpleMobTest', simpleMobTest) 32 | .maxTicks(410) 33 | .structureName('gametests:test_5x5'); 34 | -------------------------------------------------------------------------------- /gametests/data/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES2020", 4 | "moduleResolution": "node", 5 | "allowSyntheticDefaultImports": true, 6 | "target": "ES2020", 7 | "noImplicitAny": true, 8 | "removeComments": true, 9 | "preserveConstEnums": true, 10 | "sourceMap": false, 11 | "outDir": "scripts/", 12 | "strict": true, 13 | "noImplicitReturns": true, 14 | "allowJs": true, 15 | "resolveJsonModule": true, 16 | }, 17 | "include": [ 18 | "src", 19 | ], 20 | "exclude": [ 21 | "node_modules", 22 | "**/__tests__/*" 23 | ] 24 | } -------------------------------------------------------------------------------- /gametests/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Compiles gametests into pack.", 3 | "filters": [ 4 | { 5 | "name": "Compiling gametests", 6 | "runWith": "nodejs", 7 | "script": "./gametests.js" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /gametests/gametests.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const { randomUUID } = require("crypto"); 5 | const { glob, globSync } = require("glob"); 6 | const json5 = require("json5"); 7 | const { spawnSync } = require("child_process"); 8 | 9 | const projectRoot = process.env.ROOT_DIR; 10 | 11 | let uuidFile = "data/gametests/uuid.txt"; 12 | if (projectRoot) { 13 | uuidFile = path.join(projectRoot, "packs", uuidFile); 14 | } else { 15 | console.error("No project root found"); 16 | process.exit(1); 17 | } 18 | let defaultUUID = /** @type {string} */ (randomUUID()); 19 | if (fs.existsSync(uuidFile)) { 20 | defaultUUID = fs.readFileSync(uuidFile).toString(); 21 | } else { 22 | fs.writeFileSync(uuidFile, defaultUUID); 23 | } 24 | 25 | const defLaunchConfig = { 26 | mode: "listen", 27 | port: 19144, 28 | }; 29 | 30 | const launchConfigConsts = { 31 | type: "minecraft-js", 32 | request: "attach", 33 | sourceMapRoot: "${workspaceFolder}/.regolith/tmp/BP/scripts/", 34 | generatedSourceRoot: "${workspaceFolder}/.regolith/tmp/BP/scripts/", 35 | localRoot: "${workspaceFolder}/packs/data/gametests/src/", 36 | name: "(gametests) Debug with Minecraft", 37 | }; 38 | 39 | const defSettings = { 40 | buildOptions: { 41 | external: [""], // Empty string to mark as string[] 42 | entryPoints: ["data/gametests/src/main.ts"], 43 | target: "es2020", 44 | format: "esm", 45 | bundle: true, 46 | minify: true, 47 | }, 48 | moduleUUID: defaultUUID, 49 | modules: ["@minecraft/server@1.0.0"], 50 | moduleType: "script", 51 | language: "javascript", 52 | manifest: "BP/manifest.json", 53 | outfile: "BP/scripts/main.js", 54 | outdir: "BP/scripts", 55 | debugBuild: false, 56 | injectSourceMapping: false, 57 | disableManifestModification: false, 58 | }; 59 | // Reset external property so that it does not cause issues 60 | defSettings.buildOptions.external = []; 61 | 62 | /** @type {typeof defSettings} */ 63 | const argParsed = process.argv[2] ? JSON.parse(process.argv[2]) : {}; 64 | const settings = Object.assign({}, defSettings, argParsed); 65 | settings.buildOptions = Object.assign({}, defSettings.buildOptions, settings.buildOptions); 66 | 67 | // find all `*.esbuild.config.js` files in the project 68 | const configFiles = globSync("**/*.esbuild.config.js", { 69 | ignore: ["**/node_modules/**"], 70 | cwd: 'data/gametests', 71 | }); 72 | 73 | for (const configFile of configFiles) { 74 | const configPath = path.join(process.cwd(), 'data', 'gametests', configFile); 75 | console.log(`Loading config file ${configFile}`); 76 | const config = require(configPath).config; 77 | if (!config) { 78 | console.warn(`No config function exported found for ${configFile}`); 79 | continue; 80 | } 81 | config(settings); 82 | } 83 | 84 | if (settings.debugBuild) { 85 | settings.buildOptions.sourcemap = true; 86 | // It is generated in the `.regolith/tmp/BP/scripts` directory, so we need to exit to the project root and back to the actual source 87 | settings.buildOptions.sourceRoot = "../../../../packs/data/gametests"; 88 | if (projectRoot && fs.existsSync(path.join(projectRoot, ".vscode", "launch.json"))) { 89 | const configPath = path.join(projectRoot, ".vscode", "launch.json"); 90 | const config = json5.parse(fs.readFileSync(configPath, "utf8")); 91 | if (config && config.configurations) { 92 | let found = false; 93 | for (const c of config.configurations) { 94 | if (c.name === "(gametests) Debug with Minecraft") { 95 | let changed = ensureLaunchConfiguration(c); 96 | if (changed) { 97 | fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); 98 | } 99 | found = true; 100 | break; 101 | } 102 | } 103 | if (!found) { 104 | const newConfig = Object.assign({}, launchConfigConsts, defLaunchConfig); 105 | ensureLaunchConfiguration(newConfig); 106 | config.configurations.push(newConfig); 107 | fs.writeFileSync(configPath, JSON.stringify(config, null, 4)); 108 | } 109 | } 110 | } 111 | } 112 | 113 | function ensureLaunchConfiguration(config) { 114 | let changed = false; 115 | for (const k in launchConfigConsts) { 116 | if (config[k] !== launchConfigConsts[k]) { 117 | config[k] = launchConfigConsts[k]; 118 | changed = true; 119 | } 120 | } 121 | if (settings.moduleUUID) { 122 | if (config.targetModuleUuid !== settings.moduleUUID) { 123 | config.targetModuleUuid = settings.moduleUUID; 124 | changed = true; 125 | } 126 | } else if (config.targetModuleUuid) { 127 | delete config.targetModuleUuid; 128 | changed = true; 129 | } 130 | 131 | return changed; 132 | } 133 | 134 | function entryPathify(str) { 135 | return str.split("/").slice(1).join("/"); 136 | } 137 | const bundle = settings.buildOptions.bundle; 138 | let entry = ""; 139 | const out = settings.outfile ?? "BP/scripts/main.js" 140 | settings.buildOptions.outfile = out; 141 | entry = entryPathify(out); 142 | if (!bundle) { 143 | entry = entryPathify(out); 144 | delete settings.buildOptions.outfile; 145 | settings.buildOptions.outdir = settings.outdir ?? "BP/scripts"; 146 | } 147 | 148 | const external = bundle ? settings.buildOptions.external : []; 149 | 150 | // Ensure types for settings 151 | const typeMap = { 152 | buildOptions: "object", 153 | moduleUUID: "string", 154 | modules: "array", 155 | outfile: "string", 156 | outdir: "string", 157 | moduleType: "string", 158 | language: "string", 159 | manifest: "string", 160 | }; 161 | const throwTypeError = (k) => { 162 | throw new TypeError(`${k}: ${JSON.stringify(settings[k])} is not an ${typeMap[k]}`); 163 | }; 164 | for (let k in typeMap) { 165 | if (typeMap[k] === "array") { 166 | if (!Array.isArray(settings[k])) throwTypeError(k); 167 | } else if (typeMap[k] === "object") { 168 | if (typeof settings[k] !== "object" || Array.isArray(settings[k])) throwTypeError(k); 169 | } else if (typeof settings[k] !== typeMap[k]) throwTypeError(k); 170 | } 171 | 172 | // Add script module dependencies to manifest 173 | const parsedModules = []; 174 | for (let module of settings.modules) { 175 | const match = module.match(/(@[^@]+)@(.+)/); 176 | if (!match) { 177 | throw "Invalid module provided in settings, please follow the format '@' or ''"; 178 | } 179 | const name = match[1]; 180 | let version = match[2]; 181 | 182 | if (!version) throw `No version provided for module '${name}'`; 183 | const versionMatch = version.match(/\d+\.\d+\.\d+(?:-beta)?/); 184 | if (!versionMatch || versionMatch[0] !== version) { 185 | throw `Version '${version}' is not a valid module version`; 186 | } 187 | external.push(name); 188 | parsedModules.push({ name, version }); 189 | } 190 | 191 | if (!settings.disableManifestModification) { 192 | console.log("Modifying manifest.json"); 193 | const manifestStr = fs.readFileSync("BP/manifest.json", "utf8"); 194 | /** @type {{ 195 | format_version: number; 196 | header: { 197 | name: string; 198 | description: string; 199 | uuid: string; 200 | version: [number, number, number]; 201 | min_engine_version: [number, number, number]; 202 | }; 203 | modules: { 204 | description?: string; 205 | type: string; 206 | language?: string; 207 | entry?: string; 208 | uuid: string; 209 | version: string | [number, number, number]; 210 | }[]; 211 | dependencies: ({module_name: string; version: string} | {uuid: string; version: [number, number, number]})[]; 212 | }} */ 213 | const manifest = JSON.parse(manifestStr); 214 | 215 | // Ensure manifest contains dependencies array 216 | if (!manifest.dependencies) manifest.dependencies = []; 217 | 218 | // Add script module dependencies to manifest 219 | for (let module of parsedModules) { 220 | const name = module.name; 221 | let version = module.version; 222 | 223 | let exists = false; 224 | if ( 225 | manifest.dependencies.findIndex((v) => { 226 | if (typeof v.version !== "string") return; 227 | //@ts-ignore 228 | if (v.module_name !== name) return; 229 | exists = true; 230 | return v.version !== version; 231 | }) !== -1 232 | ) { 233 | throw `Module '${name}' already exists in manifest with a different version`; 234 | } 235 | 236 | if (!exists) { 237 | manifest.dependencies.push({ 238 | module_name: name, 239 | version: version, 240 | }); 241 | } else { 242 | console.warn(`Module ${name} already exists in the manifest and will not be added again`); 243 | } 244 | } 245 | 246 | // Ensure manifest contains a modules array 247 | if (!manifest.modules) manifest.modules = []; 248 | 249 | // Add script module to manifest 250 | let hasModule = false; 251 | if ( 252 | manifest.modules.findIndex((v) => { 253 | if (v.type !== settings.moduleType) return; 254 | hasModule = true; 255 | if (v.uuid !== settings.moduleUUID) return true; 256 | if (v.entry !== entry) return true; 257 | }) !== -1 258 | ) { 259 | throw `Existing manifest module of type ${settings.moduleType} found with different properties`; 260 | } 261 | 262 | if (!hasModule) { 263 | manifest.modules.push({ 264 | description: "Scripting module", 265 | type: settings.moduleType, 266 | uuid: settings.moduleUUID, 267 | language: settings.language, 268 | version: [0, 0, 1], 269 | entry, 270 | }); 271 | } else { 272 | console.warn(`Existing manifest module found with matching properties and will not be added again`); 273 | } 274 | 275 | console.log("Saving manifest.json"); 276 | fs.writeFileSync(settings.manifest, JSON.stringify(manifest, null, 4)); 277 | } 278 | 279 | // After the build, generate a mapping JSON file 280 | const { SourceMapConsumer, SourceMapGenerator } = require("source-map"); 281 | 282 | /** 283 | * This function reads the sourcemap file generated by esbuild (assumed to be at 284 | * `.map`), decodes its mappings, and writes out a JSON file (named 285 | * `sourceMapping.json` in the same directory) where each generated line maps to an 286 | * array of original source positions. 287 | */ 288 | async function generateSourceMapping() { 289 | const outPath = path.resolve(settings.outfile ?? "BP/scripts/main.js"); 290 | const sourcemapPath = outPath + ".map"; // e.g. /absolute/path/to/BP/scripts/main.js.map 291 | 292 | if (!fs.existsSync(sourcemapPath)) { 293 | console.warn("Sourcemap file not found: " + sourcemapPath); 294 | return; 295 | } 296 | 297 | const sourcemapContent = fs.readFileSync(sourcemapPath, "utf8"); 298 | 299 | await SourceMapConsumer.with(sourcemapContent, null, consumer => { 300 | // Create an object mapping each generated line to a single mapping object 301 | // containing just the source file and the original line. 302 | const mapping = {}; 303 | 304 | consumer.eachMapping(mappingEntry => { 305 | // Only process mappings with an original source. 306 | if (!mappingEntry.source) return; 307 | 308 | // Remove the unwanted prefix from the source path. 309 | let source = mappingEntry.source; 310 | // Get index of '/data/gametests/' and slice the string from the end of it. 311 | const prefix = "/data/gametests/"; 312 | if (source.indexOf(prefix) !== -1) { 313 | source = source.slice(source.indexOf(prefix) + prefix.length); 314 | } 315 | 316 | // For each generated line, record only the first mapping encountered. 317 | const genLine = mappingEntry.generatedLine; 318 | if (mapping[genLine] === undefined) { 319 | mapping[genLine] = { 320 | source: source, 321 | originalLine: mappingEntry.originalLine 322 | }; 323 | } 324 | }); 325 | mapping.metadata = { 326 | filePath: (settings.outfile ?? "BP/scripts/main.js").substring('BP/scripts/'.length), 327 | offset: 1 328 | } 329 | // Inject the source mapping file into the generated JS file 330 | const fileContents = 331 | "var globalSourceMapping = " + JSON.stringify(mapping) + ";\n" + fs.readFileSync(outPath); 332 | fs.writeFileSync(outPath, fileContents); 333 | console.log("Injected source mapping file to " + outPath); 334 | }); 335 | } 336 | /** 337 | * Adjusts the sourcemap file so that all generated line numbers are offset by `lineOffset`. 338 | * 339 | * @param {string} mapPath - The path to the sourcemap file (e.g. 'BP/scripts/main.js.map'). 340 | * @param {number} lineOffset - The number of lines to offset the generated mappings. 341 | */ 342 | async function adjustSourceMap(mapPath, lineOffset) { 343 | // Read the original sourcemap content. 344 | const mapContent = fs.readFileSync(mapPath, 'utf8'); 345 | 346 | // Create a consumer for the original sourcemap. 347 | await SourceMapConsumer.with(mapContent, null, consumer => { 348 | // Create a new generator for the updated sourcemap. 349 | const generator = new SourceMapGenerator({ file: consumer.file }); 350 | 351 | // Iterate over each mapping in the original sourcemap. 352 | consumer.eachMapping(mapping => { 353 | // Add the mapping to the new generator with an offset on the generated line. 354 | generator.addMapping({ 355 | generated: { 356 | line: mapping.generatedLine + lineOffset, 357 | column: mapping.generatedColumn, 358 | }, 359 | original: mapping.originalLine != null ? { 360 | line: mapping.originalLine, 361 | column: mapping.originalColumn, 362 | } : null, 363 | source: mapping.source, 364 | name: mapping.name, 365 | }); 366 | }); 367 | 368 | // Copy over any source content if available. 369 | consumer.sources.forEach(source => { 370 | const content = consumer.sourceContentFor(source); 371 | if (content) { 372 | generator.setSourceContent(source, content); 373 | } 374 | }); 375 | 376 | // Convert the generator back to a sourcemap string. 377 | const newMap = generator.toString(); 378 | fs.writeFileSync(mapPath, newMap, 'utf8'); 379 | console.log(`Source map adjusted by an offset of ${lineOffset} and written to ${mapPath}`); 380 | }); 381 | } 382 | 383 | function runInShell(cmd, cwd, showStdio) { 384 | return spawnSync("cmd", ["/c", cmd], { 385 | cwd: cwd, 386 | stdio: showStdio ? 'inherit' : undefined 387 | }) 388 | } 389 | 390 | // Check if packages need to be installed 391 | const result = runInShell("npm ls --production --depth=0 --silent", path.join(process.cwd(), "data", "gametests"), false); 392 | if (result.status === 1) { 393 | console.log("Installing packages..."); 394 | runInShell("npm i", path.join(process.cwd(), "data", "gametests"), true); 395 | runInShell("npm i", path.join(projectRoot, "packs", "data", "gametests"), true); 396 | } 397 | 398 | glob(settings.buildOptions.entryPoints).then(async (paths) => { 399 | settings.buildOptions.entryPoints = paths; 400 | require("./moveFiles.js"); 401 | await require("./build.js").run(settings); 402 | 403 | // If debugBuild and injectSourceMapping is enabled, inject additional mapping data 404 | if (settings.debugBuild && settings.injectSourceMapping) { 405 | await generateSourceMapping(); 406 | await adjustSourceMap(settings.outfile + ".map", 1); 407 | } 408 | }); 409 | -------------------------------------------------------------------------------- /gametests/moveFiles.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const DIRECTORY = "data/gametests/"; 4 | 5 | if (!fs.existsSync(DIRECTORY + "extra_files")) 6 | return console.log("No extra files, skipping step"); 7 | 8 | /** 9 | * Source: https://stackoverflow.com/a/22185855/6459649 10 | * Look ma, it's cp -R. 11 | * @param {string} src The path to the thing to copy. 12 | * @param {string} dest The path to the new copy. 13 | */ 14 | var copyRecursiveSync = function (src, dest) { 15 | var exists = fs.existsSync(src); 16 | var stats = exists && fs.statSync(src); 17 | var isDirectory = exists && stats.isDirectory(); 18 | if (isDirectory) { 19 | if (!fs.existsSync(dest)) { 20 | fs.mkdirSync(dest); 21 | } 22 | fs.readdirSync(src).forEach(function (childItemName) { 23 | copyRecursiveSync( 24 | path.join(src, childItemName), 25 | path.join(dest, childItemName) 26 | ); 27 | }); 28 | } else { 29 | fs.copyFileSync(src, dest); 30 | } 31 | }; 32 | 33 | console.log("Copying extra files"); 34 | copyRecursiveSync(DIRECTORY + "extra_files", "./"); 35 | -------------------------------------------------------------------------------- /gametests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gametests", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "postinstall": "node ./setup.js" 7 | }, 8 | "dependencies": { 9 | "esbuild": "^0.19.8", 10 | "glob": "^10.3.1", 11 | "json5": "^2.2.1", 12 | "source-map": "^0.7.4" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /gametests/readme.md: -------------------------------------------------------------------------------- 1 | # Gametests 2 | 3 | This filter is for injecting into a pack a gametest module and code mainly for actual map testing. 4 | 5 | The advantage of using this specific filter is that without running this filter, no gametest content will be in the final pack (for example, dev and QA profile might include gametests and package profile might not). 6 | 7 | ## Getting the Filter 8 | 9 | Install with: `regolith install gametests`. After that, you can place the filter into one of your profiles. 10 | 11 | ```json 12 | { 13 | "filter": "gametests", 14 | // Following settings are set by default 15 | "settings": { 16 | "moduleUUID": null, 17 | "modules": ["mojang-gametest", "mojang-minecraft"], 18 | "outfile": "BP/scripts/main.js", 19 | "manifest": "BP/manifest.json", 20 | "buildOptions": { 21 | "entryPoints": ["data/gametests/src/main.ts"], 22 | "target": "es2020", 23 | "format": "esm", 24 | "bundle": true, 25 | "minify": true 26 | } 27 | } 28 | } 29 | ``` 30 | 31 | ```json 32 | { 33 | "filter": "gametests", 34 | // Following settings are set by default 35 | "settings": { 36 | "moduleUUID": null, 37 | "modules": ["mojang-gametest", "mojang-minecraft"], 38 | "outfile": "BP/scripts/main.js", 39 | "outdir": "BP/scripts", 40 | "manifest": "BP/manifest.json", 41 | "buildOptions": { 42 | "entryPoints": ["data/gametests/src/**/*.ts"], 43 | "target": "es2020", 44 | "format": "esm", 45 | "bundle": false, 46 | "minify": false 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | ## Documentation 53 | 54 | This filter will: 55 | 56 | - build the project into a single JS file using esbuild 57 | - copy compiled code to behavior pack 58 | - copy all files from `extra_files` folder into behavior pack (useful for test structures) 59 | - inject gametest module and required dependencies into behavior pack manifest 60 | 61 | The filter also has included support for importing JSON files using JSON5 parser. 62 | 63 | ## Settings 64 | 65 | | Setting | Type | Default | Description | 66 | |-------------------------------|----------------------------------------------------------|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------| 67 | | `buildOptions` | [buildOptions](https://esbuild.github.io/api/#build-api) | [Default Build Options](#default-build-options) | Specifies build options for esbuild | 68 | | `moduleUUID` | string | Random UUID generated the first time the filter is ran. | The UUID to place inside the manifest module | 69 | | `modules` | string[] | ["@minecraft/server@1.0.0"] | The scripting modules to inject as dependencies, follows the format '``@``' | 70 | | `outfile` | string | "BP/scripts/main.js" | The path to place the built script file at when buildOptions.bundle is enabled. This property is also used as the entry point for the script module | 71 | | `outdir` | string | "BP/scripts" | The path to build to when buildOptions.bundle is disabled | 72 | | `moduleType` | string | "script" | The manifest module type to inject | 73 | | `manifest` | string | "BP/manifest.json" | The manifest to edit | 74 | | `debugBuild` | boolean | false | Enables source maps and adds launch configuration to `.vscode/launch.json` if it exists | 75 | | `injectSourceMapping` | boolean | false | Injects source mapping into a compiled script file. Requires debugBuild to be enabled. | 76 | | `disableManifestModification` | boolean | false | Disables adding dependencies and script module to the manifest. | 77 | 78 | #### Default Build Options 79 | 80 | ```js 81 | { 82 | entryPoints: ["data/gametests/src/main.ts"], 83 | target: "es2020", 84 | format: "esm", 85 | bundle: true, 86 | minify: true 87 | } 88 | ``` 89 | 90 | ## Modifying config with a JS file 91 | 92 | You can modify the settings of this filter by creating a file named `*.esbuild.config.js` in `data/gametests` folder. The file should export a function `config` that takes in the current settings. Other filters ran before this filter can place their config files in data/gametests folder and they will be loaded. The config files are loaded in alphabetical order, so if you want to override a setting, make sure your config file is loaded after the other filter's config file. 93 | 94 | ```js 95 | // Example config file 96 | module.exports = { 97 | config: (settings) => { 98 | // Modify settings here 99 | settings.buildOptions.entryPoints = ["data/gametests/extra_src/**/*.ts"]; 100 | }, 101 | }; 102 | ``` 103 | 104 | ## Changelog 105 | ### 1.7.1 106 | - Improved cleaning the path of the source file. 107 | ### 1.7.0 108 | - Added `injectSourceMapping` setting, that injects source mapping into a compiled script file. Requires debugBuild to be enabled. The default value is `false`. 109 | - Updated list of modules in the schema. 110 | ### 1.6.1 111 | - Renamed `debug_build` to `debugBuild` in the schema to match the other settings' name. 112 | - Added `disableManifestModification` setting, that disables adding dependencies and script module to the manifest. The default value is `false`. 113 | ### 1.6.0 114 | - Added `debug_build` setting, that helps with connecting the debugger to the Minecraft client. When enabled, the build will include source maps and will add a correct launch configuration to `.vscode/launch.json` if it exists. The default value is `false`. 115 | - Fixed generating the module UUID, when `moduleUUID` is not set in the settings. 116 | 117 | ### 1.5.3 118 | Fixed the issue that caused the filter to fail when used in Regolith that uses the `use_project_app_data_storage` option (issue #53). 119 | 120 | ### 1.5.2 121 | 122 | - Updated the default tsconfig to include `resolveJsonModule` set to `true`. 123 | 124 | ### 1.5.1 125 | 126 | - Updated the example script to use the new `@minecraft/server` and `@minecraft/server-gametest` versions 127 | 128 | ### 1.5.0 129 | 130 | - Added a way to modify settings with a JS file. The file should be named `*.esbuild.config.js` and export a function `config` that takes in the current settings. Other filters ran before this filter can place their config files in data/gametests folder and they will be loaded. The config files are loaded in alphabetical order, so if you want to override a setting, make sure your config file is loaded after the other filter's config file. 131 | - Added a setup script, that will try to install dependencies of the script API module. 132 | 133 | ### 1.4.2 134 | 135 | - Updated esbuild to 0.19.8 136 | 137 | ### 1.4.1 138 | 139 | - Added missing `outdir` and `outfile` defaults 140 | 141 | ### 1.4.0 142 | 143 | - Swapped from a hardcoded list of supported module versions to a pattern match 144 | - Made specifying module versions in settings required 145 | - Added glob support to `buildOptions.entryPoints` 146 | - Added support for `outdir`, used when `buildOptions.bundle` is disabled 147 | 148 | ### 1.3.3 149 | 150 | - Added new `@minecraft/server` and `@minecraft/server-ui` versions 151 | - Fixed modules not being added to `buildOptions.external` if it was already specified in the filter settings 152 | 153 | ### 1.3.2 154 | 155 | - Added new `@minecraft/server` versions 156 | 157 | ### 1.3.1 158 | 159 | - Fixed full module string being added to the `buildOptions.external` property instead of just the module name 160 | 161 | ### 1.3.0 162 | 163 | - `settings.modules` now takes an array of strings in the format of `@` or ``, this change allows you to use a specific version of a script module 164 | - A warning is now printed when using an unknown module rather than throwing an error. 165 | - An error is thrown if you do not specify a version with an unknown module 166 | - Updated to use new dependency format `{module_name: string, version: string}` 167 | - Updated example script to use 1.19.60 beta script modules 168 | - Updated the schema to include some enums for module suggestions in VSCode 169 | - Added handling for attempting to add modules when manifest modules already exist 170 | 171 | ### 1.2.0 172 | 173 | Update versioning introduced in 1.19.30.20 beta 174 | 175 | ### 1.1.0 176 | 177 | - Removed the modules from data as it was causing long run times, likely due to needing to move all those files when regolith runs. The only modules kept were the mojang- typings. This change should decrease the amount of time regolith takes to run when using this filter. 178 | - Removed eslint and such since the modules were removed, kept `.prettierrc.json` as the vscode extension works with it 179 | - Moved building script to filter folder instead of data folder since the esbuild and json5 node_modules are no longer stored in data 180 | - Added a check to moving extra_files as it would cause an error before if a user decided to remove the folder 181 | 182 | Following changes are in preparation for client scripts, if they ever come out 183 | 184 | - Added manifest setting to allow the user to specify the manifest path 185 | - extra_files now needs a folder to specify whether to output to BP or RP, so what was previously `extra_files/test/jsonFile.json` would now need to be `extra_files/BP/test/jsonFile.json` 186 | 187 | These changes also fix the infinite loop issue cause by the post-install script in #36 (the script no longer exists as the node modules are no longer installed in the data folder by default) 188 | 189 | ### 1.0.3 190 | 191 | - Added `settings.moduleType` option to specify the type of module (`javascript` before 1.19 and `script` after 1.19, `javascript` by default) 192 | 193 | ### 1.0.2 194 | 195 | - Fixed `settings.buildOptions.outfile` referencing the invalid setting `settings.out`, now references `settings.outfile` instead [#35](https://github.com/Bedrock-OSS/regolith-filters/pull/35) 196 | - `settings.buildOptions` should now properly merge with defaults rather than entirely replacing them [#35](https://github.com/Bedrock-OSS/regolith-filters/pull/35) 197 | 198 | ### 1.0.1 199 | 200 | - Added `outfile` setting, used to determine where the resulting build file will be located [#33](https://github.com/Bedrock-OSS/regolith-filters/pull/33) 201 | - Added `modules` setting to choose which gametest modules to inject into the manifest dependencies, as well as which to allow during building [#33](https://github.com/Bedrock-OSS/regolith-filters/pull/33) 202 | - customizing `buildOptions` will now overwrite each individual property, rather than overwriting `buildOptions` as a whole. This allows for use cases where a user may not want to entirely overwrite the `buildOptions` [#33](https://github.com/Bedrock-OSS/regolith-filters/pull/33) 203 | 204 | ### 1.0.0 205 | 206 | The first release of Gametests filter. 207 | -------------------------------------------------------------------------------- /gametests/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "gametests", 4 | "type": "object", 5 | "properties": { 6 | "buildOptions": { 7 | "type": "object", 8 | "default": { 9 | "entryPoints": ["data/gametests/src/main.ts"], 10 | "target": "es2020", 11 | "format": "esm", 12 | "bundle": true, 13 | "minify": true 14 | }, 15 | "additionalProperties": true, 16 | "description": "Specifies build options for esbuild." 17 | }, 18 | "moduleUUID": { 19 | "type": "string", 20 | "description": "The UUID to place inside the manifest module." 21 | }, 22 | "modules": { 23 | "type": "array", 24 | "default": ["@minecraft/server@1.16.0"], 25 | "description": "The scripting modules to inject as dependencies.", 26 | "uniqueItems": true, 27 | "items": { 28 | "anyOf": [ 29 | { 30 | "type": "string", 31 | "enum": [ 32 | "@minecraft/server@1.0.0", 33 | "@minecraft/server@1.1.0", 34 | "@minecraft/server@1.2.0", 35 | "@minecraft/server@1.3.0", 36 | "@minecraft/server@1.4.0", 37 | "@minecraft/server@1.5.0", 38 | "@minecraft/server@1.6.0", 39 | "@minecraft/server@1.7.0", 40 | "@minecraft/server@1.8.0", 41 | "@minecraft/server@1.9.0", 42 | "@minecraft/server@1.10.0", 43 | "@minecraft/server@1.11.0", 44 | "@minecraft/server@1.12.0", 45 | "@minecraft/server@1.13.0", 46 | "@minecraft/server@1.14.0", 47 | "@minecraft/server@1.15.0", 48 | "@minecraft/server@1.16.0", 49 | "@minecraft/server@1.17.0-beta", 50 | 51 | "@minecraft/server-ui@1.0.0", 52 | "@minecraft/server-ui@1.1.0", 53 | "@minecraft/server-ui@1.2.0", 54 | "@minecraft/server-ui@1.3.0", 55 | "@minecraft/server-ui@1.4.0-beta", 56 | 57 | "@minecraft/server-gametest@1.0.0-beta", 58 | 59 | "@minecraft/server-admin@1.0.0-beta", 60 | 61 | "@minecraft/server-net@1.0.0-beta", 62 | 63 | "@minecraft/server-editor@0.1.0-beta", 64 | 65 | "@minecraft/debug-utilities@1.0.0-beta", 66 | 67 | "@minecraft/diagnostics@1.0.0-beta" 68 | ], 69 | "description": "Known scripting module" 70 | }, 71 | { 72 | "type": "string", 73 | "description": "Scripting module" 74 | } 75 | ] 76 | } 77 | }, 78 | "outfile": { 79 | "type": "string", 80 | "default": "BP/scripts/main.js", 81 | "description": "The path to place the built script file at when buildOptions.bundle is enabled. This property is also used as the entry point for the script module." 82 | }, 83 | "outdir": { 84 | "type": "string", 85 | "default": "BP/scripts", 86 | "description": "The path to build to when buildOptions.bundle is disabled." 87 | }, 88 | "moduleType": { 89 | "type": "string", 90 | "default": "script", 91 | "description": "The manifest module type to inject" 92 | }, 93 | "manifest": { 94 | "type": "string", 95 | "default": "BP/manifest.json", 96 | "description": "The manifest to edit" 97 | }, 98 | "debugBuild": { 99 | "type": "boolean", 100 | "default": false, 101 | "description": "Enables debug mode for the build. This will enable source maps and add debug launch configuration to `.vscode/launch.json`." 102 | }, 103 | "injectSourceMapping": { 104 | "type": "boolean", 105 | "default": false, 106 | "description": "Injects source mapping into a compiled script file. Requires debugBuild to be enabled." 107 | }, 108 | "disableManifestModification": { 109 | "type": "boolean", 110 | "default": false, 111 | "description": "Disables adding dependencies and script module to the manifest." 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /gametests/setup.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const json5 = require("json5"); 4 | const child_process = require("child_process"); 5 | 6 | //Load config.json file 7 | // The path to the config.json directory is in the ROOT_DIR environment variable 8 | const rootPath = process.env.ROOT_DIR; 9 | if (!rootPath) { 10 | console.warn("ROOT_DIR environment variable not found"); 11 | return; 12 | } 13 | const configPath = path.resolve(rootPath, "config.json"); 14 | const config = json5.parse(fs.readFileSync(configPath, "utf-8")); 15 | 16 | // We need to get path to the data folder, which is in #/regolith/dataPath 17 | const dataPath = config.regolith.dataPath; 18 | 19 | if (!dataPath) { 20 | throw new Error("dataPath not found in config.json"); 21 | } 22 | 23 | const gametestsDataPath = path.resolve(path.resolve(rootPath, dataPath), "gametests"); 24 | 25 | if (fs.existsSync(gametestsDataPath)) { 26 | console.log("Installing dependencies for gametests in", gametestsDataPath); 27 | child_process.execSync(`npm install`, { 28 | cwd: gametestsDataPath, 29 | stdio: "inherit" 30 | }); 31 | } else { 32 | console.log("gametests data folder not found, execute 'npm install' manually in", gametestsDataPath); 33 | } -------------------------------------------------------------------------------- /json_cleaner/completion.md: -------------------------------------------------------------------------------- 1 | # Json Cleaner 2 | 3 | This small utility filter is intended to be used as the first filter in your Regolith Project. It will go through your packs, reading and saving every json file. 4 | 5 | This is useful since this will strip all the comments from the json, allowing future filters to read the json safely without worrying about comments. -------------------------------------------------------------------------------- /json_cleaner/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Removes comments from all json files in the project. Useful, since some filters cannot understand files with comments.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./json_cleaner.py", 7 | "name": "Cleaning JSON files for easier reading" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /json_cleaner/json_cleaner.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import json 3 | 4 | def get_json_from_file(fh): 5 | 6 | try: 7 | # If possible, read the file as JSON 8 | return json.loads(fh) 9 | except: 10 | # If not, read the file as a string, and try to parse it as JSON 11 | contents = "" 12 | for line in fh.splitlines(): 13 | cleanedLine = line.split("//", 1)[0] 14 | if len(cleanedLine) > 0 and line.endswith("\n") and "\n" not in cleanedLine: 15 | cleanedLine += "\n" 16 | contents += cleanedLine 17 | while "/*" in contents: 18 | preComment, postComment = contents.split("/*", 1) 19 | contents = preComment + postComment.split("*/", 1)[1] 20 | return json.loads(contents) 21 | 22 | def main(): 23 | folders = ('BP', 'RP') 24 | for folder in folders: 25 | for file in glob.glob(folder + "/**/*.json", recursive=True): 26 | try: 27 | with open(file, "r", encoding="utf-8") as fh: 28 | json_data = get_json_from_file(fh.read()) 29 | 30 | with open(file, "w", encoding="utf-8") as fh: 31 | json.dump(json_data, fh, indent=2, ensure_ascii=False) 32 | except Exception as e: 33 | print("Error in file: " + file) 34 | print(e) 35 | raise 36 | 37 | main() -------------------------------------------------------------------------------- /json_cleaner/readme.md: -------------------------------------------------------------------------------- 1 | # Json Cleaner 2 | 3 | This small utility filter is intended to be used as the first filter in your Regolith Project. It will go through your packs, reading and saving every json file. 4 | 5 | This is useful since this will strip all the comments from the json, allowing future filters to read the json safely without worrying about comments. 6 | 7 | ## Using the Filter 8 | 9 | ```json 10 | { 11 | "filter": "json_cleaner" 12 | } 13 | ``` 14 | 15 | # Changelog 16 | 17 | ### 1.0.0 18 | 19 | The initial release of the Json Cleaner filter. 20 | 21 | ### 1.1.0 22 | 23 | Fix encoding issues in json_cleaner and handle errors better. 24 | 25 | ### 1.1.1 26 | 27 | Fix encoding when saving file in json_cleaner. -------------------------------------------------------------------------------- /json_convert/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /json_convert/completion.md: -------------------------------------------------------------------------------- 1 | # JSON Convert 2 | 3 | This filter tries to convert all JSON files in your addon from [JSON5](https://json5.org/) or [Hjson](https://hjson.github.io/) to standard JSON. 4 | 5 | ## Getting the Filter 6 | 7 | Install with: `regolith install json_convert`. After that, you can place the filter into one of your profiles. 8 | 9 | ```json 10 | { 11 | "filter": "json_convert" 12 | } 13 | ``` 14 | 15 | ## Documentation 16 | 17 | 18 | By default, any file with extensions `.json5`, `.hjson` and `.json` will be converted. You can change this by adding a `include` array to the filter settings. The array should contain a list of glob patterns that will be used to match files. For example, to only convert `.json5` files, you can use the following settings: 19 | 20 | ```json 21 | { 22 | "filter": "json_convert", 23 | "settings": { 24 | "include": ["**/*.json5"] 25 | } 26 | } 27 | ``` 28 | 29 | By default, all files are pretty-printed. You can disable this by setting `pretty` to `false` in the filter settings. 30 | 31 | ```json 32 | { 33 | "filter": "json_convert", 34 | "settings": { 35 | "pretty": false 36 | } 37 | } 38 | ``` 39 | 40 | To change extension of converted files, use `extension_map` setting. For example, to convert all `.json5` files to `.json` files, you can use the following settings: 41 | 42 | ```json 43 | { 44 | "filter": "json_convert", 45 | "settings": { 46 | "extension_map": { 47 | "json5": "json" 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | By default all `json5` and `hjson` extenstions are converted to `json` extension and all other files keep their original extension. 54 | 55 | 56 | ## Changelog 57 | 58 | ### 1.0.0 59 | 60 | The first release of JSON Convert. 61 | -------------------------------------------------------------------------------- /json_convert/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Converts JSON5 and Hjson files into standard JSON files.", 3 | "filters": [ 4 | { 5 | "runWith": "nodejs", 6 | "script": "./main.js", 7 | "name": "Converting JSON5 and Hjson files into standard JSON files" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /json_convert/main.js: -------------------------------------------------------------------------------- 1 | const glob = require("glob"); 2 | const fs = require("fs"); 3 | const JSON5 = require("json5"); 4 | const Hjson = require("hjson"); 5 | 6 | const defSettings = { 7 | include: ["**/*.json5", "**/*.hjson", "**/*.json"], 8 | pretty: true, 9 | extension_map: { 10 | "json5": "json", 11 | "hjson": "json" 12 | } 13 | }; 14 | 15 | const argParsed = process.argv[2] ? JSON.parse(process.argv[2]) : {}; 16 | const settings = Object.assign({}, defSettings, argParsed); 17 | 18 | for (const pattern of settings.include) { 19 | glob(pattern, null, function (er, files) { 20 | files.forEach(function (file) { 21 | fs.readFile(file, "utf8", function (err, data) { 22 | let resultName = file.substr(0, file.lastIndexOf(".")) + "." + (settings.extension_map[file.substr(file.lastIndexOf(".") + 1)] || file.substr(file.lastIndexOf(".") + 1)); 23 | console.log("Converting " + file + " into " + resultName); 24 | let output = null; 25 | try { 26 | output = Hjson.parse(data); 27 | } catch(e) { 28 | try { 29 | output = JSON5.parse(data); 30 | } catch(e) { 31 | console.log("Failed to parse " + file); 32 | return; 33 | } 34 | } 35 | fs.unlinkSync(file); 36 | fs.writeFileSync( 37 | resultName, 38 | JSON.stringify(output, null, settings.pretty ? 4 : 0) 39 | ); 40 | }); 41 | }); 42 | }); 43 | } -------------------------------------------------------------------------------- /json_convert/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json_convert", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "json_convert", 9 | "version": "1.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "glob": "^7.1.7", 13 | "hjson": "^3.2.2", 14 | "json5": "^2.2.3", 15 | "path": "^0.12.7" 16 | } 17 | }, 18 | "node_modules/balanced-match": { 19 | "version": "1.0.2", 20 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 21 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 22 | }, 23 | "node_modules/brace-expansion": { 24 | "version": "1.1.11", 25 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 26 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 27 | "dependencies": { 28 | "balanced-match": "^1.0.0", 29 | "concat-map": "0.0.1" 30 | } 31 | }, 32 | "node_modules/concat-map": { 33 | "version": "0.0.1", 34 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 35 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 36 | }, 37 | "node_modules/fs.realpath": { 38 | "version": "1.0.0", 39 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 40 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 41 | }, 42 | "node_modules/glob": { 43 | "version": "7.2.3", 44 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 45 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 46 | "dependencies": { 47 | "fs.realpath": "^1.0.0", 48 | "inflight": "^1.0.4", 49 | "inherits": "2", 50 | "minimatch": "^3.1.1", 51 | "once": "^1.3.0", 52 | "path-is-absolute": "^1.0.0" 53 | }, 54 | "engines": { 55 | "node": "*" 56 | }, 57 | "funding": { 58 | "url": "https://github.com/sponsors/isaacs" 59 | } 60 | }, 61 | "node_modules/hjson": { 62 | "version": "3.2.2", 63 | "resolved": "https://registry.npmjs.org/hjson/-/hjson-3.2.2.tgz", 64 | "integrity": "sha512-MkUeB0cTIlppeSsndgESkfFD21T2nXPRaBStLtf3cAYA2bVEFdXlodZB0TukwZiobPD1Ksax5DK4RTZeaXCI3Q==", 65 | "bin": { 66 | "hjson": "bin/hjson" 67 | } 68 | }, 69 | "node_modules/inflight": { 70 | "version": "1.0.6", 71 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 72 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 73 | "dependencies": { 74 | "once": "^1.3.0", 75 | "wrappy": "1" 76 | } 77 | }, 78 | "node_modules/inherits": { 79 | "version": "2.0.4", 80 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 81 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 82 | }, 83 | "node_modules/json5": { 84 | "version": "2.2.3", 85 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 86 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", 87 | "bin": { 88 | "json5": "lib/cli.js" 89 | }, 90 | "engines": { 91 | "node": ">=6" 92 | } 93 | }, 94 | "node_modules/minimatch": { 95 | "version": "3.1.2", 96 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 97 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 98 | "dependencies": { 99 | "brace-expansion": "^1.1.7" 100 | }, 101 | "engines": { 102 | "node": "*" 103 | } 104 | }, 105 | "node_modules/once": { 106 | "version": "1.4.0", 107 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 108 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 109 | "dependencies": { 110 | "wrappy": "1" 111 | } 112 | }, 113 | "node_modules/path": { 114 | "version": "0.12.7", 115 | "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", 116 | "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", 117 | "dependencies": { 118 | "process": "^0.11.1", 119 | "util": "^0.10.3" 120 | } 121 | }, 122 | "node_modules/path-is-absolute": { 123 | "version": "1.0.1", 124 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 125 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 126 | "engines": { 127 | "node": ">=0.10.0" 128 | } 129 | }, 130 | "node_modules/process": { 131 | "version": "0.11.10", 132 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 133 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", 134 | "engines": { 135 | "node": ">= 0.6.0" 136 | } 137 | }, 138 | "node_modules/util": { 139 | "version": "0.10.4", 140 | "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", 141 | "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", 142 | "dependencies": { 143 | "inherits": "2.0.3" 144 | } 145 | }, 146 | "node_modules/util/node_modules/inherits": { 147 | "version": "2.0.3", 148 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 149 | "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" 150 | }, 151 | "node_modules/wrappy": { 152 | "version": "1.0.2", 153 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 154 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 155 | } 156 | }, 157 | "dependencies": { 158 | "balanced-match": { 159 | "version": "1.0.2", 160 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 161 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" 162 | }, 163 | "brace-expansion": { 164 | "version": "1.1.11", 165 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 166 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 167 | "requires": { 168 | "balanced-match": "^1.0.0", 169 | "concat-map": "0.0.1" 170 | } 171 | }, 172 | "concat-map": { 173 | "version": "0.0.1", 174 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 175 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" 176 | }, 177 | "fs.realpath": { 178 | "version": "1.0.0", 179 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 180 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" 181 | }, 182 | "glob": { 183 | "version": "7.2.3", 184 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 185 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 186 | "requires": { 187 | "fs.realpath": "^1.0.0", 188 | "inflight": "^1.0.4", 189 | "inherits": "2", 190 | "minimatch": "^3.1.1", 191 | "once": "^1.3.0", 192 | "path-is-absolute": "^1.0.0" 193 | } 194 | }, 195 | "hjson": { 196 | "version": "3.2.2", 197 | "resolved": "https://registry.npmjs.org/hjson/-/hjson-3.2.2.tgz", 198 | "integrity": "sha512-MkUeB0cTIlppeSsndgESkfFD21T2nXPRaBStLtf3cAYA2bVEFdXlodZB0TukwZiobPD1Ksax5DK4RTZeaXCI3Q==" 199 | }, 200 | "inflight": { 201 | "version": "1.0.6", 202 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 203 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 204 | "requires": { 205 | "once": "^1.3.0", 206 | "wrappy": "1" 207 | } 208 | }, 209 | "inherits": { 210 | "version": "2.0.4", 211 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 212 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 213 | }, 214 | "json5": { 215 | "version": "2.2.3", 216 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", 217 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" 218 | }, 219 | "minimatch": { 220 | "version": "3.1.2", 221 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 222 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 223 | "requires": { 224 | "brace-expansion": "^1.1.7" 225 | } 226 | }, 227 | "once": { 228 | "version": "1.4.0", 229 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 230 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 231 | "requires": { 232 | "wrappy": "1" 233 | } 234 | }, 235 | "path": { 236 | "version": "0.12.7", 237 | "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", 238 | "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", 239 | "requires": { 240 | "process": "^0.11.1", 241 | "util": "^0.10.3" 242 | } 243 | }, 244 | "path-is-absolute": { 245 | "version": "1.0.1", 246 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 247 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" 248 | }, 249 | "process": { 250 | "version": "0.11.10", 251 | "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", 252 | "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" 253 | }, 254 | "util": { 255 | "version": "0.10.4", 256 | "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", 257 | "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", 258 | "requires": { 259 | "inherits": "2.0.3" 260 | }, 261 | "dependencies": { 262 | "inherits": { 263 | "version": "2.0.3", 264 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 265 | "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" 266 | } 267 | } 268 | }, 269 | "wrappy": { 270 | "version": "1.0.2", 271 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 272 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /json_convert/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json_convert", 3 | "description": "Regolith filter for converting json5 and hjson files into standard json files", 4 | "license": "MIT", 5 | "repository": { 6 | "url": "https://github.com/Bedrock-OSS/regolith-filters.git", 7 | "type": "git" 8 | }, 9 | "version": "1.0.0", 10 | "main": "./main.js", 11 | "dependencies": { 12 | "glob": "^7.1.7", 13 | "hjson": "^3.2.2", 14 | "json5": "^2.2.3", 15 | "path": "^0.12.7" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /json_convert/readme.md: -------------------------------------------------------------------------------- 1 | # JSON Convert 2 | 3 | This filter tries to convert all JSON files in your addon from [JSON5](https://json5.org/) or [Hjson](https://hjson.github.io/) to standard JSON. 4 | 5 | ## Getting the Filter 6 | 7 | Install with: `regolith install json_convert`. After that, you can place the filter into one of your profiles. 8 | 9 | ```json 10 | { 11 | "filter": "json_convert" 12 | } 13 | ``` 14 | 15 | ## Documentation 16 | 17 | 18 | By default, any file with extensions `.json5`, `.hjson` and `.json` will be converted. You can change this by adding a `include` array to the filter settings. The array should contain a list of glob patterns that will be used to match files. For example, to only convert `.json5` files, you can use the following settings: 19 | 20 | ```json 21 | { 22 | "filter": "json_convert", 23 | "settings": { 24 | "include": ["**/*.json5"] 25 | } 26 | } 27 | ``` 28 | 29 | By default, all files are pretty-printed. You can disable this by setting `pretty` to `false` in the filter settings. 30 | 31 | ```json 32 | { 33 | "filter": "json_convert", 34 | "settings": { 35 | "pretty": false 36 | } 37 | } 38 | ``` 39 | 40 | To change extension of converted files, use `extension_map` setting. For example, to convert all `.json5` files to `.json` files, you can use the following settings: 41 | 42 | ```json 43 | { 44 | "filter": "json_convert", 45 | "settings": { 46 | "extension_map": { 47 | "json5": "json" 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | By default all `json5` and `hjson` extenstions are converted to `json` extension and all other files keep their original extension. 54 | 55 | 56 | ## Changelog 57 | 58 | ### 1.0.0 59 | 60 | The first release of JSON Convert. 61 | -------------------------------------------------------------------------------- /json_convert/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "json_convert", 4 | "type": "object", 5 | "properties": { 6 | "include": { 7 | "type": "array", 8 | "default": [ 9 | "**/*.json5", 10 | "**/*.hjson", 11 | "**/*.json" 12 | ], 13 | "items": { 14 | "type": "string" 15 | }, 16 | "description": "Specifies glob patterns, that will be used to find files to convert. By default, all json5, hjson, and json files will be converted." 17 | }, 18 | "pretty": { 19 | "type": "boolean", 20 | "default": true, 21 | "description": "Specifies whether the output json files should be pretty-printed. By default, the output json files will be pretty-printed." 22 | }, 23 | "extension_map": { 24 | "type": "object", 25 | "additionalProperties": true, 26 | "default": { 27 | "json5": "json", 28 | "hjson": "json" 29 | }, 30 | "description": "Specifies a map of file extensions to use for the output files. By default, json5 and hjson files will be converted to json files. All other files will keep their original extension." 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /name_ninja/.gitignore: -------------------------------------------------------------------------------- 1 | /__pycache__ 2 | venv -------------------------------------------------------------------------------- /name_ninja/completion.md: -------------------------------------------------------------------------------- 1 | # Name Ninja 2 | 3 | This filter is used to automatically generate entity, block, spawn egg, and item names, based on a custom 'name' field, or on the entities identifier. 4 | 5 | Simply add a `name` field into the description of a file: 6 | 7 | ```jsonc 8 | { 9 | "format_version": "1.17.0", 10 | "minecraft:entity": { 11 | "description": { 12 | "identifier": "sirlich:frog", 13 | "name": "🐸 South American Horned Toad", 14 | "is_spawnable": true, 15 | "is_summonable": true, 16 | "is_experimental": false, 17 | }, 18 | // rest of file 19 | } 20 | ``` 21 | 22 | This will add a translation line such as: `entity.sirlich:frog.name=🐸 South American Horned Toad ## Generated via Regolith` to your language file of choice. -------------------------------------------------------------------------------- /name_ninja/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Automatically generates entity, block, spawn egg, and item names, based on a custom 'name' field, or on the entities identifier.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./name_ninja.py", 7 | "name": "Language Assist" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /name_ninja/name_ninja.py: -------------------------------------------------------------------------------- 1 | """ 2 | This filter is used to automatically generate entity, block, and item names, 3 | based on a custom 'name' field, or automatically generated based on the entities 4 | identifier. See 'readme.md' for more information. 5 | """ 6 | 7 | import sys 8 | import json 9 | from typing import List 10 | from enum import Enum 11 | 12 | from reticulator import * 13 | 14 | class AssetType(Enum): 15 | SPAWN_EGG = 1 16 | ITEM = 2 17 | BLOCK = 3 18 | ENTITY = 4 19 | 20 | def generate_localization_key(asset_type: AssetType, asset: JsonResource): 21 | """ 22 | Generates the localization key for the asset type. May depend on format version, 23 | or other things. 24 | """ 25 | 26 | # All assets that we are generating names for have a 'identifier' key. 27 | identifier = asset.identifier 28 | 29 | if asset_type == AssetType.ENTITY: 30 | key = "entity.identifier.name" 31 | elif asset_type == AssetType.ITEM: 32 | # TODO: What should happen if 1.16.100 items have DisplayName component? 33 | 34 | # Handle the different formats for items 35 | if FormatVersion(asset.format_version) < FormatVersion('1.16.100'): 36 | key = "item.identifier.name" 37 | else: 38 | key = "item.identifier" 39 | elif asset_type == AssetType.BLOCK: 40 | key = "tile.identifier.name" 41 | elif asset_type == AssetType.SPAWN_EGG: 42 | key = "item.spawn_egg.entity.identifier.name" 43 | 44 | # Finally, do the replacement and return 45 | return key.replace("identifier", identifier) 46 | 47 | def format_name(name: str): 48 | """ 49 | Formats a name based on the entity's identifier, removing the namespace. 50 | """ 51 | return name.split(":")[1].replace("_", " ").title() 52 | 53 | def gather_translations(asset_type: str, assets: List[JsonFileResource], settings: dict, name_jsonpath: str, ignored_namespaces) -> List[Translation]: 54 | """ 55 | Gathers translations from the behavior pack. 56 | """ 57 | auto_name = settings.get('auto_name', False) 58 | prefix = settings.get('prefix', '') 59 | postfix = settings.get('postfix', '') 60 | 61 | translations : List[Translation] = [] 62 | 63 | for asset in assets: 64 | try: 65 | identifier = asset.identifier 66 | except AssetNotFoundError: 67 | print(f"Warning: {asset.filepath} has no identifier, skipping...") 68 | continue 69 | 70 | # Skip assets that are in ignored namespaces (e.g. minecraft:zombie) 71 | if identifier.split(':')[0] in ignored_namespaces: 72 | continue 73 | 74 | localization_key = generate_localization_key(asset_type, asset) 75 | 76 | # Allow for generate_localization_key to return None (skip) 77 | if localization_key is None: 78 | continue 79 | 80 | # Try/except to handle the case where the asset doesn't have a name. 81 | # If this happens, we optionally name the entity automatically. 82 | try: 83 | # Since we process spawn_eggs before entities, we should ensure that spawn_eggs don't delete the name key 84 | if asset_type == AssetType.SPAWN_EGG: 85 | localization_value = asset.get_jsonpath(name_jsonpath) 86 | else: 87 | localization_value = asset.pop_jsonpath(name_jsonpath) 88 | except AssetNotFoundError: 89 | if auto_name: 90 | localization_value = prefix + format_name(identifier) + postfix 91 | else: 92 | continue 93 | 94 | translations.append(Translation(localization_key, localization_value, "")) 95 | 96 | return translations 97 | 98 | def main(): 99 | """ 100 | The entry point for the script. 101 | """ 102 | try: 103 | settings = json.loads(sys.argv[1]) 104 | except IndexError: 105 | print("Warning: No settings provided. Using default settings.") 106 | settings = {} 107 | 108 | # Detect settings, and set defaults if not provided. 109 | overwrite = settings.get("overwrite", False) 110 | 111 | # Handle backward compatibility 112 | if "languages" in settings: 113 | languages = settings["languages"] 114 | if isinstance(languages, str): 115 | languages = [languages] 116 | print("Warning: The 'languages' setting should be an array of strings. A single string was provided and automatically converted to a list.") 117 | elif not isinstance(languages, list): 118 | raise ValueError("The 'languages' setting must be a list of strings.") 119 | else: 120 | language = settings.get("language", "en_US.lang") 121 | if isinstance(language, str): 122 | languages = [language] 123 | print("Warning: The 'language' setting is deprecated in the latest version. Please use 'languages' instead for future configurations.") 124 | else: 125 | raise ValueError("The 'language' setting must be a string if 'languages' is not provided.") 126 | 127 | sort = settings.get("sort", False) 128 | ignored_namespaces = settings.get("ignored_namespaces", ['minecraft']) 129 | project = Project("./BP", "./RP") 130 | behavior_pack = project.behavior_pack 131 | resource_pack = project.resource_pack 132 | 133 | translations = [] 134 | 135 | translations.extend(gather_translations(AssetType.SPAWN_EGG, behavior_pack.entities, settings.get("spawn_eggs", {}), "minecraft:entity/description/name", ignored_namespaces)) 136 | translations.extend(gather_translations(AssetType.ITEM, behavior_pack.items, settings.get("items", {}), "minecraft:item/description/name", ignored_namespaces)) 137 | translations.extend(gather_translations(AssetType.BLOCK, behavior_pack.blocks, settings.get("blocks", {}), "minecraft:block/description/name", ignored_namespaces)) 138 | translations.extend(gather_translations(AssetType.ENTITY, behavior_pack.entities, settings.get("entities", {}), "minecraft:entity/description/name", ignored_namespaces)) 139 | 140 | for language in languages: 141 | try: 142 | language_file = resource_pack.get_language_file(f"texts/{language}") 143 | except AssetNotFoundError: 144 | print(f"Warning: {language} file not found, creating...") 145 | Path(os.path.join(resource_pack.input_path, 'texts')).mkdir(parents=True, exist_ok=True) 146 | open(os.path.join(resource_pack.input_path, 'texts', language), 'a').close() 147 | language_file = LanguageFile(filepath=f'texts/{language}', pack=resource_pack) 148 | 149 | for translation in translations: 150 | language_file.add_translation(translation, overwrite=overwrite) 151 | 152 | if sort: 153 | language_file.translations.sort(key=lambda t: t.key) 154 | 155 | project.save() 156 | 157 | if __name__ == "__main__": 158 | main() -------------------------------------------------------------------------------- /name_ninja/readme.md: -------------------------------------------------------------------------------- 1 | # Name Ninja 2 | 3 | This filter is used to automatically generate entity, block, spawn egg, and item names, based on a custom 'name' field, or on the entities identifier. 4 | 5 | Simply add a `name` field into the description of a file: 6 | 7 | ```jsonc 8 | { 9 | "format_version": "1.17.0", 10 | "minecraft:entity": { 11 | "description": { 12 | "identifier": "sirlich:frog", 13 | "name": "🐸 South American Horned Toad", 14 | "is_spawnable": true, 15 | "is_summonable": true, 16 | "is_experimental": false, 17 | }, 18 | // rest of file 19 | } 20 | ``` 21 | 22 | This will add a translation line such as: `entity.sirlich:frog.name=🐸 South American Horned Toad ## Generated via Regolith` to your language file of choice. 23 | 24 | ## Using this Filter 25 | 26 | 1) Install 'Python'. We have prepared [installation instructions](https://bedrock-oss.github.io/regolith/guide/python-filters). 27 | 2) Install `name_ninja` by using `regolith install name_ninja` from within a regolith project directory. 28 | 3) Add the `name_ninja` filter to a [profile of your choice.](https://bedrock-oss.github.io/regolith/guide/getting-started#adding-your-first-filter) 29 | 30 | ## Example Project 31 | 32 | An example project for this filter is contained within the `tests` folder of this repository. It shows different possible ways to name your assets: 33 | - Directly inside of the language files is always allowed 34 | - By submitting a "name" field into any BP description 35 | - By turning on 'auto_name', and allowing Regolith to generate the names fully from scratch 36 | 37 | ## Configuration 38 | 39 | Here is a filter, with all options fully defined. These options will be explained bellow. 40 | 41 | ```json 42 | { 43 | "filter": "name_ninja", 44 | "settings": { 45 | "languages": ["en_US.lang", "en_GB.lang"], 46 | "overwrite": true, 47 | "sort": true, 48 | "ignored_identifiers": ["minecraft"], 49 | "entities": { 50 | "auto_name": true, 51 | "prefix": "§1", 52 | "postfix": "§r" 53 | }, 54 | "blocks": { 55 | "auto_name": true, 56 | "prefix": "§2", 57 | "postfix": "§r" 58 | }, 59 | "items": { 60 | "auto_name": true, 61 | "prefix": "§3", 62 | "postfix": "§r" 63 | }, 64 | "spawn_eggs": { 65 | "auto_name": true, 66 | "prefix": "§4", 67 | "postfix": " Spawn Egg§r" 68 | } 69 | } 70 | } 71 | ``` 72 | 73 | | Property | Default | Description | 74 | |--------------------|------------------|-----------------------------------------------------------------------------------------------------| 75 | | languages | ["en_US.lang"] | A list of language files where you want to place the translations. Supports multiple languages. | 76 | | overwrite | False | Whether languages codes should overwrite/replace translations already defined in the language file. | 77 | | sort | False | Whether to sort the language file, on export. Useful for grouping assets. | 78 | | ignored_namespaces | ['minecraft'] | A list of namespaces which you would like to ignore. | 79 | 80 | As you can see, the settings for `entities`, `blocks`, `items` and `spawn_eggs` are always the same. The approach simply gives you more flexibility per asset-type. 81 | 82 | | Property | Default | Description | 83 | |-----------|---------|---------------------------------------------------------------------------------------------------------------------------------------------| 84 | | auto_name | False | Whether to give assets without a 'name' property an auto-generated name. For example `sirlich:woolly_mammoth` would become "Woolly Mammoth" | 85 | | prefix | "" | A prefix that is appended to the start of the translation. Useful for giving color codes to your names. | 86 | | postfix | "" | A postfix that is appended to the end of the translation. Useful for resetting color codes from your names. | 87 | 88 | # Changelog 89 | 90 | ### 1.0.0 91 | 92 | The first release of Name Ninja 93 | 94 | ### 1.1.0 95 | 96 | - Restructured code to use `reticulator` as a proper library dependency 97 | - Fix name field for `1.10` format items 98 | - Add `ignored_namespaces` option, which defaults to ['minecraft'] 99 | - Now prints a warning, and gracefully handles assets without identifiers 100 | 101 | ### 1.2.0 102 | 103 | - Updated 'reticulator' to 0.0.17-beta 104 | - Corrected a few bugs in the name ninja code especially regarding when using the name key 105 | 106 | ### 1.2.1 107 | 108 | - Updated 'reticulator' to 0.0.18-beta 109 | - Fixed associated issue with translations 'stacking' in the export, rather than replacing. 110 | 111 | ### 1.2.2 112 | 113 | - Updated 'reticulator' to 0.1.3-beta 114 | - Updated 'dpath' to 2.1.2 115 | - Fixed issue of duplicated keys in lang file while using 'overwrite'. 116 | 117 | ### 1.2.3 118 | 119 | - Fixed issue where it wouldn't create a the lang file if missing 120 | - Fixed issue where it would fail if an identifier did not exist 121 | 122 | ### 1.2.4 123 | 124 | - Added support for multiple language files via the `languages` setting. 125 | - Updated `readme.md` -------------------------------------------------------------------------------- /name_ninja/requirements.txt: -------------------------------------------------------------------------------- 1 | dpath==2.1.2 2 | reticulator==0.1.3b0 -------------------------------------------------------------------------------- /name_ninja/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "name_ninja", 4 | "type": "object", 5 | "definitions": { 6 | "typeSettings": { 7 | "type": "object", 8 | "properties": { 9 | "auto_name": { 10 | "type": "boolean", 11 | "default": false, 12 | "description": "Whether to give assets without a 'name' property an auto-generated name. For example `sirlich:woolly_mammoth` would become \"Woolly Mammoth\"" 13 | }, 14 | "prefix": { 15 | "type": "string", 16 | "default": "", 17 | "description": "A prefix that is appended to the start of the translation. Useful for giving color codes to your names." 18 | }, 19 | "postfix": { 20 | "type": "string", 21 | "default": "", 22 | "description": "A postfix that is appended to the end of the translation. Useful for resetting color codes from your names." 23 | } 24 | } 25 | } 26 | }, 27 | "properties": { 28 | "languages": { 29 | "type": "array", 30 | "default": ["en_US.lang"], 31 | "description": "A list of language files where you want to place the translations.", 32 | "items": { 33 | "type": "string" 34 | } 35 | }, 36 | "overwrite": { 37 | "type": "boolean", 38 | "default": false, 39 | "description": "Whether languages codes should overwrite/replace translations already defined in the language file." 40 | }, 41 | "sort": { 42 | "type": "boolean", 43 | "default": false, 44 | "description": "Whether to sort the language file, on export. Useful for grouping assets." 45 | }, 46 | "ignored_namespaces": { 47 | "type": "array", 48 | "default": ["minecraft"], 49 | "description": "A list of namespaces which you would like to ignore.", 50 | "items": { 51 | "type":"string" 52 | } 53 | }, 54 | "entities": { 55 | "description": "Settings applied to entities.", 56 | "$ref": "#/definitions/typeSettings" 57 | }, 58 | "blocks": { 59 | "description": "Settings applied to blocks.", 60 | "$ref": "#/definitions/typeSettings" 61 | }, 62 | "items": { 63 | "description": "Settings applied to items.", 64 | "$ref": "#/definitions/typeSettings" 65 | }, 66 | "spawn_eggs": { 67 | "description": "Settings applied to spawn eggs.", 68 | "$ref": "#/definitions/typeSettings" 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /name_ninja/test/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.regolith -------------------------------------------------------------------------------- /name_ninja/test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name_ninja_test", 3 | "author": "SirLich", 4 | "packs": { 5 | "behaviorPack": "./packs/BP", 6 | "resourcePack": "./packs/RP" 7 | }, 8 | "regolith": { 9 | "dataPath": "./packs/data", 10 | "profiles": { 11 | "default": { 12 | "_comment": "Export the test addon into your com.mojang folder for testing.", 13 | "filters": [ 14 | { 15 | "filter": "local_name_ninja", 16 | "settings": { 17 | "languages": ["en_US.lang"], 18 | "overwrite": false, 19 | "entities": { 20 | "auto_name": true, 21 | "prefix": "§1", 22 | "postfix": "§r" 23 | }, 24 | "blocks": { 25 | "auto_name": true, 26 | "prefix": "§2", 27 | "postfix": "§r" 28 | }, 29 | "items": { 30 | "auto_name": true, 31 | "prefix": "§3", 32 | "postfix": "§r" 33 | }, 34 | "spawn_eggs": { 35 | "auto_name": true, 36 | "prefix": "§4", 37 | "postfix": " Spawn Egg§r" 38 | } 39 | } 40 | } 41 | ], 42 | "export": { 43 | "target": "development" 44 | } 45 | }, 46 | "local": { 47 | "_comment": "Export the test addon locally, for quick visual analysis of the results.", 48 | "filters": [ 49 | { 50 | "profile": "default" 51 | } 52 | ], 53 | "export": { 54 | "target": "local" 55 | } 56 | } 57 | }, 58 | "filterDefinitions": { 59 | "local_name_ninja": { 60 | "runWith": "python", 61 | "script": "../name_ninja.py" 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/blocks/limestone.block.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.17.0", 3 | "minecraft:block": { 4 | "description": { 5 | "identifier": "name_ninja:limestone" 6 | }, 7 | "components": { 8 | "minecraft:destroy_time": 7, 9 | "minecraft:explosion_resistance": 3, 10 | "minecraft:geometry": "geometry.block", 11 | "minecraft:material_instances": { 12 | "*": { 13 | "texture": "limestone", 14 | "render_method": "opaque" 15 | } 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/blocks/regolith.block.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.17.0", 3 | "minecraft:block": { 4 | "description": { 5 | "identifier": "name_ninja:regolith" 6 | }, 7 | "components": { 8 | "minecraft:destroy_time": 7, 9 | "minecraft:explosion_resistance": 3, 10 | "minecraft:geometry": "geometry.block", 11 | "minecraft:material_instances": { 12 | "*": { 13 | "texture": "regolith", 14 | "render_method": "opaque" 15 | } 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/blocks/shale.block.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.17.0", 3 | "minecraft:block": { 4 | "description": { 5 | "identifier": "name_ninja:shale", 6 | "name": "Sharp Shale" 7 | }, 8 | "components": { 9 | "minecraft:destroy_time": 7, 10 | "minecraft:explosion_resistance": 3, 11 | "minecraft:geometry": "geometry.block", 12 | "minecraft:material_instances": { 13 | "*": { 14 | "texture": "shale", 15 | "render_method": "opaque" 16 | } 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/entities/house_cat.entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "name_ninja:house_cat", 6 | "is_spawnable": true, 7 | "is_summonable": true, 8 | "is_experimental": false 9 | }, 10 | "component_groups": { 11 | "minecraft:cat_baby": { 12 | "minecraft:is_baby": {}, 13 | "minecraft:scale": { 14 | "value": 0.4 15 | }, 16 | "minecraft:ageable": { 17 | "duration": 1200, 18 | "feed_items": [ 19 | "fish", 20 | "salmon" 21 | ], 22 | "grow_up": { 23 | "event": "minecraft:ageable_grow_up", 24 | "target": "self" 25 | } 26 | } 27 | }, 28 | "minecraft:cat_adult": { 29 | "minecraft:experience_reward": { 30 | "on_bred": "Math.Random(1,7)", 31 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 32 | }, 33 | "minecraft:loot": { 34 | "table": "loot_tables/entities/cat.json" 35 | }, 36 | "minecraft:scale": { 37 | "value": 0.8 38 | }, 39 | "minecraft:behavior.breed": { 40 | "priority": 3, 41 | "speed_multiplier": 1 42 | }, 43 | "minecraft:breedable": { 44 | "require_tame": true, 45 | "require_full_health": true, 46 | "allow_sitting": true, 47 | "breeds_with": { 48 | "mate_type": "minecraft:cat", 49 | "baby_type": "minecraft:cat", 50 | "breed_event": { 51 | "event": "minecraft:entity_born", 52 | "target": "baby" 53 | } 54 | }, 55 | "breed_items": [ 56 | "fish", 57 | "salmon" 58 | ] 59 | } 60 | }, 61 | "minecraft:cat_wild": { 62 | "minecraft:health": { 63 | "value": 10, 64 | "max": 10 65 | }, 66 | "minecraft:tameable": { 67 | "probability": 0.33, 68 | "tame_items": [ 69 | "fish", 70 | "salmon" 71 | ], 72 | "tame_event": { 73 | "event": "minecraft:on_tame", 74 | "target": "self" 75 | } 76 | }, 77 | "minecraft:rideable": { 78 | "seat_count": 1, 79 | "family_types": [ 80 | "zombie" 81 | ], 82 | "seats": { 83 | "position": [ 84 | 0, 85 | 0.35, 86 | 0 87 | ] 88 | } 89 | }, 90 | "minecraft:behavior.nearest_attackable_target": { 91 | "priority": 1, 92 | "reselect_targets": true, 93 | "within_radius": 16, 94 | "entity_types": [ 95 | { 96 | "filters": { 97 | "test": "is_family", 98 | "subject": "other", 99 | "value": "rabbit" 100 | }, 101 | "max_dist": 8 102 | }, 103 | { 104 | "filters": { 105 | "all_of": [ 106 | { 107 | "test": "is_family", 108 | "subject": "other", 109 | "value": "baby_turtle" 110 | }, 111 | { 112 | "test": "in_water", 113 | "subject": "other", 114 | "operator": "!=", 115 | "value": true 116 | } 117 | ] 118 | }, 119 | "max_dist": 8 120 | } 121 | ] 122 | }, 123 | "minecraft:behavior.tempt": { 124 | "priority": 5, 125 | "speed_multiplier": 0.5, 126 | "within_radius": 16, 127 | "can_get_scared": true, 128 | "items": [ 129 | "fish", 130 | "salmon" 131 | ] 132 | }, 133 | "minecraft:behavior.avoid_mob_type": { 134 | "priority": 6, 135 | "entity_types": [ 136 | { 137 | "filters": { 138 | "test": "is_family", 139 | "subject": "other", 140 | "value": "player" 141 | }, 142 | "max_dist": 10, 143 | "walk_speed_multiplier": 0.8, 144 | "sprint_speed_multiplier": 1.33 145 | } 146 | ] 147 | }, 148 | "minecraft:behavior.move_towards_dwelling_restriction": { 149 | "priority": 7 150 | } 151 | }, 152 | "minecraft:cat_tame": { 153 | "minecraft:is_tamed": {}, 154 | "minecraft:health": { 155 | "value": 20, 156 | "max": 20 157 | }, 158 | "minecraft:color": { 159 | "value": 14 160 | }, 161 | "minecraft:sittable": {}, 162 | "minecraft:behavior.tempt": { 163 | "priority": 5, 164 | "speed_multiplier": 0.5, 165 | "within_radius": 16, 166 | "items": [ 167 | "fish", 168 | "salmon" 169 | ] 170 | }, 171 | "minecraft:is_dyeable": { 172 | "interact_text": "action.interact.dye" 173 | }, 174 | "minecraft:leashable": { 175 | "soft_distance": 4, 176 | "hard_distance": 6, 177 | "max_distance": 10 178 | }, 179 | "minecraft:behavior.follow_owner": { 180 | "priority": 4, 181 | "speed_multiplier": 1, 182 | "start_distance": 10, 183 | "stop_distance": 2 184 | }, 185 | "minecraft:behavior.stay_while_sitting": { 186 | "priority": 3 187 | }, 188 | "minecraft:behavior.ocelot_sit_on_block": { 189 | "priority": 7, 190 | "speed_multiplier": 1 191 | }, 192 | "minecraft:behavior.pet_sleep_with_owner": { 193 | "priority": 2, 194 | "speed_multiplier": 1.2, 195 | "search_radius": 10, 196 | "search_height": 10, 197 | "goal_radius": 1 198 | }, 199 | "minecraft:on_wake_with_owner": { 200 | "event": "minecraft:pet_slept_with_owner", 201 | "target": "self" 202 | } 203 | }, 204 | "minecraft:cat_gift_for_owner": { 205 | "minecraft:behavior.drop_item_for": { 206 | "priority": 1, 207 | "seconds_before_pickup": 0, 208 | "cooldown": 0.25, 209 | "drop_item_chance": 0.7, 210 | "offering_distance": 5, 211 | "minimum_teleport_distance": 2, 212 | "max_head_look_at_height": 10, 213 | "target_range": [ 214 | 5, 215 | 5, 216 | 5 217 | ], 218 | "teleport_offset": [ 219 | 0, 220 | 1, 221 | 0 222 | ], 223 | "time_of_day_range": [ 224 | 0.74999, 225 | 0.8 226 | ], 227 | "speed_multiplier": 1, 228 | "search_range": 5, 229 | "search_height": 2, 230 | "search_count": 0, 231 | "goal_radius": 1, 232 | "entity_types": [ 233 | { 234 | "filters": { 235 | "test": "is_family", 236 | "subject": "other", 237 | "value": "player" 238 | }, 239 | "max_dist": 6 240 | } 241 | ], 242 | "loot_table": "loot_tables/entities/cat_gift.json", 243 | "on_drop_attempt": { 244 | "event": "minecraft:cat_gifted_owner", 245 | "target": "self" 246 | } 247 | } 248 | }, 249 | "minecraft:cat_white": { 250 | "minecraft:variant": { 251 | "value": 0 252 | } 253 | }, 254 | "minecraft:cat_tuxedo": { 255 | "minecraft:variant": { 256 | "value": 1 257 | } 258 | }, 259 | "minecraft:cat_red": { 260 | "minecraft:variant": { 261 | "value": 2 262 | } 263 | }, 264 | "minecraft:cat_siamese": { 265 | "minecraft:variant": { 266 | "value": 3 267 | } 268 | }, 269 | "minecraft:cat_british": { 270 | "minecraft:variant": { 271 | "value": 4 272 | } 273 | }, 274 | "minecraft:cat_calico": { 275 | "minecraft:variant": { 276 | "value": 5 277 | } 278 | }, 279 | "minecraft:cat_persian": { 280 | "minecraft:variant": { 281 | "value": 6 282 | } 283 | }, 284 | "minecraft:cat_ragdoll": { 285 | "minecraft:variant": { 286 | "value": 7 287 | } 288 | }, 289 | "minecraft:cat_tabby": { 290 | "minecraft:variant": { 291 | "value": 8 292 | } 293 | }, 294 | "minecraft:cat_black": { 295 | "minecraft:variant": { 296 | "value": 9 297 | } 298 | }, 299 | "minecraft:cat_jellie": { 300 | "minecraft:variant": { 301 | "value": 10 302 | } 303 | } 304 | }, 305 | "components": { 306 | "minecraft:attack_damage": { 307 | "value": 4 308 | }, 309 | "minecraft:nameable": {}, 310 | "minecraft:type_family": { 311 | "family": [ 312 | "cat", 313 | "mob" 314 | ] 315 | }, 316 | "minecraft:breathable": { 317 | "total_supply": 15, 318 | "suffocate_time": 0 319 | }, 320 | "minecraft:collision_box": { 321 | "width": 0.6, 322 | "height": 0.7 323 | }, 324 | "minecraft:healable": { 325 | "items": [ 326 | { 327 | "item": "fish", 328 | "heal_amount": 2 329 | }, 330 | { 331 | "item": "salmon", 332 | "heal_amount": 2 333 | } 334 | ] 335 | }, 336 | "minecraft:hurt_on_condition": { 337 | "damage_conditions": [ 338 | { 339 | "filters": { 340 | "test": "in_lava", 341 | "subject": "self", 342 | "operator": "==", 343 | "value": true 344 | }, 345 | "cause": "lava", 346 | "damage_per_tick": 4 347 | } 348 | ] 349 | }, 350 | "minecraft:movement": { 351 | "value": 0.3 352 | }, 353 | "minecraft:navigation.walk": { 354 | "can_float": true, 355 | "avoid_water": true, 356 | "avoid_damage_blocks": true 357 | }, 358 | "minecraft:movement.basic": {}, 359 | "minecraft:jump.static": {}, 360 | "minecraft:can_climb": {}, 361 | "minecraft:damage_sensor": { 362 | "triggers": { 363 | "cause": "fall", 364 | "deals_damage": false 365 | } 366 | }, 367 | "minecraft:dweller": { 368 | "dwelling_type": "village", 369 | "dweller_role": "passive", 370 | "update_interval_base": 60, 371 | "update_interval_variant": 40, 372 | "can_find_poi": false, 373 | "can_migrate": true, 374 | "first_founding_reward": 0 375 | }, 376 | "minecraft:despawn": { 377 | "despawn_from_distance": {} 378 | }, 379 | "minecraft:behavior.float": { 380 | "priority": 0 381 | }, 382 | "minecraft:behavior.panic": { 383 | "priority": 1, 384 | "speed_multiplier": 1.25 385 | }, 386 | "minecraft:behavior.mount_pathing": { 387 | "priority": 1, 388 | "speed_multiplier": 1.25, 389 | "target_dist": 0, 390 | "track_target": true 391 | }, 392 | "minecraft:behavior.leap_at_target": { 393 | "priority": 3, 394 | "target_dist": 0.3 395 | }, 396 | "minecraft:behavior.ocelotattack": { 397 | "priority": 4, 398 | "cooldown_time": 1, 399 | "x_max_rotation": 30, 400 | "y_max_head_rotation": 30, 401 | "max_distance": 15, 402 | "max_sneak_range": 15, 403 | "max_sprint_range": 4, 404 | "reach_multiplier": 2, 405 | "sneak_speed_multiplier": 0.6, 406 | "sprint_speed_multiplier": 1.33, 407 | "walk_speed_multiplier": 0.8 408 | }, 409 | "minecraft:behavior.random_stroll": { 410 | "priority": 8, 411 | "speed_multiplier": 0.8 412 | }, 413 | "minecraft:behavior.look_at_player": { 414 | "priority": 9 415 | }, 416 | "minecraft:physics": {}, 417 | "minecraft:pushable": { 418 | "is_pushable": true, 419 | "is_pushable_by_piston": true 420 | }, 421 | "minecraft:conditional_bandwidth_optimization": {} 422 | }, 423 | "events": { 424 | "minecraft:entity_spawned": { 425 | "sequence": [ 426 | { 427 | "randomize": [ 428 | { 429 | "weight": 3, 430 | "remove": {}, 431 | "add": { 432 | "component_groups": [ 433 | "minecraft:cat_adult", 434 | "minecraft:cat_wild" 435 | ] 436 | } 437 | }, 438 | { 439 | "weight": 1, 440 | "remove": {}, 441 | "add": { 442 | "component_groups": [ 443 | "minecraft:cat_baby", 444 | "minecraft:cat_wild" 445 | ] 446 | } 447 | } 448 | ] 449 | }, 450 | { 451 | "randomize": [ 452 | { 453 | "weight": 15, 454 | "add": { 455 | "component_groups": [ 456 | "minecraft:cat_white" 457 | ] 458 | } 459 | }, 460 | { 461 | "weight": 15, 462 | "add": { 463 | "component_groups": [ 464 | "minecraft:cat_tuxedo" 465 | ] 466 | } 467 | }, 468 | { 469 | "weight": 15, 470 | "add": { 471 | "component_groups": [ 472 | "minecraft:cat_red" 473 | ] 474 | } 475 | }, 476 | { 477 | "weight": 15, 478 | "add": { 479 | "component_groups": [ 480 | "minecraft:cat_siamese" 481 | ] 482 | } 483 | }, 484 | { 485 | "weight": 15, 486 | "add": { 487 | "component_groups": [ 488 | "minecraft:cat_british" 489 | ] 490 | } 491 | }, 492 | { 493 | "weight": 15, 494 | "add": { 495 | "component_groups": [ 496 | "minecraft:cat_calico" 497 | ] 498 | } 499 | }, 500 | { 501 | "weight": 15, 502 | "add": { 503 | "component_groups": [ 504 | "minecraft:cat_persian" 505 | ] 506 | } 507 | }, 508 | { 509 | "weight": 15, 510 | "add": { 511 | "component_groups": [ 512 | "minecraft:cat_ragdoll" 513 | ] 514 | } 515 | }, 516 | { 517 | "weight": 15, 518 | "add": { 519 | "component_groups": [ 520 | "minecraft:cat_tabby" 521 | ] 522 | } 523 | }, 524 | { 525 | "weight": 15, 526 | "add": { 527 | "component_groups": [ 528 | "minecraft:cat_black" 529 | ] 530 | } 531 | }, 532 | { 533 | "weight": 15, 534 | "add": { 535 | "component_groups": [ 536 | "minecraft:cat_jellie" 537 | ] 538 | } 539 | } 540 | ] 541 | } 542 | ] 543 | }, 544 | "minecraft:spawn_from_village": { 545 | "sequence": [ 546 | { 547 | "randomize": [ 548 | { 549 | "weight": 3, 550 | "remove": {}, 551 | "add": { 552 | "component_groups": [ 553 | "minecraft:cat_adult", 554 | "minecraft:cat_wild" 555 | ] 556 | } 557 | }, 558 | { 559 | "weight": 1, 560 | "remove": {}, 561 | "add": { 562 | "component_groups": [ 563 | "minecraft:cat_baby", 564 | "minecraft:cat_wild" 565 | ] 566 | } 567 | } 568 | ] 569 | }, 570 | { 571 | "randomize": [ 572 | { 573 | "weight": 15, 574 | "add": { 575 | "component_groups": [ 576 | "minecraft:cat_tuxedo" 577 | ] 578 | } 579 | }, 580 | { 581 | "weight": 15, 582 | "add": { 583 | "component_groups": [ 584 | "minecraft:cat_red" 585 | ] 586 | } 587 | }, 588 | { 589 | "weight": 15, 590 | "add": { 591 | "component_groups": [ 592 | "minecraft:cat_siamese" 593 | ] 594 | } 595 | }, 596 | { 597 | "weight": 15, 598 | "add": { 599 | "component_groups": [ 600 | "minecraft:cat_white" 601 | ] 602 | } 603 | }, 604 | { 605 | "weight": 15, 606 | "add": { 607 | "component_groups": [ 608 | "minecraft:cat_british" 609 | ] 610 | } 611 | }, 612 | { 613 | "weight": 15, 614 | "add": { 615 | "component_groups": [ 616 | "minecraft:cat_calico" 617 | ] 618 | } 619 | }, 620 | { 621 | "weight": 15, 622 | "add": { 623 | "component_groups": [ 624 | "minecraft:cat_persian" 625 | ] 626 | } 627 | }, 628 | { 629 | "weight": 15, 630 | "add": { 631 | "component_groups": [ 632 | "minecraft:cat_ragdoll" 633 | ] 634 | } 635 | }, 636 | { 637 | "weight": 15, 638 | "add": { 639 | "component_groups": [ 640 | "minecraft:cat_tabby" 641 | ] 642 | } 643 | }, 644 | { 645 | "weight": 15, 646 | "add": { 647 | "component_groups": [ 648 | "minecraft:cat_jellie" 649 | ] 650 | } 651 | } 652 | ] 653 | } 654 | ] 655 | }, 656 | "minecraft:spawn_midnight_cat": { 657 | "sequence": [ 658 | { 659 | "add": { 660 | "component_groups": [ 661 | "minecraft:cat_adult", 662 | "minecraft:cat_wild", 663 | "minecraft:cat_black" 664 | ] 665 | } 666 | } 667 | ] 668 | }, 669 | "minecraft:entity_born": { 670 | "sequence": [ 671 | { 672 | "filters": { 673 | "test": "has_component", 674 | "operator": "!=", 675 | "value": "minecraft:is_baby" 676 | }, 677 | "remove": {}, 678 | "add": { 679 | "component_groups": [ 680 | "minecraft:cat_baby", 681 | "minecraft:cat_tame" 682 | ] 683 | } 684 | } 685 | ] 686 | }, 687 | "minecraft:ageable_grow_up": { 688 | "remove": { 689 | "component_groups": [ 690 | "minecraft:cat_baby" 691 | ] 692 | }, 693 | "add": { 694 | "component_groups": [ 695 | "minecraft:cat_adult" 696 | ] 697 | } 698 | }, 699 | "minecraft:on_tame": { 700 | "sequence": [ 701 | { 702 | "remove": { 703 | "component_groups": [ 704 | "minecraft:cat_wild" 705 | ] 706 | } 707 | }, 708 | { 709 | "add": { 710 | "component_groups": [ 711 | "minecraft:cat_tame" 712 | ] 713 | } 714 | } 715 | ] 716 | }, 717 | "minecraft:pet_slept_with_owner": { 718 | "add": { 719 | "component_groups": [ 720 | "minecraft:cat_gift_for_owner" 721 | ] 722 | } 723 | }, 724 | "minecraft:cat_gifted_owner": { 725 | "remove": { 726 | "component_groups": [ 727 | "minecraft:cat_gift_for_owner" 728 | ] 729 | } 730 | } 731 | } 732 | } 733 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/items/bell_pepper.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.100", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:bell_pepper", 6 | "category": "Equipment" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/items/corn_flakes.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.17.0", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:corn_flakes", 6 | "name": "Tasty Corn Flakes" 7 | }, 8 | "components": { 9 | "minecraft:max_stack_size": 16 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/items/pecan.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.10", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:pecan" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "name": "pack.name", 5 | "description": "pack.description", 6 | "uuid": "b9d5d235-dada-4d0f-abf7-9c42f32127c8", 7 | "version":[ 1, 0, 0 ], 8 | "min_engine_version": [ 1, 13, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "type": "data", 13 | "uuid": "eeb27436-d622-4ae1-a293-6497bde2c9e4", 14 | "version":[ 1, 0, 0 ] 15 | } 16 | ], 17 | "dependencies": [ 18 | { 19 | "version":[ 1, 0, 0 ], 20 | "uuid": "75672465-be2b-4c40-bbdd-a56d6f43fc4a" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/BP/pack_icon.png -------------------------------------------------------------------------------- /name_ninja/test/packs/BP/texts/en_US.lang: -------------------------------------------------------------------------------- 1 | pack.name=§bName Ninja Test 2 | pack.description=Test Pack for the name_ninja Regolith Filter -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": [1, 1, 0], 3 | "sirlich:shale": { 4 | "textures": { 5 | "up": "shale", 6 | "down": "shale", 7 | "side": "shale" 8 | }, 9 | "sound": "grass" 10 | }, 11 | "sirlich:regolith": { 12 | "textures": { 13 | "up": "shale", 14 | "down": "shale", 15 | "side": "shale" 16 | }, 17 | "sound": "grass" 18 | }, 19 | "sirlich:limestone": { 20 | "textures": { 21 | "up": "limestone", 22 | "down": "limestone", 23 | "side": "limestone" 24 | }, 25 | "sound": "grass" 26 | } 27 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/entity/house_cat.entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.8.0", 3 | "minecraft:client_entity": { 4 | "description": { 5 | "identifier": "name_ninja:house_cat", 6 | "materials": { "default": "cat" }, 7 | "textures": { 8 | "default": "textures/entity/cat/persian" 9 | }, 10 | "geometry": { 11 | "default": "geometry.cat" 12 | }, 13 | "render_controllers": [ "controller.render.default" ], 14 | "spawn_egg": { 15 | "texture": "spawn_egg", 16 | "texture_index": 53 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/entity/puma.entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.8.0", 3 | "minecraft:client_entity": { 4 | "description": { 5 | "identifier": "name_ninja:puma", 6 | "materials": { "default": "cat" }, 7 | "textures": { 8 | "default": "textures/entity/cat/persian" 9 | }, 10 | "geometry": { 11 | "default": "geometry.cat" 12 | }, 13 | "render_controllers": [ "controller.render.default" ], 14 | "spawn_egg": { 15 | "texture": "spawn_egg", 16 | "texture_index": 53 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/entity/spotted_leopard.entity.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.8.0", 3 | "minecraft:client_entity": { 4 | "description": { 5 | "identifier": "name_ninja:spotted_leopard", 6 | "materials": { "default": "cat" }, 7 | "textures": { 8 | "default": "textures/entity/cat/persian" 9 | }, 10 | "geometry": { 11 | "default": "geometry.cat" 12 | }, 13 | "render_controllers": [ "controller.render.default" ], 14 | "spawn_egg": { 15 | "texture": "spawn_egg", 16 | "texture_index": 53 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/items/bell_pepper.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.10.0", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:bell_pepper", 6 | "category": "Items" 7 | }, 8 | "components": { 9 | "minecraft:icon": "name_ninja.bell_pepper" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/items/corn_flakes.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.10.0", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:corn_flakes", 6 | "category": "Items" 7 | }, 8 | "components": { 9 | "minecraft:icon": "name_ninja.corn_flakes" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/items/pecan.item.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.10.0", 3 | "minecraft:item": { 4 | "description": { 5 | "identifier": "name_ninja:pecan", 6 | "category": "Items" 7 | }, 8 | "components": { 9 | "minecraft:icon": "name_ninja.pecan" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": 2, 3 | "header": { 4 | "name": "pack.name", 5 | "description": "pack.description", 6 | "uuid": "75672465-be2b-4c40-bbdd-a56d6f43fc4a", 7 | "version":[ 1, 0, 0 ], 8 | "min_engine_version": [ 1, 13, 0 ] 9 | }, 10 | "modules": [ 11 | { 12 | "type": "resources", 13 | "uuid": "2e19161b-9089-4287-94b2-897e12e791cf", 14 | "version":[ 1, 0, 0 ] 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/pack_icon.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/texts/en_US.lang: -------------------------------------------------------------------------------- 1 | pack.name=§bName Ninja Test 2 | pack.description=Test Pack for the name_ninja Regolith Filter 3 | 4 | item.name_ninja:bell_pepper=§3Green Bell Pepper 5 | tile.name_ninja:regolith.name=§2Regolith Shards 6 | entity.name_ninja:house_cat.name=Kitty Cat ## Custom Comments must be allowed -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/blocks/limestone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/blocks/limestone.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/blocks/regolith.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/blocks/regolith.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/blocks/shale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/blocks/shale.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/item_texture.json: -------------------------------------------------------------------------------- 1 | { 2 | "resource_pack_name": "pack.name", 3 | "texture_name": "atlas.items", 4 | "texture_data": { 5 | "name_ninja.bell_pepper": { 6 | "textures": "textures/items/bell_pepper" 7 | }, 8 | "name_ninja.pecan": { 9 | "textures": "textures/items/pecan" 10 | }, 11 | "name_ninja.corn_flakes": { 12 | "textures": "textures/items/corn_flakes" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/items/bell_pepper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/items/bell_pepper.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/items/corn_flakes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/items/corn_flakes.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/items/pecan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/RP/textures/items/pecan.png -------------------------------------------------------------------------------- /name_ninja/test/packs/RP/textures/terrain_texture.json: -------------------------------------------------------------------------------- 1 | { 2 | "num_mip_levels": 4, 3 | "padding": 8, 4 | "resource_pack_name": "name_ninja", 5 | "texture_data": { 6 | "regolith": { 7 | "textures": "textures/blocks/regolith" 8 | }, 9 | "shale": { 10 | "textures": "textures/blocks/shale" 11 | }, 12 | "limestone": { 13 | "textures": "textures/blocks/limestone" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /name_ninja/test/packs/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/name_ninja/test/packs/data/.gitkeep -------------------------------------------------------------------------------- /name_ninja/test/readme.md: -------------------------------------------------------------------------------- 1 | # Name Ninja Test 2 | 3 | This project shows a valid use of the "Name Ninja" filter, and also has a robust set of test-cases, to view how Regolith will behave in different scenarios. 4 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Regolith Filters 2 | 3 | This repository contains the standard-library filters for the Regolith Addon Compiler. 4 | 5 | - 🔗 [Visit the Website](https://bedrock-oss.github.io/regolith/) 6 | - 🔗 [Visit the Repository](https://github.com/Bedrock-OSS/regolith) 7 | 8 | ## What are Filters? 9 | 10 | Filters are scripts which are ran during the compilation/packaging of your Bedrock Addon. You can learn more [here](https://bedrock-oss.github.io/regolith/guide/filters). 11 | 12 | ## How does this Repository work? 13 | 14 | Every folder in this repository contains a unique filter, with it's own readme, tests, and documentation. 15 | -------------------------------------------------------------------------------- /texture_convert/README.md: -------------------------------------------------------------------------------- 1 | # Texture Convert 2 | 3 | ## Psd Convert 4 | 5 | `.psd` files are image files from the popular image editor [Photoshop](https://www.adobe.com/products/photoshop.html). 6 | 7 | This filter will check the project for any `.psd` files, and will convert them into `.png` files, with the same name and file location. 8 | 9 | This allows you to use `.psd` files directly inside of your addon, without converting them manually whenever you make a change. 10 | 11 | ## Kra Convert 12 | 13 | `.kra` files are image files from the popular image editor [Krita](https://krita.org/en/). 14 | 15 | This filter will check the project for any `.kra` files, and will convert them into `.png` files, with the same name and file location. 16 | 17 | This allows you to use `.kra` files directly inside of your addon, without editing them manually whenever you make a change. 18 | 19 | ## Gimp Convert 20 | `.xcf` files are image files from the open source image editor [Gimp](https://www.gimp.org/). 21 | 22 | This filter will check the project for any `.xcf` files, and will convert them into `.png` files, with the same name and file location. 23 | 24 | This allows you to use `.xcf` files directly inside of your addon, without converting them manually whenever you make a change. 25 | 26 | ## Paint.net Convert 27 | `.pdn` files are image files from the free image editor [Paint.net](https://https://www.getpaint.net). 28 | 29 | This filter will check the project for any `.pdn` files, and will convert them into `.png` files, with the same name and file location. 30 | 31 | This allows you to use `.pdn` files directly inside of your addon, without converting them manually whenever you make a change. 32 | 33 | ## GIF Convert 34 | `.gif` files are converted into vertical sprite sheets (`.png` files) with the same name and file location. 35 | 36 | ## Using the Filter 37 | 38 | ```json 39 | { 40 | "filter": "texture_convert" 41 | } 42 | ``` 43 | 44 | ## Changelog 45 | 46 | ### 1.2.0 47 | Added support for `.gif` files. 48 | 49 | ### 1.1.1 50 | 51 | - Fixed an error, when file was without an extension 52 | 53 | ### 1.1.0 54 | 55 | - Add support for paint.net's `.pdt` files 56 | - Clean up the code 57 | 58 | ### 1.0.0 59 | 60 | The first release of Texture Convert. -------------------------------------------------------------------------------- /texture_convert/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Converts popular image editor file formats, such as .psd to .png.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./texture_convert.py", 7 | "name": "texture_convert" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /texture_convert/requirements.txt: -------------------------------------------------------------------------------- 1 | layeredimage==2024.3 2 | pillow==10.4.0 -------------------------------------------------------------------------------- /texture_convert/test/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.regolith -------------------------------------------------------------------------------- /texture_convert/test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Bedrock-OSS", 3 | "name": "texture_convert_test", 4 | "packs": { 5 | "behaviorPack": "./packs/BP", 6 | "resourcePack": "./packs/RP" 7 | }, 8 | "regolith": { 9 | "dataPath": "./data", 10 | "filterDefinitions": { 11 | "filter_tester": { 12 | "url": "github.com/Bedrock-OSS/regolith-filters", 13 | "version": "1.0.0" 14 | }, 15 | "texture_convert": { 16 | "runWith": "python", 17 | "script": "../texture_convert.py" 18 | } 19 | }, 20 | "profiles": { 21 | "default": { 22 | "export": { 23 | "readOnly": true, 24 | "target": "local" 25 | }, 26 | "filters": [ 27 | { 28 | "filter": "texture_convert" 29 | }, 30 | { 31 | "filter": "filter_tester", 32 | "settings": { 33 | "errors_stop_execution": false 34 | } 35 | } 36 | ] 37 | } 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/BP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/BP/pack_icon.png -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/RP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/RP/pack_icon.png -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/RP/textures/gif.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/RP/textures/gif.png -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/RP/textures/png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/RP/textures/png.png -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/RP/textures/psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/RP/textures/psd.png -------------------------------------------------------------------------------- /texture_convert/test/data/filter_tester/RP/textures/xcf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/data/filter_tester/RP/textures/xcf.png -------------------------------------------------------------------------------- /texture_convert/test/packs/BP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/BP/pack_icon.png -------------------------------------------------------------------------------- /texture_convert/test/packs/RP/pack_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/RP/pack_icon.png -------------------------------------------------------------------------------- /texture_convert/test/packs/RP/textures/gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/RP/textures/gif.gif -------------------------------------------------------------------------------- /texture_convert/test/packs/RP/textures/png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/RP/textures/png.png -------------------------------------------------------------------------------- /texture_convert/test/packs/RP/textures/psd.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/RP/textures/psd.psd -------------------------------------------------------------------------------- /texture_convert/test/packs/RP/textures/xcf.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_convert/test/packs/RP/textures/xcf.xcf -------------------------------------------------------------------------------- /texture_convert/texture_convert.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import layeredimage.io 3 | import zipfile 4 | from PIL import Image, ImageSequence 5 | 6 | def convert_kra(imgpath: Path): 7 | ''' 8 | Converts a Krita file to a PNG. 9 | ''' 10 | # Krita files are actually zip files 11 | with zipfile.ZipFile(imgpath) as z: 12 | with imgpath.with_suffix(".png").open("wb") as f2: 13 | f2.write(z.read("preview.png")) 14 | 15 | def convert_layered(imgpath: Path): 16 | ''' 17 | Converts a layered image format (like .pdn, .xcf or .psd) to a flattened 18 | PNG. 19 | ''' 20 | img = layeredimage.io.openLayerImage(imgpath) 21 | img.getFlattenLayers().save(imgpath.with_suffix(".png")) 22 | 23 | def convert_gif(imgpath: Path): 24 | ''' 25 | Converts a GIF file to a PNG sprite sheet. 26 | ''' 27 | with Image.open(imgpath) as img: 28 | frames = ImageSequence.all_frames(img) 29 | 30 | frame_width = frames[0].width 31 | frame_height = frames[0].height 32 | atlas_height = frame_height * len(frames) 33 | 34 | # Create canvas for atlas 35 | atlas = Image.new("RGBA", (frame_width, atlas_height)) 36 | 37 | for index, frame in enumerate(frames): 38 | atlas.paste(frame, (0, index * frame_height)) 39 | atlas.save(imgpath.with_suffix(".png")) 40 | 41 | 42 | # EXECUTE SCRIPT 43 | if __name__ == "__main__": 44 | for imgpath in Path(".").rglob("*"): 45 | if not imgpath.is_file(): 46 | continue 47 | if imgpath.suffix in [".pdn", ".xcf", ".psd"]: 48 | convert_layered(imgpath) 49 | imgpath.unlink() 50 | elif imgpath.suffix == ".kra": 51 | convert_kra(imgpath) 52 | imgpath.unlink() 53 | elif imgpath.suffix == ".gif": 54 | convert_gif(imgpath) 55 | imgpath.unlink() -------------------------------------------------------------------------------- /texture_list/completion.md: -------------------------------------------------------------------------------- 1 | # texture_list 2 | 3 | This filter automatically creates `texture_list.json` for you, so you don't need to! To learn more about this file, you can read our [wiki article](https://wiki.bedrock.dev/visuals/textures-list.html#top). 4 | 5 | In a nutshell, this file makes Minecraft more efficient, by caching where textures are located. In some versions of Minecraft, failing to create this file actually results in a content log. Letting Regolith generate this file for you is the easiest way to ensure MC stays fast, and content-log-free. 6 | 7 | This filter also supports subpacks. -------------------------------------------------------------------------------- /texture_list/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Automatically creates the `texture_list.json` file, based on the images you've added into your resource pack.", 3 | "filters": [ 4 | { 5 | "runWith": "python", 6 | "script": "./texture_list.py", 7 | "name": "Generating texture_list.json" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /texture_list/readme.md: -------------------------------------------------------------------------------- 1 | # texture_list 2 | 3 | This filter automatically creates `texture_list.json` for you, so you don't need to! To learn more about this file, you can read our [wiki article](https://wiki.bedrock.dev/visuals/textures-list.html#top). 4 | 5 | In a nutshell, this file makes Minecraft more efficient, by caching where textures are located. In some versions of Minecraft, failing to create this file actually results in a content log. Letting Regolith generate this file for you is the easiest way to ensure MC stays fast, and content-log-free. 6 | 7 | This filter also supports subpacks. 8 | 9 | ## Using this Filter 10 | 11 | First, ensure that you have [python installed.](https://bedrock-oss.github.io/regolith/docs/python-filters)! 12 | 13 | Then, run `regolith install texture_list`, to install the filter into your project. 14 | 15 | Finally, add this filter into the profile of your choice: 16 | 17 | ```json 18 | { 19 | "filter": "texture_list" 20 | } 21 | ``` 22 | 23 | ## Example Project 24 | 25 | An example project for this filter is contained within the `tests` folder of this repository. It contains a few nested textures, of different types. 26 | 27 | # Changelog 28 | 29 | ## 1.0.0 30 | 31 | The first release of `texture_list`. 32 | 33 | ## 1.1.0 34 | 35 | - Adds support for [subpacks](https://wiki.bedrock.dev/concepts/subpacks.html#top). 36 | 37 | ### 1.1.1 38 | 39 | - Fixes issues where resource packs without `subpack` folder would crash during compilation. 40 | 41 | ### 1.1.2 42 | 43 | - Fixes issue where subpacks without a 'texture' folder would crash. -------------------------------------------------------------------------------- /texture_list/test/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /.regolith -------------------------------------------------------------------------------- /texture_list/test/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "texture_list_test", 3 | "author": "Bedrock-OSS", 4 | "packs": { 5 | "behaviorPack": "./packs/BP", 6 | "resourcePack": "./packs/RP" 7 | }, 8 | "regolith": { 9 | "dataPath": "./packs/data", 10 | "profiles": { 11 | "test": { 12 | "filters": [ 13 | { 14 | "filter": "local_texture_list" 15 | } 16 | ], 17 | "export": { 18 | "target": "local" 19 | } 20 | } 21 | }, 22 | "filterDefinitions": { 23 | "local_texture_list": { 24 | "runWith": "python", 25 | "script": "../texture_list.py" 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /texture_list/test/packs/BP/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/BP/.gitkeep -------------------------------------------------------------------------------- /texture_list/test/packs/BP/entities/fish.json: -------------------------------------------------------------------------------- 1 | { 2 | "format_version": "1.16.0", 3 | "minecraft:entity": { 4 | "description": { 5 | "identifier": "fishy:koi", 6 | "runtime_identifier": "minecraft:tropicalfish", 7 | "is_spawnable": true, 8 | "is_summonable": true, 9 | "is_experimental": false 10 | }, 11 | "components": { 12 | "minecraft:interact": { 13 | "interactions": [ 14 | { 15 | "on_interact": { 16 | "filters": { 17 | "all_of": [ 18 | { 19 | "test": "is_family", 20 | "subject": "other", 21 | "value": "player" 22 | }, 23 | { 24 | "test": "has_equipment", 25 | "domain": "hand", 26 | "subject": "other", 27 | "value": "bucket:0" 28 | } 29 | ] 30 | } 31 | }, 32 | "use_item": "true", 33 | "transform_to_item": "bucket:1", 34 | "play_sounds": "milk", 35 | "interact_text": "action.interact.milk" 36 | } 37 | ] 38 | }, 39 | "minecraft:is_hidden_when_invisible": {}, 40 | "minecraft:experience_reward": { 41 | "on_death": "query.last_hit_by_player ? Math.Random(1,3) : 0" 42 | }, 43 | "minecraft:type_family": { 44 | "family": [ 45 | "tropicalfish" 46 | ] 47 | }, 48 | "minecraft:collision_box": { 49 | "width": 0.6, 50 | "height": 0.3 51 | }, 52 | "minecraft:health": { 53 | "value": 6, 54 | "max": 6 55 | }, 56 | "minecraft:hurt_on_condition": { 57 | "damage_conditions": [ 58 | { 59 | "filters": { 60 | "test": "in_lava", 61 | "subject": "self", 62 | "operator": "==", 63 | "value": true 64 | }, 65 | "cause": "lava", 66 | "damage_per_tick": 4 67 | } 68 | ] 69 | }, 70 | "minecraft:loot": { 71 | "table": "loot_tables/entities/koi.json" 72 | }, 73 | "minecraft:scale": { 74 | "value": 1 75 | }, 76 | "minecraft:breathable": { 77 | "total_supply": 15, 78 | "suffocate_time": 0, 79 | "breathes_air": false, 80 | "breathes_water": true 81 | }, 82 | "minecraft:movement": { 83 | "value": 0.1 84 | }, 85 | "minecraft:underwater_movement": { 86 | "value": 0.1 87 | }, 88 | "minecraft:navigation.generic": { 89 | "is_amphibious": false, 90 | "can_path_over_water": false, 91 | "can_swim": true, 92 | "can_walk": false, 93 | "can_breach": false, 94 | "can_sink": false 95 | }, 96 | "minecraft:physics": { 97 | "has_gravity": false 98 | }, 99 | "minecraft:pushable": { 100 | "is_pushable": true, 101 | "is_pushable_by_piston": true 102 | }, 103 | "minecraft:movement.sway": { 104 | "sway_amplitude": 0 105 | }, 106 | "minecraft:despawn": { 107 | "despawn_from_distance": { 108 | "min_distance": 32, 109 | "max_distance": 40 110 | } 111 | }, 112 | "minecraft:behavior.swim_idle": { 113 | "priority": 5, 114 | "idle_time": 5, 115 | "success_rate": 0.1 116 | }, 117 | "minecraft:behavior.random_swim": { 118 | "priority": 3, 119 | "speed_multiplier": 1, 120 | "xz_dist": 16, 121 | "y_dist": 4, 122 | "interval": 0 123 | }, 124 | "minecraft:behavior.swim_wander": { 125 | "priority": 4, 126 | "interval": 0.1, 127 | "look_ahead": 2, 128 | "speed_multiplier": 1, 129 | "wander_time": 5 130 | }, 131 | "minecraft:behavior.avoid_mob_type": { 132 | "priority": 1, 133 | "entity_types": [ 134 | { 135 | "filters": { 136 | "any_of": [ 137 | { 138 | "test": "is_family", 139 | "subject": "other", 140 | "value": "axolotl" 141 | } 142 | ] 143 | }, 144 | "max_dist": 6, 145 | "walk_speed_multiplier": 1.5, 146 | "sprint_speed_multiplier": 2 147 | } 148 | ] 149 | }, 150 | "minecraft:flocking": { 151 | "in_water": true, 152 | "match_variants": false, 153 | "use_center_of_mass": true, 154 | "low_flock_limit": 4, 155 | "high_flock_limit": 8, 156 | "goal_weight": 2, 157 | "loner_chance": 0.1, 158 | "influence_radius": 3, 159 | "breach_influence": 7, 160 | "separation_weight": 1.75, 161 | "separation_threshold": 0.95, 162 | "cohesion_weight": 2, 163 | "cohesion_threshold": 1.95, 164 | "innner_cohesion_threshold": 1.25, 165 | "min_height": 1.5, 166 | "max_height": 6, 167 | "block_distance": 2, 168 | "block_weight": 0.85 169 | }, 170 | "minecraft:nameable": {}, 171 | "minecraft:conditional_bandwidth_optimization": {} 172 | } 173 | } 174 | } -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack1/textures/nested/nested_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack1/textures/nested/nested_png.png -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack1/textures/nested/nested_tga.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack1/textures/nested/nested_tga.tga -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack1/textures/root_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack1/textures/root_png.png -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack1/textures/root_tga.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack1/textures/root_tga.tga -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack2/textures/new_file.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack2/textures/new_file.tga -------------------------------------------------------------------------------- /texture_list/test/packs/RP/subpacks/pack3/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/subpacks/pack3/.gitkeep -------------------------------------------------------------------------------- /texture_list/test/packs/RP/textures/nested/nested_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/textures/nested/nested_png.png -------------------------------------------------------------------------------- /texture_list/test/packs/RP/textures/nested/nested_tga.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/textures/nested/nested_tga.tga -------------------------------------------------------------------------------- /texture_list/test/packs/RP/textures/root_png.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/textures/root_png.png -------------------------------------------------------------------------------- /texture_list/test/packs/RP/textures/root_tga.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/RP/textures/root_tga.tga -------------------------------------------------------------------------------- /texture_list/test/packs/data/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bedrock-OSS/regolith-filters/5de5e96de6f1427104a69d386a254ee4f25faf4e/texture_list/test/packs/data/.gitkeep -------------------------------------------------------------------------------- /texture_list/test/readme.md: -------------------------------------------------------------------------------- 1 | # Texture List Tests 2 | 3 | This folder contains a test project for the texture list filter. 4 | 5 | ## Running Tests 6 | 7 | To run tests, you may navigate into this folder, and run `regolith run test`. The output should be viewable inside of `build` folder, which isn't checked into src control. 8 | 9 | ## Explanation 10 | 11 | This test-setup will show how the texture_list.json files are created, in relation to main textures, and subpacks. 12 | 13 | `pack1` shows a subpack with the same set of textures, effectively overriding those in the main folder. This means it's `texture_list.json` file will be the same as the main pack. 14 | 15 | `pack2` contains a new texture, which wasn't there in the main pack. In this case, the `texture_list.json` file will contain this new texture, along with the textures defined in the main pack. 16 | 17 | The logic is essentially: When the subpack is enabled, it will replace all files, including `texture_list.json`. Thus, the file contained in each subpack needs to list textures used within itself, as well as those contained outside. 18 | 19 | The `texture_list.json` file in the main pack is required, because a subpack may not contain any textures, and thus no `texture_list.json` file. -------------------------------------------------------------------------------- /texture_list/texture_list.py: -------------------------------------------------------------------------------- 1 | import json 2 | from itertools import chain 3 | from pathlib import Path 4 | 5 | ROOT_PATH = Path("RP") 6 | SUBPACK_PATH = ROOT_PATH / "subpacks" 7 | 8 | 9 | def fetch_subpack_folders(): 10 | """ 11 | Fetches a list of all subpack folders in the resource-pack. 12 | """ 13 | if SUBPACK_PATH.exists(): 14 | for subpack_folder in SUBPACK_PATH.iterdir(): 15 | if subpack_folder.is_dir() and (subpack_folder / "textures").exists(): 16 | yield subpack_folder 17 | 18 | def list_textures(root_folder: Path): 19 | """ 20 | Lists all textures within the 'textures' folder within the path. For example 21 | pass in 'RP", and it will search 'RP/textures'. 22 | """ 23 | textures = [] 24 | textures_folder = root_folder / "textures" 25 | 26 | for texture in chain(textures_folder.glob("**/*.png"), textures_folder.glob("**/*.tga")): 27 | textures.append(texture.relative_to(root_folder).with_suffix("").as_posix()) 28 | return textures 29 | 30 | def generate_texture_list_file(root_folder: Path, textures): 31 | """ 32 | Generates root_folder/textures/textures_list.json 33 | """ 34 | if len(textures) > 0: 35 | with open(root_folder / "textures" / "textures_list.json", "w") as f: 36 | json.dump(textures, f, indent='\t') 37 | 38 | 39 | def main(): 40 | # Handle the root resource pack file 41 | pack_textures = list_textures(ROOT_PATH); 42 | generate_texture_list_file(ROOT_PATH, pack_textures) 43 | 44 | for subpack_folder in fetch_subpack_folders(): 45 | # list+set is a way of crushing duplicates out of a list 46 | subpack_textures = list(set(list_textures(subpack_folder) + pack_textures)) 47 | generate_texture_list_file(subpack_folder, subpack_textures) 48 | 49 | if __name__ == "__main__": 50 | """ 51 | The entry-point of the script. 52 | """ 53 | main() -------------------------------------------------------------------------------- /type_gen/completion.md: -------------------------------------------------------------------------------- 1 | # Type Gen 2 | 3 | This filter generates a `.d.ts` file that contains all the relevant constants in your add-on. -------------------------------------------------------------------------------- /type_gen/filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Generates Files.d.ts file with some relevant constants from the current pack.", 3 | "filters": [ 4 | { 5 | "runWith": "nodejs", 6 | "script": "./type_gen.js", 7 | "name": "Generates Files.d.ts file with some relevant constants from the current pack" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /type_gen/readme.md: -------------------------------------------------------------------------------- 1 | # Type Gen 2 | 3 | This filter generates a `.d.ts` file that contains all the relevant constants in your add-on. 4 | 5 | **Requires the [gametests](https://github.com/Bedrock-OSS/regolith-filters/tree/master/gametests) filter to be installed.** 6 | 7 | ## Data 8 | 9 | 1. Entities - All entity identifiers 10 | 2. Items - All item identifiers 11 | 3. Blocks - All block identifiers 12 | 4. Sounds - All sound identifiers 13 | 5. LangKeys - Language keys between `## #sync` and `## #endsync` lines in `en_US.lang` 14 | 6. Structures - All structure identifiers 15 | 7. LootTables - All loot table paths 16 | 8. Particles - All particle identifiers 17 | 18 | ## Getting the Filter 19 | 20 | Install with: `regolith install type_gen`. After that, you can place the filter into one of your profiles. For the best results, you should run this filter as a last filter in your profile. 21 | 22 | ```json 23 | { 24 | "filter": "type_gen" 25 | } 26 | ``` 27 | 28 | ## Usage 29 | 30 | After running the filter at least once, you can import the generated `.d.ts` file in your project. 31 | 32 | ```ts 33 | import { Entities } from './Files.d'; 34 | import { Dimension } from '@minecraft/server'; 35 | 36 | function spawnExampleEntity(dimension: Dimension) { 37 | return dimension.spawnEntity(Entities.Example); 38 | } 39 | ``` 40 | 41 | ## Settings 42 | 43 | ```json 44 | { 45 | "outputFile": "Files.d.ts" 46 | } 47 | ``` 48 | 49 | ### outputFile 50 | 51 | The output file to write the generated `.d.ts` file to. Defaults to `Files.d.ts`. 52 | 53 | ## Changelog 54 | 55 | ### 1.0.1 56 | 57 | [#55](https://github.com/Bedrock-OSS/regolith-filters/pull/55) Fixed issue where the filter would create an infinite loop when editing files in watch mode by [@FrederoxDev](https://github.com/FrederoxDev) 58 | 59 | ### 1.0.0 60 | 61 | The first release of Type Gen. 62 | -------------------------------------------------------------------------------- /type_gen/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema", 3 | "$id": "type_gen", 4 | "type": "object", 5 | "properties": { 6 | "outputFile": { 7 | "type": "string", 8 | "default": "Files.d.ts", 9 | "description": "The output file to write the generated .d.ts file to. Defaults to Files.d.ts." 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /type_gen/type_gen.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const settings = { 5 | outputFile: 'Files.d.ts' 6 | } 7 | 8 | if (process.argv[2]) { 9 | const parsedSettings = JSON.parse(process.argv[2]); 10 | Object.assign(settings, parsedSettings); 11 | } 12 | 13 | if (!settings.outputFile) { 14 | console.warn("No output file specified. Please specify an output file in the settings."); 15 | return; 16 | } 17 | 18 | // Initial configuration for running script from the project root directory 19 | let packsPath = './packs/'; 20 | let gametestsPath = './packs/data/gametests/'; 21 | // If the script is run as a regolith filter 22 | if (process.env.ROOT_DIR && fs.existsSync(process.env.ROOT_DIR + '/config.json')) { 23 | let config = JSON.parse(fs.readFileSync(process.env.ROOT_DIR + '/config.json', 'utf8')); 24 | packsPath = './'; 25 | gametestsPath = path.normalize(process.env.ROOT_DIR + '/' + config.regolith.dataPath + '/gametests/'); 26 | } 27 | 28 | if (!fs.existsSync(gametestsPath)) { 29 | console.warn("Could not find gametests directory. Please make sure the gametests directory is present in the dataPath"); 30 | return; 31 | } 32 | 33 | // Helper function to convert a string to Title Case and remove the namespace 34 | function toTitleCase(str) { 35 | const split = str.split(':'); 36 | let result = split[0]; 37 | if (split.length > 1) { 38 | result = split[1]; 39 | } 40 | return result // Remove namespace 41 | .replace(/[-_\./]/g, ' ') // Replace dashes, dots and underscores with spaces 42 | .replace(/\w\S*/g, (txt) => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()) // Convert to Title Case 43 | .replace(/\s+/g, ''); // Remove spaces 44 | } 45 | 46 | // Helper function to get value from an object by path 47 | function getValueByPath(obj, path) { 48 | return path.split('/').reduce((prev, curr) => prev[curr], obj); 49 | } 50 | 51 | // Helper function to create enum declaration 52 | function createEnumDeclaration(enumName, keyValuePairs) { 53 | if (Object.keys(keyValuePairs).length === 0) { 54 | return ''; 55 | } 56 | let enumContent = `export const enum ${enumName} {\r\n`; 57 | const sortedEntries = Object.entries(keyValuePairs).sort(([keyA], [keyB]) => keyA.localeCompare(keyB)); 58 | for (const [key, value] of sortedEntries) { 59 | enumContent += ` ${key} = "${value}",\r\n`; 60 | } 61 | enumContent += `}\r\n\r\n`; 62 | 63 | return enumContent; 64 | } 65 | 66 | // Helper function to load JSON file 67 | function loadJsonFile(filePath) { 68 | return JSON.parse(fs.readFileSync(filePath, 'utf8')); 69 | } 70 | 71 | // Helper function to load a lang file 72 | function loadLangFile(filePath) { 73 | const lines = fs.readFileSync(filePath, 'utf8').split('\n'); 74 | const syncedLines = []; 75 | let isSyncing = false; 76 | for (const line of lines) { 77 | if (line.startsWith('## #sync')) { 78 | isSyncing = true; 79 | } else if (line.startsWith('## #endsync')) { 80 | isSyncing = false; 81 | } else if (isSyncing && line.trim().length > 0 && line.trim().charAt(0) !== '#') { 82 | syncedLines.push(line); 83 | } 84 | } 85 | return syncedLines.map(x => x.trim().split('=', 2).map(y => y.trim())); 86 | } 87 | 88 | function readdirSyncRecursive(dir) { 89 | let files = []; 90 | let foldersToProcess = [dir]; 91 | while (foldersToProcess.length > 0) { 92 | const folder = foldersToProcess.shift(); 93 | const folderFiles = fs.readdirSync(folder); 94 | for (const file of folderFiles) { 95 | const filePath = path.join(folder, file); 96 | if (fs.statSync(filePath).isDirectory()) { 97 | foldersToProcess.push(filePath); 98 | } else { 99 | files.push(filePath); 100 | } 101 | } 102 | } 103 | return files; 104 | } 105 | 106 | // Recursive function to get all JSON files from a directory 107 | function getJsonFiles(dir) { 108 | let jsonFiles = []; 109 | if (!fs.existsSync(dir)) { 110 | return jsonFiles; 111 | } 112 | const files = fs.readdirSync(dir); 113 | 114 | files.forEach((file) => { 115 | const filePath = path.join(dir, file); 116 | const stat = fs.statSync(filePath); 117 | 118 | if (stat.isDirectory()) { 119 | jsonFiles = jsonFiles.concat(getJsonFiles(filePath)); 120 | } else if (path.extname(file) === '.json') { 121 | jsonFiles.push(filePath); 122 | } 123 | }); 124 | 125 | return jsonFiles; 126 | } 127 | 128 | // Function to create the enum from JSON files 129 | function createEnumFromJsonFiles(enumName, valueGetter, directory) { 130 | const jsonFiles = getJsonFiles(directory); 131 | const enumEntries = {}; 132 | 133 | jsonFiles.forEach((file) => { 134 | const data = loadJsonFile(file); 135 | const identifierPath = valueGetter(data); 136 | 137 | if (identifierPath) { 138 | const enumKey = toTitleCase(identifierPath); 139 | enumEntries[enumKey] = identifierPath; 140 | } 141 | }); 142 | 143 | return createEnumDeclaration(enumName, enumEntries); 144 | } 145 | 146 | // List entities 147 | let enumContent = createEnumFromJsonFiles('Entities', obj => getValueByPath(obj, 'minecraft:entity/description/identifier'), packsPath + 'BP/entities/'); 148 | 149 | // List items 150 | enumContent += createEnumFromJsonFiles('Items', obj => getValueByPath(obj, 'minecraft:item/description/identifier'), packsPath + 'BP/items/'); 151 | 152 | // List blocks 153 | enumContent += createEnumFromJsonFiles('Blocks', obj => getValueByPath(obj, 'minecraft:block/description/identifier'), packsPath + 'BP/blocks/'); 154 | 155 | // List sounds 156 | if (fs.existsSync(packsPath + 'RP/sounds/sound_definitions.json')) { 157 | let obj = loadJsonFile(packsPath + 'RP/sounds/sound_definitions.json')['sound_definitions']; 158 | let pairs = {}; 159 | for (const key of Object.keys(obj)) { 160 | pairs[toTitleCase(key)] = key; 161 | } 162 | enumContent += createEnumDeclaration('Sounds', pairs); 163 | } 164 | 165 | // List language keys 166 | if (fs.existsSync(packsPath + 'RP/texts/en_US.lang')) { 167 | let obj = loadLangFile(packsPath + 'RP/texts/en_US.lang'); 168 | let pairs = {}; 169 | for (const [key, value] of obj) { 170 | pairs[toTitleCase(key.split('.').map(x => x.includes(':') ? x.split(':', 2)[1] : x).join('.'))] = key; 171 | } 172 | enumContent += createEnumDeclaration('LangKeys', pairs); 173 | } 174 | 175 | // List structures 176 | if (fs.existsSync(packsPath + 'BP/structures')) { 177 | let obj = fs.readdirSync(packsPath + 'BP/structures'); 178 | let pairs = {}; 179 | for (const namespace of obj) { 180 | if (fs.statSync(packsPath + `BP/structures/${namespace}`).isDirectory()) { 181 | let files = fs.readdirSync(packsPath + `BP/structures/${namespace}`); 182 | for (const file of files) { 183 | if (file.endsWith('.mcstructure')) { 184 | let id = file.split('.')[0]; 185 | pairs[toTitleCase(`${namespace}:${id}`)] = `${namespace}:${id}`; 186 | } 187 | } 188 | } 189 | } 190 | enumContent += createEnumDeclaration('Structures', pairs); 191 | } 192 | 193 | // List loot tables. Every loot table is just a path to a file in `./packs/BP/loot_tables`. Recursively search for .json files. 194 | if (fs.existsSync(packsPath + 'BP/loot_tables')) { 195 | let obj = readdirSyncRecursive(packsPath + 'BP/loot_tables').map(x => path.relative(packsPath + 'BP/loot_tables', x).replace(/\\/g, '/')); 196 | let pairs = {}; 197 | for (const file of obj) { 198 | if (file.endsWith('.json')) { 199 | const split = file.split('.'); 200 | split.length -= 1; 201 | let id = split.join('.'); 202 | pairs[toTitleCase(id)] = id; 203 | } 204 | } 205 | enumContent += createEnumDeclaration('LootTables', pairs); 206 | } 207 | 208 | // List particles 209 | enumContent += createEnumFromJsonFiles('Particles', obj => getValueByPath(obj, 'particle_effect/description/identifier'), packsPath + 'RP/particles/'); 210 | 211 | // Write to .d.ts file 212 | const outputFilePath = gametestsPath + 'src/' + settings.outputFile; 213 | 214 | let existingContent = ''; 215 | if (fs.existsSync(outputFilePath)) { 216 | existingContent = fs.readFileSync(outputFilePath, 'utf8'); 217 | } 218 | 219 | if (existingContent !== enumContent) { 220 | fs.writeFileSync(outputFilePath, enumContent, 'utf8'); 221 | console.log(`Enum written to ${path.relative(process.env.ROOT_DIR, outputFilePath)}`); 222 | } --------------------------------------------------------------------------------