From ad4cba0b6b3fe75303eff3fb05d75c748277b788 Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Tue, 18 Aug 2026 04:29:50 +0200 Subject: [PATCH 01/11] chore(files): remove legacy graves addon - Deleted all graves scripts, including event handling, dynamic properties, UI and utility functions - Removed the associated resources: entity definitions, textures and language files - Cleaned up the project structure by dropping the unused directories and files --- .gitignore | 2 + .../files/gameplay_changes/graves/.gitignore | 2 - .../graves/.vscode/extensions.json | 13 - .../graves/.vscode/launch.json | 16 - .../graves/.vscode/settings.json | 41 -- .../files/gameplay_changes/graves/config.json | 48 --- .../gameplay_changes/graves/eslint.config.mjs | 397 ----------------- .../gameplay_changes/graves/package.json | 31 -- .../graves/packs/BP/entities/grave.json | 79 ---- .../BP/functions/graves/config.mcfunction | 1 - .../BP/functions/graves/uninstall.mcfunction | 1 - .../graves/packs/BP/items/grave_key.json | 11 - .../graves/packs/BP/manifest.json | 59 --- .../graves/packs/BP/pack_icon.png | 3 - .../graves/packs/BP/scripts/Actions/Grave.ts | 408 ------------------ .../packs/BP/scripts/Actions/giveGraveKey.ts | 14 - .../graves/packs/BP/scripts/Actions/index.ts | 6 - .../packs/BP/scripts/Actions/listGraves.ts | 85 ---- .../packs/BP/scripts/Actions/settings.ts | 45 -- .../packs/BP/scripts/Actions/uninstall.ts | 15 - .../packs/BP/scripts/Events/EntityDie.ts | 13 - .../packs/BP/scripts/Events/EntityHit.ts | 17 - .../packs/BP/scripts/Events/GameRuleChange.ts | 13 - .../Events/PlayerInteractWithEntity.ts | 12 - .../BP/scripts/Events/ScriptEventReceive.ts | 26 -- .../BP/scripts/Events/WorldInitialize.ts | 6 - .../graves/packs/BP/scripts/Events/index.ts | 6 - .../BP/scripts/Models/DynamicProperties.ts | 73 ---- .../packs/BP/scripts/Models/EntityTypes.ts | 3 - .../packs/BP/scripts/Models/ItemTypes.ts | 3 - .../packs/BP/scripts/Models/ScriptEvents.ts | 4 - .../graves/packs/BP/scripts/Models/index.ts | 4 - .../graves/packs/BP/scripts/UI/Config.ts | 31 -- .../graves/packs/BP/scripts/UI/Settings.ts | 38 -- .../graves/packs/BP/scripts/UI/index.ts | 1 - .../BP/scripts/Util/DynamicProperties.ts | 42 -- .../packs/BP/scripts/Util/Scoreboard.ts | 39 -- .../graves/packs/BP/scripts/Util/index.ts | 2 - .../graves/packs/BP/scripts/main.ts | 7 - .../graves/packs/RP/entity/grave.entity.json | 20 - .../graves/packs/RP/manifest.json | 48 --- .../packs/RP/models/entity/grave.geo.json | 188 -------- .../graves/packs/RP/pack_icon.png | 3 - .../graves/packs/RP/texts/en_US.lang | 29 -- .../graves/packs/RP/textures/entity/grave.png | 3 - .../packs/RP/textures/item_texture.json | 9 - .../packs/RP/textures/items/grave_key.png | 3 - .../graves/packs/data/.gitkeep | 3 - .../gameplay_changes/graves/tsconfig.json | 35 -- yarn.lock | 112 +++-- 50 files changed, 86 insertions(+), 1984 deletions(-) delete mode 100644 addons/files/gameplay_changes/graves/.gitignore delete mode 100644 addons/files/gameplay_changes/graves/.vscode/extensions.json delete mode 100644 addons/files/gameplay_changes/graves/.vscode/launch.json delete mode 100644 addons/files/gameplay_changes/graves/.vscode/settings.json delete mode 100644 addons/files/gameplay_changes/graves/config.json delete mode 100644 addons/files/gameplay_changes/graves/eslint.config.mjs delete mode 100644 addons/files/gameplay_changes/graves/package.json delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/entities/grave.json delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/functions/graves/config.mcfunction delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/functions/graves/uninstall.mcfunction delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/items/grave_key.json delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/manifest.json delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/pack_icon.png delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/Grave.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/giveGraveKey.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/index.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/listGraves.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/settings.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/uninstall.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityDie.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityHit.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/GameRuleChange.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/PlayerInteractWithEntity.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/ScriptEventReceive.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/WorldInitialize.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Events/index.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Models/DynamicProperties.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Models/EntityTypes.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ItemTypes.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ScriptEvents.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Models/index.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Config.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Settings.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/index.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Util/DynamicProperties.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Util/Scoreboard.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/Util/index.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/manifest.json delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/pack_icon.png delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json delete mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png delete mode 100644 addons/files/gameplay_changes/graves/packs/data/.gitkeep delete mode 100644 addons/files/gameplay_changes/graves/tsconfig.json diff --git a/.gitignore b/.gitignore index 72a9468b4..7c0c61522 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ node_modules/ package-lock.json .yarn/install-state.gz +tools/vt-diff/.cache +tools/vt-diff/out \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.gitignore b/addons/files/gameplay_changes/graves/.gitignore deleted file mode 100644 index 3f195ca9a..000000000 --- a/addons/files/gameplay_changes/graves/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/.regolith \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.vscode/extensions.json b/addons/files/gameplay_changes/graves/.vscode/extensions.json deleted file mode 100644 index 25477bef4..000000000 --- a/addons/files/gameplay_changes/graves/.vscode/extensions.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "recommendations": [ - "aaron-bond.better-comments", - "blockceptionltd.blockceptionvscodeminecraftbedrockdevelopmentextension", - "dbaeumer.vscode-eslint", - "mojang-studios.minecraft-debugger", - "misodee.vscode-nbt", - "bedrockoss.regolith", - "jannisx11.snowstorm", - "netcorext.uuid-generator", - "christian-kohler.npm-intellisense" - ] -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.vscode/launch.json b/addons/files/gameplay_changes/graves/.vscode/launch.json deleted file mode 100644 index c903aaa6c..000000000 --- a/addons/files/gameplay_changes/graves/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "0.3.0", - "configurations": [ - { - "type": "minecraft-js", - "request": "attach", - "sourceMapRoot": "${env:LOCALAPPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/development_behavior_packs/bedrock-tweaks/graves/scripts/", - "generatedSourceRoot": "${env:LOCALAPPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/development_behavior_packs/bedrock-tweaks/graves/scripts/", - "localRoot": "${workspaceFolder}/packs/BP/scripts/", - "name": "Debug with Minecraft", - "mode": "listen", - "port": 19144, - "targetModuleUuid": "da5802ff-6c19-46d8-a27a-1f717580968b" - } - ] -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.vscode/settings.json b/addons/files/gameplay_changes/graves/.vscode/settings.json deleted file mode 100644 index 03f36e123..000000000 --- a/addons/files/gameplay_changes/graves/.vscode/settings.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "editor.formatOnSave": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - }, - "editor.detectIndentation": false, - "editor.insertSpaces": false, - "editor.tabSize": 2, - "[typescript]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[javascript]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[json]": { - "editor.defaultFormatter": "vscode.json-language-features", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[jsonc]": { - "editor.defaultFormatter": "vscode.json-language-features", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, - "eslint.validate": [ - "typescript", - "json", - "jsonc" - ], - "files.associations": { - "*.json": "jsonc" - }, - "eslint.enable": true, - "eslint.format.enable": true -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/config.json b/addons/files/gameplay_changes/graves/config.json deleted file mode 100644 index 53a8b723c..000000000 --- a/addons/files/gameplay_changes/graves/config.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/Bedrock-OSS/regolith-schemas/main/config/v1.4.json", - "name": "bedrock-tweaks/graves", - "author": "DrAv0011", - "packs": { - "behaviorPack": "./packs/BP", - "resourcePack": "./packs/RP" - }, - "regolith": { - "dataPath": "./packs/data", - "formatVersion": "1.4.0", - "filterDefinitions": { - "bundler": { - "url": "github.com/bedrock-core/regolith-filters", - "version": "1.0.1" - } - }, - "profiles": { - "build": { - "export": { - "build": "standard", - "readOnly": false, - "target": "local" - }, - "filters": [ - { - "filter": "bundler" - } - ] - }, - "default": { - "export": { - "readOnly": false, - "target": "development", - "build": "standard" - }, - "filters": [ - { - "filter": "bundler", - "settings": { - "debug": true - } - } - ] - } - } - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/eslint.config.mjs b/addons/files/gameplay_changes/graves/eslint.config.mjs deleted file mode 100644 index c772a561f..000000000 --- a/addons/files/gameplay_changes/graves/eslint.config.mjs +++ /dev/null @@ -1,397 +0,0 @@ -import minecraftLinting from "eslint-plugin-minecraft-linting"; -import stylistic from "@stylistic/eslint-plugin"; -import { defineConfig } from "eslint/config"; -import tseslint from "typescript-eslint"; -import json from "@eslint/json"; -import { fileURLToPath } from "url"; -import { dirname } from "path"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -export default defineConfig([ - { - ignores: [ - ".*/**", // Any directory starting with dot (.yarn, .vscode, .regolith, etc.) - ".*", // Any file starting with dot - "node_modules/**", - "**/*.*js", // Generated JS files - "filters/**", // The filters directory - "build/**", // Build output - "*.json", // Root level JSON files (config.json, package.json, tsconfig.json) - "*.md", // Root level markdown files - "*.mjs", // Root level mjs files (like this config) - "*.js", // Root level js files - ], - }, - - { - plugins: { - json, - }, - }, - - // lint JSON files - { - files: ["**/*.json"], - language: "json/jsonc", - rules: { - "json/no-duplicate-keys": "error", - "json/no-empty-keys": "off", - "json/no-unnormalized-keys": "error", - "json/no-unsafe-values": "error", - "json/sort-keys": "off", - "json/top-level-interop": "off", - } - }, - - { - files: ["packs/BP/scripts/**/*.ts"], - plugins: { - "@stylistic": stylistic, - "@typescript-eslint": tseslint.plugin, - "@minecraft": minecraftLinting - }, - - languageOptions: { - parser: tseslint.parser, - ecmaVersion: "latest", - sourceType: "module", - parserOptions: { - project: ["tsconfig.json"], - tsconfigRootDir: __dirname, - }, - }, - - rules: { - "no-unused-expressions": "off", - "@typescript-eslint/no-unused-expressions": "off", - "@typescript-eslint/explicit-member-accessibility": ["error", - { - accessibility: "no-public", - } - ], - - "@typescript-eslint/no-restricted-types": [ - "error", - { - "types": { - "Object": { - "message": "Avoid using the `Object` type. Did you mean `object`?" - }, - "Function": { - "message": "Avoid using the `Function` type. Prefer a specific function type, like `() => void`." - }, - "Boolean": { - "message": "Avoid using the `Boolean` type. Did you mean `boolean`?" - }, - "Number": { - "message": "Avoid using the `Number` type. Did you mean `number`?" - }, - "String": { - "message": "Avoid using the `String` type. Did you mean `string`?" - }, - "Symbol": { - "message": "Avoid using the `Symbol` type. Did you mean `symbol`?" - } - } - } - ], - "@typescript-eslint/no-wrapper-object-types": "error", - "@typescript-eslint/no-unsafe-function-type": "error", - "@typescript-eslint/no-empty-object-type": ["error", - { - allowInterfaces: 'always' - } - ], - "@typescript-eslint/ban-ts-comment": ["error", - { - 'ts-expect-error': 'allow-with-description' - } - ], - - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/dot-notation": "error", - - "@typescript-eslint/member-ordering": ["error", - { - default: [ - "private-field", - "protected-field", - "public-field", - "constructor", - "public-method", - "protected-method", - "private-method", - ], - } - ], - - "@typescript-eslint/naming-convention": ["error", - { - selector: "default", - format: ["camelCase"], - }, - { - selector: "default", - modifiers: ["unused"], - - filter: { - regex: "^(_|_.*_?)$", - match: true, - }, - - format: null, - }, - { - selector: "variable", - format: ["camelCase"], - }, - { - selector: ["parameter"], - format: ["camelCase", "snake_case"], - }, - { - selector: "variable", - modifiers: ["const"], - format: ["camelCase", "UPPER_CASE", "PascalCase"], - }, - { - selector: ["enum", "enumMember", "function"], - format: ["camelCase", "UPPER_CASE", "PascalCase"], - }, - { - selector: ["property", "parameterProperty", "accessor"], - modifiers: ["private"], - format: ["camelCase", "snake_case"], - leadingUnderscore: "require", - }, - { - selector: ["property", "parameterProperty", "accessor"], - modifiers: ["private", "readonly"], - format: ["UPPER_CASE", "camelCase"], - leadingUnderscore: "allow", - }, - { - selector: ["property"], - modifiers: ["readonly"], - format: ["camelCase", "UPPER_CASE"], - }, - { - selector: ["objectLiteralProperty", "typeProperty"], - format: ["camelCase", "snake_case", "UPPER_CASE"], - }, - { - selector: "typeLike", - format: ["PascalCase"], - } - ], - - "@typescript-eslint/explicit-function-return-type": ["error", - { - allowExpressions: true, - } - ], - - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/default-param-last": "warn", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn", - "@typescript-eslint/prefer-enum-initializers": "warn", - - "@typescript-eslint/unbound-method": ["error", - { - ignoreStatic: true, - } - ], - - eqeqeq: ["error", "always", - { - null: "ignore", - } - ], - - "object-shorthand": "error", - - "@typescript-eslint/no-unused-vars": ["error", - { - argsIgnorePattern: "^(_|_.*_?)$", - } - ], - - "arrow-body-style": "warn", - curly: ["warn", "multi-line", "consistent"], - "@typescript-eslint/array-type": "warn", - "@typescript-eslint/prefer-optional-chain": "warn", - "@typescript-eslint/prefer-reduce-type-parameter": "warn", - "@stylistic/array-bracket-newline": "off", - "@stylistic/array-bracket-spacing": ["warn"], - "@stylistic/array-element-newline": "off", - "@stylistic/arrow-parens": ["warn", "as-needed"], - "@stylistic/arrow-spacing": ["warn"], - "@stylistic/block-spacing": ["warn", "always"], - - "@stylistic/brace-style": ["warn", "1tbs", - { - allowSingleLine: true, - } - ], - - "@stylistic/comma-dangle": ["warn", - { - arrays: "always-multiline", - objects: "always-multiline", - imports: "never", - exports: "never", - functions: "always-multiline", - enums: "never", - generics: "never", - tuples: "always-multiline", - } - ], - - "@stylistic/comma-spacing": ["warn", - { - before: false, - after: true, - } - ], - - "@stylistic/computed-property-spacing": ["warn"], - "@stylistic/dot-location": ["warn", "property"], - "@stylistic/eol-last": ["warn"], - "@stylistic/function-call-argument-newline": ["warn", "consistent"], - "@stylistic/function-paren-newline": ["warn", "consistent"], - "@stylistic/generator-star-spacing": "off", - "@stylistic/implicit-arrow-linebreak": ["warn", "beside"], - "@stylistic/indent": ["warn", "tab"], - "@stylistic/jsx-quotes": "off", - - "@stylistic/key-spacing": ["warn", - { - beforeColon: false, - afterColon: true, - mode: "strict", - } - ], - - "@stylistic/keyword-spacing": ["warn"], - "@stylistic/linebreak-style": ["warn", "windows"], - - "@stylistic/lines-around-comment": ["warn", - { - afterBlockComment: false, - beforeLineComment: false, - afterLineComment: false, - allowBlockStart: true, - allowClassStart: true, - allowObjectStart: true, - allowArrayStart: true, - } - ], - - "@stylistic/lines-between-class-members": ["warn", "always", - { - exceptAfterSingleLine: true, - exceptAfterOverload: false, - } - ], - - "@stylistic/max-len": "off", - "@stylistic/max-statements-per-line": "off", - "@stylistic/multiline-ternary": "off", - "@stylistic/new-parens": ["warn"], - "@stylistic/newline-per-chained-call": "off", - "@stylistic/no-confusing-arrow": "off", - "@stylistic/no-extra-parens": ["warn"], - "@stylistic/no-extra-semi": ["warn"], - "@stylistic/no-floating-decimal": ["warn"], - "@stylistic/no-mixed-operators": "off", - "@stylistic/no-mixed-spaces-and-tabs": ["warn"], - "@stylistic/no-multi-spaces": ["warn"], - - "@stylistic/no-multiple-empty-lines": ["warn", - { - max: 1, - } - ], - - "@stylistic/no-tabs": ["off"], - "@stylistic/no-trailing-spaces": ["warn"], - "@stylistic/no-whitespace-before-property": ["warn"], - "@stylistic/nonblock-statement-body-position": "off", - - "@stylistic/object-curly-newline": ["warn", - { - multiline: true, - } - ], - - "@stylistic/object-curly-spacing": ["warn", "always"], - "@stylistic/object-property-newline": "off", - "@stylistic/one-var-declaration-per-line": "off", - "@stylistic/operator-linebreak": "off", - "@stylistic/padded-blocks": ["warn", "never"], - - "@stylistic/padding-line-between-statements": ["warn", - { - blankLine: "always", - prev: "*", - next: "return", - } - ], - - "@stylistic/quote-props": ["warn", "as-needed"], - - "@stylistic/quotes": ["warn", "single", - { - avoidEscape: true, - allowTemplateLiterals: 'always', - } - ], - - "@stylistic/rest-spread-spacing": ["warn", "never"], - "@stylistic/semi": ["warn", "always"], - "@stylistic/semi-spacing": ["warn"], - "@stylistic/semi-style": ["warn", "last"], - "@stylistic/space-before-blocks": ["warn"], - "@stylistic/space-before-function-paren": ["warn", { - "anonymous": "never", - "named": "never", - "asyncArrow": "never", - "catch": "always" - }], - "@stylistic/space-in-parens": ["warn", "never"], - "@stylistic/space-infix-ops": "warn", - "@stylistic/space-unary-ops": "off", - "@stylistic/spaced-comment": ["warn", "always"], - "@stylistic/switch-colon-spacing": ["warn"], - "@stylistic/template-curly-spacing": ["warn"], - "@stylistic/template-tag-spacing": "off", - "@stylistic/wrap-iife": "off", - "@stylistic/wrap-regex": "off", - "@stylistic/yield-star-spacing": "off", - "@stylistic/member-delimiter-style": ["warn"], - "@stylistic/type-annotation-spacing": "warn", - "@stylistic/jsx-child-element-spacing": "off", - "@stylistic/jsx-closing-bracket-location": "off", - "@stylistic/jsx-closing-tag-location": "off", - "@stylistic/jsx-curly-brace-presence": "off", - "@stylistic/jsx-curly-newline": "off", - "@stylistic/jsx-curly-spacing": "off", - "@stylistic/jsx-equals-spacing": "off", - "@stylistic/jsx-first-prop-new-line": "off", - "@stylistic/jsx-indent": "off", - "@stylistic/jsx-indent-props": "off", - "@stylistic/jsx-max-props-per-line": "off", - "@stylistic/jsx-newline": "off", - "@stylistic/jsx-one-expression-per-line": "off", - "@stylistic/jsx-props-no-multi-spaces": "off", - "@stylistic/jsx-self-closing-comp": "off", - "@stylistic/jsx-sort-props": "off", - "@stylistic/jsx-tag-spacing": "off", - "@stylistic/jsx-wrap-multilines": "off", - "@minecraft/avoid-unnecessary-command": "error", - }, - } -]); diff --git a/addons/files/gameplay_changes/graves/package.json b/addons/files/gameplay_changes/graves/package.json deleted file mode 100644 index 7393208ee..000000000 --- a/addons/files/gameplay_changes/graves/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@bedrock-tweaks/graves", - "version": "1.21.0-1.0.0", - "description": "Graves addon - place graves when you die to retrieve your items", - "private": true, - "scripts": { - "regolith-install": "regolith install-all", - "build": "regolith run build", - "dev": "regolith watch", - "lint": "npx eslint ." - }, - "dependencies": { - "@minecraft/common": "1.2.0", - "@minecraft/math": "2.2.11", - "@minecraft/server": "2.2.0", - "@minecraft/server-ui": "^2.0.0", - "@minecraft/vanilla-data": "1.21.114", - "@stylistic/eslint-config": "^1.1.0" - }, - "devDependencies": { - "@eslint/js": "^9.38.0", - "@eslint/json": "^0.13.2", - "@stylistic/eslint-plugin": "^5.4.0", - "@typescript-eslint/eslint-plugin": "^8.46.1", - "@typescript-eslint/parser": "^8.46.1", - "eslint": "^9.38.0", - "eslint-plugin-minecraft-linting": "^2.0.10", - "typescript": "^5.9.3", - "typescript-eslint": "^8.46.1" - } -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/entities/grave.json b/addons/files/gameplay_changes/graves/packs/BP/entities/grave.json deleted file mode 100644 index bc5425815..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/entities/grave.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "format_version": "1.21.50", - "minecraft:entity": { - "description": { - "identifier": "bt:g.grave", - "is_spawnable": false, - "is_summonable": true - }, - "components": { - "minecraft:type_family": { - "family": [ - "bedrock_tweaks", - "graves", - "grave" - ] - }, - "minecraft:physics": { - "has_collision": true, - "has_gravity": false - }, - "minecraft:knockback_resistance": { - "value": 1 - }, - "minecraft:collision_box": { - "width": 0.8, - "height": 0.8 - }, - "minecraft:health": { - "value": 1, - "min": 1, - "max": 1 - }, - "minecraft:push_through": { - "value": 1 - }, - "minecraft:fire_immune": {}, - "minecraft:water_movement": { - "drag_factor": 0 - }, - "minecraft:pushable": { - "is_pushable": false, - "is_pushable_by_piston": true - }, - "minecraft:inventory": { - "additional_slots_per_strength": 0, - "can_be_siphoned_from": false, - "container_type": "container", - "inventory_size": 45, - "private": true, - "restrict_to_owner": true - }, - "minecraft:damage_sensor": { - "triggers": [ - { - "cause": "all", - "deals_damage": "no" - } - ] - }, - "minecraft:interact": { - "interactions": [ - { - "on_interact": { - "filters": [ - { - "test": "has_equipment", - "subject": "other", - "domain": "hand", - "value": "bt:g.grave_key" - } - ] - }, - "interact_text": "bt.graves.force_open_grave" - } - ] - } - } - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/functions/graves/config.mcfunction b/addons/files/gameplay_changes/graves/packs/BP/functions/graves/config.mcfunction deleted file mode 100644 index 138e15e22..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/functions/graves/config.mcfunction +++ /dev/null @@ -1 +0,0 @@ -execute as @s run scriptevent bt:g.config \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/functions/graves/uninstall.mcfunction b/addons/files/gameplay_changes/graves/packs/BP/functions/graves/uninstall.mcfunction deleted file mode 100644 index 3eb4b501c..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/functions/graves/uninstall.mcfunction +++ /dev/null @@ -1 +0,0 @@ -execute as @s run scriptevent bt:g.uninstall \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.json b/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.json deleted file mode 100644 index a5608d1f2..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "format_version": "1.20.50", - "minecraft:item": { - "description": { - "identifier": "bt:g.grave_key" - }, - "components": { - "minecraft:icon": "bt:grave_key" - } - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/manifest.json b/addons/files/gameplay_changes/graves/packs/BP/manifest.json deleted file mode 100644 index d768bee1d..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/manifest.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "format_version": 2, - "header": { - "name": "Graves BP - Gameplay Changes - Bedrock Tweaks", - "description": "When you die it places a grave to save all your drops", - "uuid": "c7bec17f-9394-4cad-8f14-4aae4374d51f", - "pack_scope": "world", - "version": [ - 1, - 0, - 0 - ], - "min_engine_version": [ - 1, - 21, - 50 - ] - }, - "modules": [ - { - "type": "data", - "uuid": "36f57ff3-733b-41e0-9ba5-6e204969bd0c", - "version": [ - 1, - 0, - 0 - ] - }, - { - "type": "script", - "language": "javascript", - "uuid": "da5802ff-6c19-46d8-a27a-1f717580968b", - "entry": "scripts/main.js", - "version": [ - 1, - 0, - 0 - ] - } - ], - "dependencies": [ - { - "uuid": "e9412201-d5f3-4d0f-bb25-cc3bb051c9b9", - "version": [ - 1, - 0, - 0 - ] - } - ], - "metadata": { - "product_type": "addon", - "authors": [ - "Vanilla Tweaks", - "Bedrock Tweaks", - "DrAv0011" - ] - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png b/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png deleted file mode 100644 index 83ab8fd5d..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d49a6cf5a2f50ddae7f0d0cb724fb82247c01dd90e21dce305a8b0dd9759938 -size 6369 diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/Grave.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/Grave.ts deleted file mode 100644 index 55acaad30..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/Grave.ts +++ /dev/null @@ -1,408 +0,0 @@ -import { - Block, - Container, - Dimension, - DimensionType, - DimensionTypes, - Entity, - EntityComponentTypes, - EntityEquippableComponent, - EntityInventoryComponent, - EquipmentSlot, - ItemStack, - Player, - system, - TicksPerSecond, - Vector3, - world -} from '@minecraft/server'; -import { Grave, GraveDynamicProperties, GravesList, GravesListDynamicProperties, GravesSettings } from '../Models'; -import { getProperties, setProperties } from '../Util'; -import { getSettings } from './settings'; -import { clampNumber, Vector3Utils } from '@minecraft/math'; -import { MinecraftBlockTypes, MinecraftDimensionTypes } from '@minecraft/vanilla-data'; -import { GravesEntityTypes } from '../Models'; - -/** - * Spawns a grave for a player, transferring their items and experience to the grave. - * - * @param {Player} player - The player whose items and experience will be stored in the grave. - */ -export const spawnGrave = (player: Player): void => { - if (!isInventoryEmpty(player)) { - const gravesSettings: GravesSettings = getSettings(); - - // TODO if bottom of the world and empty below place on top of cobble slab - // TODO After spawning tp grave 1 block up to be able to do above - const graveLocation: Vector3 = calculateGraveLocation(player.location, player.dimension); - - // Spawn grave entity at the determined location - const graveEntity: Entity = player.dimension.spawnEntity(GravesEntityTypes.Grave, graveLocation); - - graveEntity.nameTag = player.nameTag; - - // Transfer items to the grave and set its properties - const itemCount: number = transferItemsToGrave(player, graveEntity); - - setGraveProperties(player, graveEntity, itemCount); - - // Notify player of grave location if enabled in settings - if (gravesSettings.graveLocating) { - player.sendMessage({ - rawtext: [{ - translate: 'bt.graves.location', - with: [graveEntity.location.x.toString(), graveEntity.location.y.toString(), graveEntity.location.z.toString(), graveEntity.dimension.id], - }], - }); - } - } -}; - -/** - * Opens a grave for a player, transferring items and experience to the player, - * and removing the grave from the world. - * - * @param {Player} player - The player attempting to open the grave. - * @param {Entity} grave - The grave entity to be opened. - */ -export const openGrave = (player: Player, grave: Entity): void => { - const gravesSettings: GravesSettings = getSettings(); - const graveProperties: Grave = getProperties(grave, GraveDynamicProperties); - - if (player.id === graveProperties.ownerId || gravesSettings.graveRobbing) { - transferItemsToPlayer(player, grave); - - if (gravesSettings.xpCollection) { - player.addExperience(graveProperties.playerExperience); - } - - removeGraveFromList(grave); - - grave.remove(); - } -}; - -/** - * Forcibly opens a grave, retrieving all items and removing the grave. - * - * @param {Entity} grave - The grave entity to be force-opened. - */ -export const forceOpenGrave = (grave: Entity): void => { - getAllItems(grave); - removeGraveFromList(grave); - - grave.remove(); -}; - -/** - * Checks all graves in all dimensions and removes those that have exceeded the despawn time. - */ -export const tickGrave = (): void => { - const gravesSettings: GravesSettings = getSettings(); - - if (gravesSettings.despawnTime > 0) { - DimensionTypes.getAll().forEach((dimensionType: DimensionType): void => { - const dimension: Dimension = world.getDimension(dimensionType.typeId); - const currentDimensionGraves: Entity[] = dimension.getEntities({ type: GravesEntityTypes.Grave }); - - currentDimensionGraves.forEach((grave: Entity): void => { - const graveProperties: Grave = getProperties(grave, GraveDynamicProperties); - - if ((system.currentTick - graveProperties.spawnTime) / TicksPerSecond > gravesSettings.despawnTime) { - removeGraveFromList(grave); - - grave.remove(); - } - }); - }); - } -}; - -/** - * Transfers all items from a player's inventory and equipment slots to a grave. - * - * @param {Player} player - The player whose items will be transferred. - * @param {Entity} grave - The grave entity that will store the items. - * @returns {number} - The total number of items transferred. - */ -const transferItemsToGrave = (player: Player, grave: Entity): number => { - let itemCount: number = 0; - - const playerContainer: Container = (player.getComponent(EntityComponentTypes.Inventory) as EntityInventoryComponent) - ?.container; - const graveContainer: Container = (grave.getComponent(EntityComponentTypes.Inventory) as EntityInventoryComponent) - ?.container; - const playerArmor: EntityEquippableComponent = player.getComponent(EntityComponentTypes.Equippable) as EntityEquippableComponent; - - const playerContainerSize: number = playerContainer?.size; - if (playerContainer && graveContainer && playerContainerSize !== undefined) { - // Transfer items from inventory - for (let i: number = 0; i < playerContainerSize; i++) { - const itemStack: ItemStack | undefined = playerContainer.getItem(i); - if (itemStack) { - itemCount += itemStack.amount; - } - - playerContainer.moveItem(i, i, graveContainer); - } - - // Transfer items from equipment slots - let j: number = playerContainerSize; - for (const value of Object.values(EquipmentSlot)) { - j++; - - const itemStack: ItemStack | undefined = playerArmor.getEquipmentSlot(value).getItem(); - - if (itemStack) { - itemCount += itemStack.amount; - } - - graveContainer.setItem(j, itemStack); - playerArmor.setEquipment(value, undefined); - } - - // Clear remaining inventory - playerContainer.clearAll(); - } - - return itemCount; -}; - -/** - * Transfers items from a grave to a player's inventory or drops items - * that cannot be stored in the player's inventory. - * - * @param {Player} player - The player receiving the items. - * @param {Entity} grave - The grave containing items to be transferred. - */ -const transferItemsToPlayer = (player: Player, grave: Entity): void => { - const playerContainer: Container = (player.getComponent(EntityComponentTypes.Inventory) as EntityInventoryComponent) - ?.container; - const graveContainer: Container = (grave.getComponent(EntityComponentTypes.Inventory) as EntityInventoryComponent) - ?.container; - const playerArmor: EntityEquippableComponent = player.getComponent(EntityComponentTypes.Equippable) as EntityEquippableComponent; - - const playerContainerSize: number = playerContainer?.size; - - if (playerContainer && graveContainer && playerContainerSize !== undefined) { - const itemsToSpawn: ItemStack[] = []; - - // Transfer items from grave to player inventory - for (let i: number = 0; i < playerContainerSize; i++) { - if (!playerContainer.getSlot(i).hasItem()) { - graveContainer.moveItem(i, i, playerContainer); - } else { - const slotItem: ItemStack | undefined = graveContainer.getItem(i); - - if (slotItem) { - itemsToSpawn.push(playerContainer.getSlot(i).getItem() as ItemStack); - graveContainer.moveItem(i, i, playerContainer); - } - } - } - - // Transfer items from grave to player equipment slots - let j = playerContainerSize; - for (const value of Object.values(EquipmentSlot)) { - j++; - - const slotItem: ItemStack | undefined = graveContainer.getItem(j); - - if (!playerArmor.getEquipmentSlot(value).hasItem()) { - playerArmor.getEquipmentSlot(value).setItem(slotItem); - } else { - if (slotItem) { - itemsToSpawn.push(playerArmor.getEquipmentSlot(value).getItem() as ItemStack); - playerArmor.getEquipmentSlot(value).setItem(slotItem); - } - } - } - - spawnItemsInWorld(grave, itemsToSpawn); - } -}; - -/** - * Retrieves all items from a grave and spawns them in the world. - * - * @param {Entity} grave - The grave containing items. - */ -const getAllItems = (grave: Entity): void => { - const graveContainer: Container = (grave.getComponent(EntityComponentTypes.Inventory) as EntityInventoryComponent) - ?.container; - - const containerSize: number = graveContainer?.size; - - const itemsToSpawn: ItemStack[] = []; - - for (let i: number = 0; i < containerSize; i++) { - const item: ItemStack | undefined = graveContainer.getSlot(i).getItem(); - - if (item) { - itemsToSpawn.push(item); - } - } - - spawnItemsInWorld(grave, itemsToSpawn); -}; - -/** - * Spawns items in the world at the grave's location. - * - * @param {Entity} grave - The grave entity where items should spawn. - * @param {ItemStack[]} itemsToSpawn - List of items to be spawned in the world. - */ -const spawnItemsInWorld = (grave: Entity, itemsToSpawn: ItemStack[]): void => { - itemsToSpawn.forEach((item: ItemStack): void => { - grave.dimension.spawnItem(item, grave.location); - }); -}; - -/** - * Checks if the player's inventory, armor, and offhand slots are empty. - * - * @param {Player} player - The player whose inventory will be checked. - * @returns {boolean} - Returns true if all slots are empty; false otherwise. - */ -const isInventoryEmpty = (player: Player): boolean => { - const playerContainer: Container | undefined = player.getComponent(EntityComponentTypes.Inventory)?.container; - const playerArmor: EntityEquippableComponent | undefined = player.getComponent(EntityComponentTypes.Equippable); - - if (!playerContainer || !playerArmor) { - return true; - } - - let emptySlotsCount: number = playerContainer.emptySlotsCount || 0; - playerArmor.getEquipment(EquipmentSlot.Head) === undefined && emptySlotsCount++; - playerArmor.getEquipment(EquipmentSlot.Chest) === undefined && emptySlotsCount++; - playerArmor.getEquipment(EquipmentSlot.Legs) === undefined && emptySlotsCount++; - playerArmor.getEquipment(EquipmentSlot.Feet) === undefined && emptySlotsCount++; - playerArmor.getEquipment(EquipmentSlot.Offhand) === undefined && emptySlotsCount++; - - // Total slots include inventory, armor, and offhand - const totalSlotCount: number = playerContainer.size + 4 + 1; - - return emptySlotsCount === totalSlotCount; -}; - -/** - * Sets the properties of a grave, such as owner, experience, and item count. - * - * @param {Player} player - The player whose grave is being created. - * @param {Entity} grave - The grave entity to set properties on. - * @param {number} itemCount - The total number of items in the grave. - */ -const setGraveProperties = (player: Player, grave: Entity, itemCount: number): void => { - const graveProperties: Grave = { - id: grave.id, - ownerId: player.id, - ownerName: player.nameTag, - playerExperience: player.getTotalXp(), - spawnTime: system.currentTick, - location: grave.location, - dimension: grave.dimension.id as MinecraftDimensionTypes, - itemCount, - }; - - setProperties(grave, GraveDynamicProperties, graveProperties); - - // Clear player experience (min bound) - player.addLevels(-(2 ** 24)); - - saveGraveToList(graveProperties); -}; - -/** - * Calculates a valid grave spawning location. - * - * @param {Vector3} initialLocation - The starting location for the grave. - * @param {Dimension} dimension - The spawning dimension of the grave. - * - * @returns {Vector3} - A valid grave location. - */ -const calculateGraveLocation = (initialLocation: Vector3, dimension: Dimension): Vector3 => { - // Define spawning limits - const minLimit: Vector3 = Vector3Utils.floor({ - x: initialLocation.x - 1, - y: dimension.heightRange.min, - z: initialLocation.z - 1, - }); - const maxLimit: Vector3 = Vector3Utils.floor({ - x: initialLocation.x + 1, - y: dimension.heightRange.max, - z: initialLocation.z + 1, - }); - - if (!isValidSpawnBlock(initialLocation, dimension)) { - for (let y: number = minLimit.y; y <= maxLimit.y; y++) { - for (let x: number = minLimit.x; x <= maxLimit.x; x++) { - for (let z: number = minLimit.z; z <= maxLimit.z; z++) { - if (isValidSpawnBlock({ x, y, z }, dimension)) { - return { x, y, z }; - } - } - } - } - } - - // If initialLocation is valid or no other valid location found, return the initial location clamped to world limits - return Vector3Utils.floor({ ...initialLocation, y: clampNumber(initialLocation.y, dimension.heightRange.min, dimension.heightRange.max) }); -}; - -/** - * Determines whether the block at a given location within a dimension is valid for spawning a grave. - * - * A valid spawn block is one that matches any of the types specified in the `validBlocks` array. - * This should include any "pass-through" blocks (like Air, Water, TallGrass, Flowers, Kelp, Bubbles, etc.). - * - * @param {Vector3} location - The location of the block to validate. - * @param {Dimension} dimension - The dimension in which to check the block. - * - * @returns {boolean} - If the block at the specified location is valid for spawning a grave - * - * - * Note: using {"@minecraft/server": "1.16.0"} we cannot check if a block is "pass-through" - * Needs to be updated when it will possible, so it can also be compatible with Addon blocks - */ -const isValidSpawnBlock = (location: Vector3, dimension: Dimension): boolean => { - const validBlocks: MinecraftBlockTypes[] = [ - MinecraftBlockTypes.Air, - MinecraftBlockTypes.Water, - // TODO add "pass-through" blocks like tall grass, flowers, kelp... - // TODO check flowing_water - ]; - - const block: Block | undefined = dimension.getBlock(location); - - if (!block) { - return false; - } - - return validBlocks.some((validBlockType: MinecraftBlockTypes): boolean => block.matches(validBlockType)); -}; - -/** - * Saves a grave's properties to the global graves list. - * - * @param {Grave} grave - The grave properties to be added to the list. - */ -const saveGraveToList = (grave: Grave): void => { - const gravesList: Grave[] = JSON.parse(getProperties(world, GravesListDynamicProperties).list); - - gravesList.push(grave); - - setProperties(world, GravesListDynamicProperties, { list: JSON.stringify(gravesList) }); -}; - -/** - * Removes a grave entity from the global list of graves. - * - * @param {Entity} grave - The grave entity to be removed. - */ -const removeGraveFromList = (grave: Entity): void => { - const gravesList: Grave[] = JSON.parse(getProperties(world, GravesListDynamicProperties).list); - - const filteredGraves: Grave[] = gravesList.filter((g: Grave): boolean => g.id !== grave.id); - - setProperties(world, GravesListDynamicProperties, { list: JSON.stringify(filteredGraves) }); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/giveGraveKey.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/giveGraveKey.ts deleted file mode 100644 index 38c570bbe..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/giveGraveKey.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Container, EntityComponentTypes, EntityInventoryComponent, ItemStack, Player } from '@minecraft/server'; -import { GravesItemTypes } from '../Models'; - -/** - * Adds a Grave Key item to the player's inventory. - * - * @param {Player} player - The player to which the Grave Key will be given. - */ -export const giveGraveKey = (player: Player): void => { - const playerInventory: EntityInventoryComponent | undefined = player.getComponent(EntityComponentTypes.Inventory); - const playerContainer: Container | undefined = playerInventory?.container; - - playerContainer?.addItem(new ItemStack(GravesItemTypes.GraveKey)); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/index.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/index.ts deleted file mode 100644 index ca6ce91fc..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -export { giveGraveKey } from './giveGraveKey'; -export { forceOpenGrave, openGrave, spawnGrave, tickGrave } from './Grave'; -export { listAllGraves } from './listGraves'; -export { getSettings, initializeSettings, setSettings } from './settings'; -export { uninstall } from './uninstall'; - diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/listGraves.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/listGraves.ts deleted file mode 100644 index b84e90309..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/listGraves.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { - Player, - system, - TicksPerSecond, - world -} from '@minecraft/server'; -import { Grave, GravesList, GravesListDynamicProperties } from '../Models'; -import { getProperties } from '../Util'; - -/** - * Lists all graves associated with the player. - * - * Retrieves the global graves list, organizes them by owner, sorts each owner's graves by spawn time, - * and sends the information to the specified player. If no graves are found, notifies the player accordingly. - * - * @param {Player} player - The player to whom the grave list will be sent. - */ -// TODO move to UI in UI update -export const listAllGraves = (player: Player): void => { - const gravesList: Grave[] = JSON.parse(getProperties(world, GravesListDynamicProperties).list); - - // Groups graves by owner - const gravesByOwner: { [owner: string]: Grave[] } = - gravesList.reduce((accumulator: { [owner: string]: Grave[] }, grave: Grave): { [owner: string]: Grave[] } => { - if (!accumulator[grave.ownerId]) { - accumulator[grave.ownerId] = []; - } - accumulator[grave.ownerId].push(grave); - - return accumulator; - }, {}); - - // Sort each owner's graves by spawnTime (oldest to newest) - const sortedGraves: Grave[][] = Object.values(gravesByOwner).map((group: Grave[]): Grave[] => group.sort((a: Grave, b: Grave): number => a.spawnTime - b.spawnTime)); - - if (sortedGraves.length) { - player.sendMessage({ translate: 'bt.graves.list_graves.title' }); - sortedGraves.forEach((playerGraves: Grave[]): void => { - player.sendMessage({ translate: 'bt.graves.list_graves.owner', with: [playerGraves[0].ownerName ?? ''] }); - - playerGraves.forEach((grave: Grave): void => { - const { hours, minutes }: { hours: number; minutes: number } = getTimeDifference(grave.spawnTime, system.currentTick); - - player.sendMessage({ - translate: 'bt.graves.list_graves.grave', - with: [ - grave.location.x.toFixed(0), - grave.location.y.toFixed(0), - grave.location.z.toFixed(0), - grave.dimension, - hours + '', - minutes + '', - grave.itemCount + '', - grave.playerExperience + '', - ], - }); - }); - }); - } else { - player.sendMessage({ translate: 'bt.graves.list_graves.empty' }); - } -}; - -/** - * Calculates the time difference between two times. - * - * Converts the difference in ticks to hours and minutes. - * - * @param {number} time1 - The first time in ticks. - * @param {number} time2 - The second time in ticks. - * @returns {{ hours: number; minutes: number }} - The difference represented in hours and minutes. - */ -const getTimeDifference = (time1: number, time2: number): { hours: number; minutes: number } => { - // Calculate the difference in ticks - const tickDifference: number = Math.abs(time1 - time2); - - // Convert the tick difference to seconds - const timeDifferenceInSeconds: number = tickDifference / TicksPerSecond; - - // Convert the time difference to hours and minutes - const hours: number = Math.floor(timeDifferenceInSeconds / 3600); // 3600 seconds in an hour - const minutes: number = Math.floor(timeDifferenceInSeconds % 3600 / 60); // Remaining seconds to minutes - - return { hours, minutes }; -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/settings.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/settings.ts deleted file mode 100644 index 74998d4e6..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/settings.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { world } from '@minecraft/server'; -import { GravesListDynamicProperties, GravesSettings, GravesSettingsDynamicProperties } from '../Models'; -import { getProperties, setProperties } from '../Util'; - -/** - * Initializes the general addon settings for graves if they are not already initialized. - * Sets default values for them and ensures required properties are set. - */ -export const initializeSettings = (): void => { - if (!getProperties(world, GravesSettingsDynamicProperties).initialized) { - setProperties( - world, - GravesSettingsDynamicProperties, - { - initialized: true, - graveLocating: true, - xpCollection: true, - graveRobbing: false, - despawnTime: 0, - keepInventory: world.gameRules.keepInventory, - }, - ); - - // Technically not a setting, but needed to be initialized - setProperties(world, GravesListDynamicProperties, { list: JSON.stringify([]) }); - - world.gameRules.keepInventory = true; - } -}; - -/** - * Retrieves the current addon settings from the world properties. - * - * @returns {GravesSettings} - The current graves settings - */ -export const getSettings = (): GravesSettings => getProperties(world, GravesSettingsDynamicProperties); - -/** - * Updates the addon settings in the world properties. - * - * @param {GravesSettings} graveSettings - The updated settings to be saved. - */ -export const setSettings = (graveSettings: GravesSettings): void => { - setProperties(world, GravesSettingsDynamicProperties, graveSettings); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/uninstall.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/uninstall.ts deleted file mode 100644 index 85dca476c..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Actions/uninstall.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { world } from '@minecraft/server'; -import { getSettings } from './settings'; -import { GravesSettings } from '../Models'; - -/** - * Uninstalls the grave addon by restoring game rules and clearing dynamic properties. - */ -export const uninstall = (): void => { - const settings: GravesSettings = getSettings(); - - world.gameRules.keepInventory = settings.keepInventory; - - world.clearDynamicProperties(); - world.sendMessage({ translate: 'bt.graves.uninstall' }); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityDie.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityDie.ts deleted file mode 100644 index 6254642c2..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityDie.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { - EntityDieAfterEvent, - Player, - world -} from '@minecraft/server'; -import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; -import { spawnGrave } from '../Actions'; - -world.afterEvents.entityDie.subscribe(({ deadEntity }: EntityDieAfterEvent): void => { - if (deadEntity.matches({ type: MinecraftEntityTypes.Player })) { - spawnGrave(deadEntity as Player); - } -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityHit.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityHit.ts deleted file mode 100644 index 8ea89476f..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/EntityHit.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - EntityHitEntityAfterEvent, - Player, - world -} from '@minecraft/server'; -import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; -import { openGrave } from '../Actions'; -import { GravesEntityTypes } from '../Models'; - -world.afterEvents.entityHitEntity.subscribe(({ damagingEntity, hitEntity }: EntityHitEntityAfterEvent): void => { - if ( - damagingEntity.matches({ type: MinecraftEntityTypes.Player }) && - hitEntity.matches({ type: GravesEntityTypes.Grave }) - ) { - openGrave(damagingEntity as Player, hitEntity); - } -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/GameRuleChange.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/GameRuleChange.ts deleted file mode 100644 index dbca4379c..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/GameRuleChange.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { - GameRule, - GameRuleChangeAfterEvent, - world -} from '@minecraft/server'; - -world.afterEvents.gameRuleChange.subscribe(({ rule, value }: GameRuleChangeAfterEvent): void => { - if (rule === GameRule.KeepInventory && value === false) { - world.sendMessage({ translate: 'bt.graves.keep_inventory' }); - - world.gameRules.keepInventory = true; - } -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/PlayerInteractWithEntity.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/PlayerInteractWithEntity.ts deleted file mode 100644 index bd272cad1..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/PlayerInteractWithEntity.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { - PlayerInteractWithEntityAfterEvent, - world -} from '@minecraft/server'; -import { forceOpenGrave } from '../Actions'; -import { GravesEntityTypes, GravesItemTypes } from '../Models'; - -world.afterEvents.playerInteractWithEntity.subscribe(({ target, itemStack }: PlayerInteractWithEntityAfterEvent): void => { - if (target.matches({ type: GravesEntityTypes.Grave }) && itemStack?.matches(GravesItemTypes.GraveKey)) { - forceOpenGrave(target); - } -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/ScriptEventReceive.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/ScriptEventReceive.ts deleted file mode 100644 index dd9c778fb..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/ScriptEventReceive.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { - Player, - ScriptEventCommandMessageAfterEvent, - system -} from '@minecraft/server'; -import { uninstall } from '../Actions'; -import { GravesScriptEvents } from '../Models'; -import { openConfigInterface } from '../UI'; -import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; - -system.afterEvents.scriptEventReceive.subscribe(({ id, sourceEntity }: ScriptEventCommandMessageAfterEvent): void => { - switch (id) { - case GravesScriptEvents.config: - if (sourceEntity?.matches({ type: MinecraftEntityTypes.Player })) { - openConfigInterface(sourceEntity as Player); - } - break; - - case GravesScriptEvents.uninstall: - uninstall(); - break; - - default: - break; - } -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/WorldInitialize.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/WorldInitialize.ts deleted file mode 100644 index 424ef0a3a..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/WorldInitialize.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { world } from '@minecraft/server'; -import { initializeSettings } from '../Actions'; - -world.afterEvents.worldLoad.subscribe((): void => { - initializeSettings(); -}); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/index.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/index.ts deleted file mode 100644 index aac30dd90..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Events/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import './EntityDie'; -import './EntityHit'; -import './GameRuleChange'; -import './PlayerInteractWithEntity'; -import './ScriptEventReceive'; -import './WorldInitialize'; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/DynamicProperties.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/DynamicProperties.ts deleted file mode 100644 index 8be516ec6..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/DynamicProperties.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Vector3 } from '@minecraft/server'; -import { MinecraftDimensionTypes } from '@minecraft/vanilla-data'; - -/** - * Definitions to use multiple Dynamic Properties as a Typed Object - * Enum keys and object properties should match for proper conversion. - */ -export enum GravesSettingsDynamicProperties { - initialized = 'bt:g.settings_initialized', - graveRobbing = 'bt:g.settings_grave_robbing', - xpCollection = 'bt:g.settings_xp_collection', - graveLocating = 'bt:g.settings_grave_locating', - despawnTime = 'bt:g.settings_despawn_time', - keepInventory = 'bt:g.keep_inventory' -} - -export interface GravesSettings { - // Indicates whether the graves system settings have been initialized. - initialized: boolean; - // Controls whether grave robbing is allowed. - graveRobbing: boolean; - // Controls whether XP can be collected from graves. - xpCollection: boolean; - // Controls whether there will be a chat message with the coordinates of their death - graveLocating: boolean; - // Specifies the time (in seconds) before graves automatically despawn and their contents are deleted - despawnTime: number; - // Stores the value of the keepInventory gamerule before installing the addon to be restored on uninstall - keepInventory: boolean; -} - -export enum GraveDynamicProperties { - id = 'bt:g.id', - ownerId = 'bt:g.owner_id', - ownerName = 'bt.g.owner_name', - location = 'bt:g.location', - dimension = 'bt:g.dimension', - spawnTime = 'bt:g.spawn_time', - itemCount = 'bt:g.item_count', - playerExperience = 'bt:g.player_experience' -} - -export interface Grave { - // The ID of the Grave - id: string; - // The ID of the player who owns the grave. - ownerId: string; - // The nametag of the player who owns the grave. - // Note: player nametags might change, so we check owner with id, but we also save the name to be able to display it in - // the graves list as you cannot get offline player data. If player changes nametag while there are graves on the world, - // and they do not spawn a new grave, it will show the old nametag but because is associated with id, the player will - // still be able to open the grave - ownerName: string; - // The location of the grave in the world - location: Vector3; - // The dimension where the grave is located - dimension: MinecraftDimensionTypes; - // The time in ticks since the start of the world indicating when the grave was created - spawnTime: number; - // The total number of items stored in the grave - itemCount: number; - // The amount of experience points stored in the grave - playerExperience: number; -} - -export enum GravesListDynamicProperties { - list = 'bt:g.graves_list' -} - -export interface GravesList { - // Stringified Grave[] - list: string; -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/EntityTypes.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/EntityTypes.ts deleted file mode 100644 index d62bd58dd..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/EntityTypes.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum GravesEntityTypes { - Grave = 'bt:g.grave' -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ItemTypes.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ItemTypes.ts deleted file mode 100644 index 658bf282d..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ItemTypes.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum GravesItemTypes { - GraveKey = 'bt:g.grave_key' -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ScriptEvents.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ScriptEvents.ts deleted file mode 100644 index 1d4b1a3b5..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/ScriptEvents.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum GravesScriptEvents { - config = 'bt:g.config', - uninstall = 'bt:g.uninstall' -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/index.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/index.ts deleted file mode 100644 index db1d71cbb..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Models/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { Grave, GraveDynamicProperties, GravesList, GravesListDynamicProperties, GravesSettings, GravesSettingsDynamicProperties } from './DynamicProperties'; -export { GravesEntityTypes } from './EntityTypes'; -export { GravesItemTypes } from './ItemTypes'; -export { GravesScriptEvents } from './ScriptEvents'; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Config.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Config.ts deleted file mode 100644 index 09ababd80..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Config.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Player } from '@minecraft/server'; -import { ActionFormData, ActionFormResponse } from '@minecraft/server-ui'; -import { giveGraveKey, listAllGraves } from '../Actions'; -import { openSettingsInterface } from './Settings'; - -export const openConfigInterface = (player: Player): void => { - const form: ActionFormData = new ActionFormData() - .title({ translate: 'bt.graves.config.title' }) - .button({ translate: 'bt.graves.config.change_settings' }) - .button({ translate: 'bt.graves.config.list_all_graves' }) - .button({ translate: 'bt.graves.config.receive_grave_key' }); - - form.show(player).then((response: ActionFormResponse): void => { - switch (response.selection) { - case 0: - openSettingsInterface(player); - - break; - case 1: - listAllGraves(player); - - break; - case 2: - giveGraveKey(player); - - break; - default: - break; - } - }); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Settings.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Settings.ts deleted file mode 100644 index 37aec0ec6..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/Settings.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Player } from '@minecraft/server'; -import { ModalFormData, ModalFormResponse } from '@minecraft/server-ui'; -import { getSettings, setSettings } from '../Actions'; -import { GravesSettings } from '../Models'; -import { openConfigInterface } from './Config'; - -export const openSettingsInterface = (player: Player): void => { - const gravesSettings: GravesSettings = getSettings(); - - const form: ModalFormData = new ModalFormData() - .title({ translate: 'bt.graves.settings.title' }) - .toggle({ translate: 'bt.graves.settings.grave_robbing', with: ['\n'] }, { defaultValue: gravesSettings.graveRobbing }) - .toggle({ translate: 'bt.graves.settings.xp_collection', with: ['\n'] }, { defaultValue: gravesSettings.xpCollection }) - .toggle({ translate: 'bt.graves.settings.grave_locating', with: ['\n'] }, { defaultValue: gravesSettings.graveLocating }) - .slider({ translate: 'bt.graves.settings.despawn_time.hours', with: ['\n'] }, 0, 24, { defaultValue: Math.floor(gravesSettings.despawnTime / 3600), valueStep: 1 }) - .slider({ translate: 'bt.graves.settings.despawn_time.minutes', with: ['\n'] }, 0, 59, { defaultValue: Math.floor(gravesSettings.despawnTime % 3600 / 60), valueStep: 1 }) - .slider({ translate: 'bt.graves.settings.despawn_time.seconds', with: ['\n'] }, 0, 59, { defaultValue: gravesSettings.despawnTime % 60, valueStep: 1 }); - - form.show(player).then((response: ModalFormResponse): void => { - if (response.formValues) { - // Toggle = boolean - // Slider = number - // Togggle, Toggle, Toggle, Slider, Slider, Slider - const formValues: [boolean, boolean, boolean, number, number, number] = response.formValues as [boolean, boolean, boolean, number, number, number]; - - gravesSettings.graveRobbing = formValues[0]; - gravesSettings.xpCollection = formValues[1]; - gravesSettings.graveLocating = formValues[2]; - gravesSettings.despawnTime = formValues[3] * 3600 + formValues[4] * 60 + formValues[5]; - } - - if (!response.canceled) { - setSettings(gravesSettings); - - openConfigInterface(player); - } - }); -}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/index.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/index.ts deleted file mode 100644 index 13a5d6510..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { openConfigInterface } from './Config'; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/DynamicProperties.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/DynamicProperties.ts deleted file mode 100644 index 811d374e6..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/DynamicProperties.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Entity, Vector3, World } from '@minecraft/server'; - -type PropertiesTypes = boolean | number | string | Vector3 | undefined; - -/** - * Converts the properties of the world or an entity from the enumType to a JS Object of type T - * Enum keys and object properties should match for proper conversion. - * - * @template T - The type of the resulting object. - * @param {World | Entity} from - The world or entity from which the properties are retrieved. - * @param {{ [key: string]: string }} enumType - An object mapping property keys to dynamic property identifiers. - * - * @returns {T} - An object containing the properties extracted based on the enumType. - */ -export function getProperties(from: World | Entity, enumType: { [key: string]: string }): T { - const propertiesObject: Record = {}; - - Object.entries(enumType).forEach(([key, value]: [string, string]): void => { - propertiesObject[key] = from.getDynamicProperty(value); - }); - - return propertiesObject as T; -} - -/** - * Converts a JavaScript object into dynamic properties based on the `enumType` - * and saves them in the provided world or entity. - * Enum keys and object properties should match for proper conversion. - * - * @param {World | Entity} to - The world or entity where the properties will be set. - * @param {{ [key: string]: string }} enumType - An object mapping property keys to dynamic property identifiers. - * @param {object} propertyObject - A JavaScript object containing the properties to be set. - */ -export function setProperties(to: World | Entity, enumType: { [key: string]: string }, propertyObject: object): void { - Object.entries(propertyObject).forEach(([key, value]: [string, PropertiesTypes]): void => { - const identifier: string = enumType[key]; - - if (identifier) { - to.setDynamicProperty(identifier, value); - } - }); -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/Scoreboard.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/Scoreboard.ts deleted file mode 100644 index 10da408e4..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/Scoreboard.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ScoreboardIdentity, ScoreboardObjective, ScoreboardScoreInfo, world } from '@minecraft/server'; - -/** - * Converts the in-game Scoreboard Objective participants to a JS Object - * - * @template T - The type of the resulting object. - * @param {string} objectiveName - The name of the scoreboard objective to retrieve. - * - * @returns {T} - An object where the keys are participant display names (Player, Entity, or FakePlayer) and the values are their respective scores. - */ -export function getObjective(objectiveName: string): T { - // Any object with keys as string and values as number - const scoreboardObject: Record = {}; - const scoreboardObjective: ScoreboardObjective | undefined = world.scoreboard.getObjective(objectiveName); - - scoreboardObjective?.getScores().forEach((score: ScoreboardScoreInfo): void => { - scoreboardObject[score.participant.displayName] = score.score; - }); - - return scoreboardObject as T; -} - -/** - * Converts a JS Object with key, value pairs of to an in-game Scoreboard Objective participants - * - * @param {string} objectiveName - The name of the scoreboard objective to update. - * @param {object} scoreboardObject - An object where the keys are participant display names (Player, Entity, or FakePlayer) and the values are their respective scores. - */ -export function setObjective(objectiveName: string, scoreboardObject: object): void { - const scoreboardObjective: ScoreboardObjective | undefined = world.scoreboard.getObjective(objectiveName); - const participants: ScoreboardIdentity[] | undefined = scoreboardObjective?.getParticipants(); - - Object.entries(scoreboardObject).forEach(([key, value]: [string, number]): void => { - const player: ScoreboardIdentity | undefined = participants?.find( - (participant: ScoreboardIdentity): boolean => participant.displayName === key, - ); - player && scoreboardObjective?.setScore(player, value); - }); -} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/index.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/index.ts deleted file mode 100644 index 58c2584f7..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/Util/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { getObjective, setObjective } from './Scoreboard'; -export { getProperties, setProperties } from './DynamicProperties'; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts deleted file mode 100644 index 39c20a7bf..000000000 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { system, TicksPerSecond } from '@minecraft/server'; -import { tickGrave } from './Actions'; -import './Events'; - -system.runInterval((): void => { - tickGrave(); -}, TicksPerSecond); diff --git a/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json b/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json deleted file mode 100644 index 3336bd857..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "format_version": "1.20.41", - "minecraft:client_entity": { - "description": { - "identifier": "bt:g.grave", - "materials": { - "default": "entity_alphatest" - }, - "textures": { - "default": "textures/entity/grave" - }, - "geometry": { - "default": "geometry.bt.grave" - }, - "render_controllers": [ - "controller.render.default" - ] - } - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/manifest.json b/addons/files/gameplay_changes/graves/packs/RP/manifest.json deleted file mode 100644 index 4c4ff560e..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/manifest.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "format_version": 2, - "header": { - "name": "Graves RP - Gameplay Changes - Bedrock Tweaks", - "description": "When you die it places a grave to save all your drops", - "uuid": "e9412201-d5f3-4d0f-bb25-cc3bb051c9b9", - "pack_scope": "world", - "version": [ - 1, - 0, - 0 - ], - "min_engine_version": [ - 1, - 21, - 50 - ] - }, - "modules": [ - { - "type": "resources", - "uuid": "7976dd03-48fb-4705-8a46-5d378c59a913", - "version": [ - 1, - 0, - 0 - ] - } - ], - "dependencies": [ - { - "uuid": "c7bec17f-9394-4cad-8f14-4aae4374d51f", - "version": [ - 1, - 0, - 0 - ] - } - ], - "metadata": { - "product_type": "addon", - "authors": [ - "Vanilla Tweaks", - "Bedrock Tweaks", - "DrAv0011" - ] - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json b/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json deleted file mode 100644 index fccb2a50e..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "format_version": "1.12.0", - "minecraft:geometry": [ - { - "description": { - "identifier": "geometry.bt.grave", - "texture_width": 64, - "texture_height": 64, - "visible_bounds_width": 6145, - "visible_bounds_height": 65, - "visible_bounds_offset": [ - 0, - 0.5, - 0 - ] - }, - "bones": [ - { - "name": "root", - "pivot": [ - 0, - 0, - 0 - ] - }, - { - "name": "bone", - "parent": "root", - "pivot": [ - 0, - 0, - 0 - ], - "cubes": [ - { - "origin": [ - -4, - 0, - -4 - ], - "size": [ - 8, - 16, - 8 - ], - "uv": { - "north": { - "uv": [ - 16, - 0 - ], - "uv_size": [ - 8, - 16 - ] - }, - "east": { - "uv": [ - 16, - 16 - ], - "uv_size": [ - 8, - 16 - ] - }, - "south": { - "uv": [ - 24, - 0 - ], - "uv_size": [ - 8, - 16 - ] - }, - "west": { - "uv": [ - 24, - 16 - ], - "uv_size": [ - 8, - 16 - ] - }, - "up": { - "uv": [ - 16, - 32 - ], - "uv_size": [ - 8, - 8 - ] - }, - "down": { - "uv": [ - 24, - 40 - ], - "uv_size": [ - 8, - -8 - ] - } - } - }, - { - "origin": [ - -8, - 0, - -3 - ], - "size": [ - 16, - 13, - 6 - ], - "uv": { - "north": { - "uv": [ - 0, - 0 - ], - "uv_size": [ - 16, - 13 - ] - }, - "east": { - "uv": [ - 32, - 0 - ], - "uv_size": [ - 6, - 13 - ] - }, - "south": { - "uv": [ - 0, - 13 - ], - "uv_size": [ - 16, - 13 - ] - }, - "west": { - "uv": [ - 32, - 13 - ], - "uv_size": [ - 6, - 13 - ] - }, - "up": { - "uv": [ - 0, - 26 - ], - "uv_size": [ - 16, - 6 - ] - }, - "down": { - "uv": [ - 0, - 38 - ], - "uv_size": [ - 16, - -6 - ] - } - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png b/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png deleted file mode 100644 index 83ab8fd5d..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d49a6cf5a2f50ddae7f0d0cb724fb82247c01dd90e21dce305a8b0dd9759938 -size 6369 diff --git a/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang b/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang deleted file mode 100644 index 6ca8a5a8c..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang +++ /dev/null @@ -1,29 +0,0 @@ - -item.bt:g.grave_key=Grave Key - -bt.graves.force_open_grave=Force open grave - -bt.graves.keep_inventory=§4Graves requires keepInventory to be enabled, rolling back change - -bt.graves.location=§6Your last grave is at §e(%1, %2, %3)§6 in %4. - -bt.graves.list_graves.title=§l- All graves - -bt.graves.list_graves.owner=§7%s: -bt.graves.list_graves.grave= §e(%1, %2, %3) §6in the %4 §7§o- %5h, %6m ago - §r§7Item count: §f%7§7, XP points: §f%8 -bt.graves.list_graves.empty=§4This world doesn't have any graves - -bt.graves.config.title=Graves / Configuration -bt.graves.config.change_settings=Change Settings -bt.graves.config.list_all_graves=List All Graves -bt.graves.config.receive_grave_key=Receive Grave Key - -bt.graves.settings.title=Graves / Global Settings - -bt.graves.settings.grave_robbing=Grave Robbing%1§7When enabled, player can open graves they do not own. %1§8Default: Disabled -bt.graves.settings.xp_collection=XP Collection%1§7When enabled, graves collect player XP on death. %1§8Default: Enabled -bt.graves.settings.grave_locating=Grave Locating%1§7When enabled, players can see the coordinates of their graves. %1§8Default: Enabled -bt.graves.settings.despawn_time.hours=Grave Despawn Time%1§7The time after which a grave and it's contents are deleted.%1A grave's despawn timer only runs while the grave is loaded.%1Enter §f0§7 to disable grave despawning. Items normally take §f5§7 minutes to despawn.%1§8Default: 0 hours, 0 minutes, 0 seconds§r%1Current hours -bt.graves.settings.despawn_time.minutes=Current minutes -bt.graves.settings.despawn_time.seconds=Current seconds - -bt.graves.uninstall=Uninstalled correctly, remove the pack from Behavior Packs to complete the uninstall \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png b/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png deleted file mode 100644 index d2ab40fb7..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a04e9dcb3daac4dc81065e37c18a3fcd24b9dd7c9d5822f950dbf8ffbc566ab8 -size 473 diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json b/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json deleted file mode 100644 index 44839f31d..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "resource_pack_name": "bedrock_tweaks_graves", - "texture_name": "atlas.items", - "texture_data": { - "bt:grave_key": { - "textures": "textures/items/grave_key" - } - } -} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png b/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png deleted file mode 100644 index 8141f73bb..000000000 --- a/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:17ba97eaf45db9315aee31bd24c32460eae1e07955d93b5da15d1d2435d3b9b2 -size 289 diff --git a/addons/files/gameplay_changes/graves/packs/data/.gitkeep b/addons/files/gameplay_changes/graves/packs/data/.gitkeep deleted file mode 100644 index 49ae7b096..000000000 --- a/addons/files/gameplay_changes/graves/packs/data/.gitkeep +++ /dev/null @@ -1,3 +0,0 @@ -# Info - -Custom data folder used by some filters, regolith requires the existence of this directory. diff --git a/addons/files/gameplay_changes/graves/tsconfig.json b/addons/files/gameplay_changes/graves/tsconfig.json deleted file mode 100644 index 381cd5c03..000000000 --- a/addons/files/gameplay_changes/graves/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2023", - "module": "esnext", - "declaration": true, - "declarationMap": false, - "sourceMap": true, - "inlineSources": true, - "rootDir": "packs/BP/scripts", - "stripInternal": true, - "strict": true, - "moduleResolution": "bundler", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - "allowSyntheticDefaultImports": true, - "noImplicitAny": true, - "removeComments": true, - "preserveConstEnums": true, - "noImplicitReturns": true, - "allowJs": false, - "resolveJsonModule": true - }, - "files": [ - "packs/BP/scripts/main.ts" - ], - "include": [ - "packs/BP/scripts/**/*" - ], - "exclude": [ - "node_modules", - "**/__tests__/*", - "eslint.config.mjs" - ] -} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 49dc8f943..afd95c8b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,17 +5,73 @@ __metadata: version: 9 cacheKey: 10c0 +"@bedrock-core/config@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/config@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/flexbox@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/flexbox@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/guides@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/guides@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/navigation@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/navigation@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/ore-styled@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/ore-styled@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/server-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/server-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/sync@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/sync@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + +"@bedrock-core/ui-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": + version: 0.0.0-use.local + resolution: "@bedrock-core/ui-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" + languageName: node + linkType: soft + "@bedrock-tweaks/graves@workspace:addons/files/gameplay_changes/graves": version: 0.0.0-use.local resolution: "@bedrock-tweaks/graves@workspace:addons/files/gameplay_changes/graves" dependencies: + "@bedrock-core/config": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config" + "@bedrock-core/flexbox": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox" + "@bedrock-core/guides": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides" + "@bedrock-core/navigation": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation" + "@bedrock-core/ore-styled": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled" + "@bedrock-core/server-runtime": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime" + "@bedrock-core/sync": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync" + "@bedrock-core/ui-runtime": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime" "@eslint/js": "npm:^9.38.0" "@eslint/json": "npm:^0.13.2" - "@minecraft/common": "npm:1.2.0" - "@minecraft/math": "npm:2.2.11" - "@minecraft/server": "npm:2.2.0" - "@minecraft/server-ui": "npm:^2.0.0" - "@minecraft/vanilla-data": "npm:1.21.114" + "@minecraft/common": "npm:1.3.0" + "@minecraft/math": "npm:2.4.0" + "@minecraft/server": "npm:2.8.0" + "@minecraft/server-ui": "npm:2.1.0" + "@minecraft/vanilla-data": "npm:1.26.32" "@stylistic/eslint-config": "npm:^1.1.0" "@stylistic/eslint-plugin": "npm:^5.4.0" "@typescript-eslint/eslint-plugin": "npm:^8.46.1" @@ -265,46 +321,46 @@ __metadata: languageName: node linkType: hard -"@minecraft/common@npm:1.2.0, @minecraft/common@npm:^1.0.0": - version: 1.2.0 - resolution: "@minecraft/common@npm:1.2.0" - checksum: 10c0/597c3ff8ab275ba5d5fb3037e68970e59ac96e8793b7738178c95b17d22a8f48a4412425314ed494f664466dd9a342e2a9eff52af544e57689f103c2ae965e10 +"@minecraft/common@npm:1.3.0": + version: 1.3.0 + resolution: "@minecraft/common@npm:1.3.0" + checksum: 10c0/6f335029426b6bab2b0c22562863c6a0681d855767ffa8d8e3f0e579ce940505ad2175bd4c96bb5c35b90344ee758757d1d7807908623b6fe00f5f73ea596911 languageName: node linkType: hard -"@minecraft/math@npm:2.2.11": - version: 2.2.11 - resolution: "@minecraft/math@npm:2.2.11" +"@minecraft/math@npm:2.4.0": + version: 2.4.0 + resolution: "@minecraft/math@npm:2.4.0" peerDependencies: "@minecraft/server": ^1.15.0 || ^2.0.0 - checksum: 10c0/28600e0d9933f9a0e0feb1d5fb338eb2d193e6ca7567eb8573f517cdabfa1609b52e4860f59dab66faece29f92120a5c981a6645efd73a0182d573668d595d7d + checksum: 10c0/b38640384285cef98d7757b1e2587e729dd2dd00cdd25716b0e594f12598d7dfca35ec9c0fc02631f246967d5276e0a8b2a312cc890a42fefa56c4e8429d87e8 languageName: node linkType: hard -"@minecraft/server-ui@npm:^2.0.0": - version: 2.0.0 - resolution: "@minecraft/server-ui@npm:2.0.0" - dependencies: - "@minecraft/common": "npm:^1.0.0" - "@minecraft/server": "npm:^2.0.0" - checksum: 10c0/5cf1b82b87b86837530e7d63a879dbca33bc04d3ceaa9398bf504e9f0f2a0e3a352d1635aef82388d453e815716c841c101616cb66fbba6c39d3f610a2c09ab7 +"@minecraft/server-ui@npm:2.1.0": + version: 2.1.0 + resolution: "@minecraft/server-ui@npm:2.1.0" + peerDependencies: + "@minecraft/common": ^1.0.0 + "@minecraft/server": ^2.0.0 + checksum: 10c0/b32fd20d0d384b227d6168860d43b0094069c5503a9982061cb324e85f8be432f2efb6daec9556c5c4a399f203a9c0fbea6d16d2d375249fdda0b676cffe1aa3 languageName: node linkType: hard -"@minecraft/server@npm:2.2.0, @minecraft/server@npm:^2.0.0": - version: 2.2.0 - resolution: "@minecraft/server@npm:2.2.0" +"@minecraft/server@npm:2.8.0": + version: 2.8.0 + resolution: "@minecraft/server@npm:2.8.0" peerDependencies: "@minecraft/common": ^1.2.0 "@minecraft/vanilla-data": ">=1.20.70" - checksum: 10c0/16ea1fb69c7e322a1738b22cc3b183170f71da1f1a41e64b13e74f0135b1ec92d4ff991cacd2cc55b71849ad059827e56827b462f23cbe51e40f78e1b79ccdac + checksum: 10c0/bb4f8a8a90b52831786f51ce320bbf2a8494f7d71d4d6329c22c9710a10a36ed7096acd4f1ff9d2c9971d95c391b8ba36ef674bda47629f9c671c9134994786d languageName: node linkType: hard -"@minecraft/vanilla-data@npm:1.21.114": - version: 1.21.114 - resolution: "@minecraft/vanilla-data@npm:1.21.114" - checksum: 10c0/77a66fe4643ec08f0054dc9afe0f021d9250ea245c5ca5b2a0d3fda13df8bf5c40231ec5d759611a392a7dbff5f377945c66ad6594d840128dfa406f70cd95c2 +"@minecraft/vanilla-data@npm:1.26.32": + version: 1.26.32 + resolution: "@minecraft/vanilla-data@npm:1.26.32" + checksum: 10c0/7e17dc24e0c588194f6f196e3f69ea0589e23f55187e456e635ecc2e28bc3cd68960e67e56cd53ff8d122f44c0c85c3f32686aab86da0d7a860dd736e8f210fc languageName: node linkType: hard From 8f5b49bdce3dcb399a0dd19aa9edd9573d95c8ba Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 02:05:43 +0200 Subject: [PATCH 02/11] chore(files): addon scaffolding - Added `yarn create-addon` (tools/create-addon.mjs), a wrapper around the bedrock-core CLI which scaffolds addons into addons/files/// with the BT conventions (creator `bt`, `bt__` namespace, author credit, private `@bedrock-tweaks/` workspace package, category workspace registration) - Removed the legacy templates/addon boilerplate, replaced by templates/addon.md pointing to the scaffolder --- package.json | 1 + templates/addon.md | 10 + templates/addon/.gitignore | 2 - templates/addon/.mcignore | 2 - templates/addon/.vscode/extensions.json | 13 - templates/addon/.vscode/launch.json | 16 - templates/addon/.vscode/settings.json | 41 - templates/addon/config.json | 48 - templates/addon/eslint.config.mjs | 397 ----- templates/addon/filters/.gitkeep | 3 - templates/addon/package.json | 31 - templates/addon/packs/BP/manifest.json | 60 - .../packs/BP/scripts/Actions/Uninstall.ts | 6 - .../addon/packs/BP/scripts/Actions/index.ts | 10 - .../packs/BP/scripts/Events/EntityDie.ts | 13 - .../addon/packs/BP/scripts/Events/index.ts | 7 - .../packs/BP/scripts/Models/EntityTypes.ts | 3 - .../addon/packs/BP/scripts/Models/index.ts | 10 - .../addon/packs/BP/scripts/UI/Example.ts | 14 - templates/addon/packs/BP/scripts/UI/index.ts | 7 - .../BP/scripts/Util/DynamicProperties.ts | 45 - .../addon/packs/BP/scripts/Util/Scoreboard.ts | 39 - .../addon/packs/BP/scripts/Util/index.ts | 9 - templates/addon/packs/BP/scripts/main.ts | 14 - templates/addon/packs/BP/texts/en_GB.lang | 2 - templates/addon/packs/BP/texts/en_US.lang | 2 - templates/addon/packs/BP/texts/languages.json | 4 - templates/addon/packs/RP/manifest.json | 48 - templates/addon/packs/RP/texts/en_GB.lang | 14 - templates/addon/packs/RP/texts/en_US.lang | 14 - templates/addon/packs/RP/texts/languages.json | 4 - templates/addon/packs/data/.gitkeep | 3 - templates/addon/tsconfig.json | 35 - templates/addon/yarn.lock | 1458 ----------------- tools/create-addon.mjs | 184 +++ 35 files changed, 195 insertions(+), 2374 deletions(-) create mode 100644 templates/addon.md delete mode 100644 templates/addon/.gitignore delete mode 100644 templates/addon/.mcignore delete mode 100644 templates/addon/.vscode/extensions.json delete mode 100644 templates/addon/.vscode/launch.json delete mode 100644 templates/addon/.vscode/settings.json delete mode 100644 templates/addon/config.json delete mode 100644 templates/addon/eslint.config.mjs delete mode 100644 templates/addon/filters/.gitkeep delete mode 100644 templates/addon/package.json delete mode 100644 templates/addon/packs/BP/manifest.json delete mode 100644 templates/addon/packs/BP/scripts/Actions/Uninstall.ts delete mode 100644 templates/addon/packs/BP/scripts/Actions/index.ts delete mode 100644 templates/addon/packs/BP/scripts/Events/EntityDie.ts delete mode 100644 templates/addon/packs/BP/scripts/Events/index.ts delete mode 100644 templates/addon/packs/BP/scripts/Models/EntityTypes.ts delete mode 100644 templates/addon/packs/BP/scripts/Models/index.ts delete mode 100644 templates/addon/packs/BP/scripts/UI/Example.ts delete mode 100644 templates/addon/packs/BP/scripts/UI/index.ts delete mode 100644 templates/addon/packs/BP/scripts/Util/DynamicProperties.ts delete mode 100644 templates/addon/packs/BP/scripts/Util/Scoreboard.ts delete mode 100644 templates/addon/packs/BP/scripts/Util/index.ts delete mode 100644 templates/addon/packs/BP/scripts/main.ts delete mode 100644 templates/addon/packs/BP/texts/en_GB.lang delete mode 100644 templates/addon/packs/BP/texts/en_US.lang delete mode 100644 templates/addon/packs/BP/texts/languages.json delete mode 100644 templates/addon/packs/RP/manifest.json delete mode 100644 templates/addon/packs/RP/texts/en_GB.lang delete mode 100644 templates/addon/packs/RP/texts/en_US.lang delete mode 100644 templates/addon/packs/RP/texts/languages.json delete mode 100644 templates/addon/packs/data/.gitkeep delete mode 100644 templates/addon/tsconfig.json delete mode 100644 templates/addon/yarn.lock create mode 100644 tools/create-addon.mjs diff --git a/package.json b/package.json index ea240dc74..6053aeb6f 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "addons/files/*" ], "scripts": { + "create-addon": "node tools/create-addon.mjs", "regolith-install": "yarn workspaces foreach -i -A --topological --jobs=1 run regolith-install", "build": "yarn workspaces foreach -i -A run build", "lint": "yarn workspaces foreach -i -A run lint", diff --git a/templates/addon.md b/templates/addon.md new file mode 100644 index 000000000..45c757eaf --- /dev/null +++ b/templates/addon.md @@ -0,0 +1,10 @@ +# Addon Template + +Scaffold new addons from the repo root with the wrapper around the +[bedrock-core](https://bedrock-core.drav.dev/) CLI: + +```bash +yarn create-addon [description...] +``` + +See the Addons section in [CONTRIBUTING.md](../CONTRIBUTING.md). diff --git a/templates/addon/.gitignore b/templates/addon/.gitignore deleted file mode 100644 index 3f195ca9a..000000000 --- a/templates/addon/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/.regolith \ No newline at end of file diff --git a/templates/addon/.mcignore b/templates/addon/.mcignore deleted file mode 100644 index 704c8aa46..000000000 --- a/templates/addon/.mcignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/.regolith diff --git a/templates/addon/.vscode/extensions.json b/templates/addon/.vscode/extensions.json deleted file mode 100644 index 25477bef4..000000000 --- a/templates/addon/.vscode/extensions.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "recommendations": [ - "aaron-bond.better-comments", - "blockceptionltd.blockceptionvscodeminecraftbedrockdevelopmentextension", - "dbaeumer.vscode-eslint", - "mojang-studios.minecraft-debugger", - "misodee.vscode-nbt", - "bedrockoss.regolith", - "jannisx11.snowstorm", - "netcorext.uuid-generator", - "christian-kohler.npm-intellisense" - ] -} \ No newline at end of file diff --git a/templates/addon/.vscode/launch.json b/templates/addon/.vscode/launch.json deleted file mode 100644 index 3fbbe8bd7..000000000 --- a/templates/addon/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "version": "0.3.0", - "configurations": [ - { - "type": "minecraft-js", - "request": "attach", - "sourceMapRoot": "${env:LOCALAPPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/development_behavior_packs/ - - Bedrock Tweaks/scripts/", - "generatedSourceRoot": "${env:LOCALAPPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang/development_behavior_packs/ - - Bedrock Tweaks/scripts/", - "localRoot": "${workspaceFolder}/packs/BP/scripts/", - "name": "Debug with Minecraft", - "mode": "listen", - "port": 19144, - "targetModuleUuid": "" - } - ] -} \ No newline at end of file diff --git a/templates/addon/.vscode/settings.json b/templates/addon/.vscode/settings.json deleted file mode 100644 index 03f36e123..000000000 --- a/templates/addon/.vscode/settings.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "editor.formatOnSave": false, - "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit" - }, - "editor.detectIndentation": false, - "editor.insertSpaces": false, - "editor.tabSize": 2, - "[typescript]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[javascript]": { - "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[json]": { - "editor.defaultFormatter": "vscode.json-language-features", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "[jsonc]": { - "editor.defaultFormatter": "vscode.json-language-features", - "editor.insertSpaces": false, - "editor.tabSize": 4 - }, - "typescript.tsdk": "node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true, - "eslint.validate": [ - "typescript", - "json", - "jsonc" - ], - "files.associations": { - "*.json": "jsonc" - }, - "eslint.enable": true, - "eslint.format.enable": true -} \ No newline at end of file diff --git a/templates/addon/config.json b/templates/addon/config.json deleted file mode 100644 index da80d1913..000000000 --- a/templates/addon/config.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/Bedrock-OSS/regolith-schemas/main/config/v1.4.json", - "name": "bedrock-tweaks/", - "author": "", - "packs": { - "behaviorPack": "./packs/BP", - "resourcePack": "./packs/RP" - }, - "regolith": { - "dataPath": "./packs/data", - "formatVersion": "1.4.0", - "filterDefinitions": { - "bundler": { - "url": "github.com/bedrock-core/regolith-filters", - "version": "1.0.1" - } - }, - "profiles": { - "build": { - "export": { - "build": "standard", - "readOnly": false, - "target": "local" - }, - "filters": [ - { - "filter": "bundler" - } - ] - }, - "default": { - "export": { - "build": "standard", - "readOnly": false, - "target": "development" - }, - "filters": [ - { - "filter": "bundler", - "settings": { - "debug": true - } - } - ] - } - } - } -} \ No newline at end of file diff --git a/templates/addon/eslint.config.mjs b/templates/addon/eslint.config.mjs deleted file mode 100644 index c772a561f..000000000 --- a/templates/addon/eslint.config.mjs +++ /dev/null @@ -1,397 +0,0 @@ -import minecraftLinting from "eslint-plugin-minecraft-linting"; -import stylistic from "@stylistic/eslint-plugin"; -import { defineConfig } from "eslint/config"; -import tseslint from "typescript-eslint"; -import json from "@eslint/json"; -import { fileURLToPath } from "url"; -import { dirname } from "path"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -export default defineConfig([ - { - ignores: [ - ".*/**", // Any directory starting with dot (.yarn, .vscode, .regolith, etc.) - ".*", // Any file starting with dot - "node_modules/**", - "**/*.*js", // Generated JS files - "filters/**", // The filters directory - "build/**", // Build output - "*.json", // Root level JSON files (config.json, package.json, tsconfig.json) - "*.md", // Root level markdown files - "*.mjs", // Root level mjs files (like this config) - "*.js", // Root level js files - ], - }, - - { - plugins: { - json, - }, - }, - - // lint JSON files - { - files: ["**/*.json"], - language: "json/jsonc", - rules: { - "json/no-duplicate-keys": "error", - "json/no-empty-keys": "off", - "json/no-unnormalized-keys": "error", - "json/no-unsafe-values": "error", - "json/sort-keys": "off", - "json/top-level-interop": "off", - } - }, - - { - files: ["packs/BP/scripts/**/*.ts"], - plugins: { - "@stylistic": stylistic, - "@typescript-eslint": tseslint.plugin, - "@minecraft": minecraftLinting - }, - - languageOptions: { - parser: tseslint.parser, - ecmaVersion: "latest", - sourceType: "module", - parserOptions: { - project: ["tsconfig.json"], - tsconfigRootDir: __dirname, - }, - }, - - rules: { - "no-unused-expressions": "off", - "@typescript-eslint/no-unused-expressions": "off", - "@typescript-eslint/explicit-member-accessibility": ["error", - { - accessibility: "no-public", - } - ], - - "@typescript-eslint/no-restricted-types": [ - "error", - { - "types": { - "Object": { - "message": "Avoid using the `Object` type. Did you mean `object`?" - }, - "Function": { - "message": "Avoid using the `Function` type. Prefer a specific function type, like `() => void`." - }, - "Boolean": { - "message": "Avoid using the `Boolean` type. Did you mean `boolean`?" - }, - "Number": { - "message": "Avoid using the `Number` type. Did you mean `number`?" - }, - "String": { - "message": "Avoid using the `String` type. Did you mean `string`?" - }, - "Symbol": { - "message": "Avoid using the `Symbol` type. Did you mean `symbol`?" - } - } - } - ], - "@typescript-eslint/no-wrapper-object-types": "error", - "@typescript-eslint/no-unsafe-function-type": "error", - "@typescript-eslint/no-empty-object-type": ["error", - { - allowInterfaces: 'always' - } - ], - "@typescript-eslint/ban-ts-comment": ["error", - { - 'ts-expect-error': 'allow-with-description' - } - ], - - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/dot-notation": "error", - - "@typescript-eslint/member-ordering": ["error", - { - default: [ - "private-field", - "protected-field", - "public-field", - "constructor", - "public-method", - "protected-method", - "private-method", - ], - } - ], - - "@typescript-eslint/naming-convention": ["error", - { - selector: "default", - format: ["camelCase"], - }, - { - selector: "default", - modifiers: ["unused"], - - filter: { - regex: "^(_|_.*_?)$", - match: true, - }, - - format: null, - }, - { - selector: "variable", - format: ["camelCase"], - }, - { - selector: ["parameter"], - format: ["camelCase", "snake_case"], - }, - { - selector: "variable", - modifiers: ["const"], - format: ["camelCase", "UPPER_CASE", "PascalCase"], - }, - { - selector: ["enum", "enumMember", "function"], - format: ["camelCase", "UPPER_CASE", "PascalCase"], - }, - { - selector: ["property", "parameterProperty", "accessor"], - modifiers: ["private"], - format: ["camelCase", "snake_case"], - leadingUnderscore: "require", - }, - { - selector: ["property", "parameterProperty", "accessor"], - modifiers: ["private", "readonly"], - format: ["UPPER_CASE", "camelCase"], - leadingUnderscore: "allow", - }, - { - selector: ["property"], - modifiers: ["readonly"], - format: ["camelCase", "UPPER_CASE"], - }, - { - selector: ["objectLiteralProperty", "typeProperty"], - format: ["camelCase", "snake_case", "UPPER_CASE"], - }, - { - selector: "typeLike", - format: ["PascalCase"], - } - ], - - "@typescript-eslint/explicit-function-return-type": ["error", - { - allowExpressions: true, - } - ], - - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/default-param-last": "warn", - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-unnecessary-boolean-literal-compare": "warn", - "@typescript-eslint/prefer-enum-initializers": "warn", - - "@typescript-eslint/unbound-method": ["error", - { - ignoreStatic: true, - } - ], - - eqeqeq: ["error", "always", - { - null: "ignore", - } - ], - - "object-shorthand": "error", - - "@typescript-eslint/no-unused-vars": ["error", - { - argsIgnorePattern: "^(_|_.*_?)$", - } - ], - - "arrow-body-style": "warn", - curly: ["warn", "multi-line", "consistent"], - "@typescript-eslint/array-type": "warn", - "@typescript-eslint/prefer-optional-chain": "warn", - "@typescript-eslint/prefer-reduce-type-parameter": "warn", - "@stylistic/array-bracket-newline": "off", - "@stylistic/array-bracket-spacing": ["warn"], - "@stylistic/array-element-newline": "off", - "@stylistic/arrow-parens": ["warn", "as-needed"], - "@stylistic/arrow-spacing": ["warn"], - "@stylistic/block-spacing": ["warn", "always"], - - "@stylistic/brace-style": ["warn", "1tbs", - { - allowSingleLine: true, - } - ], - - "@stylistic/comma-dangle": ["warn", - { - arrays: "always-multiline", - objects: "always-multiline", - imports: "never", - exports: "never", - functions: "always-multiline", - enums: "never", - generics: "never", - tuples: "always-multiline", - } - ], - - "@stylistic/comma-spacing": ["warn", - { - before: false, - after: true, - } - ], - - "@stylistic/computed-property-spacing": ["warn"], - "@stylistic/dot-location": ["warn", "property"], - "@stylistic/eol-last": ["warn"], - "@stylistic/function-call-argument-newline": ["warn", "consistent"], - "@stylistic/function-paren-newline": ["warn", "consistent"], - "@stylistic/generator-star-spacing": "off", - "@stylistic/implicit-arrow-linebreak": ["warn", "beside"], - "@stylistic/indent": ["warn", "tab"], - "@stylistic/jsx-quotes": "off", - - "@stylistic/key-spacing": ["warn", - { - beforeColon: false, - afterColon: true, - mode: "strict", - } - ], - - "@stylistic/keyword-spacing": ["warn"], - "@stylistic/linebreak-style": ["warn", "windows"], - - "@stylistic/lines-around-comment": ["warn", - { - afterBlockComment: false, - beforeLineComment: false, - afterLineComment: false, - allowBlockStart: true, - allowClassStart: true, - allowObjectStart: true, - allowArrayStart: true, - } - ], - - "@stylistic/lines-between-class-members": ["warn", "always", - { - exceptAfterSingleLine: true, - exceptAfterOverload: false, - } - ], - - "@stylistic/max-len": "off", - "@stylistic/max-statements-per-line": "off", - "@stylistic/multiline-ternary": "off", - "@stylistic/new-parens": ["warn"], - "@stylistic/newline-per-chained-call": "off", - "@stylistic/no-confusing-arrow": "off", - "@stylistic/no-extra-parens": ["warn"], - "@stylistic/no-extra-semi": ["warn"], - "@stylistic/no-floating-decimal": ["warn"], - "@stylistic/no-mixed-operators": "off", - "@stylistic/no-mixed-spaces-and-tabs": ["warn"], - "@stylistic/no-multi-spaces": ["warn"], - - "@stylistic/no-multiple-empty-lines": ["warn", - { - max: 1, - } - ], - - "@stylistic/no-tabs": ["off"], - "@stylistic/no-trailing-spaces": ["warn"], - "@stylistic/no-whitespace-before-property": ["warn"], - "@stylistic/nonblock-statement-body-position": "off", - - "@stylistic/object-curly-newline": ["warn", - { - multiline: true, - } - ], - - "@stylistic/object-curly-spacing": ["warn", "always"], - "@stylistic/object-property-newline": "off", - "@stylistic/one-var-declaration-per-line": "off", - "@stylistic/operator-linebreak": "off", - "@stylistic/padded-blocks": ["warn", "never"], - - "@stylistic/padding-line-between-statements": ["warn", - { - blankLine: "always", - prev: "*", - next: "return", - } - ], - - "@stylistic/quote-props": ["warn", "as-needed"], - - "@stylistic/quotes": ["warn", "single", - { - avoidEscape: true, - allowTemplateLiterals: 'always', - } - ], - - "@stylistic/rest-spread-spacing": ["warn", "never"], - "@stylistic/semi": ["warn", "always"], - "@stylistic/semi-spacing": ["warn"], - "@stylistic/semi-style": ["warn", "last"], - "@stylistic/space-before-blocks": ["warn"], - "@stylistic/space-before-function-paren": ["warn", { - "anonymous": "never", - "named": "never", - "asyncArrow": "never", - "catch": "always" - }], - "@stylistic/space-in-parens": ["warn", "never"], - "@stylistic/space-infix-ops": "warn", - "@stylistic/space-unary-ops": "off", - "@stylistic/spaced-comment": ["warn", "always"], - "@stylistic/switch-colon-spacing": ["warn"], - "@stylistic/template-curly-spacing": ["warn"], - "@stylistic/template-tag-spacing": "off", - "@stylistic/wrap-iife": "off", - "@stylistic/wrap-regex": "off", - "@stylistic/yield-star-spacing": "off", - "@stylistic/member-delimiter-style": ["warn"], - "@stylistic/type-annotation-spacing": "warn", - "@stylistic/jsx-child-element-spacing": "off", - "@stylistic/jsx-closing-bracket-location": "off", - "@stylistic/jsx-closing-tag-location": "off", - "@stylistic/jsx-curly-brace-presence": "off", - "@stylistic/jsx-curly-newline": "off", - "@stylistic/jsx-curly-spacing": "off", - "@stylistic/jsx-equals-spacing": "off", - "@stylistic/jsx-first-prop-new-line": "off", - "@stylistic/jsx-indent": "off", - "@stylistic/jsx-indent-props": "off", - "@stylistic/jsx-max-props-per-line": "off", - "@stylistic/jsx-newline": "off", - "@stylistic/jsx-one-expression-per-line": "off", - "@stylistic/jsx-props-no-multi-spaces": "off", - "@stylistic/jsx-self-closing-comp": "off", - "@stylistic/jsx-sort-props": "off", - "@stylistic/jsx-tag-spacing": "off", - "@stylistic/jsx-wrap-multilines": "off", - "@minecraft/avoid-unnecessary-command": "error", - }, - } -]); diff --git a/templates/addon/filters/.gitkeep b/templates/addon/filters/.gitkeep deleted file mode 100644 index 9d375552f..000000000 --- a/templates/addon/filters/.gitkeep +++ /dev/null @@ -1,3 +0,0 @@ -# Info - -Add any custom filters to automate part of the regolith build diff --git a/templates/addon/package.json b/templates/addon/package.json deleted file mode 100644 index eee8de63b..000000000 --- a/templates/addon/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@bedrock-tweaks/", - "version": "", - "description": "", - "private": true, - "scripts": { - "regolith-install": "regolith install-all", - "build": "regolith run build", - "dev": "regolith watch", - "lint": "npx eslint ." - }, - "dependencies": { - "@minecraft/common": "^1.2.0", - "@minecraft/math": "^2.4.0", - "@minecraft/server": "^2.6.0", - "@minecraft/server-ui": "^2.0.0", - "@minecraft/vanilla-data": "^1.26.13", - "@stylistic/eslint-config": "^1.1.0" - }, - "devDependencies": { - "@eslint/js": "^9.38.0", - "@eslint/json": "^0.13.2", - "@stylistic/eslint-plugin": "^5.4.0", - "@typescript-eslint/eslint-plugin": "^8.46.1", - "@typescript-eslint/parser": "^8.46.1", - "eslint": "^9.38.0", - "eslint-plugin-minecraft-linting": "^2.0.10", - "typescript": "^5.9.3", - "typescript-eslint": "^8.46.1" - } -} \ No newline at end of file diff --git a/templates/addon/packs/BP/manifest.json b/templates/addon/packs/BP/manifest.json deleted file mode 100644 index fd6e81ce4..000000000 --- a/templates/addon/packs/BP/manifest.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "format_version": 2, - "header": { - "name": "pack.name", - "description": "pack.description", - "uuid": "", - "pack_scope": "world", - "version": [ - 1, - 0, - 0 - ], - // Update to latest minecraft release - "min_engine_version": [ - 1, - 26, - 1 - ] - }, - "modules": [ - { - "type": "data", - "uuid": "", - "version": [ - 1, - 0, - 0 - ] - }, - { - "type": "script", - "language": "javascript", - "uuid": "", - "entry": "scripts/main.js", - "version": [ - 1, - 0, - 0 - ] - } - ], - "dependencies": [ - { - "uuid": "", - "version": [ - 1, - 0, - 0 - ] - } - ], - "metadata": { - "product_type": "addon", - "authors": [ - "Vanilla Tweaks", - "Bedrock Tweaks", - "" - ] - } -} \ No newline at end of file diff --git a/templates/addon/packs/BP/scripts/Actions/Uninstall.ts b/templates/addon/packs/BP/scripts/Actions/Uninstall.ts deleted file mode 100644 index a02638fce..000000000 --- a/templates/addon/packs/BP/scripts/Actions/Uninstall.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { world } from '@minecraft/server'; - -export const uninstall = (): void => { - world.clearDynamicProperties(); - world.sendMessage({ translate: 'bt.example.misc.uninstall', with: ['\n'] }); -}; diff --git a/templates/addon/packs/BP/scripts/Actions/index.ts b/templates/addon/packs/BP/scripts/Actions/index.ts deleted file mode 100644 index 53c0d2c1d..000000000 --- a/templates/addon/packs/BP/scripts/Actions/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Actions Module - * - * This module exports the functions which interact between the game and the addon - * - * For more examples, refer to - * @see addons/files/gameplay_changes/graves/packs/BP/scripts/Models - */ -export { uninstall } from './Uninstall'; - diff --git a/templates/addon/packs/BP/scripts/Events/EntityDie.ts b/templates/addon/packs/BP/scripts/Events/EntityDie.ts deleted file mode 100644 index 223b4767b..000000000 --- a/templates/addon/packs/BP/scripts/Events/EntityDie.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { EntityDieAfterEvent, world } from '@minecraft/server'; -import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; - -/** - * We check for conditions in the Event Handlers and run the Actions related to them if the conditions are met - * Prefer casting here after the type was checked rather than in Actions - */ -world.afterEvents.entityDie.subscribe(({ deadEntity }: EntityDieAfterEvent): void => { - if (deadEntity.matches({ type: MinecraftEntityTypes.Player })) { - // Action to run when a player dies - // exampleAction(deadEntity as Player); - } -}); diff --git a/templates/addon/packs/BP/scripts/Events/index.ts b/templates/addon/packs/BP/scripts/Events/index.ts deleted file mode 100644 index 55c14fcfb..000000000 --- a/templates/addon/packs/BP/scripts/Events/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Events Module - * - * This module imports and registers all event handlers. - * Events that have both "before" and "after" versions are organized within the same file. - */ -import './EntityDie'; diff --git a/templates/addon/packs/BP/scripts/Models/EntityTypes.ts b/templates/addon/packs/BP/scripts/Models/EntityTypes.ts deleted file mode 100644 index c48cafe10..000000000 --- a/templates/addon/packs/BP/scripts/Models/EntityTypes.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum ExampleEntityTypes { - Example = 'bt:ex.example' -} diff --git a/templates/addon/packs/BP/scripts/Models/index.ts b/templates/addon/packs/BP/scripts/Models/index.ts deleted file mode 100644 index efa32a781..000000000 --- a/templates/addon/packs/BP/scripts/Models/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Models Module - * - * This module exports the definitions of the types, interfaces and enums of the addon - * It centralizes all model-related exports for easier import and management across different parts of the application. - * - * For more examples, refer to - * @see addons/files/gameplay_changes/graves/packs/BP/scripts/Models - */ -export { ExampleEntityTypes } from './EntityTypes'; diff --git a/templates/addon/packs/BP/scripts/UI/Example.ts b/templates/addon/packs/BP/scripts/UI/Example.ts deleted file mode 100644 index 2857b6828..000000000 --- a/templates/addon/packs/BP/scripts/UI/Example.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Player } from '@minecraft/server'; -import { ActionFormData, ActionFormResponse } from '@minecraft/server-ui'; - -export const openExampleInterface = (player: Player): void => { - const form: ActionFormData = new ActionFormData() - .title({ translate: 'bt.example.config.title' }) - .button({ translate: 'bt.example.config.button' }); - - form.show(player).then((response: ActionFormResponse): void => { - if (response.selection !== undefined && response.selection === 0) { - // Action to run on button press - } - }); -}; diff --git a/templates/addon/packs/BP/scripts/UI/index.ts b/templates/addon/packs/BP/scripts/UI/index.ts deleted file mode 100644 index 5e26d2a21..000000000 --- a/templates/addon/packs/BP/scripts/UI/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * UI Module - * - * This module contains the @minecraft/server-ui forms used in the addon - */ - -export { openExampleInterface } from './Example'; diff --git a/templates/addon/packs/BP/scripts/Util/DynamicProperties.ts b/templates/addon/packs/BP/scripts/Util/DynamicProperties.ts deleted file mode 100644 index 3f734dac2..000000000 --- a/templates/addon/packs/BP/scripts/Util/DynamicProperties.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Entity, Vector3, World } from '@minecraft/server'; - -type PropertiesTypes = boolean | number | string | Vector3 | undefined; - -/** - * Converts the properties of the world or an entity from the enumType to a JS Object of type T - * Enum keys and object properties should match for proper conversion. - * - * @template T - The type of the resulting object. - * @param {World | Entity} from - The world or entity from which the properties are retrieved. - * @param {{ [key: string]: string }} enumType - An object mapping property keys to dynamic property identifiers. - * - * @returns {T} - An object containing the properties extracted based on the enumType. - */ -export function getProperties(from: World | Entity, enumType: { [key: string]: string }): T { - const propertiesObject: Record = {}; - - Object.entries(enumType).forEach(([key, value]: [string, string]): void => { - propertiesObject[key] = from.getDynamicProperty(value); - }); - - return propertiesObject as T; -} - -/** - * Converts a JavaScript object into dynamic properties based on the `enumType` - * and saves them in the provided world or entity. - * Enum keys and object properties should match for proper conversion. - * - * @param {World | Entity} to - The world or entity where the properties will be set. - * @param {{ [key: string]: string }} enumType - An object mapping property keys to dynamic property identifiers. - * @param {object} propertyObject - A JavaScript object containing the properties to be set. - */ -export function setProperties< - T extends Record, - K extends Record ->(to: World | Entity, enumType: K, propertyObject: T): void { - Object.entries(propertyObject).forEach(([key, value]: [string, PropertiesTypes]): void => { - const identifier: string = enumType[key]; - - if (identifier) { - to.setDynamicProperty(identifier, value); - } - }); -} diff --git a/templates/addon/packs/BP/scripts/Util/Scoreboard.ts b/templates/addon/packs/BP/scripts/Util/Scoreboard.ts deleted file mode 100644 index 10da408e4..000000000 --- a/templates/addon/packs/BP/scripts/Util/Scoreboard.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { ScoreboardIdentity, ScoreboardObjective, ScoreboardScoreInfo, world } from '@minecraft/server'; - -/** - * Converts the in-game Scoreboard Objective participants to a JS Object - * - * @template T - The type of the resulting object. - * @param {string} objectiveName - The name of the scoreboard objective to retrieve. - * - * @returns {T} - An object where the keys are participant display names (Player, Entity, or FakePlayer) and the values are their respective scores. - */ -export function getObjective(objectiveName: string): T { - // Any object with keys as string and values as number - const scoreboardObject: Record = {}; - const scoreboardObjective: ScoreboardObjective | undefined = world.scoreboard.getObjective(objectiveName); - - scoreboardObjective?.getScores().forEach((score: ScoreboardScoreInfo): void => { - scoreboardObject[score.participant.displayName] = score.score; - }); - - return scoreboardObject as T; -} - -/** - * Converts a JS Object with key, value pairs of to an in-game Scoreboard Objective participants - * - * @param {string} objectiveName - The name of the scoreboard objective to update. - * @param {object} scoreboardObject - An object where the keys are participant display names (Player, Entity, or FakePlayer) and the values are their respective scores. - */ -export function setObjective(objectiveName: string, scoreboardObject: object): void { - const scoreboardObjective: ScoreboardObjective | undefined = world.scoreboard.getObjective(objectiveName); - const participants: ScoreboardIdentity[] | undefined = scoreboardObjective?.getParticipants(); - - Object.entries(scoreboardObject).forEach(([key, value]: [string, number]): void => { - const player: ScoreboardIdentity | undefined = participants?.find( - (participant: ScoreboardIdentity): boolean => participant.displayName === key, - ); - player && scoreboardObjective?.setScore(player, value); - }); -} diff --git a/templates/addon/packs/BP/scripts/Util/index.ts b/templates/addon/packs/BP/scripts/Util/index.ts deleted file mode 100644 index 4224b8232..000000000 --- a/templates/addon/packs/BP/scripts/Util/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Util Module - * - * This module provides utility functions for the addons - * If you think something might be of use could discuss in the Discord to add it to the template - */ - -export { getObjective, setObjective } from './Scoreboard'; -export { getProperties, setProperties } from './DynamicProperties'; diff --git a/templates/addon/packs/BP/scripts/main.ts b/templates/addon/packs/BP/scripts/main.ts deleted file mode 100644 index d901975f5..000000000 --- a/templates/addon/packs/BP/scripts/main.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { system, TicksPerSecond } from '@minecraft/server'; -import './Events'; - -/** - * Entry point of the addon - * - * We do 2 things here - * Import the Events module - * Execute any system functions which need to be constantly running - */ - -system.runInterval((): void => { - // Action to run on an interval -}, TicksPerSecond); diff --git a/templates/addon/packs/BP/texts/en_GB.lang b/templates/addon/packs/BP/texts/en_GB.lang deleted file mode 100644 index b3a287682..000000000 --- a/templates/addon/packs/BP/texts/en_GB.lang +++ /dev/null @@ -1,2 +0,0 @@ -pack.name= - - Bedrock Tweaks -pack.description= \ No newline at end of file diff --git a/templates/addon/packs/BP/texts/en_US.lang b/templates/addon/packs/BP/texts/en_US.lang deleted file mode 100644 index b3a287682..000000000 --- a/templates/addon/packs/BP/texts/en_US.lang +++ /dev/null @@ -1,2 +0,0 @@ -pack.name= - - Bedrock Tweaks -pack.description= \ No newline at end of file diff --git a/templates/addon/packs/BP/texts/languages.json b/templates/addon/packs/BP/texts/languages.json deleted file mode 100644 index 5f61e6961..000000000 --- a/templates/addon/packs/BP/texts/languages.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "en_GB", - "en_US" -] \ No newline at end of file diff --git a/templates/addon/packs/RP/manifest.json b/templates/addon/packs/RP/manifest.json deleted file mode 100644 index eff630138..000000000 --- a/templates/addon/packs/RP/manifest.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "format_version": 2, - "header": { - "name": "pack.name", - "description": "pack.description", - "uuid": "", - "pack_scope": "world", - "version": [ - 1, - 0, - 0 - ], - // Update to latest minecraft release - "min_engine_version": [ - 1, - 26, - 1 - ] - }, - "modules": [ - { - "type": "resources", - "uuid": "", - "version": [ - 1, - 0, - 0 - ] - } - ], - "dependencies": [ - { - "uuid": "", - "version": [ - 1, - 0, - 0 - ] - } - ], - "metadata": { - "authors": [ - "Vanilla Tweaks", - "Bedrock Tweaks", - "" - ] - } -} \ No newline at end of file diff --git a/templates/addon/packs/RP/texts/en_GB.lang b/templates/addon/packs/RP/texts/en_GB.lang deleted file mode 100644 index 4b401483a..000000000 --- a/templates/addon/packs/RP/texts/en_GB.lang +++ /dev/null @@ -1,14 +0,0 @@ -## In here, we add all the texts that are send to the chat, use in a UI like ModalFormData, etc -## The main idea of adding texts here is to keep them all organized and make them easily translatable to other languages - -## The format of all the keys with their text is as follows -## bt.addon_name.category.action=Text -## addon_name - The name of the addon -## category - The category describes all the related actions associated with it, like "settings" -## action - The action is the specific operation performed related to the category - -## Replace the word "example" with your addon name, using an underscore for spaces if needed, like "anti_creeper_grief" -## Replace the word "Example" with your addon name, like "Anti Creeper Grief" -pack.name= - - Bedrock Tweaks -pack.description= -bt.example.misc.uninstall=§eUninstalled Correctly§r%1Remove the Example pack from Behaviour Packs to complete the uninstall. \ No newline at end of file diff --git a/templates/addon/packs/RP/texts/en_US.lang b/templates/addon/packs/RP/texts/en_US.lang deleted file mode 100644 index 152f8aa7c..000000000 --- a/templates/addon/packs/RP/texts/en_US.lang +++ /dev/null @@ -1,14 +0,0 @@ -## In here, we add all the texts that are send to the chat, use in a UI like ModalFormData, etc -## The main idea of adding texts here is to keep them all organized and make them easily translatable to other languages - -## The format of all the keys with their text is as follows -## bt.addon_name.category.action=Text -## addon_name - The name of the addon -## category - The category describes all the related actions associated with it, like "settings" -## action - The action is the specific operation performed related to the category - -## Replace the word "example" with your addon name, using an underscore for spaces if needed, like "anti_creeper_grief" -## Replace the word "Example" with your addon name, like "Anti Creeper Grief" -pack.name= - - Bedrock Tweaks -pack.description= -bt.example.misc.uninstall=§eUninstalled Correctly§r%1Remove the Example pack from Behavior Packs to complete the uninstall. \ No newline at end of file diff --git a/templates/addon/packs/RP/texts/languages.json b/templates/addon/packs/RP/texts/languages.json deleted file mode 100644 index 5f61e6961..000000000 --- a/templates/addon/packs/RP/texts/languages.json +++ /dev/null @@ -1,4 +0,0 @@ -[ - "en_GB", - "en_US" -] \ No newline at end of file diff --git a/templates/addon/packs/data/.gitkeep b/templates/addon/packs/data/.gitkeep deleted file mode 100644 index 49ae7b096..000000000 --- a/templates/addon/packs/data/.gitkeep +++ /dev/null @@ -1,3 +0,0 @@ -# Info - -Custom data folder used by some filters, regolith requires the existence of this directory. diff --git a/templates/addon/tsconfig.json b/templates/addon/tsconfig.json deleted file mode 100644 index 381cd5c03..000000000 --- a/templates/addon/tsconfig.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2023", - "module": "esnext", - "declaration": true, - "declarationMap": false, - "sourceMap": true, - "inlineSources": true, - "rootDir": "packs/BP/scripts", - "stripInternal": true, - "strict": true, - "moduleResolution": "bundler", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, - "allowSyntheticDefaultImports": true, - "noImplicitAny": true, - "removeComments": true, - "preserveConstEnums": true, - "noImplicitReturns": true, - "allowJs": false, - "resolveJsonModule": true - }, - "files": [ - "packs/BP/scripts/main.ts" - ], - "include": [ - "packs/BP/scripts/**/*" - ], - "exclude": [ - "node_modules", - "**/__tests__/*", - "eslint.config.mjs" - ] -} \ No newline at end of file diff --git a/templates/addon/yarn.lock b/templates/addon/yarn.lock deleted file mode 100644 index f326349f1..000000000 --- a/templates/addon/yarn.lock +++ /dev/null @@ -1,1458 +0,0 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 8 - cacheKey: 10c0 - -"@bedrock-tweaks/@workspace:.": - version: 0.0.0-use.local - resolution: "@bedrock-tweaks/@workspace:." - dependencies: - "@eslint/js": "npm:^9.38.0" - "@eslint/json": "npm:^0.13.2" - "@minecraft/common": "npm:1.2.0" - "@minecraft/math": "npm:2.2.11" - "@minecraft/server": "npm:2.2.0" - "@minecraft/server-ui": "npm:^2.0.0" - "@minecraft/vanilla-data": "npm:1.21.114" - "@stylistic/eslint-config": "npm:^1.1.0" - "@stylistic/eslint-plugin": "npm:^5.4.0" - "@typescript-eslint/eslint-plugin": "npm:^8.46.1" - "@typescript-eslint/parser": "npm:^8.46.1" - eslint: "npm:^9.38.0" - eslint-plugin-minecraft-linting: "npm:^2.0.10" - typescript: "npm:^5.9.3" - typescript-eslint: "npm:^8.46.1" - languageName: unknown - linkType: soft - -"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0, @eslint-community/eslint-utils@npm:^4.9.0": - version: 4.9.0 - resolution: "@eslint-community/eslint-utils@npm:4.9.0" - dependencies: - eslint-visitor-keys: "npm:^3.4.3" - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - checksum: 10c0/8881e22d519326e7dba85ea915ac7a143367c805e6ba1374c987aa2fbdd09195cc51183d2da72c0e2ff388f84363e1b220fd0d19bef10c272c63455162176817 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.12.1": - version: 4.12.1 - resolution: "@eslint-community/regexpp@npm:4.12.1" - checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 - languageName: node - linkType: hard - -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" - dependencies: - "@eslint/object-schema": "npm:^2.1.7" - debug: "npm:^4.3.1" - minimatch: "npm:^3.1.2" - checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c - languageName: node - linkType: hard - -"@eslint/config-helpers@npm:^0.4.1": - version: 0.4.1 - resolution: "@eslint/config-helpers@npm:0.4.1" - dependencies: - "@eslint/core": "npm:^0.16.0" - checksum: 10c0/bb7dd534019a975320ac0f8e0699b37433cee9a3731354c1ee941648e6651032386e7848792060fb53a0fd603ea6cf7a101ed3bd5b82ee2f641598986d1e080a - languageName: node - linkType: hard - -"@eslint/core@npm:^0.15.2": - version: 0.15.2 - resolution: "@eslint/core@npm:0.15.2" - dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/c17a6dc4f5a6006ecb60165cc38bcd21fefb4a10c7a2578a0cfe5813bbd442531a87ed741da5adab5eb678e8e693fda2e2b14555b035355537e32bcec367ea17 - languageName: node - linkType: hard - -"@eslint/core@npm:^0.16.0": - version: 0.16.0 - resolution: "@eslint/core@npm:0.16.0" - dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/f27496a244ccfdca3e0fbc3331f9da3f603bdf1aa431af0045a3205826789a54493bc619ad6311a9090eaf7bc25798ff4e265dea1eccd2df9ce3b454f7e7da27 - languageName: node - linkType: hard - -"@eslint/eslintrc@npm:^3.3.1": - version: 3.3.1 - resolution: "@eslint/eslintrc@npm:3.3.1" - dependencies: - ajv: "npm:^6.12.4" - debug: "npm:^4.3.2" - espree: "npm:^10.0.1" - globals: "npm:^14.0.0" - ignore: "npm:^5.2.0" - import-fresh: "npm:^3.2.1" - js-yaml: "npm:^4.1.0" - minimatch: "npm:^3.1.2" - strip-json-comments: "npm:^3.1.1" - checksum: 10c0/b0e63f3bc5cce4555f791a4e487bf999173fcf27c65e1ab6e7d63634d8a43b33c3693e79f192cbff486d7df1be8ebb2bd2edc6e70ddd486cbfa84a359a3e3b41 - languageName: node - linkType: hard - -"@eslint/js@npm:9.38.0, @eslint/js@npm:^9.38.0": - version: 9.38.0 - resolution: "@eslint/js@npm:9.38.0" - checksum: 10c0/b4a0d561ab93f0b1bc6a3f5e3f83764c9cccade59f2c54f1d718c1dcc71ac4d1be97bef7300cca641932d72e7555c79a7bf07e4e4ce1d0a1ddccc84d6440d2a6 - languageName: node - linkType: hard - -"@eslint/json@npm:^0.13.2": - version: 0.13.2 - resolution: "@eslint/json@npm:0.13.2" - dependencies: - "@eslint/core": "npm:^0.15.2" - "@eslint/plugin-kit": "npm:^0.3.5" - "@humanwhocodes/momoa": "npm:^3.3.9" - natural-compare: "npm:^1.4.0" - checksum: 10c0/1e024e33687a2cb26261856f0c5d459d06100b2e09718d6b1e8b63f6595e879fea6a7944f97abd07309b937773fc75c7dc4d61f4fc070422a0c8740c40334774 - languageName: node - linkType: hard - -"@eslint/object-schema@npm:^2.1.7": - version: 2.1.7 - resolution: "@eslint/object-schema@npm:2.1.7" - checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.3.5": - version: 0.3.5 - resolution: "@eslint/plugin-kit@npm:0.3.5" - dependencies: - "@eslint/core": "npm:^0.15.2" - levn: "npm:^0.4.1" - checksum: 10c0/c178c1b58c574200c0fd125af3e4bc775daba7ce434ba6d1eeaf9bcb64b2e9fea75efabffb3ed3ab28858e55a016a5efa95f509994ee4341b341199ca630b89e - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.4.0": - version: 0.4.0 - resolution: "@eslint/plugin-kit@npm:0.4.0" - dependencies: - "@eslint/core": "npm:^0.16.0" - levn: "npm:^0.4.1" - checksum: 10c0/125614e902bb34c041da859794c47ac2ec4a814f5d9e7c4d37fcd34b38d8ee5cf1f97020d38d168885d9bf4046a9a7decb86b4cee8dac9eedcc6ad08ebafe204 - languageName: node - linkType: hard - -"@humanfs/core@npm:^0.19.1": - version: 0.19.1 - resolution: "@humanfs/core@npm:0.19.1" - checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67 - languageName: node - linkType: hard - -"@humanfs/node@npm:^0.16.6": - version: 0.16.7 - resolution: "@humanfs/node@npm:0.16.7" - dependencies: - "@humanfs/core": "npm:^0.19.1" - "@humanwhocodes/retry": "npm:^0.4.0" - checksum: 10c0/9f83d3cf2cfa37383e01e3cdaead11cd426208e04c44adcdd291aa983aaf72d7d3598844d2fe9ce54896bb1bf8bd4b56883376611c8905a19c44684642823f30 - languageName: node - linkType: hard - -"@humanwhocodes/module-importer@npm:^1.0.1": - version: 1.0.1 - resolution: "@humanwhocodes/module-importer@npm:1.0.1" - checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 - languageName: node - linkType: hard - -"@humanwhocodes/momoa@npm:^3.3.9": - version: 3.3.9 - resolution: "@humanwhocodes/momoa@npm:3.3.9" - checksum: 10c0/591e1ef6b6e299a57006a052199a0b5f854dfbd0016c7862eb21fe7441a1426ef2ea75c006863693eb98fc46d4a60a82319546adb0277df2814b543a8dad7a7b - languageName: node - linkType: hard - -"@humanwhocodes/retry@npm:^0.4.0, @humanwhocodes/retry@npm:^0.4.2": - version: 0.4.3 - resolution: "@humanwhocodes/retry@npm:0.4.3" - checksum: 10c0/3775bb30087d4440b3f7406d5a057777d90e4b9f435af488a4923ef249e93615fb78565a85f173a186a076c7706a81d0d57d563a2624e4de2c5c9c66c486ce42 - languageName: node - linkType: hard - -"@minecraft/common@npm:1.2.0, @minecraft/common@npm:^1.0.0": - version: 1.2.0 - resolution: "@minecraft/common@npm:1.2.0" - checksum: 10c0/597c3ff8ab275ba5d5fb3037e68970e59ac96e8793b7738178c95b17d22a8f48a4412425314ed494f664466dd9a342e2a9eff52af544e57689f103c2ae965e10 - languageName: node - linkType: hard - -"@minecraft/math@npm:2.2.11": - version: 2.2.11 - resolution: "@minecraft/math@npm:2.2.11" - peerDependencies: - "@minecraft/server": ^1.15.0 || ^2.0.0 - checksum: 10c0/28600e0d9933f9a0e0feb1d5fb338eb2d193e6ca7567eb8573f517cdabfa1609b52e4860f59dab66faece29f92120a5c981a6645efd73a0182d573668d595d7d - languageName: node - linkType: hard - -"@minecraft/server-ui@npm:^2.0.0": - version: 2.0.0 - resolution: "@minecraft/server-ui@npm:2.0.0" - dependencies: - "@minecraft/common": "npm:^1.0.0" - "@minecraft/server": "npm:^2.0.0" - checksum: 10c0/5cf1b82b87b86837530e7d63a879dbca33bc04d3ceaa9398bf504e9f0f2a0e3a352d1635aef82388d453e815716c841c101616cb66fbba6c39d3f610a2c09ab7 - languageName: node - linkType: hard - -"@minecraft/server@npm:2.2.0, @minecraft/server@npm:^2.0.0": - version: 2.2.0 - resolution: "@minecraft/server@npm:2.2.0" - peerDependencies: - "@minecraft/common": ^1.2.0 - "@minecraft/vanilla-data": ">=1.20.70" - checksum: 10c0/16ea1fb69c7e322a1738b22cc3b183170f71da1f1a41e64b13e74f0135b1ec92d4ff991cacd2cc55b71849ad059827e56827b462f23cbe51e40f78e1b79ccdac - languageName: node - linkType: hard - -"@minecraft/vanilla-data@npm:1.21.114": - version: 1.21.114 - resolution: "@minecraft/vanilla-data@npm:1.21.114" - checksum: 10c0/77a66fe4643ec08f0054dc9afe0f021d9250ea245c5ca5b2a0d3fda13df8bf5c40231ec5d759611a392a7dbff5f377945c66ad6594d840128dfa406f70cd95c2 - languageName: node - linkType: hard - -"@nodelib/fs.scandir@npm:2.1.5": - version: 2.1.5 - resolution: "@nodelib/fs.scandir@npm:2.1.5" - dependencies: - "@nodelib/fs.stat": "npm:2.0.5" - run-parallel: "npm:^1.1.9" - checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb - languageName: node - linkType: hard - -"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": - version: 2.0.5 - resolution: "@nodelib/fs.stat@npm:2.0.5" - checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d - languageName: node - linkType: hard - -"@nodelib/fs.walk@npm:^1.2.3": - version: 1.2.8 - resolution: "@nodelib/fs.walk@npm:1.2.8" - dependencies: - "@nodelib/fs.scandir": "npm:2.1.5" - fastq: "npm:^1.6.0" - checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 - languageName: node - linkType: hard - -"@stylistic/eslint-config@npm:^1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-config@npm:1.1.0" - dependencies: - "@stylistic/eslint-plugin-js": "npm:1.1.0" - "@stylistic/eslint-plugin-ts": "npm:1.1.0" - checksum: 10c0/bb19a26f0c329c6454416f5c170ef3ffde1ed924c448c2faa34aabffbe04c65f47b004ef812e7253c2325c49c064726ddfe35c6dab6c66cdce50390f06fec34c - languageName: node - linkType: hard - -"@stylistic/eslint-plugin-js@npm:1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-plugin-js@npm:1.1.0" - dependencies: - acorn: "npm:^8.11.2" - escape-string-regexp: "npm:^4.0.0" - eslint-visitor-keys: "npm:^3.4.3" - espree: "npm:^9.6.1" - graphemer: "npm:^1.4.0" - checksum: 10c0/15cb4cf4e47f0f63c06592b2c399c3a3795781f2efe099c55e3a29cb380b3043407e86f090e58e7e55419a3acfd7429195c752c9eb8d94e0358b45758efe64c1 - languageName: node - linkType: hard - -"@stylistic/eslint-plugin-ts@npm:1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-plugin-ts@npm:1.1.0" - dependencies: - "@stylistic/eslint-plugin-js": "npm:1.1.0" - "@typescript-eslint/utils": "npm:^6.10.0" - graphemer: "npm:^1.4.0" - peerDependencies: - eslint: "*" - checksum: 10c0/6f89f7b73794e3f346c35008fbfc28a530a5ad66f03d8b94c4571220a720053298f1ab815909170c1df22fdd44353084dd6b6530f9d0f7a7bddda6f5a1e7b336 - languageName: node - linkType: hard - -"@stylistic/eslint-plugin@npm:^5.4.0": - version: 5.4.0 - resolution: "@stylistic/eslint-plugin@npm:5.4.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.9.0" - "@typescript-eslint/types": "npm:^8.44.0" - eslint-visitor-keys: "npm:^4.2.1" - espree: "npm:^10.4.0" - estraverse: "npm:^5.3.0" - picomatch: "npm:^4.0.3" - peerDependencies: - eslint: ">=9.0.0" - checksum: 10c0/02db4ec387c75300f07417641fb26eb41fd2a202608d1d752ed799cb72a8cea270abcc0a36eafa2ab7488e8cbe5a51e778afa56100f69ade572d1ec4051e8883 - languageName: node - linkType: hard - -"@types/estree@npm:^1.0.6": - version: 1.0.8 - resolution: "@types/estree@npm:1.0.8" - checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5 - languageName: node - linkType: hard - -"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.15": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db - languageName: node - linkType: hard - -"@types/semver@npm:^7.5.0": - version: 7.7.1 - resolution: "@types/semver@npm:7.7.1" - checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268 - languageName: node - linkType: hard - -"@typescript-eslint/eslint-plugin@npm:8.46.1, @typescript-eslint/eslint-plugin@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.46.1" - dependencies: - "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.46.1" - "@typescript-eslint/type-utils": "npm:8.46.1" - "@typescript-eslint/utils": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - graphemer: "npm:^1.4.0" - ignore: "npm:^7.0.0" - natural-compare: "npm:^1.4.0" - ts-api-utils: "npm:^2.1.0" - peerDependencies: - "@typescript-eslint/parser": ^8.46.1 - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/7a269f7dc3f6d900b9a7caefc0ab455406aae7fc0c0a198b1f18623c1c47bd54c6769777b0d8a2ef2e674a60124470d85394feb5fae4991c84c6a37875f75410 - languageName: node - linkType: hard - -"@typescript-eslint/parser@npm:8.46.1, @typescript-eslint/parser@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/parser@npm:8.46.1" - dependencies: - "@typescript-eslint/scope-manager": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - debug: "npm:^4.3.4" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/4d14e9dbd5b4ba6001d35ae8833b1b03588911d44b1e01a7e38b1883148c3b1d22e4d4de50e5c6a698a4697ef067e235524b521023d0f5a830767d54c8c5fff5 - languageName: node - linkType: hard - -"@typescript-eslint/project-service@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/project-service@npm:8.46.1" - dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.46.1" - "@typescript-eslint/types": "npm:^8.46.1" - debug: "npm:^4.3.4" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/7218bb343eb371e468596947ef66f0ad5024a76f2787550e093af0fc2b34e1bba3e86840bdec719afd26368e9f75c1ea4ab09bdc84610a746acd89b66910cf8b - languageName: node - linkType: hard - -"@typescript-eslint/rule-tester@npm:^8.18.1": - version: 8.46.1 - resolution: "@typescript-eslint/rule-tester@npm:8.46.1" - dependencies: - "@typescript-eslint/parser": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/utils": "npm:8.46.1" - ajv: "npm:^6.12.6" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - lodash.merge: "npm:4.6.2" - semver: "npm:^7.6.0" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - checksum: 10c0/1528fe639881d842424525912811cc21ae970e2d807c8717f9152f3347e656b09f373d3441f5e966cf323a9de5065f9bc0a3770c9abc2d1d8c3fc061ca69d733 - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - checksum: 10c0/eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526 - languageName: node - linkType: hard - -"@typescript-eslint/scope-manager@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/scope-manager@npm:8.46.1" - dependencies: - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - checksum: 10c0/5cff63677e90f3307fe924b739a3fe9f5239f74ec389fa06d6fa0a3fa51f592d8fb038c0c71088157b5b6fb426145bff1239aa3676c05c7d71d3b9be0f8c2cba - languageName: node - linkType: hard - -"@typescript-eslint/tsconfig-utils@npm:8.46.1, @typescript-eslint/tsconfig-utils@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.1" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/c373bd4e2f43e03d8d4dc91cacbc0acdb217809f0e7b23fb4dd349fdab2503489dd79a3adb394491763ec967fa1312c5c9aebdbc5799ad3ed773b036a6eddb9d - languageName: node - linkType: hard - -"@typescript-eslint/type-utils@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/type-utils@npm:8.46.1" - dependencies: - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/utils": "npm:8.46.1" - debug: "npm:^4.3.4" - ts-api-utils: "npm:^2.1.0" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/bcd87755912ad626b496a78e5f3dd8182dd59e815683d6b82a3e9fffc1b52384abfbe4d3faf2ec9b15be67b88e5082a798f35f96624517f82a5026973c251074 - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 10c0/020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:8.46.1, @typescript-eslint/types@npm:^8.44.0, @typescript-eslint/types@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/types@npm:8.46.1" - checksum: 10c0/90887acaa5b33b45af20cf7f87ec4ae098c0daa88484245473e73903fa6e542f613247c22148132167891ca06af6549a60b9d2fd14a65b22871e016901ce3756 - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - debug: "npm:^4.3.4" - globby: "npm:^11.1.0" - is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" - peerDependenciesMeta: - typescript: - optional: true - checksum: 10c0/af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.46.1" - dependencies: - "@typescript-eslint/project-service": "npm:8.46.1" - "@typescript-eslint/tsconfig-utils": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - debug: "npm:^4.3.4" - fast-glob: "npm:^3.3.2" - is-glob: "npm:^4.0.3" - minimatch: "npm:^9.0.4" - semver: "npm:^7.6.0" - ts-api-utils: "npm:^2.1.0" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/610048f615d4487f3dc57b7440214a14614a9dca8783d142e7dd29e2948d9c8239773839a3bcdf509c266d5f8595ea9f3a20c53c38d7b3bf3cf2305de1491bd8 - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:8.46.1, @typescript-eslint/utils@npm:^8.18.1": - version: 8.46.1 - resolution: "@typescript-eslint/utils@npm:8.46.1" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/9089be6b88a934843fd4eead61739e43dc79ba3db3dbaebcd9908eed819765b6414da983254a7d619e89d28b441bd131f53c9f163c39ca5b2369b76cd6699121 - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:^6.10.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.4.0" - "@types/json-schema": "npm:^7.0.12" - "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" - semver: "npm:^7.5.4" - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 10c0/ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" - dependencies: - "@typescript-eslint/types": "npm:6.21.0" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 10c0/7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf - languageName: node - linkType: hard - -"@typescript-eslint/visitor-keys@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.46.1" - dependencies: - "@typescript-eslint/types": "npm:8.46.1" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/4139a8d78ad95e59fff2285beb623a530b7c2e6af89b994a92e9d8728d0c86eb8d86f64f2372aa874f9f24924253ba9887a2f77bec6bfc6028380b024c24e582 - languageName: node - linkType: hard - -"acorn-jsx@npm:^5.3.2": - version: 5.3.2 - resolution: "acorn-jsx@npm:5.3.2" - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 - languageName: node - linkType: hard - -"acorn@npm:^8.11.2, acorn@npm:^8.15.0, acorn@npm:^8.9.0": - version: 8.15.0 - resolution: "acorn@npm:8.15.0" - bin: - acorn: bin/acorn - checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec - languageName: node - linkType: hard - -"ajv@npm:^6.12.4, ajv@npm:^6.12.6": - version: 6.12.6 - resolution: "ajv@npm:6.12.6" - dependencies: - fast-deep-equal: "npm:^3.1.1" - fast-json-stable-stringify: "npm:^2.0.0" - json-schema-traverse: "npm:^0.4.1" - uri-js: "npm:^4.2.2" - checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 - languageName: node - linkType: hard - -"ansi-styles@npm:^4.1.0": - version: 4.3.0 - resolution: "ansi-styles@npm:4.3.0" - dependencies: - color-convert: "npm:^2.0.1" - checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 - languageName: node - linkType: hard - -"argparse@npm:^2.0.1": - version: 2.0.1 - resolution: "argparse@npm:2.0.1" - checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e - languageName: node - linkType: hard - -"array-union@npm:^2.1.0": - version: 2.1.0 - resolution: "array-union@npm:2.1.0" - checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee - languageName: node - linkType: hard - -"brace-expansion@npm:^1.1.7": - version: 1.1.12 - resolution: "brace-expansion@npm:1.1.12" - dependencies: - balanced-match: "npm:^1.0.0" - concat-map: "npm:0.0.1" - checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73 - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.1": - version: 2.0.2 - resolution: "brace-expansion@npm:2.0.2" - dependencies: - balanced-match: "npm:^1.0.0" - checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf - languageName: node - linkType: hard - -"braces@npm:^3.0.3": - version: 3.0.3 - resolution: "braces@npm:3.0.3" - dependencies: - fill-range: "npm:^7.1.1" - checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 - languageName: node - linkType: hard - -"callsites@npm:^3.0.0": - version: 3.1.0 - resolution: "callsites@npm:3.1.0" - checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 - languageName: node - linkType: hard - -"chalk@npm:^4.0.0": - version: 4.1.2 - resolution: "chalk@npm:4.1.2" - dependencies: - ansi-styles: "npm:^4.1.0" - supports-color: "npm:^7.1.0" - checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 - languageName: node - linkType: hard - -"color-convert@npm:^2.0.1": - version: 2.0.1 - resolution: "color-convert@npm:2.0.1" - dependencies: - color-name: "npm:~1.1.4" - checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 - languageName: node - linkType: hard - -"color-name@npm:~1.1.4": - version: 1.1.4 - resolution: "color-name@npm:1.1.4" - checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f - languageName: node - linkType: hard - -"cross-spawn@npm:^7.0.6": - version: 7.0.6 - resolution: "cross-spawn@npm:7.0.6" - dependencies: - path-key: "npm:^3.1.0" - shebang-command: "npm:^2.0.0" - which: "npm:^2.0.1" - checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 - languageName: node - linkType: hard - -"debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": - version: 4.4.3 - resolution: "debug@npm:4.4.3" - dependencies: - ms: "npm:^2.1.3" - peerDependenciesMeta: - supports-color: - optional: true - checksum: 10c0/d79136ec6c83ecbefd0f6a5593da6a9c91ec4d7ddc4b54c883d6e71ec9accb5f67a1a5e96d00a328196b5b5c86d365e98d8a3a70856aaf16b4e7b1985e67f5a6 - languageName: node - linkType: hard - -"deep-is@npm:^0.1.3": - version: 0.1.4 - resolution: "deep-is@npm:0.1.4" - checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c - languageName: node - linkType: hard - -"dir-glob@npm:^3.0.1": - version: 3.0.1 - resolution: "dir-glob@npm:3.0.1" - dependencies: - path-type: "npm:^4.0.0" - checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c - languageName: node - linkType: hard - -"escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 - languageName: node - linkType: hard - -"eslint-plugin-minecraft-linting@npm:^2.0.10": - version: 2.0.10 - resolution: "eslint-plugin-minecraft-linting@npm:2.0.10" - dependencies: - "@typescript-eslint/rule-tester": "npm:^8.18.1" - "@typescript-eslint/utils": "npm:^8.18.1" - eslint: "npm:^9.17.0" - typescript: "npm:^5.6.0" - checksum: 10c0/5865a85a818bbd224eb3be56e95fe45b07b2674c323e9296753dcbf20b8b15e6049faa2bd31a246e64400db5d570612ade00b1a97dbab7fc2d28a5fef5165a81 - languageName: node - linkType: hard - -"eslint-scope@npm:^8.4.0": - version: 8.4.0 - resolution: "eslint-scope@npm:8.4.0" - dependencies: - esrecurse: "npm:^4.3.0" - estraverse: "npm:^5.2.0" - checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": - version: 3.4.3 - resolution: "eslint-visitor-keys@npm:3.4.3" - checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^4.2.1": - version: 4.2.1 - resolution: "eslint-visitor-keys@npm:4.2.1" - checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 - languageName: node - linkType: hard - -"eslint@npm:^9.17.0, eslint@npm:^9.38.0": - version: 9.38.0 - resolution: "eslint@npm:9.38.0" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.8.0" - "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.1" - "@eslint/config-helpers": "npm:^0.4.1" - "@eslint/core": "npm:^0.16.0" - "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.38.0" - "@eslint/plugin-kit": "npm:^0.4.0" - "@humanfs/node": "npm:^0.16.6" - "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.4.2" - "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.12.4" - chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.6" - debug: "npm:^4.3.2" - escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.4.0" - eslint-visitor-keys: "npm:^4.2.1" - espree: "npm:^10.4.0" - esquery: "npm:^1.5.0" - esutils: "npm:^2.0.2" - fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^8.0.0" - find-up: "npm:^5.0.0" - glob-parent: "npm:^6.0.2" - ignore: "npm:^5.2.0" - imurmurhash: "npm:^0.1.4" - is-glob: "npm:^4.0.0" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" - natural-compare: "npm:^1.4.0" - optionator: "npm:^0.9.3" - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true - bin: - eslint: bin/eslint.js - checksum: 10c0/51b0978dce04233580263fd4b5c4f128ecffdcde44fbddfedb5bced48a60d4fc619f5ae91800a1461a78a860b14c77a5081b0b2cf628b705580b70126a11e14b - languageName: node - linkType: hard - -"espree@npm:^10.0.1, espree@npm:^10.4.0": - version: 10.4.0 - resolution: "espree@npm:10.4.0" - dependencies: - acorn: "npm:^8.15.0" - acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b - languageName: node - linkType: hard - -"espree@npm:^9.6.1": - version: 9.6.1 - resolution: "espree@npm:9.6.1" - dependencies: - acorn: "npm:^8.9.0" - acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 10c0/1a2e9b4699b715347f62330bcc76aee224390c28bb02b31a3752e9d07549c473f5f986720483c6469cf3cfb3c9d05df612ffc69eb1ee94b54b739e67de9bb460 - languageName: node - linkType: hard - -"esquery@npm:^1.5.0": - version: 1.6.0 - resolution: "esquery@npm:1.6.0" - dependencies: - estraverse: "npm:^5.1.0" - checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 - languageName: node - linkType: hard - -"esrecurse@npm:^4.3.0": - version: 4.3.0 - resolution: "esrecurse@npm:4.3.0" - dependencies: - estraverse: "npm:^5.2.0" - checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 - languageName: node - linkType: hard - -"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0, estraverse@npm:^5.3.0": - version: 5.3.0 - resolution: "estraverse@npm:5.3.0" - checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 - languageName: node - linkType: hard - -"esutils@npm:^2.0.2": - version: 2.0.3 - resolution: "esutils@npm:2.0.3" - checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 - languageName: node - linkType: hard - -"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": - version: 3.1.3 - resolution: "fast-deep-equal@npm:3.1.3" - checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 - languageName: node - linkType: hard - -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": - version: 3.3.3 - resolution: "fast-glob@npm:3.3.3" - dependencies: - "@nodelib/fs.stat": "npm:^2.0.2" - "@nodelib/fs.walk": "npm:^1.2.3" - glob-parent: "npm:^5.1.2" - merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.8" - checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe - languageName: node - linkType: hard - -"fast-json-stable-stringify@npm:^2.0.0": - version: 2.1.0 - resolution: "fast-json-stable-stringify@npm:2.1.0" - checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b - languageName: node - linkType: hard - -"fast-levenshtein@npm:^2.0.6": - version: 2.0.6 - resolution: "fast-levenshtein@npm:2.0.6" - checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 - languageName: node - linkType: hard - -"fastq@npm:^1.6.0": - version: 1.19.1 - resolution: "fastq@npm:1.19.1" - dependencies: - reusify: "npm:^1.0.4" - checksum: 10c0/ebc6e50ac7048daaeb8e64522a1ea7a26e92b3cee5cd1c7f2316cdca81ba543aa40a136b53891446ea5c3a67ec215fbaca87ad405f102dd97012f62916905630 - languageName: node - linkType: hard - -"file-entry-cache@npm:^8.0.0": - version: 8.0.0 - resolution: "file-entry-cache@npm:8.0.0" - dependencies: - flat-cache: "npm:^4.0.0" - checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 - languageName: node - linkType: hard - -"fill-range@npm:^7.1.1": - version: 7.1.1 - resolution: "fill-range@npm:7.1.1" - dependencies: - to-regex-range: "npm:^5.0.1" - checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 - languageName: node - linkType: hard - -"find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: "npm:^6.0.0" - path-exists: "npm:^4.0.0" - checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a - languageName: node - linkType: hard - -"flat-cache@npm:^4.0.0": - version: 4.0.1 - resolution: "flat-cache@npm:4.0.1" - dependencies: - flatted: "npm:^3.2.9" - keyv: "npm:^4.5.4" - checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc - languageName: node - linkType: hard - -"flatted@npm:^3.2.9": - version: 3.3.3 - resolution: "flatted@npm:3.3.3" - checksum: 10c0/e957a1c6b0254aa15b8cce8533e24165abd98fadc98575db082b786b5da1b7d72062b81bfdcd1da2f4d46b6ed93bec2434e62333e9b4261d79ef2e75a10dd538 - languageName: node - linkType: hard - -"glob-parent@npm:^5.1.2": - version: 5.1.2 - resolution: "glob-parent@npm:5.1.2" - dependencies: - is-glob: "npm:^4.0.1" - checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee - languageName: node - linkType: hard - -"glob-parent@npm:^6.0.2": - version: 6.0.2 - resolution: "glob-parent@npm:6.0.2" - dependencies: - is-glob: "npm:^4.0.3" - checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 - languageName: node - linkType: hard - -"globals@npm:^14.0.0": - version: 14.0.0 - resolution: "globals@npm:14.0.0" - checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d - languageName: node - linkType: hard - -"globby@npm:^11.1.0": - version: 11.1.0 - resolution: "globby@npm:11.1.0" - dependencies: - array-union: "npm:^2.1.0" - dir-glob: "npm:^3.0.1" - fast-glob: "npm:^3.2.9" - ignore: "npm:^5.2.0" - merge2: "npm:^1.4.1" - slash: "npm:^3.0.0" - checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 - languageName: node - linkType: hard - -"graphemer@npm:^1.4.0": - version: 1.4.0 - resolution: "graphemer@npm:1.4.0" - checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 - languageName: node - linkType: hard - -"has-flag@npm:^4.0.0": - version: 4.0.0 - resolution: "has-flag@npm:4.0.0" - checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 - languageName: node - linkType: hard - -"ignore@npm:^5.2.0": - version: 5.3.2 - resolution: "ignore@npm:5.3.2" - checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 - languageName: node - linkType: hard - -"ignore@npm:^7.0.0": - version: 7.0.5 - resolution: "ignore@npm:7.0.5" - checksum: 10c0/ae00db89fe873064a093b8999fe4cc284b13ef2a178636211842cceb650b9c3e390d3339191acb145d81ed5379d2074840cf0c33a20bdbd6f32821f79eb4ad5d - languageName: node - linkType: hard - -"import-fresh@npm:^3.2.1": - version: 3.3.1 - resolution: "import-fresh@npm:3.3.1" - dependencies: - parent-module: "npm:^1.0.0" - resolve-from: "npm:^4.0.0" - checksum: 10c0/bf8cc494872fef783249709385ae883b447e3eb09db0ebd15dcead7d9afe7224dad7bd7591c6b73b0b19b3c0f9640eb8ee884f01cfaf2887ab995b0b36a0cbec - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 - languageName: node - linkType: hard - -"is-extglob@npm:^2.1.1": - version: 2.1.1 - resolution: "is-extglob@npm:2.1.1" - checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 - languageName: node - linkType: hard - -"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": - version: 4.0.3 - resolution: "is-glob@npm:4.0.3" - dependencies: - is-extglob: "npm:^2.1.1" - checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a - languageName: node - linkType: hard - -"is-number@npm:^7.0.0": - version: 7.0.0 - resolution: "is-number@npm:7.0.0" - checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d - languageName: node - linkType: hard - -"js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" - dependencies: - argparse: "npm:^2.0.1" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f - languageName: node - linkType: hard - -"json-buffer@npm:3.0.1": - version: 3.0.1 - resolution: "json-buffer@npm:3.0.1" - checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 - languageName: node - linkType: hard - -"json-schema-traverse@npm:^0.4.1": - version: 0.4.1 - resolution: "json-schema-traverse@npm:0.4.1" - checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce - languageName: node - linkType: hard - -"json-stable-stringify-without-jsonify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" - checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 - languageName: node - linkType: hard - -"keyv@npm:^4.5.4": - version: 4.5.4 - resolution: "keyv@npm:4.5.4" - dependencies: - json-buffer: "npm:3.0.1" - checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e - languageName: node - linkType: hard - -"levn@npm:^0.4.1": - version: 0.4.1 - resolution: "levn@npm:0.4.1" - dependencies: - prelude-ls: "npm:^1.2.1" - type-check: "npm:~0.4.0" - checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e - languageName: node - linkType: hard - -"locate-path@npm:^6.0.0": - version: 6.0.0 - resolution: "locate-path@npm:6.0.0" - dependencies: - p-locate: "npm:^5.0.0" - checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 - languageName: node - linkType: hard - -"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.2": - version: 4.6.2 - resolution: "lodash.merge@npm:4.6.2" - checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 - languageName: node - linkType: hard - -"merge2@npm:^1.3.0, merge2@npm:^1.4.1": - version: 1.4.1 - resolution: "merge2@npm:1.4.1" - checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb - languageName: node - linkType: hard - -"micromatch@npm:^4.0.8": - version: 4.0.8 - resolution: "micromatch@npm:4.0.8" - dependencies: - braces: "npm:^3.0.3" - picomatch: "npm:^2.3.1" - checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 - languageName: node - linkType: hard - -"minimatch@npm:9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac - languageName: node - linkType: hard - -"minimatch@npm:^3.1.2": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 - languageName: node - linkType: hard - -"minimatch@npm:^9.0.4": - version: 9.0.5 - resolution: "minimatch@npm:9.0.5" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed - languageName: node - linkType: hard - -"ms@npm:^2.1.3": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 - languageName: node - linkType: hard - -"natural-compare@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare@npm:1.4.0" - checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 - languageName: node - linkType: hard - -"optionator@npm:^0.9.3": - version: 0.9.4 - resolution: "optionator@npm:0.9.4" - dependencies: - deep-is: "npm:^0.1.3" - fast-levenshtein: "npm:^2.0.6" - levn: "npm:^0.4.1" - prelude-ls: "npm:^1.2.1" - type-check: "npm:^0.4.0" - word-wrap: "npm:^1.2.5" - checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675 - languageName: node - linkType: hard - -"p-limit@npm:^3.0.2": - version: 3.1.0 - resolution: "p-limit@npm:3.1.0" - dependencies: - yocto-queue: "npm:^0.1.0" - checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a - languageName: node - linkType: hard - -"p-locate@npm:^5.0.0": - version: 5.0.0 - resolution: "p-locate@npm:5.0.0" - dependencies: - p-limit: "npm:^3.0.2" - checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a - languageName: node - linkType: hard - -"parent-module@npm:^1.0.0": - version: 1.0.1 - resolution: "parent-module@npm:1.0.1" - dependencies: - callsites: "npm:^3.0.0" - checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 - languageName: node - linkType: hard - -"path-exists@npm:^4.0.0": - version: 4.0.0 - resolution: "path-exists@npm:4.0.0" - checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b - languageName: node - linkType: hard - -"path-key@npm:^3.1.0": - version: 3.1.1 - resolution: "path-key@npm:3.1.1" - checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c - languageName: node - linkType: hard - -"path-type@npm:^4.0.0": - version: 4.0.0 - resolution: "path-type@npm:4.0.0" - checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c - languageName: node - linkType: hard - -"picomatch@npm:^2.3.1": - version: 2.3.1 - resolution: "picomatch@npm:2.3.1" - checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be - languageName: node - linkType: hard - -"picomatch@npm:^4.0.3": - version: 4.0.3 - resolution: "picomatch@npm:4.0.3" - checksum: 10c0/9582c951e95eebee5434f59e426cddd228a7b97a0161a375aed4be244bd3fe8e3a31b846808ea14ef2c8a2527a6eeab7b3946a67d5979e81694654f939473ae2 - languageName: node - linkType: hard - -"prelude-ls@npm:^1.2.1": - version: 1.2.1 - resolution: "prelude-ls@npm:1.2.1" - checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd - languageName: node - linkType: hard - -"punycode@npm:^2.1.0": - version: 2.3.1 - resolution: "punycode@npm:2.3.1" - checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 - languageName: node - linkType: hard - -"queue-microtask@npm:^1.2.2": - version: 1.2.3 - resolution: "queue-microtask@npm:1.2.3" - checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 - languageName: node - linkType: hard - -"resolve-from@npm:^4.0.0": - version: 4.0.0 - resolution: "resolve-from@npm:4.0.0" - checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 - languageName: node - linkType: hard - -"reusify@npm:^1.0.4": - version: 1.1.0 - resolution: "reusify@npm:1.1.0" - checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa - languageName: node - linkType: hard - -"run-parallel@npm:^1.1.9": - version: 1.2.0 - resolution: "run-parallel@npm:1.2.0" - dependencies: - queue-microtask: "npm:^1.2.2" - checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 - languageName: node - linkType: hard - -"semver@npm:^7.5.4, semver@npm:^7.6.0": - version: 7.7.3 - resolution: "semver@npm:7.7.3" - bin: - semver: bin/semver.js - checksum: 10c0/4afe5c986567db82f44c8c6faef8fe9df2a9b1d98098fc1721f57c696c4c21cebd572f297fc21002f81889492345b8470473bc6f4aff5fb032a6ea59ea2bc45e - languageName: node - linkType: hard - -"shebang-command@npm:^2.0.0": - version: 2.0.0 - resolution: "shebang-command@npm:2.0.0" - dependencies: - shebang-regex: "npm:^3.0.0" - checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e - languageName: node - linkType: hard - -"shebang-regex@npm:^3.0.0": - version: 3.0.0 - resolution: "shebang-regex@npm:3.0.0" - checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 - languageName: node - linkType: hard - -"slash@npm:^3.0.0": - version: 3.0.0 - resolution: "slash@npm:3.0.0" - checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b - languageName: node - linkType: hard - -"strip-json-comments@npm:^3.1.1": - version: 3.1.1 - resolution: "strip-json-comments@npm:3.1.1" - checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd - languageName: node - linkType: hard - -"supports-color@npm:^7.1.0": - version: 7.2.0 - resolution: "supports-color@npm:7.2.0" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 - languageName: node - linkType: hard - -"to-regex-range@npm:^5.0.1": - version: 5.0.1 - resolution: "to-regex-range@npm:5.0.1" - dependencies: - is-number: "npm:^7.0.0" - checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 - languageName: node - linkType: hard - -"ts-api-utils@npm:^1.0.1": - version: 1.4.3 - resolution: "ts-api-utils@npm:1.4.3" - peerDependencies: - typescript: ">=4.2.0" - checksum: 10c0/e65dc6e7e8141140c23e1dc94984bf995d4f6801919c71d6dc27cf0cd51b100a91ffcfe5217626193e5bea9d46831e8586febdc7e172df3f1091a7384299e23a - languageName: node - linkType: hard - -"ts-api-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "ts-api-utils@npm:2.1.0" - peerDependencies: - typescript: ">=4.8.4" - checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f - languageName: node - linkType: hard - -"type-check@npm:^0.4.0, type-check@npm:~0.4.0": - version: 0.4.0 - resolution: "type-check@npm:0.4.0" - dependencies: - prelude-ls: "npm:^1.2.1" - checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 - languageName: node - linkType: hard - -"typescript-eslint@npm:^8.46.1": - version: 8.46.1 - resolution: "typescript-eslint@npm:8.46.1" - dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.46.1" - "@typescript-eslint/parser": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/utils": "npm:8.46.1" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/002934d83eec1afcf94e9785399740efe39f1fe6538e469a01ed36c004303af8736e3aea9100c8733798fcb0d1e0301177bd70aa29e6d05d8cefbd8e18887ea6 - languageName: node - linkType: hard - -"typescript@npm:^5.6.0, typescript@npm:^5.9.3": - version: 5.9.3 - resolution: "typescript@npm:5.9.3" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 - languageName: node - linkType: hard - -"typescript@patch:typescript@npm%3A^5.6.0#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": - version: 5.9.3 - resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 - languageName: node - linkType: hard - -"uri-js@npm:^4.2.2": - version: 4.4.1 - resolution: "uri-js@npm:4.4.1" - dependencies: - punycode: "npm:^2.1.0" - checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c - languageName: node - linkType: hard - -"which@npm:^2.0.1": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: "npm:^2.0.0" - bin: - node-which: ./bin/node-which - checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f - languageName: node - linkType: hard - -"word-wrap@npm:^1.2.5": - version: 1.2.5 - resolution: "word-wrap@npm:1.2.5" - checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 - languageName: node - linkType: hard - -"yocto-queue@npm:^0.1.0": - version: 0.1.0 - resolution: "yocto-queue@npm:0.1.0" - checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f - languageName: node - linkType: hard diff --git a/tools/create-addon.mjs b/tools/create-addon.mjs new file mode 100644 index 000000000..cd89355e8 --- /dev/null +++ b/tools/create-addon.mjs @@ -0,0 +1,184 @@ +#!/usr/bin/env node +/** + * Bedrock Tweaks wrapper around `@bedrock-core/cli`. + * + * Scaffolds an addon straight into `addons/files///` + * with the BT conventions applied — no manual renaming or package surgery: + * + * - project is scaffolded as `_` so the + * bedrock-core namespace becomes `bt__` + * (creator id `bt` — every addon here belongs to Bedrock Tweaks) + * - the author stays YOU: `--author` flag, or your git user.name — it lands + * in config.json, the manifests, and the bedrock-core registry UI shows + * it as "Bedrock Tweaks, " + * - the directory is renamed to just `` + * - package.json becomes a private `@bedrock-tweaks/` workspace + * package (machine-specific scripts dropped) + * - addon-local `yarn.lock` / `.yarnrc.yml` / `.yarn/` are removed + * (installs are managed from the repo root) + * - the category package.json gains the new workspace entry + * + * Usage: + * yarn create-addon [/...] [--author ] [description...] + * + * Example: + * yarn create-addon gameplay_changes graves When you die, a grave saves all your drops. + */ +import { spawnSync } from 'node:child_process'; +import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const NAME_RE = /^[a-z][a-z0-9_]*$/; +const BRAND = 'Bedrock Tweaks'; + +const fail = (message) => { + console.error(`❌ ${message}`); + process.exit(1); +}; + +const rawArgs = process.argv.slice(2); +const authorFlag = rawArgs.indexOf('--author'); +let authorArg; + +if (authorFlag !== -1) { + authorArg = rawArgs[authorFlag + 1]; + rawArgs.splice(authorFlag, 2); +} + +const [category, name, ...descriptionWords] = rawArgs; + +if (!category || !name) { + console.error('Usage: yarn create-addon [/...] [--author ] [description...]'); + console.error('Example: yarn create-addon gameplay_changes graves When you die, a grave saves all your drops.'); + process.exit(1); +} + +const gitName = spawnSync('git', ['config', 'user.name'], { encoding: 'utf8' }).stdout?.trim(); +const author = authorArg || gitName || BRAND; + +const segments = category.split('/'); + +for (const segment of segments) { + if (!NAME_RE.test(segment)) { + fail(`Invalid category segment '${segment}' — lowercase letters, digits and underscores only.`); + } +} + +if (!NAME_RE.test(name)) { + fail(`Invalid addon name '${name}' — lowercase letters, digits and underscores only.`); +} + +// gameplay_changes -> gc; nested categories join their initials with '_'. +const initials = (segment) => segment.split('_').map(word => word[0]).join(''); +const prefix = segments.map(initials).join('_'); +const projectName = `${prefix}_${name}`; +const categoryDir = path.join(root, 'addons', 'files', ...segments); +const scaffoldDir = path.join(categoryDir, projectName); +const targetDir = path.join(categoryDir, name); +const description = descriptionWords.join(' ') || 'A Bedrock Tweaks addon.'; + +if (existsSync(targetDir)) { + fail(`${targetDir} already exists.`); +} + +if (existsSync(scaffoldDir)) { + fail(`${scaffoldDir} already exists.`); +} + +mkdirSync(categoryDir, { recursive: true }); + +// BEDROCK_CORE_CLI may point at a checked-out cli entry (dist/index.js). +// The CLI author must be `bt` — it becomes the creator id half of the addon +// namespace. The real author is patched in below; that split is a BT thing, +// not a bedrock-core one. +const quote = (value) => `"${value.replace(/"/g, '\\"')}"`; +const cliArgs = [projectName, '--author', 'bt', '--description', description]; +const command = process.env.BEDROCK_CORE_CLI + ? `node ${quote(process.env.BEDROCK_CORE_CLI)} ${cliArgs.map(quote).join(' ')}` + : `npx @bedrock-core/cli ${cliArgs.map(quote).join(' ')}`; + +console.info(`▶ ${command}`); +const result = spawnSync(command, { cwd: categoryDir, stdio: 'inherit', shell: true }); + +if (result.status !== 0 || !existsSync(scaffoldDir)) { + fail('The bedrock-core CLI did not produce a project.'); +} + +renameSync(scaffoldDir, targetDir); + +// Monorepo adaptation — the root workspace owns installs. +for (const file of ['yarn.lock', '.yarnrc.yml', '.yarn']) { + rmSync(path.join(targetDir, file), { recursive: true, force: true }); +} + +const packagePath = path.join(targetDir, 'package.json'); +const pkg = JSON.parse(readFileSync(packagePath, 'utf8')); + +pkg.name = `@bedrock-tweaks/${name}`; +pkg.private = true; +pkg.scripts ??= {}; + +delete pkg.scripts.loopback; +delete pkg.scripts['loopback:preview']; + +writeFileSync(packagePath, `${JSON.stringify(pkg, null, '\t')}\n`); + +// The render pack the CLI downloads is a build artifact, not source. +appendFileSync(path.join(targetDir, '.gitignore'), '\n# Render pack (downloaded by the bedrock-core CLI, not committed)\ncore-ui-*.mcpack\n'); + +// Authorship: creator id is `bt`, but the credit is the human author. +const configPath = path.join(targetDir, 'config.json'); +const regolithConfig = JSON.parse(readFileSync(configPath, 'utf8')); + +regolithConfig.author = author; +writeFileSync(configPath, `${JSON.stringify(regolithConfig, null, '\t')}\n`); + +// The bedrock-core registry UI shows the creator display name: author + brand. +const i18nPath = path.join(targetDir, 'packs', 'data', 'i18n', 'en_US.ts'); + +if (existsSync(i18nPath)) { + const i18nSource = readFileSync(i18nPath, 'utf8'); + + writeFileSync(i18nPath, i18nSource.replace("creator: 'bt'", `creator: '${author === BRAND ? BRAND : `${BRAND}, ${author}`}'`)); +} + +// Pack-list credits: the author of record plus the brand. +for (const pack of ['BP', 'RP']) { + const manifestPath = path.join(targetDir, 'packs', pack, 'manifest.json'); + + if (!existsSync(manifestPath)) { + continue; + } + + const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')); + const authors = [...new Set([BRAND, author])]; + + manifest.metadata = { ...manifest.metadata, authors }; + writeFileSync(manifestPath, `${JSON.stringify(manifest, null, '\t')}\n`); +} + +// Register the addon in the category workspaces (creating the package if new). +const categoryPackagePath = path.join(categoryDir, 'package.json'); +const categoryPkg = existsSync(categoryPackagePath) + ? JSON.parse(readFileSync(categoryPackagePath, 'utf8')) + : { private: true, workspaces: [] }; + +categoryPkg.workspaces ??= []; + +if (!categoryPkg.workspaces.includes(name)) { + categoryPkg.workspaces.push(name); + categoryPkg.workspaces.sort(); +} + +writeFileSync(categoryPackagePath, `${JSON.stringify(categoryPkg, null, '\t')}\n`); + +console.info(''); +console.info(`✔ addons/files/${segments.join('/')}/${name} — namespace bt_${projectName}`); +console.info(''); +console.info('Next steps:'); +console.info(' 1. yarn install (repo root)'); +console.info(` 2. cd addons/files/${segments.join('/')}/${name}`); +console.info(' 3. yarn run regolith-install && yarn run build'); +console.info(` 4. Add the pack to addons/packs.json with id '${name}' under the '${segments.join('/')}' category`); From cc17f2a969e82a306267f4505dc18bda0875cb0d Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 02:06:08 +0200 Subject: [PATCH 03/11] update(files): Graves - Rebuilt the addon on the bedrock-core stack (creator `bt`, namespace `bt_gc_graves`) with a typed RPC layer and custom server UI - Graves keep inventory, armor, offhand and XP: interact to open like a chest, sneak + interact to restore items to their original slots, hit twice to scatter them - Graves are fireproof, explosion proof, float on lava and are rescued from the void - Only the owner can open a grave unless grave robbing is enabled or the opener holds a grave key - Added `/bt_gc_graves:graves`, `:config`, `:guide`, `:gravekey` and `:gravesadmin` commands with an in-game guide and admin panel - Registered the pack in addons/packs.json without a version: addon versions and the minimum engine version now come from the pack manifest --- .../files/gameplay_changes/graves/.gitignore | 71 + .../files/gameplay_changes/graves/.mcignore | 2 + .../graves/.vscode/extensions.json | 12 + .../graves/.vscode/launch.json | 16 + .../graves/.vscode/settings.json | 47 + .../files/gameplay_changes/graves/README.md | 36 + .../files/gameplay_changes/graves/config.json | 94 ++ .../gameplay_changes/graves/eslint.config.mjs | 146 ++ .../gameplay_changes/graves/package.json | 29 + .../graves/packs/BP/entities/grave.entity.ts | 59 + .../graves/packs/BP/items/grave_key.item.ts | 21 + .../graves/packs/BP/manifest.json | 47 + .../graves/packs/BP/pack_icon.png | 3 + .../graves/packs/BP/scripts/UI/AdminPanel.tsx | 38 + .../packs/BP/scripts/UI/GraveDetail.tsx | 86 ++ .../graves/packs/BP/scripts/UI/GraveList.tsx | 39 + .../graves/packs/BP/scripts/UI/GravesApp.tsx | 39 + .../graves/packs/BP/scripts/UI/i18n.ts | 20 + .../graves/packs/BP/scripts/commands.ts | 205 +++ .../graves/packs/BP/scripts/config.ts | 155 +++ .../graves/packs/BP/scripts/constants.ts | 67 + .../graves/packs/BP/scripts/death/capture.ts | 139 ++ .../packs/BP/scripts/death/impenetrable.ts | 40 + .../packs/BP/scripts/death/keepInventory.ts | 84 ++ .../packs/BP/scripts/death/placement.ts | 185 +++ .../graves/packs/BP/scripts/death/spawn.ts | 128 ++ .../graves/packs/BP/scripts/despawn.ts | 37 + .../graves/packs/BP/scripts/index/purge.ts | 154 +++ .../packs/BP/scripts/index/reconcile.ts | 83 ++ .../graves/packs/BP/scripts/index/store.ts | 124 ++ .../graves/packs/BP/scripts/lifecycle.ts | 52 + .../graves/packs/BP/scripts/main.ts | 30 + .../graves/packs/BP/scripts/open/attack.ts | 73 + .../graves/packs/BP/scripts/open/auth.ts | 76 ++ .../graves/packs/BP/scripts/open/container.ts | 54 + .../graves/packs/BP/scripts/open/gate.ts | 61 + .../graves/packs/BP/scripts/open/restore.ts | 85 ++ .../graves/packs/BP/scripts/registration.ts | 25 + .../graves/packs/BP/scripts/rpc.ts | 22 + .../graves/packs/BP/scripts/types.ts | 50 + .../graves/packs/BP/scripts/util.ts | 69 + .../graves/packs/BP/texts/en_US.lang | 2 + .../graves/packs/BP/texts/languages.json | 1 + .../packs/RP/animations/grave.animation.json | 14 + .../graves/packs/RP/entity/grave.entity.json | 31 + .../graves/packs/RP/manifest.json | 28 + .../packs/RP/models/entity/grave.geo.json | 188 +++ .../graves/packs/RP/pack_icon.png | 3 + .../packs/RP/render_controllers/grave.rc.json | 16 + .../graves/packs/RP/texts/en_US.lang | 2 + .../graves/packs/RP/texts/languages.json | 1 + .../graves/packs/RP/textures/entity/grave.png | 3 + .../packs/RP/textures/item_texture.json | 9 + .../packs/RP/textures/items/grave_key.png | 3 + .../graves/packs/RP/textures/ui/icon.png | 3 + .../graves/packs/data/guides/en_US/admin.mdx | 45 + .../packs/data/guides/en_US/configuration.mdx | 18 + .../graves/packs/data/guides/en_US/intro.mdx | 40 + .../data/guides/en_US/server-settings.mdx | 24 + .../graves/packs/data/i18n/en_US.ts | 190 +++ .../graves/packs/data/i18n/es_ES.ts | 189 +++ .../gameplay_changes/graves/tsconfig.json | 48 + addons/packs.json | 8 +- yarn.lock | 1207 +++++++++-------- 64 files changed, 4287 insertions(+), 589 deletions(-) create mode 100644 addons/files/gameplay_changes/graves/.gitignore create mode 100644 addons/files/gameplay_changes/graves/.mcignore create mode 100644 addons/files/gameplay_changes/graves/.vscode/extensions.json create mode 100644 addons/files/gameplay_changes/graves/.vscode/launch.json create mode 100644 addons/files/gameplay_changes/graves/.vscode/settings.json create mode 100644 addons/files/gameplay_changes/graves/README.md create mode 100644 addons/files/gameplay_changes/graves/config.json create mode 100644 addons/files/gameplay_changes/graves/eslint.config.mjs create mode 100644 addons/files/gameplay_changes/graves/package.json create mode 100644 addons/files/gameplay_changes/graves/packs/BP/entities/grave.entity.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/items/grave_key.item.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/manifest.json create mode 100644 addons/files/gameplay_changes/graves/packs/BP/pack_icon.png create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GravesApp.tsx create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/i18n.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/commands.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/death/capture.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/death/impenetrable.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/death/keepInventory.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/death/placement.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/death/spawn.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/despawn.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/index/purge.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/index/reconcile.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/index/store.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/lifecycle.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/open/attack.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/open/auth.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/open/container.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/open/restore.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/registration.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/rpc.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts create mode 100644 addons/files/gameplay_changes/graves/packs/BP/texts/en_US.lang create mode 100644 addons/files/gameplay_changes/graves/packs/BP/texts/languages.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/animations/grave.animation.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/manifest.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/pack_icon.png create mode 100644 addons/files/gameplay_changes/graves/packs/RP/render_controllers/grave.rc.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang create mode 100644 addons/files/gameplay_changes/graves/packs/RP/texts/languages.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png create mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json create mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png create mode 100644 addons/files/gameplay_changes/graves/packs/RP/textures/ui/icon.png create mode 100644 addons/files/gameplay_changes/graves/packs/data/guides/en_US/admin.mdx create mode 100644 addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx create mode 100644 addons/files/gameplay_changes/graves/packs/data/guides/en_US/intro.mdx create mode 100644 addons/files/gameplay_changes/graves/packs/data/guides/en_US/server-settings.mdx create mode 100644 addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts create mode 100644 addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts create mode 100644 addons/files/gameplay_changes/graves/tsconfig.json diff --git a/addons/files/gameplay_changes/graves/.gitignore b/addons/files/gameplay_changes/graves/.gitignore new file mode 100644 index 000000000..cd93e26d3 --- /dev/null +++ b/addons/files/gameplay_changes/graves/.gitignore @@ -0,0 +1,71 @@ +# Dependencies +node_modules +**/node_modules +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions + +# Build outputs +/build +/.regolith +dist +**/dist +*.tsbuildinfo +**/*.tsbuildinfo + +# Testing +coverage +**/coverage +.nyc_output + +# Environment +.env +.env.local +.env.*.local + +# IDE +.vscode/* +!.vscode/launch.json +!.vscode/settings.json +!.vscode/extensions.json +!.vscode/mcp.json +.idea +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db +desktop.ini + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Temporary files +*.tmp +*.temp +.cache +**.regolith/ + +TODO +.claude +# Filter-generated artifacts (rebuilt by regolith) +**/*.generated.json +**/*.generated.d.ts + +# Render pack (downloaded by the bedrock-core CLI, not committed) +core-ui-*.mcpack + +# Minecraft schema types (regenerated by the generator filter on build) +/packs/data/generated/ diff --git a/addons/files/gameplay_changes/graves/.mcignore b/addons/files/gameplay_changes/graves/.mcignore new file mode 100644 index 000000000..704c8aa46 --- /dev/null +++ b/addons/files/gameplay_changes/graves/.mcignore @@ -0,0 +1,2 @@ +/build +/.regolith diff --git a/addons/files/gameplay_changes/graves/.vscode/extensions.json b/addons/files/gameplay_changes/graves/.vscode/extensions.json new file mode 100644 index 000000000..8cbb4a4ff --- /dev/null +++ b/addons/files/gameplay_changes/graves/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "blockceptionltd.blockceptionvscodeminecraftbedrockdevelopmentextension", + "dbaeumer.vscode-eslint", + "mojang-studios.minecraft-debugger", + "misodee.vscode-nbt", + "bedrockoss.regolith", + "jannisx11.snowstorm", + "netcorext.uuid-generator", + "christian-kohler.npm-intellisense" + ] +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.vscode/launch.json b/addons/files/gameplay_changes/graves/.vscode/launch.json new file mode 100644 index 000000000..bfd91823e --- /dev/null +++ b/addons/files/gameplay_changes/graves/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "minecraft-js", + "request": "attach", + "sourceMapRoot": "${env:APPDATA}/Minecraft Bedrock/Users/Shared/games/com.mojang/development_behavior_packs/gc_graves/scripts/", + "generatedSourceRoot": "${env:APPDATA}/Minecraft Bedrock/Users/Shared/games/com.mojang/development_behavior_packs/gc_graves/scripts/", + "localRoot": "${workspaceFolder}/packs/BP/scripts/", + "name": "Debug with Minecraft", + "mode": "listen", + "port": 19144, + "targetModuleUuid": "8ec2e94f-b226-4d04-b966-69f89812700e" + } + ] +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/.vscode/settings.json b/addons/files/gameplay_changes/graves/.vscode/settings.json new file mode 100644 index 000000000..06b133e2d --- /dev/null +++ b/addons/files/gameplay_changes/graves/.vscode/settings.json @@ -0,0 +1,47 @@ +{ + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "editor.detectIndentation": false, + "editor.insertSpaces": true, + "editor.tabSize": 2, + "[typescript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.insertSpaces": true, + "editor.tabSize": 2 + }, + "[javascript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.insertSpaces": true, + "editor.tabSize": 2 + }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features", + "editor.insertSpaces": false, + "editor.tabSize": 4 + }, + "[jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features", + "editor.insertSpaces": false, + "editor.tabSize": 4 + }, + "js/ts.tsdk.path": "node_modules/typescript/lib", + "js/ts.tsdk.promptToUseWorkspaceVersion": true, + "eslint.validate": [ + "javascript", + "typescript", + "json", + "jsonc" + ], + "files.associations": { + "*.json": "jsonc" + }, + "eslint.enable": true, + "eslint.format.enable": true, + "eslint.workingDirectories": [ + { + "mode": "auto" + } + ], +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/README.md b/addons/files/gameplay_changes/graves/README.md new file mode 100644 index 000000000..ebcd71770 --- /dev/null +++ b/addons/files/gameplay_changes/graves/README.md @@ -0,0 +1,36 @@ +# Graves + +When you die, a grave keeps everything — inventory, armor, offhand and XP. It +never burns, never explodes, floats on lava, and gets rescued from the void. + +- **Interact** — opens the grave like a chest; it disappears once emptied. +- **Sneak + interact** — everything straight back into its original slots. +- **Hit it twice** — scatters the contents on the ground. + +Only the owner can open a grave, unless grave robbing is enabled or the opener +holds a grave key (consumed on use). + +## Commands + +| Command | Who | What | +| --- | --- | --- | +| `/bt_gc_graves:graves` | anyone | your grave list | +| `/bt_gc_graves:config` | anyone | settings | +| `/bt_gc_graves:guide` | anyone | in-game guide | +| `/bt_gc_graves:gravekey [player] [amount]` | operator | hand out grave keys | +| `/bt_gc_graves:gravesadmin ` | operator | panel, purge, enable/disable | + +## Development + +```bash +yarn run watch # live recompilation +yarn run build +yarn run lint +``` + +- Run a build once after cloning so the generated i18n/guides modules exist. +- Install the `core-ui-*.mcpack` in your test world to see the custom UI. +- The grave entity is unkillable by design; the only removal path is + `entity.remove()` — never `/kill`, never `runCommand('kill ...')`. +- The grave index (world dynamic properties) is the source of truth; grave + entities in unloaded chunks do not exist to `getEntities()`. diff --git a/addons/files/gameplay_changes/graves/config.json b/addons/files/gameplay_changes/graves/config.json new file mode 100644 index 000000000..cf3a75568 --- /dev/null +++ b/addons/files/gameplay_changes/graves/config.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://raw.githubusercontent.com/Bedrock-OSS/regolith-schemas/main/config/v1.4.json", + "author": "DrAv0011", + "description": "When you die, it places a grave to save all your drops instead of dropping them.", + "name": "gc_graves", + "packs": { + "behaviorPack": "./packs/BP", + "resourcePack": "./packs/RP" + }, + "regolith": { + "dataPath": "./packs/data", + "filterDefinitions": { + "bundler": { + "url": "github.com/bedrock-core/regolith-filters", + "version": "1.1.1" + }, + "generator": { + "url": "github.com/bedrock-core/regolith-filters", + "version": "1.1.0" + }, + "guides": { + "url": "github.com/bedrock-core/regolith-filters", + "version": "1.1.1" + }, + "i18n": { + "url": "github.com/bedrock-core/regolith-filters", + "version": "1.0.1" + } + }, + "formatVersion": "1.4.0", + "profiles": { + "build": { + "export": { + "build": "standard", + "readOnly": true, + "target": "local" + }, + "filters": [ + { + "filter": "generator", + "settings": { + "include": ["**/*.ts"], + "exclude": ["BP/scripts/**", "data/**", "**/*.d.ts"], + "pretty": false + } + }, + { + "filter": "guides", + "settings": { + "namespace": "bt_gc_graves" + } + }, + { "filter": "i18n" }, + { + "filter": "bundler", + "settings": { + "debug": false + } + } + ] + }, + "default": { + "export": { + "build": "standard", + "readOnly": false, + "target": "development" + }, + "filters": [ + { + "filter": "generator", + "settings": { + "include": ["**/*.ts"], + "exclude": ["BP/scripts/**", "data/**", "**/*.d.ts"], + "pretty": true + } + }, + { + "filter": "guides", + "settings": { + "namespace": "bt_gc_graves" + } + }, + { "filter": "i18n" }, + { + "filter": "bundler", + "settings": { + "debug": true + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/eslint.config.mjs b/addons/files/gameplay_changes/graves/eslint.config.mjs new file mode 100644 index 000000000..eed74cf97 --- /dev/null +++ b/addons/files/gameplay_changes/graves/eslint.config.mjs @@ -0,0 +1,146 @@ +import js from '@eslint/js'; +import json from '@eslint/json'; +import stylistic from '@stylistic/eslint-plugin'; +import { defineConfig } from 'eslint/config'; +import minecraftLinting from 'eslint-plugin-minecraft-linting'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; +import tseslint from 'typescript-eslint'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig([ + { + ignores: [ + 'node_modules/**', + '**/*.generated.*', // Filter-generated files (i18n bundle + declarations) + 'packs/data/generated/**', // Minecraft schema types (generator filter) + '.*/**', + '**/*.*js', + 'filters/**', + 'build/**', + ], + }, + + { + files: ['**/*.json', '**/*.jsonc'], + plugins: { json }, + language: 'json/jsonc', + extends: ['json/recommended'], + }, + + // Stylistic configuration factory (only for JS/TS files) + { + files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], + ...stylistic.configs.customize({ + indent: 2, + quotes: 'single', + semi: true, + jsx: true, + braceStyle: '1tbs', + }), + }, + + // Global padding rules (only for JS/TS files) + { + files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'], + rules: { + '@stylistic/padding-line-between-statements': [ + 'error', + { blankLine: 'always', prev: ['const', 'let', 'var'], next: '*' }, + { blankLine: 'any', prev: ['const', 'let', 'var'], next: ['const', 'let', 'var'] }, + { blankLine: 'any', prev: ['case', 'default'], next: 'break' }, + { blankLine: 'any', prev: 'case', next: 'case' }, + { blankLine: 'always', prev: '*', next: 'return' }, + { blankLine: 'always', prev: 'block', next: '*' }, + { blankLine: 'always', prev: '*', next: 'block' }, + { blankLine: 'always', prev: 'block-like', next: '*' }, + { blankLine: 'always', prev: '*', next: 'block-like' }, + { blankLine: 'always', prev: ['import'], next: ['const', 'let', 'var'] }, + ], + }, + }, + + // TypeScript files + { + files: ['**/*.ts', '**/*.tsx'], + ignores: ['**/*.d.ts'], + extends: [ + js.configs.recommended, + ...tseslint.configs.recommended, + ], + plugins: { + '@minecraft': minecraftLinting, + }, + languageOptions: { + parser: tseslint.parser, + ecmaVersion: 'latest', + sourceType: 'module', + parserOptions: { + projectService: true, + tsconfigRootDir: __dirname, + }, + }, + rules: { + // TypeScript + '@typescript-eslint/no-unsafe-function-type': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-empty-object-type': ['error', { allowInterfaces: 'always' }], + '@typescript-eslint/no-inferrable-types': 'off', + '@typescript-eslint/explicit-function-return-type': ['warn', { allowExpressions: true }], + '@typescript-eslint/no-unsafe-type-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-wrapper-object-types': 'error', + '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/member-ordering': [ + 'error', + { + default: [ + 'private-field', + 'protected-field', + 'public-field', + 'constructor', + 'public-method', + 'protected-method', + 'private-method', + ], + }, + ], + '@typescript-eslint/naming-convention': [ + 'error', + { selector: 'default', format: ['camelCase'] }, + { selector: 'default', modifiers: ['unused'], filter: { regex: '^(_|_.*_?)$', match: true }, format: null }, + { selector: 'variable', format: ['camelCase'] }, + { selector: 'variable', modifiers: ['const'], format: ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + { selector: ['enum', 'enumMember', 'function'], format: ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + { selector: ['property', 'parameterProperty', 'accessor'], modifiers: ['private'], format: ['camelCase'], leadingUnderscore: 'require' }, + { selector: ['property', 'parameterProperty', 'accessor'], modifiers: ['private', 'readonly'], format: ['UPPER_CASE', 'camelCase'], leadingUnderscore: 'allow' }, + { selector: ['property'], modifiers: ['readonly'], format: ['camelCase', 'UPPER_CASE'] }, + { selector: ['objectLiteralProperty', 'typeProperty'], format: ['camelCase', 'snake_case', 'UPPER_CASE', 'PascalCase'], leadingUnderscore: 'allowDouble' }, + // Namespaced Minecraft JSON keys ("minecraft:physics", "bt:gc.graves") in generator templates + { selector: ['objectLiteralProperty', 'typeProperty'], modifiers: ['requiresQuotes'], format: null }, + // Config group metadata keys ($label, $description) + { selector: ['objectLiteralProperty', 'typeProperty'], filter: { regex: '^\\$[a-z][a-zA-Z0-9]*$', match: true }, format: null }, + // i18n plural leaves: camelCase base + CLDR category suffix (stock_one, keyGiven_other) + { selector: ['objectLiteralProperty', 'typeProperty'], filter: { regex: '^[a-z][a-zA-Z0-9]*_(zero|one|two|few|many|other)$', match: true }, format: null }, + { selector: 'typeLike', format: ['PascalCase'] }, + ], + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^(_|_.*_?)$' }], + '@typescript-eslint/prefer-optional-chain': 'warn', + '@typescript-eslint/unbound-method': ['error', { ignoreStatic: true }], + // @stylistic + '@stylistic/jsx-curly-brace-presence': ['warn', 'always'], + // eslint + 'arrow-body-style': ['error', 'as-needed'], + 'curly': 'warn', + 'no-console': ['warn', { allow: ['info', 'warn', 'error'] }], + 'prefer-const': 'warn', + 'eqeqeq': ['error', 'always', { null: 'ignore' }], + 'object-shorthand': 'error', + // Minecraft + '@minecraft/avoid-unnecessary-command': 'error', + }, + }, +]); diff --git a/addons/files/gameplay_changes/graves/package.json b/addons/files/gameplay_changes/graves/package.json new file mode 100644 index 000000000..37fca6f28 --- /dev/null +++ b/addons/files/gameplay_changes/graves/package.json @@ -0,0 +1,29 @@ +{ + "name": "@bedrock-tweaks/graves", + "version": "1.0.0", + "description": "When you die, it places a grave to save all your drops instead of dropping them.", + "private": true, + "scripts": { + "regolith-install": "regolith install-all", + "build": "regolith run build", + "watch": "regolith watch", + "lint": "eslint ." + }, + "dependencies": { + "@bedrock-core/server": "^0.1.0", + "@bedrock-core/ui": "^0.10.0", + "@minecraft/common": "1.2.0", + "@minecraft/server": "2.8.0", + "@minecraft/server-ui": "2.1.0", + "@minecraft/vanilla-data": "1.26.32" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@eslint/json": "^2.0.0", + "@stylistic/eslint-plugin": "^5.10.0", + "eslint": "^10.4.1", + "eslint-plugin-minecraft-linting": "^1.2.7", + "typescript": "^6.0.3", + "typescript-eslint": "^8.60.1" + } +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/entities/grave.entity.ts b/addons/files/gameplay_changes/graves/packs/BP/entities/grave.entity.ts new file mode 100644 index 000000000..cbdb7f80a --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/entities/grave.entity.ts @@ -0,0 +1,59 @@ +/** + * The grave (§3a/§3b): ONE custom entity — an engine-persisted 41-slot + * container (36 inventory + 5 equipment), frozen in place (no gravity, no + * collision, no push), immune to everything. The damage sensor lets a + * player's swing land (so entityHitEntity fires and weapons behave normally) + * while dealing zero damage — the ONLY removal path is entity.remove() from + * script. /kill does not work on graves; that is intended. + */ +export default { + 'format_version': '1.21.0', + 'minecraft:entity': { + description: { + identifier: 'bt:gc_graves.grave', + is_spawnable: false, + is_summonable: true, + is_experimental: false, + properties: { + // Drives the RP shake animation; set from script on the first hit. + 'bt:shaking': { type: 'bool', default: false, client_sync: true }, + }, + }, + components: { + 'minecraft:inventory': { + container_type: 'container', + inventory_size: 41, + // `private: true` also blocks the container screen from ever opening + // (observed in-game; the docs only mention death drops), and + // `restrict_to_owner` is an engine gate that would break the grave-key + // and robbing paths. Both stay off: access control is OUR before-event + // (open/gate.ts), and a grave cannot die, so nothing ever drops. + private: false, + restrict_to_owner: false, + can_be_siphoned_from: false, + }, + 'minecraft:physics': { has_gravity: false, has_collision: false }, + 'minecraft:pushable': { is_pushable: false, is_pushable_by_piston: false }, + 'minecraft:knockback_resistance': { value: 1.0 }, + 'minecraft:damage_sensor': { + triggers: [ + { + // Players: the hit lands (event fires, weapon durability applies), grave takes 0 damage. + cause: 'entity_attack', + deals_damage: 'no_but_side_effects_apply', + on_damage: { filters: { test: 'is_family', subject: 'other', value: 'player' } }, + }, + // Everything else — creeper, TNT, lava, wither, void — nothing. + { cause: 'all', deals_damage: 'no' }, + ], + }, + 'minecraft:health': { value: 1024, max: 1024 }, + 'minecraft:collision_box': { width: 0.8, height: 0.9 }, + 'minecraft:fire_immune': {}, + 'minecraft:persistent': {}, + 'minecraft:type_family': { family: ['grave', 'inanimate'] }, + 'minecraft:nameable': { allow_name_tag_renaming: false, always_show: true }, + 'minecraft:conditional_bandwidth_optimization': {}, + }, + }, +} satisfies Entity; diff --git a/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.item.ts b/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.item.ts new file mode 100644 index 000000000..47f78b337 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/items/grave_key.item.ts @@ -0,0 +1,21 @@ +/** + * The grave key (§3a gate 2): held in either hand it opens any grave, and one + * is consumed on success (creative players keep theirs). Given out with + * /:gravekey. The display name resolves client-side from the addon's own + * i18n key, so it follows each player's language. + */ +export default { + 'format_version': '1.21.0', + 'minecraft:item': { + description: { + identifier: 'bt:gc_graves.grave_key', + menu_category: { category: 'items' }, + }, + components: { + 'minecraft:icon': 'bt.gc_graves.grave_key', + 'minecraft:glint': true, + 'minecraft:max_stack_size': 16, + 'minecraft:display_name': { value: 'bt_gc_graves.item.graveKey' }, + }, + }, +} satisfies Item; diff --git a/addons/files/gameplay_changes/graves/packs/BP/manifest.json b/addons/files/gameplay_changes/graves/packs/BP/manifest.json new file mode 100644 index 000000000..526b785c9 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/manifest.json @@ -0,0 +1,47 @@ +{ + "format_version": 2, + "header": { + "name": "pack.name", + "description": "pack.description", + "uuid": "b3dcb4e4-69cb-4088-8ff3-2313367c42b8", + "pack_scope": "world", + "version": [1, 0, 0], + "min_engine_version": [1, 26, 30] + }, + "modules": [ + { + "type": "data", + "uuid": "3f2ff7a8-2482-49e7-9a76-5622b61c5318", + "version": [1, 0, 0] + }, + { + "type": "script", + "language": "javascript", + "uuid": "8ec2e94f-b226-4d04-b966-69f89812700e", + "entry": "scripts/main.js", + "version": [1, 0, 0] + } + ], + "dependencies": [ + { + "uuid": "49af505d-e4b5-43b7-9e6e-847a41228ece", + "version": [1, 0, 0] + }, + { + "uuid": "761ecd37-ad1c-4a64-862a-d6cc38767426", + "version": [1, 10, 0] + }, + { + "module_name": "@minecraft/server", + "version": "2.8.0" + }, + { + "module_name": "@minecraft/server-ui", + "version": "2.1.0" + } + ], + "metadata": { + "product_type": "addon", + "authors": ["Bedrock Tweaks", "DrAv0011"] + } +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png b/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png new file mode 100644 index 000000000..83ab8fd5d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/pack_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d49a6cf5a2f50ddae7f0d0cb724fb82247c01dd90e21dce305a8b0dd9759938 +size 6369 diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx new file mode 100644 index 000000000..e85c1f22f --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx @@ -0,0 +1,38 @@ +/** + * Every grave in the world, straight from the index — no chunk needs to be + * loaded (§6b). Rows navigate into the same detail screen the list uses. + */ +import { Panel, Text, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; +import type { ScreenProps } from '@bedrock-core/ui/navigation'; +import { i18n } from './i18n'; +import { allRecords } from '../index/store'; +import { agoStr, dimName, posStr } from '../util'; +import type { GravesRoutes } from './GravesApp'; + +const { spacing } = theme.tokens; + +export function AdminPanel({ navigation }: ScreenProps): JSX.Element { + const bound = useTranslation(i18n); + const exit = useExit(); + const { t, key } = bound; + + const records = [...allRecords()].sort((a, b) => b.diedAt - a.diedAt); + + return ( + +
$.admin.title)} onClose={exit} /> + + {records.length === 0 + ? {t($ => $.admin.empty)} + : records.map(record => ( + $.list.row, { count: record.items, xp: record.xp })} · ${agoStr(bound, record.diedAt)}`} + onPress={() => navigation.navigate('Detail', { graveId: record.id })} + /> + ))} + + + ); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx new file mode 100644 index 000000000..d8a5053b8 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx @@ -0,0 +1,86 @@ +/** + * One grave: text summary only — no item icons by design (§4: ItemRenderer + * aux ids are unreliable in multi-addon worlds). Operators get teleport and + * purge actions. + */ +import { Panel, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Button, Card, Divider, Header, theme } from '@bedrock-core/ui/ore-styled'; +import { isOperator } from '@bedrock-core/server'; +import { world } from '@minecraft/server'; +import type { ScreenProps } from '@bedrock-core/ui/navigation'; +import { i18n } from './i18n'; +import { byEntityId, markPurge } from '../index/store'; +import { graveEntity, removeGrave } from '../lifecycle'; +import { agoStr, causeText, dimName, posStr } from '../util'; +import type { GravesRoutes } from './GravesApp'; + +const { fontColor, spacing } = theme.tokens; + +export function GraveDetail({ navigation, route }: ScreenProps): JSX.Element { + const player = usePlayer(); + const bound = useTranslation(i18n); + const exit = useExit(); + const { t, key } = bound; + + const record = byEntityId(route.params.graveId); + + if (!record) { + // Looted or purged while this screen was open. + return ( + +
$.detail.title)} onBack={() => navigation.goBack()} onClose={exit} /> + {t($ => $.list.empty)} + + ); + } + + const admin = isOperator(player); + const cause = causeText(bound, record.cause, record.killer); + + const teleport = (): void => { + player.teleport( + { x: record.x + 0.5, y: record.y, z: record.z + 0.5 }, + { dimension: world.getDimension(record.dim) }, + ); + exit(); + }; + + const purge = (): void => { + const entity = graveEntity(record.id); + + if (entity) { + removeGrave(entity); + } else { + markPurge(record.id); + } + + navigation.goBack(); + }; + + return ( + +
$.detail.title)} breadcrumbs={[record.ownerName]} onBack={() => navigation.goBack()} onClose={exit} /> + + {`${fontColor.muted}${t($ => $.detail.owner)}: §r${record.ownerName}`} + {`${fontColor.muted}${t($ => $.detail.dimension)}: §r${dimName(bound, record.dim)}`} + {`${fontColor.muted}${t($ => $.detail.position)}: §r${posStr(record.x, record.y, record.z)}`} + + {t($ => $.detail.items, { count: record.items })} + {t($ => $.detail.xp, { amount: record.xp })} + {`${t($ => $.detail.when)}: ${agoStr(bound, record.diedAt)}`} + {cause ? {cause} : undefined} + {record.floating ? {`§6${t($ => $.list.floating)}`} : undefined} + {record.purge ? {`§c${t($ => $.detail.pendingPurge)}`} : undefined} + + {admin + ? ( + + + + + ) + : undefined} + + + ); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx new file mode 100644 index 000000000..98f53034a --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx @@ -0,0 +1,39 @@ +/** + * The player's own graves — one MenuRow per grave, newest first. Replaces the + * Java pack's paginated tellraw list; the root scroll handles overflow. + */ +import { Panel, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; +import type { ScreenProps } from '@bedrock-core/ui/navigation'; +import { i18n } from './i18n'; +import { visibleRecordsOf } from '../index/store'; +import { agoStr, dimName, posStr } from '../util'; +import type { GravesRoutes } from './GravesApp'; + +const { spacing } = theme.tokens; + +export function GraveList({ navigation }: ScreenProps): JSX.Element { + const player = usePlayer(); + const bound = useTranslation(i18n); + const exit = useExit(); + const { t, key } = bound; + + const records = [...visibleRecordsOf(player.id)].sort((a, b) => b.diedAt - a.diedAt); + + return ( + +
$.list.title)} onClose={exit} /> + + {records.length === 0 + ? {t($ => $.list.empty)} + : records.map(record => ( + $.list.row, { count: record.items, xp: record.xp })} + subtitle={`${dimName(bound, record.dim)} ${posStr(record.x, record.y, record.z)} · ${agoStr(bound, record.diedAt)}${record.floating ? ` · ${t($ => $.list.floating)}` : ''}`} + onPress={() => navigation.navigate('Detail', { graveId: record.id })} + /> + ))} + + + ); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GravesApp.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GravesApp.tsx new file mode 100644 index 000000000..0da0dcedc --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GravesApp.tsx @@ -0,0 +1,39 @@ +/** + * The addon's own screen stack: grave list → detail, plus the operator panel. + * The shared config/guide/list screens come from `ui(core)` — nothing here + * touches those. Note our list command is `:graves`, because `ui(core)` + * already claims `:list` for the addon list. + */ +import { render, type JSX } from '@bedrock-core/ui'; +import { createStackNavigator, NavigationContainer } from '@bedrock-core/ui/navigation'; +import type { Player } from '@minecraft/server'; +import { GraveList } from './GraveList'; +import { GraveDetail } from './GraveDetail'; +import { AdminPanel } from './AdminPanel'; + +export type GravesRoutes = { + List: undefined; + Admin: undefined; + Detail: { graveId: string }; +}; + +const Stack = createStackNavigator({ + screens: { + List: GraveList, + Admin: AdminPanel, + Detail: GraveDetail, + }, + initialRouteName: 'List', +}); + +function GravesApp({ initialRoute }: { initialRoute: 'List' | 'Admin' }): JSX.Element { + return ( + + + + ); +} + +export const openGraveList = (player: Player): void => render(, player); + +export const openAdminPanel = (player: Player): void => render(, player); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/i18n.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/i18n.ts new file mode 100644 index 000000000..2df62a669 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/i18n.ts @@ -0,0 +1,20 @@ +/** + * The addon's one i18n instance — typed verbs over packs/data/i18n. + * + * Components bind it with the library hook: + * + * const { t, key, raw, display } = useTranslation(i18n); // from '@bedrock-core/ui' + * t($ => $.example.greeting, { name: player.name }); // server-filled string + * key($ => $.meta.name); // client-resolved key + * + * Outside components (main.ts, chat, logs) bind explicitly with + * `i18n.forPlayer(player)` / `i18n.forLocale(locale)`. + * + * Creating the instance also registers the default translation source, so + * localized `` children — `key()` strings and `raw()` messages — + * measure correctly with no further wiring. + */ +import bundle from '@bedrock-core/generated/i18n'; +import { createI18n } from '@bedrock-core/ui/i18n'; + +export const i18n = createI18n(bundle); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/commands.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/commands.ts new file mode 100644 index 000000000..580d59831 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/commands.ts @@ -0,0 +1,205 @@ +/** + * Custom commands. `ui(core)` already provides `:config`, `:configat`, + * `:guide` and `:list`; these are the addon's own, and the namespace + * is taken from `core.id` — never spelled by hand. + * + * Command callbacks may not touch the world, so every action defers into + * `system.run()` and reports through chat rather than the command result. + */ +import { + CommandPermissionLevel, + CustomCommandParamType, + CustomCommandStatus, + EntityComponentTypes, + ItemStack, + system, +} from '@minecraft/server'; +import type { CustomCommandOrigin, CustomCommandResult, Player } from '@minecraft/server'; +import { config, core } from './registration'; +import { GRAVE_KEY_ITEM } from './constants'; +import { allRecords } from './index/store'; +import { forcePurge, isForcePurgeRunning, pendingTombstones, purgeGraves } from './index/purge'; +import { disableAddon, enableAddon, isAddonDisabled } from './death/keepInventory'; +import { openAdminPanel, openGraveList } from './UI/GravesApp'; +import { i18n } from './UI/i18n'; +import { isPlayer } from './util'; + +const ADMIN_ACTIONS = ['panel', 'purge', 'purgestatus', 'forcepurge', 'enable', 'disable', 'forcedisable'] as const; + +type AdminAction = typeof ADMIN_ACTIONS[number]; + +const asPlayer = (origin: CustomCommandOrigin): Player | undefined => (origin.sourceEntity && isPlayer(origin.sourceEntity) ? origin.sourceEntity : undefined); + +const notAPlayer = (): CustomCommandResult => ({ status: CustomCommandStatus.Failure, message: i18n.t($ => $.cmd.notAPlayer) }); + +const giveKeys = (target: Player, amount: number): void => { + const inventory = target.getComponent(EntityComponentTypes.Inventory)?.container; + + if (!inventory) { + return; + } + + const leftover = inventory.addItem(new ItemStack(GRAVE_KEY_ITEM, amount)); + + if (leftover) { + target.dimension.spawnItem(leftover, target.location); + } +}; + +const runAdminAction = (player: Player, action: AdminAction, target?: Player): void => { + const { t } = i18n.forPlayer(player); + + switch (action) { + case 'panel': { + openAdminPanel(player); + break; + } + + case 'purge': { + const report = purgeGraves(target?.id); + + player.sendMessage(report.total === 0 + ? t($ => $.admin.purgedNone) + : t($ => $.admin.purged, { total: report.total, removed: report.removed, tombstoned: report.tombstoned })); + break; + } + + case 'purgestatus': { + player.sendMessage(t($ => $.admin.purgeStatus, { count: pendingTombstones() })); + break; + } + + case 'forcepurge': { + if (isForcePurgeRunning()) { + player.sendMessage(t($ => $.admin.forcePurgeBusy)); + break; + } + + const pending = forcePurge((count) => { + if (player.isValid) { + player.sendMessage(i18n.forPlayer(player).t($ => $.admin.forcePurgeDone, { count })); + } + }); + + player.sendMessage(pending === 0 ? t($ => $.admin.purgedNone) : t($ => $.admin.forcePurgeStart, { count: pending })); + break; + } + + case 'enable': { + player.sendMessage(enableAddon() ? t($ => $.admin.enabled) : t($ => $.admin.alreadyEnabled)); + break; + } + + case 'disable': { + if (isAddonDisabled()) { + player.sendMessage(t($ => $.admin.alreadyDisabled)); + break; + } + + const count = allRecords().length; + + if (count > 0) { + player.sendMessage(t($ => $.admin.refuseDisable, { count })); + break; + } + + player.sendMessage(t($ => $.admin.disabled, { value: String(disableAddon()) })); + break; + } + + case 'forcedisable': { + if (isAddonDisabled()) { + player.sendMessage(t($ => $.admin.alreadyDisabled)); + break; + } + + player.sendMessage(t($ => $.admin.disabled, { value: String(disableAddon()) })); + break; + } + } +}; + +export function initCommands(): void { + system.beforeEvents.startup.subscribe(({ customCommandRegistry: registry }) => { + const ns = core.id; + + registry.registerEnum(`${ns}:adminaction`, [...ADMIN_ACTIONS]); + + registry.registerCommand({ + name: `${ns}:graves`, + description: i18n.t($ => $.cmd.graves), + permissionLevel: CommandPermissionLevel.Any, + cheatsRequired: false, + }, (origin) => { + const player = asPlayer(origin); + + if (!player) { + return notAPlayer(); + } + + system.run(() => openGraveList(player)); + + return { status: CustomCommandStatus.Success }; + }); + + registry.registerCommand({ + name: `${ns}:gravekey`, + description: i18n.t($ => $.cmd.gravekey), + permissionLevel: CommandPermissionLevel.Admin, + cheatsRequired: false, + optionalParameters: [ + { name: 'player', type: CustomCommandParamType.PlayerSelector }, + { name: 'amount', type: CustomCommandParamType.Integer }, + ], + }, (origin, targets?: Player[], amount?: number) => { + const player = asPlayer(origin); + const receivers = targets && targets.length > 0 ? targets : player ? [player] : []; + + if (receivers.length === 0) { + return notAPlayer(); + } + + const count = Math.min(Math.max(amount ?? 1, 1), 16); + + system.run(() => { + if (!config.server.access.graveKeyEnabled.get()) { + player?.sendMessage(i18n.forPlayer(player).t($ => $.cmd.keyDisabled)); + + return; + } + + for (const target of receivers) { + if (target.isValid) { + giveKeys(target, count); + player?.sendMessage(i18n.forPlayer(player).t($ => $.cmd.keyGiven, { count, player: target.name })); + } + } + }); + + return { status: CustomCommandStatus.Success }; + }); + + registry.registerCommand({ + name: `${ns}:gravesadmin`, + description: i18n.t($ => $.cmd.gravesadmin), + permissionLevel: CommandPermissionLevel.Admin, + cheatsRequired: false, + mandatoryParameters: [ + { name: `${ns}:adminaction`, type: CustomCommandParamType.Enum }, + ], + optionalParameters: [ + { name: 'player', type: CustomCommandParamType.PlayerSelector }, + ], + }, (origin, action: AdminAction, targets?: Player[]) => { + const player = asPlayer(origin); + + if (!player) { + return notAPlayer(); + } + + system.run(() => runAdminAction(player, action, targets?.[0])); + + return { status: CustomCommandStatus.Success }; + }); + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts new file mode 100644 index 000000000..c79b4de1a --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts @@ -0,0 +1,155 @@ +/** + * Config schema (§5), declared via `core.register({ config })` in main.ts. + * Every leaf becomes a widget in the shared bedrock-core config UI; labels and + * descriptions are i18n keys resolved in each player's language. Exported as + * a type so other addons can read it via `core.config.of()`. + */ +import { i18n } from './UI/i18n'; + +export const configDef = { + server: { + access: { + $label: i18n.key($ => $.config.groups.access.label), + $description: i18n.key($ => $.config.groups.access.desc), + allowRobbing: { + type: 'boolean', + default: false, + label: i18n.key($ => $.config.allowRobbing.label), + description: i18n.key($ => $.config.allowRobbing.desc), + }, + graveKeyEnabled: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.graveKeyEnabled.label), + description: i18n.key($ => $.config.graveKeyEnabled.desc), + }, + }, + contents: { + $label: i18n.key($ => $.config.groups.contents.label), + $description: i18n.key($ => $.config.groups.contents.desc), + pickUpXp: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.pickUpXp.label), + description: i18n.key($ => $.config.pickUpXp.desc), + }, + xpPercent: { + type: 'number', + default: 100, + min: 0, + max: 100, + step: 5, + label: i18n.key($ => $.config.xpPercent.label), + description: i18n.key($ => $.config.xpPercent.desc), + }, + restoreToOriginalSlots: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.restoreToOriginalSlots.label), + description: i18n.key($ => $.config.restoreToOriginalSlots.desc), + }, + }, + capture: { + $label: i18n.key($ => $.config.groups.capture.label), + $description: i18n.key($ => $.config.groups.capture.desc), + enforceKeepInventory: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.enforceKeepInventory.label), + description: i18n.key($ => $.config.enforceKeepInventory.desc), + }, + warnOnGameRuleChange: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.warnOnGameRuleChange.label), + description: i18n.key($ => $.config.warnOnGameRuleChange.desc), + }, + }, + lifetime: { + $label: i18n.key($ => $.config.groups.lifetime.label), + $description: i18n.key($ => $.config.groups.lifetime.desc), + despawnSeconds: { + type: 'number', + default: 0, + min: 0, + max: 86400, + step: 60, + label: i18n.key($ => $.config.despawnSeconds.label), + description: i18n.key($ => $.config.despawnSeconds.desc), + }, + maxGravesPerPlayer: { + type: 'number', + default: 0, + min: 0, + max: 50, + label: i18n.key($ => $.config.maxGravesPerPlayer.label), + description: i18n.key($ => $.config.maxGravesPerPlayer.desc), + }, + onLimitReached: { + type: 'enum', + default: 'drop_oldest', + options: ['drop_oldest', 'block_new'], + label: i18n.key($ => $.config.onLimitReached.label), + description: i18n.key($ => $.config.onLimitReached.desc), + }, + }, + protection: { + $label: i18n.key($ => $.config.groups.protection.label), + $description: i18n.key($ => $.config.groups.protection.desc), + voidPlatform: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.voidPlatform.label), + description: i18n.key($ => $.config.voidPlatform.desc), + }, + voidRescueY: { + type: 'number', + default: -60, + min: -64, + max: 320, + label: i18n.key($ => $.config.voidRescueY.label), + description: i18n.key($ => $.config.voidRescueY.desc), + }, + voidPlatformBlock: { + type: 'enum', + default: 'cobblestone_slab', + options: ['cobblestone_slab', 'stone', 'obsidian'], + label: i18n.key($ => $.config.voidPlatformBlock.label), + description: i18n.key($ => $.config.voidPlatformBlock.desc), + }, + floatOnLava: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.floatOnLava.label), + description: i18n.key($ => $.config.floatOnLava.desc), + }, + }, + // At the scope root, beside only sections, so the config UI gives it a + // real list editor instead of the stranded-in-a-form chat fallback. + extraImpenetrableBlocks: { + type: 'list', + itemType: 'string', + maxItems: 64, + default: [], + label: i18n.key($ => $.config.extraImpenetrableBlocks.label), + description: i18n.key($ => $.config.extraImpenetrableBlocks.desc), + }, + }, + player: { + showDeathToast: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.showDeathToast.label), + description: i18n.key($ => $.config.showDeathToast.desc), + }, + graveNameStyle: { + type: 'enum', + default: 'name', + options: ['name', 'name_and_time', 'hidden'], + label: i18n.key($ => $.config.graveNameStyle.label), + description: i18n.key($ => $.config.graveNameStyle.desc), + }, + }, +} as const; + +export type GravesConfigDef = typeof configDef; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts new file mode 100644 index 000000000..6c11c03f0 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts @@ -0,0 +1,67 @@ +/** + * Identifiers shared across the addon. The bedrock-core namespace + * (`bt_gc_graves`) prefixes commands, i18n keys and dynamic properties; + * entity/item identifiers follow the repo's `bt:.` convention. + */ +export const NAMESPACE = 'bt_gc_graves'; + +export const GRAVE_ENTITY = 'bt:gc_graves.grave'; +export const GRAVE_KEY_ITEM = 'bt:gc_graves.grave_key'; + +/** Entity property (client_sync) driving the RP shake animation. */ +export const SHAKING_PROPERTY = 'bt:shaking'; + +/** Dynamic-property keys on the grave entity. */ +export const PROP_OWNER = `${NAMESPACE}:owner`; +export const PROP_OWNER_NAME = `${NAMESPACE}:ownerName`; +export const PROP_XP = `${NAMESPACE}:xp`; +export const PROP_CAUSE = `${NAMESPACE}:cause`; +export const PROP_KILLER = `${NAMESPACE}:killer`; +export const PROP_DIED_AT = `${NAMESPACE}:diedAt`; +export const PROP_SHAKE_UNTIL = `${NAMESPACE}:shakeUntilTick`; + +/** World dynamic-property keys. */ +export const PROP_IDX_PREFIX = `${NAMESPACE}:idx:`; +export const PROP_OWNERS = `${NAMESPACE}:owners`; +export const PROP_DISABLED = `${NAMESPACE}:disabled`; +export const PROP_PREV_KEEP_INVENTORY = `${NAMESPACE}:prevKeepInventory`; + +/** Grave container layout: 0–35 mirror the player container, 36–40 equipment. */ +export const GRAVE_INVENTORY_SIZE = 41; +export const PLAYER_CONTAINER_SLOTS = 36; + +/** Ticks the grave keeps shaking after the first hit — the second-hit window. */ +export const SHAKE_WINDOW_TICKS = 10; + +/** + * §3b-i layer 4 — vanilla blocks a grave must never occupy or replace. + * Layers 1–3 (block tag, config list, RPC) extend this at runtime. + */ +export const VANILLA_IMPENETRABLE: ReadonlySet = new Set([ + 'minecraft:bedrock', + 'minecraft:barrier', + 'minecraft:command_block', + 'minecraft:chain_command_block', + 'minecraft:repeating_command_block', + 'minecraft:structure_block', + 'minecraft:structure_void', + 'minecraft:jigsaw', + 'minecraft:light_block', + 'minecraft:end_portal', + 'minecraft:end_portal_frame', + 'minecraft:end_gateway', + 'minecraft:allow', + 'minecraft:deny', + 'minecraft:border_block', +]); + +/** Block tag other addons can put on their blocks to keep graves out (§3b-i layer 1). */ +export const IMPENETRABLE_TAG = 'bt:gc_graves.impenetrable'; + +/** Entities a grave placement cell must not share (§3b rows 13–14). */ +export const REPELLING_ENTITIES: ReadonlySet = new Set([ + GRAVE_ENTITY, + 'minecraft:ender_crystal', + 'minecraft:shulker', + 'minecraft:armor_stand', +]); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/death/capture.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/capture.ts new file mode 100644 index 000000000..49094520b --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/capture.ts @@ -0,0 +1,139 @@ +/** + * §3c — death capture. With keepInventory forced on, nothing is dropped: at + * `entityDie` the player's container, equipment and XP are still populated, + * so we read them, copy them into the grave, and clear them ourselves. No + * drop-vacuuming, no tick ids, no races between same-tick deaths. + */ +import { EntityComponentTypes, GameMode, world } from '@minecraft/server'; +import { config } from '../registration'; +import { PLAYER_CONTAINER_SLOTS } from '../constants'; +import { EQUIP_SLOTS, graveEntity, removeGrave } from '../lifecycle'; +import { markPurge, visibleRecordsOf } from '../index/store'; +import { solvePlacement } from './placement'; +import { spawnGrave, dimensionFor, type DeathSnapshot } from './spawn'; +import { isAddonDisabled } from './keepInventory'; +import { i18n } from '../UI/i18n'; +import { dimName, isPlayer } from '../util'; + +/** drop_oldest: tombstone (or remove, if loaded) the owner's oldest grave. */ +const dropOldest = (ownerId: string): void => { + const oldest = [...visibleRecordsOf(ownerId)].sort((a, b) => a.diedAt - b.diedAt)[0]; + + if (!oldest) { + return; + } + + const entity = graveEntity(oldest.id); + + if (entity) { + removeGrave(entity); + } else { + markPurge(oldest.id); + } +}; + +export function initCapture(): void { + world.afterEvents.entityDie.subscribe(({ deadEntity, damageSource }) => { + if (!isPlayer(deadEntity) || isAddonDisabled()) { + return; + } + + const player = deadEntity; + + if (player.getGameMode() === GameMode.Spectator) { + return; + } + + const inventory = player.getComponent(EntityComponentTypes.Inventory)?.container; + const equippable = player.getComponent(EntityComponentTypes.Equippable); + + if (!inventory || !equippable) { + return; + } + + const contents = config.server.contents.get(); + const totalXp = player.getTotalXp(); + const xp = contents.pickUpXp ? Math.floor((totalXp * contents.xpPercent) / 100) : 0; + + const snapshot: DeathSnapshot = { items: [], equipment: [], itemCount: 0, xp }; + + for (let slot = 0; slot < PLAYER_CONTAINER_SLOTS; slot++) { + const item = inventory.getItem(slot); + + snapshot.items.push(item); + + if (item) { + snapshot.itemCount += item.amount; + } + } + + for (const equipSlot of EQUIP_SLOTS) { + const item = equippable.getEquipment(equipSlot); + + snapshot.equipment.push(item); + + if (item) { + snapshot.itemCount += item.amount; + } + } + + if (snapshot.itemCount === 0 && xp <= 0) { + return; + } + + snapshot.cause = damageSource.cause; + snapshot.killer = damageSource.damagingEntity?.typeId; + + const { t } = i18n.forPlayer(player); + const lifetime = config.server.lifetime.get(); + const graveCount = visibleRecordsOf(player.id).length; + + if (lifetime.maxGravesPerPlayer > 0 && graveCount >= lifetime.maxGravesPerPlayer) { + if (lifetime.onLimitReached === 'block_new') { + // keepInventory is on — skipping the grave means the player keeps everything. + player.sendMessage(t($ => $.death.capBlocked, { count: graveCount })); + + return; + } + + dropOldest(player.id); + player.sendMessage(t($ => $.death.capDropped)); + } + + const placement = solvePlacement(player.dimension, player.location, player); + const record = spawnGrave(dimensionFor(placement, player.dimension), placement, player, snapshot); + + if (!record) { + // Engine refused the grave (spike 2 failure path) — the player keeps everything. + return; + } + + // Only now that the grave holds the items do we take them from the player. + inventory.clearAll(); + + for (const equipSlot of EQUIP_SLOTS) { + equippable.setEquipment(equipSlot, undefined); + } + + if (contents.pickUpXp) { + player.resetLevel(); + } + + if (config.player.for(player).showDeathToast.get()) { + const bound = i18n.forPlayer(player); + const args = { x: record.x, y: record.y, z: record.z, dimension: dimName(bound, record.dim) }; + + if (placement.fallback) { + player.sendMessage(bound.t($ => $.death.fallback, { x: record.x, y: record.y, z: record.z })); + } else if (record.floating) { + player.sendMessage(bound.t($ => $.death.floating, { x: record.x, y: record.y, z: record.z })); + } else { + player.sendMessage(bound.t($ => $.death.toast, args)); + } + } + + // Spike 1 diagnostics (§10): confirms the container was still populated and + // whether Bedrock's keepInventory retains XP. Cheap enough to keep. + console.info(`[graves] captured ${String(snapshot.itemCount)} items, xp=${String(totalXp)} (stored ${String(xp)}) for ${player.name}`); + }, { entityTypes: ['minecraft:player'] }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/death/impenetrable.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/impenetrable.ts new file mode 100644 index 000000000..d919e5bc8 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/impenetrable.ts @@ -0,0 +1,40 @@ +/** + * §3b-i — "may a grave occupy or replace this block?" is policy, not physics, + * so it is as dynamic as Bedrock allows. Four layers, checked in order: + * 1. block tag `bt:gc_graves.impenetrable` — any addon opts its blocks in, zero coupling + * 2. config list `server.protection.extraImpenetrableBlocks` — operator-editable + * 3. RPC `registerImpenetrable` (served in rpc.ts) — other addons, at runtime + * 4. a small vanilla fallback set + */ +import type { Block } from '@minecraft/server'; +import { config } from '../registration'; +import { IMPENETRABLE_TAG, VANILLA_IMPENETRABLE } from '../constants'; + +const runtimeIds = new Set(); +let configIds: readonly string[] = []; + +const normalize = (id: string): string => (id.includes(':') ? id : `minecraft:${id}`); + +export const isImpenetrable = (block: Block): boolean => + block.hasTag(IMPENETRABLE_TAG) + || runtimeIds.has(block.typeId) + || configIds.includes(block.typeId) + || VANILLA_IMPENETRABLE.has(block.typeId); + +/** Layer 3 — called by the typed RPC surface. Returns the runtime set's size. */ +export const addRuntimeImpenetrable = (ids: readonly string[], from: string): number => { + for (const id of ids) { + runtimeIds.add(normalize(id)); + } + + console.info(`[graves] '${from}' registered ${String(ids.length)} impenetrable block id(s)`); + + return runtimeIds.size; +}; + +export function initImpenetrable(): void { + config.server.extraImpenetrableBlocks.subscribe((next) => { + configIds = next.map(normalize); + }); + configIds = config.server.extraImpenetrableBlocks.get().map(normalize); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/death/keepInventory.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/keepInventory.ts new file mode 100644 index 000000000..a087507af --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/keepInventory.ts @@ -0,0 +1,84 @@ +/** + * §3c — there is no before-death event, so the addon owns `keepInventory`: + * forced on while enabled, re-asserted if anything turns it off, and released + * (restoring the operator's original value) through the gravesadmin escape + * hatch. `gameRules` cannot be written in restricted/early execution, so + * every write goes through `system.run()`. + */ +import { GameRule, system, world } from '@minecraft/server'; +import { isOperator } from '@bedrock-core/server'; +import { config } from '../registration'; +import { PROP_DISABLED, PROP_PREV_KEEP_INVENTORY } from '../constants'; +import { i18n } from '../UI/i18n'; + +export const isAddonDisabled = (): boolean => world.getDynamicProperty(PROP_DISABLED) === true; + +const snapshotAndEnforce = (): void => { + // Remember the operator's original value once, so force-disable can put it back. + if (world.getDynamicProperty(PROP_PREV_KEEP_INVENTORY) === undefined) { + world.setDynamicProperty(PROP_PREV_KEEP_INVENTORY, world.gameRules.keepInventory); + } + + world.gameRules.keepInventory = true; +}; + +const warnOperators = (): void => { + for (const player of world.getAllPlayers()) { + if (isOperator(player)) { + player.sendMessage(i18n.forPlayer(player).t($ => $.warn.keepInventoryReasserted)); + } + } +}; + +export function initKeepInventory(): void { + system.run(() => { + if (!isAddonDisabled() && config.server.capture.enforceKeepInventory.get()) { + snapshotAndEnforce(); + } + }); + + world.afterEvents.gameRuleChange.subscribe(({ rule }) => { + if (rule !== GameRule.KeepInventory || isAddonDisabled()) { + return; + } + + if (!config.server.capture.enforceKeepInventory.get() || world.gameRules.keepInventory) { + return; + } + + system.run(() => { + world.gameRules.keepInventory = true; + }); + + if (config.server.capture.warnOnGameRuleChange.get()) { + warnOperators(); + } + }); +} + +/** gravesadmin enable — re-arm capture, re-snapshotting the previous value first. */ +export function enableAddon(): boolean { + if (!isAddonDisabled()) { + return false; + } + + world.setDynamicProperty(PROP_DISABLED, undefined); + snapshotAndEnforce(); + + return true; +} + +/** + * gravesadmin disable/forcedisable — stop capturing and hand `keepInventory` + * back with the operator's original value. Existing graves are left alone. + * Returns the restored value. + */ +export function disableAddon(): boolean { + const previous = world.getDynamicProperty(PROP_PREV_KEEP_INVENTORY); + + world.setDynamicProperty(PROP_DISABLED, true); + world.setDynamicProperty(PROP_PREV_KEEP_INVENTORY, undefined); + world.gameRules.keepInventory = previous === true; + + return previous === true; +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/death/placement.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/placement.ts new file mode 100644 index 000000000..b0a866678 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/placement.ts @@ -0,0 +1,185 @@ +/** + * The placement solver (§3b): solve the grave's position once, in script — + * the entity itself is frozen (no gravity, no collision, no push), so this is + * the only thing that ever decides where a grave stands. + * + * Two engine raycasts replace the Java pack's 19 recursive functions: the + * engine's own collision model decides what is passable (torches, plants, + * addon decorations), and `liquid_depth === 0` separates source fluid + * (float) from flowing fluid (keep descending). + */ +import type { Block, Dimension, Player, Vector3 } from '@minecraft/server'; +import { world } from '@minecraft/server'; +import { config } from '../registration'; +import { REPELLING_ENTITIES } from '../constants'; +import { isImpenetrable } from './impenetrable'; +import type { Placement } from '../types'; + +const DOWN: Vector3 = { x: 0, y: -1, z: 0 }; + +const clamp = (value: number, min: number, max: number): number => Math.min(Math.max(value, min), max); + +/** `getBlock` throws outside world bounds and returns undefined in unloaded chunks — never assume. */ +const safeBlock = (dim: Dimension, pos: Vector3): Block | undefined => { + try { + return dim.getBlock(pos); + } catch { + return undefined; + } +}; + +const hasRepeller = (dim: Dimension, pos: Vector3): boolean => { + try { + return dim.getEntitiesAtBlockLocation(pos).some(e => REPELLING_ENTITIES.has(e.typeId)); + } catch { + return false; + } +}; + +/** A cell the grave may occupy: air or liquid, not impenetrable, not already claimed. */ +const isFreeCell = (dim: Dimension, pos: Vector3): boolean => { + const block = safeBlock(dim, pos); + + if (!block || (!block.isAir && !block.isLiquid) || isImpenetrable(block)) { + return false; + } + + return !hasRepeller(dim, pos); +}; + +/** + * §3b row 4 — a death inside solid terrain (suffocation, /tp into a wall) + * searches outward r = 0...4 for the nearest free cell, horizontal preferred. + */ +const findFreeCell = (dim: Dimension, base: Vector3, min: number, max: number): Vector3 | undefined => { + for (let r = 0; r <= 4; r++) { + const ring: Vector3[] = []; + + for (let dy = -r; dy <= r; dy++) { + for (let dx = -r; dx <= r; dx++) { + for (let dz = -r; dz <= r; dz++) { + if (Math.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)) === r) { + ring.push({ x: base.x + dx, y: base.y + dy, z: base.z + dz }); + } + } + } + } + + ring.sort((a, b) => Math.abs(a.y - base.y) - Math.abs(b.y - base.y)); + + for (const pos of ring) { + if (pos.y > min && pos.y < max && isFreeCell(dim, pos)) { + return pos; + } + } + } + + return undefined; +}; + +/** §3b rows 13–14 — never stack two graves in one cell; nudge sideways instead. */ +const antiStack = (dim: Dimension, pos: Vector3): Vector3 => { + if (!hasRepeller(dim, pos)) { + return pos; + } + + for (const [dx, dz] of [[1, 0], [-1, 0], [0, 1], [0, -1], [1, 1], [-1, -1], [1, -1], [-1, 1]]) { + const candidate = { x: pos.x + dx, y: pos.y, z: pos.z + dz }; + + if (isFreeCell(dim, candidate)) { + return candidate; + } + } + + return pos; +}; + +/** §3b row 15 — never lose the items: player spawn, then world spawn. */ +const fallbackToSpawn = (owner: Player): Placement => { + const spawn = owner.getSpawnPoint(); + + if (spawn) { + return { x: Math.floor(spawn.x), y: Math.floor(spawn.y) + 1, z: Math.floor(spawn.z), dim: spawn.dimension.id, fallback: true }; + } + + const overworld = world.getDimension('minecraft:overworld'); + const worldSpawn = world.getDefaultSpawnLocation(); + const { min, max } = overworld.heightRange; + let y = worldSpawn.y; + + // The default spawn's y is a sentinel (32767) until the world computes it. + if (y <= min || y >= max) { + const top = overworld.getTopmostBlock({ x: worldSpawn.x, z: worldSpawn.z }); + + y = top ? top.y + 1 : 100; + } + + return { x: Math.floor(worldSpawn.x), y, z: Math.floor(worldSpawn.z), dim: overworld.id, fallback: true }; +}; + +export function solvePlacement(dim: Dimension, start: Vector3, owner: Player): Placement { + const { min, max } = dim.heightRange; // rows 2, 3, 12 — never hardcode ±64/320 + const protection = config.server.protection.get(); + + const base = { + x: Math.floor(start.x), + y: clamp(Math.floor(start.y), min + 1, max - 2), + z: Math.floor(start.z), + }; + + const free = findFreeCell(dim, base, min, max); + + if (!free) { + return fallbackToSpawn(owner); + } + + const maxDistance = free.y - min; + + if (maxDistance > 0) { + // 1. First block that actually STOPS something — engine decides passability (row 6). + const ground = dim.getBlockFromRay(free, DOWN, { + includePassableBlocks: false, + includeLiquidBlocks: false, + maxDistance, + }); + + // 2. First fluid surface. Only matters if it sits ABOVE the ground hit (rows 7–9). + const fluid = dim.getBlockFromRay(free, DOWN, { + includePassableBlocks: false, + includeLiquidBlocks: true, + maxDistance, + }); + + if (fluid?.block.isLiquid) { + const isSource = fluid.block.permutation.getState('liquid_depth') === 0; + const isLava = fluid.block.typeId.includes('lava'); + const aboveGround = !ground || fluid.block.location.y > ground.block.location.y; + + if (isSource && aboveGround && (!isLava || protection.floatOnLava)) { + const rest = antiStack(dim, fluid.block.location); + + return { x: rest.x, y: rest.y, z: rest.z }; + } + } + + if (ground) { + const rest = antiStack(dim, { x: ground.block.location.x, y: ground.block.location.y + 1, z: ground.block.location.z }); + + return { x: rest.x, y: rest.y, z: rest.z }; + } + } + + // Void below (rows 10–11). + if (protection.voidPlatform) { + const graveY = clamp(Math.max(min + 1, protection.voidRescueY), min + 1, max - 2); + const under = safeBlock(dim, { x: free.x, y: graveY - 1, z: free.z }); + + if (under && (under.isAir || under.isLiquid) && !isImpenetrable(under)) { + dim.setBlockType(under.location, `minecraft:${protection.voidPlatformBlock}`); + + return { x: free.x, y: graveY, z: free.z }; + } + } + + return { x: free.x, y: min + 1, z: free.z, floating: true }; +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/death/spawn.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/spawn.ts new file mode 100644 index 000000000..1a767aaff --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/death/spawn.ts @@ -0,0 +1,128 @@ +/** + * Spawn the grave entity at a solved placement, fill its engine-persisted + * container (slots 0–35 mirror the player container, 36–40 the equipment), + * stamp its dynamic properties and index it. The entity is frozen by its own + * components — position is set exactly once, here. + */ +import { world } from '@minecraft/server'; +import type { Dimension, Entity, ItemStack, Player } from '@minecraft/server'; +import { config } from '../registration'; +import { + GRAVE_ENTITY, + GRAVE_INVENTORY_SIZE, + PLAYER_CONTAINER_SLOTS, + PROP_CAUSE, + PROP_DIED_AT, + PROP_KILLER, + PROP_OWNER, + PROP_OWNER_NAME, + PROP_XP, +} from '../constants'; +import { graveContainer } from '../lifecycle'; +import { addRecord } from '../index/store'; +import { i18n } from '../UI/i18n'; +import { dateStamp } from '../util'; +import type { GraveRecord, Placement } from '../types'; + +export interface DeathSnapshot { + /** Player container by slot, 0–35. */ + items: (ItemStack | undefined)[]; + /** Head, Chest, Legs, Feet, Offhand. */ + equipment: (ItemStack | undefined)[]; + itemCount: number; + xp: number; + cause?: string; + killer?: string; +} + +const nameFor = (owner: Player, diedAt: number): string => { + const style = config.player.for(owner).graveNameStyle.get(); + + if (style === 'hidden') { + return ''; + } + + const name = i18n.forPlayer(owner).t($ => $.grave.name, { owner: owner.name }); + + return style === 'name_and_time' ? `${name}\n§7${dateStamp(diedAt)}` : name; +}; + +/** + * Returns the index record, or undefined if the engine refused the 41-slot + * container — in that case the grave is removed again and the caller leaves + * the player's inventory untouched (keepInventory means nothing is lost). + */ +export function spawnGrave(dim: Dimension, placement: Placement, owner: Player, snapshot: DeathSnapshot): GraveRecord | undefined { + const diedAt = Date.now(); + let grave: Entity; + + try { + grave = dim.spawnEntity(GRAVE_ENTITY, { x: placement.x + 0.5, y: placement.y, z: placement.z + 0.5 }); + } catch (error) { + console.error(`[graves] failed to spawn grave entity: ${String(error)}`); + + return undefined; + } + + const container = graveContainer(grave); + + if (!container || container.size < GRAVE_INVENTORY_SIZE) { + console.error(`[graves] grave container unavailable or too small (${String(container?.size)}) — aborting, player keeps items`); + grave.remove(); + + return undefined; + } + + for (let slot = 0; slot < PLAYER_CONTAINER_SLOTS; slot++) { + const item = snapshot.items[slot]; + + if (item) { + container.setItem(slot, item); + } + } + + for (let i = 0; i < snapshot.equipment.length; i++) { + const item = snapshot.equipment[i]; + + if (item) { + container.setItem(PLAYER_CONTAINER_SLOTS + i, item); + } + } + + grave.setDynamicProperty(PROP_OWNER, owner.id); + grave.setDynamicProperty(PROP_OWNER_NAME, owner.name); + grave.setDynamicProperty(PROP_XP, snapshot.xp); + grave.setDynamicProperty(PROP_DIED_AT, diedAt); + + if (snapshot.cause) { + grave.setDynamicProperty(PROP_CAUSE, snapshot.cause); + } + + if (snapshot.killer) { + grave.setDynamicProperty(PROP_KILLER, snapshot.killer); + } + + grave.nameTag = nameFor(owner, diedAt); + + const record: GraveRecord = { + id: grave.id, + owner: owner.id, + ownerName: owner.name, + dim: grave.dimension.id, + x: placement.x, + y: placement.y, + z: placement.z, + diedAt, + items: snapshot.itemCount, + xp: snapshot.xp, + ...snapshot.cause ? { cause: snapshot.cause } : {}, + ...snapshot.killer ? { killer: snapshot.killer } : {}, + ...placement.floating ? { floating: true as const } : {}, + }; + + addRecord(record); + + return record; +} + +export const dimensionFor = (placement: Placement, fallback: Dimension): Dimension => (placement.dim ? world.getDimension(placement.dim) : fallback); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/despawn.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/despawn.ts new file mode 100644 index 000000000..b9c3f705b --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/despawn.ts @@ -0,0 +1,37 @@ +/** + * Despawn timer. Ages come from the index (epoch ms), so a grave's age + * survives chunk unloads and restarts. Loaded expired graves are removed on + * the spot; unloaded ones are tombstoned and die when their chunk loads. + */ +import { system } from '@minecraft/server'; +import { config } from './registration'; +import { graveEntity, removeGrave } from './lifecycle'; +import { allRecords, markPurge } from './index/store'; + +const SWEEP_INTERVAL_TICKS = 200; // every 10 s — despawn granularity is seconds anyway + +export function initDespawn(): void { + system.runInterval(() => { + const seconds = config.server.lifetime.despawnSeconds.get(); + + if (seconds <= 0) { + return; + } + + const cutoff = Date.now() - seconds * 1000; + + for (const record of allRecords()) { + if (record.diedAt > cutoff) { + continue; + } + + const entity = graveEntity(record.id); + + if (entity) { + removeGrave(entity); + } else { + markPurge(record.id); + } + } + }, SWEEP_INTERVAL_TICKS); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/index/purge.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/purge.ts new file mode 100644 index 000000000..58510e3ea --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/purge.ts @@ -0,0 +1,154 @@ +/** + * §6a — removing graves in unloaded chunks. Mechanism A (default): tombstone + * the record now, delete the entity the moment its chunk loads. Mechanism B + * (forcepurge): `world.tickingAreaManager` loads each grave's chunk with ONE + * area at a time — `createTickingArea` resolves once the chunks are ticking — + * and every area is removed in a `finally`; ticking chunks are a scarce, + * capped resource. The manager is pack-scoped, so leftovers from a crashed + * run are ours to sweep and cannot touch other packs' areas. + */ +import { system, world } from '@minecraft/server'; +import { graveEntity, removeGrave } from '../lifecycle'; +import { allRecords, markPurge, removeRecord, tombstoneCount } from './store'; + +export interface PurgeReport { + total: number; + removed: number; + tombstoned: number; +} + +/** Mechanism A. `ownerId` narrows the purge to one player's graves. */ +export const purgeGraves = (ownerId?: string): PurgeReport => { + const targets = allRecords().filter(r => !ownerId || r.owner === ownerId); + let removed = 0; + + for (const record of targets) { + const entity = graveEntity(record.id); + + if (entity) { + removeGrave(entity); + removed++; + } else { + markPurge(record.id); + } + } + + return { total: targets.length, removed, tombstoned: targets.length - removed }; +}; + +export const pendingTombstones = (): number => tombstoneCount(); + +let forcePurgeRunning = false; + +export const isForcePurgeRunning = (): boolean => forcePurgeRunning; + +const AREA_PREFIX = 'graves_purge_'; + +const nextTick = async (): Promise => new Promise((resolve) => { + system.run(resolve); +}); + +/** Remove areas a crashed previous run may have left behind (ours only). */ +const sweepStaleAreas = (): void => { + const manager = world.tickingAreaManager; + + for (const area of manager.getAllTickingAreas()) { + if (area.identifier.startsWith(AREA_PREFIX)) { + try { + manager.removeTickingArea(area); + } catch (error) { + console.warn(`[graves] could not remove stale ticking area '${area.identifier}': ${String(error)}`); + } + } + } +}; + +const runForcePurge = async (): Promise => { + const manager = world.tickingAreaManager; + const pending = allRecords(true).filter(r => r.purge); + let count = 0; + + sweepStaleAreas(); + + for (let i = 0; i < pending.length; i++) { + const record = pending[i]; + const area = `${AREA_PREFIX}${String(i)}`; + let dimension; + + try { + dimension = world.getDimension(record.dim); + } catch { + continue; // dimension no longer exists — leave the tombstone alone + } + + const location = { x: record.x, y: record.y, z: record.z }; + const options = { dimension, from: location, to: location }; + + if (!manager.hasCapacity(options)) { + console.warn(`[graves] no ticking-area capacity left — force purge stopped at ${String(count)} removed`); + break; + } + + try { + // Resolves once the chunk is loaded and ticking. + await manager.createTickingArea(area, options); + + // The entity itself can trail the chunk by a few ticks. + for (let tick = 0; tick < 60 && !world.getEntity(record.id); tick++) { + await nextTick(); + } + + const entity = world.getEntity(record.id); + + if (entity) { + entity.remove(); + count++; + } + + removeRecord(record.id); + } catch (error) { + console.warn(`[graves] force purge failed for grave ${record.id}: ${String(error)}`); + } finally { + try { + if (manager.hasTickingArea(area)) { + manager.removeTickingArea(area); + } + } catch (error) { + console.warn(`[graves] could not remove ticking area '${area}': ${String(error)}`); + } + } + + await nextTick(); + } + + return count; +}; + +/** + * Mechanism B. Physically removes every tombstoned grave by force-loading its + * chunk. Reports back through `done(count)` when the run finishes. Returns + * how many tombstones the run set out to remove. + */ +export function forcePurge(done: (count: number) => void): number { + const pending = tombstoneCount(); + + if (forcePurgeRunning || pending === 0) { + return pending; + } + + forcePurgeRunning = true; + + runForcePurge() + .then((count) => { + done(count); + }) + .catch((error: unknown) => { + console.error(`[graves] force purge aborted: ${String(error)}`); + done(0); + }) + .finally(() => { + forcePurgeRunning = false; + }); + + return pending; +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/index/reconcile.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/reconcile.ts new file mode 100644 index 000000000..1c5d83f2a --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/reconcile.ts @@ -0,0 +1,83 @@ +/** + * §6 — lazy self-healing. Whenever a grave entity loads: adopt orphans the + * index lost (crash, restored backup), execute pending tombstones (§6a), and + * refresh a drifted position. Index records are NEVER deleted from + * `entityRemove` — that event cannot tell unload from deletion. + */ +import { world } from '@minecraft/server'; +import { GRAVE_INVENTORY_SIZE, PROP_DIED_AT, PROP_OWNER, PROP_OWNER_NAME, PROP_XP, PROP_CAUSE, PROP_KILLER } from '../constants'; +import { graveContainer, isGrave, removeGrave } from '../lifecycle'; +import { addRecord, byEntityId, updateRecord } from './store'; +import type { Entity } from '@minecraft/server'; +import type { GraveRecord } from '../types'; + +const adopt = (entity: Entity): void => { + const owner = entity.getDynamicProperty(PROP_OWNER); + const ownerName = entity.getDynamicProperty(PROP_OWNER_NAME); + + if (typeof owner !== 'string' || typeof ownerName !== 'string') { + return; // not a grave we ever filled — leave it alone + } + + const container = graveContainer(entity); + let items = 0; + + if (container) { + for (let slot = 0; slot < GRAVE_INVENTORY_SIZE && slot < container.size; slot++) { + items += container.getItem(slot)?.amount ?? 0; + } + } + + const xp = entity.getDynamicProperty(PROP_XP); + const diedAt = entity.getDynamicProperty(PROP_DIED_AT); + const cause = entity.getDynamicProperty(PROP_CAUSE); + const killer = entity.getDynamicProperty(PROP_KILLER); + + const record: GraveRecord = { + id: entity.id, + owner, + ownerName, + dim: entity.dimension.id, + x: Math.floor(entity.location.x), + y: Math.floor(entity.location.y), + z: Math.floor(entity.location.z), + diedAt: typeof diedAt === 'number' ? diedAt : Date.now(), + items, + xp: typeof xp === 'number' ? xp : 0, + ...typeof cause === 'string' ? { cause } : {}, + ...typeof killer === 'string' ? { killer } : {}, + }; + + addRecord(record); + console.info(`[graves] adopted orphan grave ${entity.id} for ${ownerName}`); +}; + +export function initReconcile(): void { + world.afterEvents.entityLoad.subscribe(({ entity }) => { + if (!isGrave(entity)) { + return; + } + + const record = byEntityId(entity.id); + + if (!record) { + adopt(entity); + + return; + } + + if (record.purge) { + removeGrave(entity); + + return; + } + + const x = Math.floor(entity.location.x); + const y = Math.floor(entity.location.y); + const z = Math.floor(entity.location.z); + + if (record.x !== x || record.y !== y || record.z !== z || record.dim !== entity.dimension.id) { + updateRecord({ ...record, x, y, z, dim: entity.dimension.id }); + } + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/index/store.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/store.ts new file mode 100644 index 000000000..b07a476fa --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/index/store.ts @@ -0,0 +1,124 @@ +/** + * The grave index (§6) — world dynamic properties, one JSON array per owner, + * plus an owners list so the admin surface can enumerate without scanning. + * The index is authoritative; grave entities are payloads that may or may not + * be loaded. Every mutation re-publishes a summary to `core.state` so other + * addons can read counts synchronously (§6/§7). + */ +import { world } from '@minecraft/server'; +import { core } from '../registration'; +import { PROP_IDX_PREFIX, PROP_OWNERS } from '../constants'; +import type { GraveRecord } from '../types'; + +const readJson = (key: string): T | undefined => { + const value = world.getDynamicProperty(key); + + if (typeof value !== 'string') { + return undefined; + } + + try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- JSON we wrote ourselves; shape is by construction + return JSON.parse(value) as T; + } catch { + console.error(`[graves] corrupt index property '${key}' — dropping it`); + world.setDynamicProperty(key, undefined); + + return undefined; + } +}; + +export const ownerIds = (): string[] => readJson(PROP_OWNERS) ?? []; + +/** All records for one owner, tombstones included. */ +export const recordsOf = (ownerId: string): GraveRecord[] => readJson(`${PROP_IDX_PREFIX}${ownerId}`) ?? []; + +/** The owner's graves as shown in UIs and counts — tombstones excluded. */ +export const visibleRecordsOf = (ownerId: string): GraveRecord[] => recordsOf(ownerId).filter(r => !r.purge); + +export const allRecords = (includeTombstones = false): GraveRecord[] => { + const all: GraveRecord[] = []; + + for (const owner of ownerIds()) { + for (const record of recordsOf(owner)) { + if (includeTombstones || !record.purge) { + all.push(record); + } + } + } + + return all; +}; + +export const byEntityId = (graveId: string): GraveRecord | undefined => { + for (const owner of ownerIds()) { + const match = recordsOf(owner).find(r => r.id === graveId); + + if (match) { + return match; + } + } + + return undefined; +}; + +const publishSummary = (): void => { + const owners: Record = {}; + let total = 0; + + for (const owner of ownerIds()) { + const count = visibleRecordsOf(owner).length; + + if (count > 0) { + owners[owner] = count; + total += count; + } + } + + core.state.set('graves', { total, owners }); +}; + +const writeOwner = (ownerId: string, records: GraveRecord[]): void => { + const owners = ownerIds(); + + if (records.length === 0) { + world.setDynamicProperty(`${PROP_IDX_PREFIX}${ownerId}`, undefined); + world.setDynamicProperty(PROP_OWNERS, JSON.stringify(owners.filter(o => o !== ownerId))); + } else { + world.setDynamicProperty(`${PROP_IDX_PREFIX}${ownerId}`, JSON.stringify(records)); + + if (!owners.includes(ownerId)) { + world.setDynamicProperty(PROP_OWNERS, JSON.stringify([...owners, ownerId])); + } + } + + publishSummary(); +}; + +export const addRecord = (record: GraveRecord): void => { + writeOwner(record.owner, [...recordsOf(record.owner).filter(r => r.id !== record.id), record]); +}; + +/** Write back a mutated record (position drift, purge flag, ...). */ +export const updateRecord = (record: GraveRecord): void => { + writeOwner(record.owner, recordsOf(record.owner).map(r => (r.id === record.id ? record : r))); +}; + +export const removeRecord = (graveId: string): void => { + const record = byEntityId(graveId); + + if (record) { + writeOwner(record.owner, recordsOf(record.owner).filter(r => r.id !== graveId)); + } +}; + +/** Tombstone: hidden everywhere, entity deleted the moment its chunk loads (§6a). */ +export const markPurge = (graveId: string): void => { + const record = byEntityId(graveId); + + if (record && !record.purge) { + updateRecord({ ...record, purge: true }); + } +}; + +export const tombstoneCount = (): number => allRecords(true).filter(r => r.purge).length; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/lifecycle.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/lifecycle.ts new file mode 100644 index 000000000..dd24962aa --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/lifecycle.ts @@ -0,0 +1,52 @@ +/** + * Grave entity plumbing shared by every open/removal path. The ONLY removal + * call in the codebase is `entity.remove()` — never `kill` (the damage sensor + * makes graves unkillable by design, §3a). + */ +import { EntityComponentTypes, EquipmentSlot, world } from '@minecraft/server'; +import type { Container, Entity, ItemStack, Player, Vector3 } from '@minecraft/server'; +import { GRAVE_ENTITY, PROP_XP } from './constants'; +import { removeRecord } from './index/store'; + +/** Grave container slots 36–40, in order (§6 slot mapping). */ +export const EQUIP_SLOTS: readonly EquipmentSlot[] = [ + EquipmentSlot.Head, + EquipmentSlot.Chest, + EquipmentSlot.Legs, + EquipmentSlot.Feet, + EquipmentSlot.Offhand, +]; + +export const isGrave = (entity: Entity): boolean => entity.typeId === GRAVE_ENTITY; + +/** The grave entity for a record, if its chunk is loaded. */ +export const graveEntity = (graveId: string): Entity | undefined => { + const entity = world.getEntity(graveId); + + return entity && isGrave(entity) ? entity : undefined; +}; + +export const graveContainer = (grave: Entity): Container | undefined => grave.getComponent(EntityComponentTypes.Inventory)?.container; + +export const graveXp = (grave: Entity): number => { + const xp = grave.getDynamicProperty(PROP_XP); + + return typeof xp === 'number' ? xp : 0; +}; + +/** Delete the index record, then the entity — always in that order (§6). */ +export const removeGrave = (grave: Entity): void => { + removeRecord(grave.id); + grave.remove(); +}; + +export const grantXp = (player: Player, amount: number): void => { + if (amount > 0) { + player.addExperience(amount); + player.playSound('random.levelup', { volume: 0.4 }); + } +}; + +export const dropAt = (grave: Entity, item: ItemStack, at?: Vector3): void => { + grave.dimension.spawnItem(item, at ?? grave.location); +}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts new file mode 100644 index 000000000..370aee041 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts @@ -0,0 +1,30 @@ +/** + * Entry point. `core.register()` happens in ./registration (imported first so + * every module sees the typed config); `ui(core)` mounts the shared config / + * guide / addon-list UI and this addon's `:config|configat|guide|list` + * commands. Everything else is explicit wiring, in dependency order. + */ +import { ui } from '@bedrock-core/ui/config'; +import { core } from './registration'; +import { initImpenetrable } from './death/impenetrable'; +import { initKeepInventory } from './death/keepInventory'; +import { initCapture } from './death/capture'; +import { initGate } from './open/gate'; +import { initAttack } from './open/attack'; +import { initReconcile } from './index/reconcile'; +import { initDespawn } from './despawn'; +import { initCommands } from './commands'; +import { initRpc } from './rpc'; + +ui(core); + +// initCommands must subscribe to system.beforeEvents.startup in early execution. +initCommands(); +initKeepInventory(); +initImpenetrable(); +initCapture(); +initGate(); +initAttack(); +initReconcile(); +initDespawn(); +initRpc(); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/attack.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/attack.ts new file mode 100644 index 000000000..993c9196d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/attack.ts @@ -0,0 +1,73 @@ +/** + * §3a action 3 — attack ×2. The first hit shakes the grave (RP animation via + * the client-synced `bt:shaking` property) and shows the tip; a second hit + * inside the window scatters everything on the ground. The grave itself is + * unkillable — the damage sensor lets the swing land without damage. + */ +import { system, world } from '@minecraft/server'; +import type { Entity, Player } from '@minecraft/server'; +import { PROP_SHAKE_UNTIL, SHAKE_WINDOW_TICKS, SHAKING_PROPERTY } from '../constants'; +import { dropAt, grantXp, graveContainer, graveXp, isGrave, removeGrave } from '../lifecycle'; +import { authorize, consumeKey, refuse } from './auth'; +import { i18n } from '../UI/i18n'; +import { isPlayer } from '../util'; + +const scatter = (grave: Entity, player: Player): void => { + const container = graveContainer(grave); + + if (container) { + for (let slot = 0; slot < container.size; slot++) { + const item = container.getItem(slot); + + if (item) { + dropAt(grave, item); + container.setItem(slot, undefined); + } + } + } + + grantXp(player, graveXp(grave)); + grave.dimension.playSound('dig.stone', grave.location); + removeGrave(grave); +}; + +export function initAttack(): void { + world.afterEvents.entityHitEntity.subscribe(({ damagingEntity, hitEntity }) => { + if (!isGrave(hitEntity) || !isPlayer(damagingEntity)) { + return; + } + + const player = damagingEntity; + const auth = authorize(player, hitEntity); + + if (!auth.allowed) { + refuse(player); + + return; + } + + const shakeUntil = hitEntity.getDynamicProperty(PROP_SHAKE_UNTIL); + + if (typeof shakeUntil === 'number' && system.currentTick <= shakeUntil) { + if (auth.needsKey) { + consumeKey(player); + } + + scatter(hitEntity, player); + + return; + } + + // First hit: shake, and nudge the player toward the tidy path. + hitEntity.setDynamicProperty(PROP_SHAKE_UNTIL, system.currentTick + SHAKE_WINDOW_TICKS); + hitEntity.setProperty(SHAKING_PROPERTY, true); + hitEntity.dimension.playSound('hit.stone', hitEntity.location); + system.runTimeout(() => { + if (hitEntity.isValid) { + hitEntity.setProperty(SHAKING_PROPERTY, false); + } + }, SHAKE_WINDOW_TICKS); + + player.onScreenDisplay.setActionBar(i18n.forPlayer(player).t($ => $.open.tip)); + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/auth.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/auth.ts new file mode 100644 index 000000000..8233c27f6 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/auth.ts @@ -0,0 +1,76 @@ +/** + * §3a — the three authorisation gates: owner, robbing enabled, grave key. + * `authorize` is read-only (safe inside a before-event); key consumption is a + * separate call for the normal-execution side. + */ +import { EntityComponentTypes, EquipmentSlot, GameMode } from '@minecraft/server'; +import type { Entity, Player } from '@minecraft/server'; +import { config } from '../registration'; +import { GRAVE_KEY_ITEM, PROP_OWNER } from '../constants'; +import { i18n } from '../UI/i18n'; + +export interface AuthResult { + allowed: boolean; + /** Access was granted by a grave key — consume one on success. */ + needsKey: boolean; +} + +const KEY_HANDS: readonly EquipmentSlot[] = [EquipmentSlot.Mainhand, EquipmentSlot.Offhand]; + +const holdsKey = (player: Player): boolean => { + const equippable = player.getComponent(EntityComponentTypes.Equippable); + + return KEY_HANDS.some(slot => equippable?.getEquipment(slot)?.typeId === GRAVE_KEY_ITEM); +}; + +export const authorize = (player: Player, grave: Entity): AuthResult => { + if (grave.getDynamicProperty(PROP_OWNER) === player.id) { + return { allowed: true, needsKey: false }; + } + + const access = config.server.access.get(); + + if (access.allowRobbing) { + return { allowed: true, needsKey: false }; + } + + if (access.graveKeyEnabled && holdsKey(player)) { + return { allowed: true, needsKey: true }; + } + + return { allowed: false, needsKey: false }; +}; + +/** Take one key from whichever hand holds it. Creative players keep theirs. */ +export const consumeKey = (player: Player): void => { + if (player.getGameMode() === GameMode.Creative) { + return; + } + + const equippable = player.getComponent(EntityComponentTypes.Equippable); + + if (!equippable) { + return; + } + + for (const slot of KEY_HANDS) { + const item = equippable.getEquipment(slot); + + if (item?.typeId === GRAVE_KEY_ITEM) { + if (item.amount > 1) { + item.amount -= 1; + equippable.setEquipment(slot, item); + } else { + equippable.setEquipment(slot, undefined); + } + + player.sendMessage(i18n.forPlayer(player).t($ => $.open.keyConsumed)); + + return; + } + } +}; + +export const refuse = (player: Player): void => { + player.sendMessage(`§c${i18n.forPlayer(player).t($ => $.open.robbingDisabled)}`); +}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/container.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/container.ts new file mode 100644 index 000000000..9a4b2530d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/container.ts @@ -0,0 +1,54 @@ +/** + * §3a action 1 — right-click opens the grave's native container screen (the + * engine does that for free). There is no close event for entity containers, + * so an opened grave is watched with a short poll: fully emptied → XP goes to + * the opener and the grave removes itself; anything left → it survives. + */ +import { system } from '@minecraft/server'; +import type { Entity, Player } from '@minecraft/server'; +import { grantXp, graveContainer, graveXp, removeGrave } from '../lifecycle'; + +const WATCH_INTERVAL_TICKS = 10; +const WATCH_TIMEOUT_TICKS = 6000; // 5 minutes — a screen never stays open that long + +const watched = new Set(); + +export function watchGrave(grave: Entity, opener: Player): void { + if (watched.has(grave.id)) { + return; + } + + watched.add(grave.id); + const startedAt = system.currentTick; + + const interval = system.runInterval(() => { + const stop = (): void => { + watched.delete(grave.id); + system.clearRun(interval); + }; + + if (!grave.isValid || system.currentTick - startedAt > WATCH_TIMEOUT_TICKS) { + stop(); + + return; + } + + const container = graveContainer(grave); + + if (!container) { + stop(); + + return; + } + + if (container.emptySlotsCount === container.size) { + if (opener.isValid) { + grantXp(opener, graveXp(grave)); + } + + grave.dimension.playSound('armor.equip_generic', grave.location); + removeGrave(grave); + stop(); + } + }, WATCH_INTERVAL_TICKS); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts new file mode 100644 index 000000000..352b2b8bc --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts @@ -0,0 +1,61 @@ +/** + * §3a — the interact gate. The native container screen can only be refused + * here, in the before-event, so authorization for BOTH branches runs here. + * Before-event callbacks are restricted execution: only `cancel` is touched + * synchronously; every side effect goes through `system.run()`. + */ +import { system, world } from '@minecraft/server'; +import { isGrave } from '../lifecycle'; +import { authorize, consumeKey, refuse } from './auth'; +import { restoreToPlayer } from './restore'; +import { watchGrave } from './container'; + +export function initGate(): void { + world.beforeEvents.playerInteractWithEntity.subscribe((event) => { + const { player, target } = event; + + if (!isGrave(target)) { + return; + } + + const auth = authorize(player, target); + + if (!auth.allowed) { + event.cancel = true; + system.run(() => refuse(player)); + + return; + } + + if (player.isSneaking) { + // Suppress the native screen and restore straight to the inventory. + event.cancel = true; + system.run(() => { + if (!target.isValid || !player.isValid) { + return; + } + + if (auth.needsKey) { + consumeKey(player); + } + + restoreToPlayer(target, player); + }); + + return; + } + + // Native container path: let the engine open the screen, then watch it. + system.run(() => { + if (!target.isValid) { + return; + } + + if (auth.needsKey) { + consumeKey(player); + } + + watchGrave(target, player); + }); + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/restore.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/restore.ts new file mode 100644 index 000000000..f13e0531c --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/restore.ts @@ -0,0 +1,85 @@ +/** + * §3a action 2 — sneak + interact: everything back into the player's own + * inventory. Two passes: every item whose original slot is still free claims + * it FIRST, and only then do the displaced ones fall back to any free slot + * (via addItem) and finally the floor. A single pass would let a displaced + * item squat in a later item's original slot and cascade the misplacement. + * XP is added directly. + */ +import { EntityComponentTypes } from '@minecraft/server'; +import type { Entity, ItemStack, Player } from '@minecraft/server'; +import { config } from '../registration'; +import { PLAYER_CONTAINER_SLOTS } from '../constants'; +import { EQUIP_SLOTS, dropAt, grantXp, graveContainer, graveXp, removeGrave } from '../lifecycle'; +import { i18n } from '../UI/i18n'; + +export function restoreToPlayer(grave: Entity, player: Player): void { + const container = graveContainer(grave); + const inventory = player.getComponent(EntityComponentTypes.Inventory)?.container; + const equippable = player.getComponent(EntityComponentTypes.Equippable); + + if (!container || !inventory || !equippable) { + return; + } + + const toOriginalSlots = config.server.contents.restoreToOriginalSlots.get(); + const displaced: ItemStack[] = []; + let overflow = 0; + + // Pass 1 — exact restores, while no displaced item has taken anyone's home. + for (let slot = 0; slot < PLAYER_CONTAINER_SLOTS && slot < container.size; slot++) { + const item = container.getItem(slot); + + if (!item) { + continue; + } + + if (toOriginalSlots && !inventory.getItem(slot)) { + inventory.setItem(slot, item); + } else { + displaced.push(item); + } + } + + for (let i = 0; i < EQUIP_SLOTS.length; i++) { + const item = container.getItem(PLAYER_CONTAINER_SLOTS + i); + + if (!item) { + continue; + } + + if (toOriginalSlots && !equippable.getEquipment(EQUIP_SLOTS[i])) { + equippable.setEquipment(EQUIP_SLOTS[i], item); + } else { + displaced.push(item); + } + } + + // Pass 2 — whatever could not go home: stack/free slot, then the floor. + for (const item of displaced) { + const leftover = inventory.addItem(item); + + if (leftover) { + dropAt(grave, leftover, player.location); + overflow += leftover.amount; + } + } + + const xp = graveXp(grave); + + grantXp(player, xp); + removeGrave(grave); + player.playSound('armor.equip_generic'); + + const { t } = i18n.forPlayer(player); + + player.sendMessage(t($ => $.open.restored)); + + if (overflow > 0) { + player.sendMessage(t($ => $.open.overflow, { count: overflow })); + } + + if (xp > 0) { + player.sendMessage(t($ => $.open.xp, { amount: xp })); + } +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/registration.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/registration.ts new file mode 100644 index 000000000..e06a88496 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/registration.ts @@ -0,0 +1,25 @@ +/** + * `core.register()` lives here (not in main.ts) so every module can import + * the typed config accessors without a circular dependency on the entry + * point. main.ts imports this module first. + */ +import { core } from '@bedrock-core/server'; +import bundle from '@bedrock-core/generated/i18n'; +import guides from '@bedrock-core/generated/guides'; +import { configDef } from './config'; +import { i18n } from './UI/i18n'; + +export const config = core.register({ + creator: 'bt', + pack: 'gc_graves', + packName: i18n.key($ => $.meta.name), + creatorName: i18n.key($ => $.meta.creator), + version: '1.0.0', + description: i18n.key($ => $.meta.description), + translations: bundle, + guide: guides, + config: configDef, + icon: 'textures/ui/icon.png', +}); + +export { core }; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/rpc.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/rpc.ts new file mode 100644 index 000000000..4e17915ee --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/rpc.ts @@ -0,0 +1,22 @@ +/** + * §7 — the cross-addon surface. Typed end to end: consumers call it with + * `core.rpc.typed('bt_gc_graves')` and get the same signatures. + */ +import { core } from './registration'; +import { visibleRecordsOf } from './index/store'; +import { addRuntimeImpenetrable } from './death/impenetrable'; +import type { GravesRPC } from './types'; + +export function initRpc(): void { + core.rpc.serve({ + getGraves: ({ playerId }) => visibleRecordsOf(playerId), + countGraves: ({ playerId }) => visibleRecordsOf(playerId).length, + registerImpenetrable: ({ ids }, from) => { + if (!Array.isArray(ids) || ids.some(id => typeof id !== 'string')) { + throw new Error('registerImpenetrable expects { ids: string[] }'); + } + + return addRuntimeImpenetrable(ids, from); + }, + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts new file mode 100644 index 000000000..4671cba45 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts @@ -0,0 +1,50 @@ +/** + * The grave index record (§6). The index — world dynamic properties, one per + * owner — is authoritative; the grave entity is the payload. A grave in an + * unloaded chunk exists only here. + */ +export interface GraveRecord { + /** entity.id — the handle for world.getEntity() once its chunk is loaded. */ + id: string; + /** Owner player.id. */ + owner: string; + ownerName: string; + /** dimension.id — works for custom dimensions. */ + dim: string; + x: number; + y: number; + z: number; + /** Epoch ms at death — survives restarts, unlike tick counters. */ + diedAt: number; + /** Occupied slot count, for list summaries. */ + items: number; + xp: number; + /** damageSource.cause at death. */ + cause?: string; + /** typeId of the killing entity, if any. */ + killer?: string; + /** §3b row 11 — hovering over the void (no platform placed). */ + floating?: true; + /** Tombstone — remove the entity the moment its chunk loads (§6a). */ + purge?: true; +} + +/** Cross-addon RPC surface (§7), served in rpc.ts via `core.rpc.serve`. */ +export interface GravesRPC { + getGraves(params: { playerId: string }): GraveRecord[]; + countGraves(params: { playerId: string }): number; + /** §3b-i layer 3 — register block ids graves must never occupy or replace. */ + registerImpenetrable(params: { ids: string[] }): number; +} + +export interface Placement { + x: number; + y: number; + z: number; + /** Hovering over the void — no ground and no platform. */ + floating?: boolean; + /** Solver gave up; position is the player/world spawn. */ + fallback?: boolean; + /** Dimension id override (only set by the fallback path). */ + dim?: string; +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts new file mode 100644 index 000000000..713da340e --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts @@ -0,0 +1,69 @@ +/** Small formatting helpers and guards shared across the addon. */ +import type { Entity, Player, RawMessage } from '@minecraft/server'; +import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; +import { i18n } from './UI/i18n'; + +export const isPlayer = (entity: Entity): entity is Player => entity.typeId === MinecraftEntityTypes.Player; + +/** The verb set i18n.forPlayer()/useTranslation() hand out. */ +export type Bound = ReturnType; + +export const posStr = (x: number, y: number, z: number): string => `${x} ${y} ${z}`; + +/** Localized dimension name; unknown (custom) dimensions fall back to their id. */ +export const dimName = ({ t }: Bound, dimId: string): string => { + switch (dimId) { + case 'minecraft:overworld': return t($ => $.dim.overworld); + case 'minecraft:nether': return t($ => $.dim.nether); + case 'minecraft:the_end': return t($ => $.dim.the_end); + default: return dimId.replace('minecraft:', ''); + } +}; + +/** "2h 14m ago" from an epoch-ms timestamp. */ +export const agoStr = ({ t }: Bound, diedAt: number): string => { + const totalMinutes = Math.max(0, Math.floor((Date.now() - diedAt) / 60_000)); + + if (totalMinutes < 1) { + return t($ => $.time.now); + } + + const days = Math.floor(totalMinutes / 1440); + const hours = Math.floor((totalMinutes % 1440) / 60); + const minutes = totalMinutes % 60; + + if (days > 0) { + return t($ => $.time.days, { d: days, h: hours }); + } + + if (hours > 0) { + return t($ => $.time.hours, { h: hours, m: minutes }); + } + + return t($ => $.time.minutes, { m: minutes }); +}; + +/** + * "Slain by Zombie" with the killer's name resolved on the client in the + * viewer's language via the vanilla entity key, or a plain cause string. + */ +export const causeText = ({ raw, t }: Bound, cause?: string, killer?: string): RawMessage | string | undefined => { + if (killer) { + return raw($ => $.detail.slainBy, { killer: { translate: `entity.${killer.replace('minecraft:', '')}.name` } }); + } + + if (cause) { + return t($ => $.detail.cause, { cause }); + } + + return undefined; +}; + +const pad = (n: number): string => String(n).padStart(2, '0'); + +/** Static date stamp baked into a grave's name tag (style `name_and_time`). */ +export const dateStamp = (ms: number): string => { + const d = new Date(ms); + + return `${String(d.getUTCFullYear())}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}`; +}; diff --git a/addons/files/gameplay_changes/graves/packs/BP/texts/en_US.lang b/addons/files/gameplay_changes/graves/packs/BP/texts/en_US.lang new file mode 100644 index 000000000..e586bf7eb --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/texts/en_US.lang @@ -0,0 +1,2 @@ +## The pack's own name and description come from `meta.*` in packs/data/i18n/, +## emitted here by the i18n filter as .meta.* — edit them there. diff --git a/addons/files/gameplay_changes/graves/packs/BP/texts/languages.json b/addons/files/gameplay_changes/graves/packs/BP/texts/languages.json new file mode 100644 index 000000000..bfea92acd --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/texts/languages.json @@ -0,0 +1 @@ +["en_US"] diff --git a/addons/files/gameplay_changes/graves/packs/RP/animations/grave.animation.json b/addons/files/gameplay_changes/graves/packs/RP/animations/grave.animation.json new file mode 100644 index 000000000..9afaae91d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/animations/grave.animation.json @@ -0,0 +1,14 @@ +{ + "format_version": "1.10.0", + "animations": { + "animation.rp.bt.grave_shake": { + "loop": true, + "animation_length": 0.5, + "bones": { + "root": { + "rotation": [0, 0, "math.sin(q.anim_time * 2160) * 6"] + } + } + } + } +} diff --git a/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json b/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json new file mode 100644 index 000000000..fcc652ce1 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/entity/grave.entity.json @@ -0,0 +1,31 @@ +// mc-disable behaviorpack.entities.missing +{ + "format_version": "1.10.0", + "minecraft:client_entity": { + "description": { + "identifier": "bt:gc_graves.grave", + "materials": { + "default": "entity_alphatest" + }, + "textures": { + "default": "textures/entity/grave" + }, + "geometry": { + "default": "geometry.bt.grave" + }, + "animations": { + "shake": "animation.rp.bt.grave_shake" + }, + "scripts": { + "animate": [ + { + "shake": "q.property('bt:shaking')" + } + ] + }, + "render_controllers": [ + "controller.render.bt.grave" + ] + } + } +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/manifest.json b/addons/files/gameplay_changes/graves/packs/RP/manifest.json new file mode 100644 index 000000000..1f125eb37 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/manifest.json @@ -0,0 +1,28 @@ +{ + "format_version": 2, + "header": { + "name": "pack.name", + "description": "pack.description", + "uuid": "49af505d-e4b5-43b7-9e6e-847a41228ece", + "pack_scope": "world", + "version": [1, 0, 0], + "min_engine_version": [1, 26, 30] + }, + "modules": [ + { + "type": "resources", + "uuid": "9751c88f-10f1-4f6c-9ca0-e139ee9b48df", + "version": [1, 0, 0] + } + ], + "dependencies": [ + { + "uuid": "b3dcb4e4-69cb-4088-8ff3-2313367c42b8", + "version": [1, 0, 0] + } + ], + "metadata": { + "product_type": "addon", + "authors": ["Bedrock Tweaks", "DrAv0011"] + } +} diff --git a/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json b/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json new file mode 100644 index 000000000..fccb2a50e --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/models/entity/grave.geo.json @@ -0,0 +1,188 @@ +{ + "format_version": "1.12.0", + "minecraft:geometry": [ + { + "description": { + "identifier": "geometry.bt.grave", + "texture_width": 64, + "texture_height": 64, + "visible_bounds_width": 6145, + "visible_bounds_height": 65, + "visible_bounds_offset": [ + 0, + 0.5, + 0 + ] + }, + "bones": [ + { + "name": "root", + "pivot": [ + 0, + 0, + 0 + ] + }, + { + "name": "bone", + "parent": "root", + "pivot": [ + 0, + 0, + 0 + ], + "cubes": [ + { + "origin": [ + -4, + 0, + -4 + ], + "size": [ + 8, + 16, + 8 + ], + "uv": { + "north": { + "uv": [ + 16, + 0 + ], + "uv_size": [ + 8, + 16 + ] + }, + "east": { + "uv": [ + 16, + 16 + ], + "uv_size": [ + 8, + 16 + ] + }, + "south": { + "uv": [ + 24, + 0 + ], + "uv_size": [ + 8, + 16 + ] + }, + "west": { + "uv": [ + 24, + 16 + ], + "uv_size": [ + 8, + 16 + ] + }, + "up": { + "uv": [ + 16, + 32 + ], + "uv_size": [ + 8, + 8 + ] + }, + "down": { + "uv": [ + 24, + 40 + ], + "uv_size": [ + 8, + -8 + ] + } + } + }, + { + "origin": [ + -8, + 0, + -3 + ], + "size": [ + 16, + 13, + 6 + ], + "uv": { + "north": { + "uv": [ + 0, + 0 + ], + "uv_size": [ + 16, + 13 + ] + }, + "east": { + "uv": [ + 32, + 0 + ], + "uv_size": [ + 6, + 13 + ] + }, + "south": { + "uv": [ + 0, + 13 + ], + "uv_size": [ + 16, + 13 + ] + }, + "west": { + "uv": [ + 32, + 13 + ], + "uv_size": [ + 6, + 13 + ] + }, + "up": { + "uv": [ + 0, + 26 + ], + "uv_size": [ + 16, + 6 + ] + }, + "down": { + "uv": [ + 0, + 38 + ], + "uv_size": [ + 16, + -6 + ] + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png b/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png new file mode 100644 index 000000000..83ab8fd5d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/pack_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d49a6cf5a2f50ddae7f0d0cb724fb82247c01dd90e21dce305a8b0dd9759938 +size 6369 diff --git a/addons/files/gameplay_changes/graves/packs/RP/render_controllers/grave.rc.json b/addons/files/gameplay_changes/graves/packs/RP/render_controllers/grave.rc.json new file mode 100644 index 000000000..907a8a8b0 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/render_controllers/grave.rc.json @@ -0,0 +1,16 @@ +{ + "format_version": "1.10.0", + "render_controllers": { + "controller.render.bt.grave": { + "geometry": "geometry.default", + "materials": [ + { + "*": "material.default" + } + ], + "textures": [ + "texture.default" + ] + } + } +} diff --git a/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang b/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang new file mode 100644 index 000000000..e586bf7eb --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/texts/en_US.lang @@ -0,0 +1,2 @@ +## The pack's own name and description come from `meta.*` in packs/data/i18n/, +## emitted here by the i18n filter as .meta.* — edit them there. diff --git a/addons/files/gameplay_changes/graves/packs/RP/texts/languages.json b/addons/files/gameplay_changes/graves/packs/RP/texts/languages.json new file mode 100644 index 000000000..bfea92acd --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/texts/languages.json @@ -0,0 +1 @@ +["en_US"] diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png b/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png new file mode 100644 index 000000000..d2ab40fb7 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a04e9dcb3daac4dc81065e37c18a3fcd24b9dd7c9d5822f950dbf8ffbc566ab8 +size 473 diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json b/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json new file mode 100644 index 000000000..7b5dbe44f --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/item_texture.json @@ -0,0 +1,9 @@ +{ + "resource_pack_name": "bt.gc_graves", + "texture_name": "atlas.items", + "texture_data": { + "bt.gc_graves.grave_key": { + "textures": "textures/items/grave_key" + } + } +} diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png b/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png new file mode 100644 index 000000000..8141f73bb --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/items/grave_key.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17ba97eaf45db9315aee31bd24c32460eae1e07955d93b5da15d1d2435d3b9b2 +size 289 diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/ui/icon.png b/addons/files/gameplay_changes/graves/packs/RP/textures/ui/icon.png new file mode 100644 index 000000000..83ab8fd5d --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/ui/icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d49a6cf5a2f50ddae7f0d0cb724fb82247c01dd90e21dce305a8b0dd9759938 +size 6369 diff --git a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/admin.mdx b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/admin.mdx new file mode 100644 index 000000000..47441fb31 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/admin.mdx @@ -0,0 +1,45 @@ +--- +title: For Operators +sidebar_position: 4 +access: op +description: The admin panel, purging, and the keepInventory contract. +--- + +# For Operators + +## The admin panel + +`/bt_gc_graves:gravesadmin panel` lists every grave in the world — owner, +dimension, position, contents and age — without loading a single chunk. From a +grave's detail screen you can teleport to it or purge it. + +## Purging + +- `/bt_gc_graves:gravesadmin purge [player]` — removes graves now where their + chunks are loaded, and *tombstones* the rest: they vanish the moment their + chunk next loads. Tombstoned graves are invisible everywhere and cost + nothing. +- `/bt_gc_graves:gravesadmin purgestatus` — how many tombstones are pending. +- `/bt_gc_graves:gravesadmin forcepurge` — physically removes every tombstoned + grave right away by briefly ticking each chunk. Use before a world export. + +## keepInventory + +There is no before-death event in Bedrock scripting, so Graves keeps the +`keepInventory` gamerule **on** and does the dropping itself — that is what +makes capture lossless. If anything turns the rule off, Graves turns it back +on and tells operators. + +To hand the gamerule back: + +- `/bt_gc_graves:gravesadmin disable` — refuses while graves still exist. +- `/bt_gc_graves:gravesadmin forcedisable` — releases immediately, restoring + the value `keepInventory` had before Graves was installed. Existing graves + are left alone and can still be opened. +- `/bt_gc_graves:gravesadmin enable` — re-arms everything. + +## For addon developers + +Blocks tagged `bt:gc_graves.impenetrable` are never occupied or replaced by a grave. +Over RPC (`bt_gc_graves`): `getGraves`, `countGraves` and +`registerImpenetrable` — see the repository for the typed interface. diff --git a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx new file mode 100644 index 000000000..9d0577fe6 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx @@ -0,0 +1,18 @@ +--- +title: Your Settings +sidebar_position: 2 +description: The personal settings every player can change. +--- + +# Your Settings + +Open the shared config screen with `/bt_gc_graves:config`. These settings are +yours alone — they only change how Graves behaves for you. + +- **Show Grave Location On Death** — the coordinates message in chat when you + die. +- **Grave Name Tag** — what the floating name over your graves shows: your + name, your name with the death time, or nothing. + +Server-wide behaviour (robbing, despawn times, XP, the void platform...) is +configured by operators. diff --git a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/intro.mdx b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/intro.mdx new file mode 100644 index 000000000..9c787de83 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/intro.mdx @@ -0,0 +1,40 @@ +--- +title: Graves +sidebar_position: 1 +home: true +description: What happens when you die, and how to get everything back. +--- + +# Graves + +When you die, a grave is placed where you fell and keeps **everything**: your +inventory, your armor, your offhand and your XP. Nothing despawns in lava, +nothing falls into the void — the grave is indestructible and fireproof. + +Your grave never moves. It floats on water and lava, and if you die over the +void it is rescued onto a small platform. + +## Getting your items back + +There are three ways to open a grave: + +1. **Interact** — opens the grave like a chest. Take what you want; the grave + disappears once it is empty. +2. **Sneak + interact** — everything goes straight back into your inventory, + into the slots it came from. Anything that does not fit drops at your feet. +3. **Hit it twice** — the grave shakes on the first hit; a second hit while it + shakes scatters everything on the ground. + +## Finding your graves + +Run `/bt_gc_graves:graves` to list your graves — dimension, coordinates, how +long ago you died and what killed you. + +## Other players' graves + +By default only you can open your graves. Two exceptions: + +- An operator can enable **grave robbing** in the config, which lets anyone + open anything. +- A **grave key** (given by operators with `/bt_gc_graves:gravekey`) opens any + grave and is consumed on use. diff --git a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/server-settings.mdx b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/server-settings.mdx new file mode 100644 index 000000000..39b30e2f5 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/server-settings.mdx @@ -0,0 +1,24 @@ +--- +title: Server Settings +sidebar_position: 3 +access: op +description: Every server-wide setting, and where to change it. +--- + +# Server Settings + +Open the shared config screen with `/bt_gc_graves:config`, or reach any scope +with `/bt_gc_graves:configat`. + +- **Allow Grave Robbing** — anyone can open anyone's grave. +- **Grave Key** — whether keys work at all. +- **Collect XP** and **XP Kept (%)** — how much experience the grave stores. +- **Restore Original Slots** — sneak-opening puts items back where they were. +- **Despawn Time** — graves older than this are deleted, contents included. +- **Max Graves Per Player** — cap with either *drop oldest* or *block new* + (blocking a new grave means the player simply keeps their items on death). +- **Void Rescue Platform**, **Height** and **Block** — the anti-void safety + net. +- **Extra Impenetrable Blocks** — block ids a grave must never occupy. Edit it + in the config screen, or from chat: + `/bt_gc_graves:config add extraImpenetrableBlocks `. diff --git a/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts b/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts new file mode 100644 index 000000000..9b4dd8f16 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts @@ -0,0 +1,190 @@ +/** + * Default locale — the shape contract. Every other locale (es_ES.ts) must + * carry exactly this key set. `meta.*` feeds the pack manifests and the + * bedrock-core registry; `config.*` keys are the schema labels/descriptions. + */ +export default { + meta: { + name: 'Graves', + description: 'When you die, a grave saves all your drops. Open it to get them back.', + creator: 'Bedrock Tweaks, DrAv0011', + }, + config: { + groups: { + access: { + label: 'Access', + desc: 'Who may open whose graves, and how.', + }, + contents: { + label: 'Contents', + desc: 'What a grave stores and how it hands it back.', + }, + capture: { + label: 'Capture', + desc: 'The keepInventory contract that makes capture lossless.', + }, + lifetime: { + label: 'Lifetime', + desc: 'Despawning and the per-player grave cap.', + }, + protection: { + label: 'Placement', + desc: 'Where graves may stand: lava, the void, forbidden blocks.', + }, + }, + allowRobbing: { + label: 'Allow Grave Robbing', + desc: 'When enabled, anyone can open anyone else\'s grave without a key.', + }, + graveKeyEnabled: { + label: 'Grave Key', + desc: 'When enabled, a grave key in either hand opens any grave (one key is consumed).', + }, + pickUpXp: { + label: 'Collect XP', + desc: 'Graves store the XP you had when you died.', + }, + xpPercent: { + label: 'XP Kept (%)', + desc: 'Percentage of your XP the grave stores.', + }, + restoreToOriginalSlots: { + label: 'Restore Original Slots', + desc: 'Sneak-opening a grave puts items back into the slots they were in.', + }, + enforceKeepInventory: { + label: 'Enforce keepInventory', + desc: 'Graves needs the keepInventory gamerule and re-enables it if something turns it off.', + }, + warnOnGameRuleChange: { + label: 'Warn On Gamerule Change', + desc: 'Tell operators when Graves re-enables keepInventory.', + }, + despawnSeconds: { + label: 'Despawn Time (seconds)', + desc: 'Graves older than this are deleted with their contents. 0 disables despawning.', + }, + maxGravesPerPlayer: { + label: 'Max Graves Per Player', + desc: 'Cap on simultaneous graves per player. 0 means unlimited.', + }, + onLimitReached: { + label: 'When The Cap Is Reached', + desc: 'drop_oldest deletes the oldest grave; block_new skips the grave so you keep your items.', + }, + voidPlatform: { + label: 'Void Rescue Platform', + desc: 'Place a single block under a grave that would otherwise fall into the void.', + }, + voidRescueY: { + label: 'Void Rescue Height', + desc: 'Y level where void graves (and their platform) are placed.', + }, + voidPlatformBlock: { + label: 'Void Platform Block', + desc: 'The block used for the void rescue platform.', + }, + floatOnLava: { + label: 'Float On Lava', + desc: 'Graves float on lava source blocks instead of sinking to the bottom.', + }, + extraImpenetrableBlocks: { + label: 'Extra Impenetrable Blocks', + desc: 'Block ids a grave must never occupy or replace, in addition to the built-in list.', + }, + showDeathToast: { + label: 'Show Grave Location On Death', + desc: 'Get a chat message with your grave\'s coordinates when you die.', + }, + graveNameStyle: { + label: 'Grave Name Tag', + desc: 'What the floating name over your graves shows.', + }, + }, + cmd: { + graves: 'Graves: open your grave list.', + gravekey: 'Graves: give a grave key.', + gravesadmin: 'Graves: admin panel, purge and enable/disable.', + notAPlayer: 'This command must be run by a player.', + keyGiven_one: 'Gave {{count}} grave key to {{player}}.', + keyGiven_other: 'Gave {{count}} grave keys to {{player}}.', + keyDisabled: 'The grave key is disabled in the config.', + }, + death: { + toast: 'Your grave is at {{x}}, {{y}}, {{z}} in {{dimension}}.', + floating: 'Your grave is floating over the void at {{x}}, {{y}}, {{z}} — approach with care.', + fallback: 'Your grave could not be placed where you died; it was moved to spawn at {{x}}, {{y}}, {{z}}.', + capBlocked: 'You already have {{count}} graves, so no new grave was made — your items stayed with you.', + capDropped: 'You were over the grave cap, so your oldest grave was removed.', + }, + open: { + robbingDisabled: 'Grave robbing is disabled.', + tip: 'Tip: sneak + interact to load a grave straight into your inventory.', + restored: 'Your items are back where they belong.', + overflow_one: '{{count}} item did not fit and was dropped at your feet.', + overflow_other: '{{count}} items did not fit and were dropped at your feet.', + xp: 'Recovered {{amount}} XP.', + keyConsumed: 'The grave key crumbles to dust.', + }, + grave: { + name: '{{owner}}\'s Grave', + }, + item: { + graveKey: 'Grave Key', + }, + dim: { + overworld: 'Overworld', + nether: 'Nether', + the_end: 'The End', + }, + time: { + now: 'just now', + minutes: '{{m}}m ago', + hours: '{{h}}h {{m}}m ago', + days: '{{d}}d {{h}}h ago', + }, + list: { + title: 'Your Graves', + empty: 'You have no graves. Lucky you!', + row_one: '{{count}} item · {{xp}} XP', + row_other: '{{count}} items · {{xp}} XP', + floating: 'floating over the void', + }, + detail: { + title: 'Grave', + owner: 'Owner', + dimension: 'Dimension', + position: 'Position', + items_one: '{{count}} item', + items_other: '{{count}} items', + xp: '{{amount}} XP', + when: 'When', + slainBy: 'Slain by {{killer}}', + cause: 'Cause: {{cause}}', + pendingPurge: 'Marked for deletion — it disappears when its chunk next loads.', + teleport: 'Teleport', + purge: 'Purge', + close: 'Close', + }, + admin: { + title: 'All Graves', + empty: 'No graves anywhere in this world.', + enabled: 'Graves enabled — keepInventory is now enforced again.', + disabled: 'Graves disabled — keepInventory was restored to {{value}}.', + refuseDisable_one: '{{count}} grave still exists. Empty it first, or use forcedisable.', + refuseDisable_other: '{{count}} graves still exist. Empty them first, or use forcedisable.', + alreadyEnabled: 'Graves is already enabled.', + alreadyDisabled: 'Graves is already disabled.', + purged: 'Purged {{total}} graves ({{removed}} removed now, {{tombstoned}} tombstoned — they are deleted when their chunks next load).', + purgedNone: 'Nothing to purge.', + purgeStatus_one: '{{count}} grave is tombstoned and waiting for its chunk to load.', + purgeStatus_other: '{{count}} graves are tombstoned and waiting for their chunks to load.', + forcePurgeStart_one: 'Force-purging {{count}} grave with a ticking area — this may take a while.', + forcePurgeStart_other: 'Force-purging {{count}} graves with a ticking area — this may take a while.', + forcePurgeDone: 'Force purge finished: {{count}} graves physically removed.', + forcePurgeBusy: 'A force purge is already running.', + }, + warn: { + keepInventoryReasserted: 'Graves re-enabled the keepInventory gamerule. Disable the addon with /bt_gc_graves:gravesadmin forcedisable if that is not wanted.', + }, +} as const; diff --git a/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts b/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts new file mode 100644 index 000000000..5ae29c74c --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts @@ -0,0 +1,189 @@ +/** + * Spanish (Spain). Must mirror en_US.ts key-for-key — the build enforces + * parity in both directions, including each key's {{variable}} set. + */ +export default { + meta: { + name: 'Tumbas', + description: 'Al morir, una tumba guarda todos tus objetos. Ábrela para recuperarlos.', + creator: 'Bedrock Tweaks, DrAv0011', + }, + config: { + groups: { + access: { + label: 'Acceso', + desc: 'Quién puede abrir qué tumbas, y cómo.', + }, + contents: { + label: 'Contenido', + desc: 'Qué guarda una tumba y cómo lo devuelve.', + }, + capture: { + label: 'Captura', + desc: 'El contrato de keepInventory que hace la captura sin pérdidas.', + }, + lifetime: { + label: 'Duración', + desc: 'La desaparición y el límite de tumbas por jugador.', + }, + protection: { + label: 'Colocación', + desc: 'Dónde puede estar una tumba: lava, el vacío, bloques prohibidos.', + }, + }, + allowRobbing: { + label: 'Permitir Saqueo De Tumbas', + desc: 'Si está activado, cualquiera puede abrir la tumba de otro jugador sin llave.', + }, + graveKeyEnabled: { + label: 'Llave De Tumba', + desc: 'Si está activada, una llave de tumba en cualquier mano abre cualquier tumba (se consume una llave).', + }, + pickUpXp: { + label: 'Guardar XP', + desc: 'Las tumbas guardan la experiencia que tenías al morir.', + }, + xpPercent: { + label: 'XP Conservada (%)', + desc: 'Porcentaje de tu experiencia que guarda la tumba.', + }, + restoreToOriginalSlots: { + label: 'Restaurar Ranuras Originales', + desc: 'Abrir la tumba agachado devuelve los objetos a las ranuras donde estaban.', + }, + enforceKeepInventory: { + label: 'Forzar keepInventory', + desc: 'Tumbas necesita la regla keepInventory y la reactiva si algo la desactiva.', + }, + warnOnGameRuleChange: { + label: 'Avisar Al Cambiar La Regla', + desc: 'Avisa a los operadores cuando Tumbas reactiva keepInventory.', + }, + despawnSeconds: { + label: 'Tiempo De Desaparición (segundos)', + desc: 'Las tumbas más antiguas que esto se eliminan con su contenido. 0 lo desactiva.', + }, + maxGravesPerPlayer: { + label: 'Máximo De Tumbas Por Jugador', + desc: 'Límite de tumbas simultáneas por jugador. 0 significa sin límite.', + }, + onLimitReached: { + label: 'Al Alcanzar El Límite', + desc: 'drop_oldest elimina la tumba más antigua; block_new omite la tumba y conservas tus objetos.', + }, + voidPlatform: { + label: 'Plataforma Del Vacío', + desc: 'Coloca un bloque bajo una tumba que caería al vacío.', + }, + voidRescueY: { + label: 'Altura De Rescate Del Vacío', + desc: 'Nivel Y donde se colocan las tumbas del vacío (y su plataforma).', + }, + voidPlatformBlock: { + label: 'Bloque De La Plataforma', + desc: 'El bloque usado para la plataforma de rescate del vacío.', + }, + floatOnLava: { + label: 'Flotar En Lava', + desc: 'Las tumbas flotan sobre bloques de lava en vez de hundirse hasta el fondo.', + }, + extraImpenetrableBlocks: { + label: 'Bloques Impenetrables Extra', + desc: 'Ids de bloques que una tumba nunca debe ocupar ni reemplazar, además de la lista integrada.', + }, + showDeathToast: { + label: 'Mostrar Ubicación Al Morir', + desc: 'Recibe un mensaje con las coordenadas de tu tumba cuando mueres.', + }, + graveNameStyle: { + label: 'Nombre De La Tumba', + desc: 'Qué muestra el nombre flotante sobre tus tumbas.', + }, + }, + cmd: { + graves: 'Tumbas: abre tu lista de tumbas.', + gravekey: 'Tumbas: entrega una llave de tumba.', + gravesadmin: 'Tumbas: panel de administración, purga y activar/desactivar.', + notAPlayer: 'Este comando debe ejecutarlo un jugador.', + keyGiven_one: 'Se dio {{count}} llave de tumba a {{player}}.', + keyGiven_other: 'Se dieron {{count}} llaves de tumba a {{player}}.', + keyDisabled: 'La llave de tumba está desactivada en la configuración.', + }, + death: { + toast: 'Tu tumba está en {{x}}, {{y}}, {{z}} en {{dimension}}.', + floating: 'Tu tumba flota sobre el vacío en {{x}}, {{y}}, {{z}} — acércate con cuidado.', + fallback: 'Tu tumba no se pudo colocar donde moriste; se movió al punto de aparición en {{x}}, {{y}}, {{z}}.', + capBlocked: 'Ya tienes {{count}} tumbas, así que no se creó una nueva — tus objetos se quedaron contigo.', + capDropped: 'Superaste el límite de tumbas, así que tu tumba más antigua fue eliminada.', + }, + open: { + robbingDisabled: 'El saqueo de tumbas está desactivado.', + tip: 'Consejo: agáchate e interactúa para cargar la tumba directamente en tu inventario.', + restored: 'Tus objetos volvieron a su lugar.', + overflow_one: '{{count}} objeto no cupo y se dejó caer a tus pies.', + overflow_other: '{{count}} objetos no cupieron y se dejaron caer a tus pies.', + xp: 'Recuperaste {{amount}} de XP.', + keyConsumed: 'La llave de tumba se deshace en polvo.', + }, + grave: { + name: 'Tumba de {{owner}}', + }, + item: { + graveKey: 'Llave De Tumba', + }, + dim: { + overworld: 'Mundo Principal', + nether: 'Nether', + the_end: 'El End', + }, + time: { + now: 'justo ahora', + minutes: 'hace {{m}}m', + hours: 'hace {{h}}h {{m}}m', + days: 'hace {{d}}d {{h}}h', + }, + list: { + title: 'Tus Tumbas', + empty: 'No tienes tumbas. ¡Qué suerte!', + row_one: '{{count}} objeto · {{xp}} XP', + row_other: '{{count}} objetos · {{xp}} XP', + floating: 'flotando sobre el vacío', + }, + detail: { + title: 'Tumba', + owner: 'Dueño', + dimension: 'Dimensión', + position: 'Posición', + items_one: '{{count}} objeto', + items_other: '{{count}} objetos', + xp: '{{amount}} XP', + when: 'Cuándo', + slainBy: 'Asesinado por {{killer}}', + cause: 'Causa: {{cause}}', + pendingPurge: 'Marcada para eliminación — desaparecerá cuando su chunk vuelva a cargarse.', + teleport: 'Teletransportar', + purge: 'Purgar', + close: 'Cerrar', + }, + admin: { + title: 'Todas Las Tumbas', + empty: 'No hay tumbas en este mundo.', + enabled: 'Tumbas activado — keepInventory vuelve a estar forzado.', + disabled: 'Tumbas desactivado — keepInventory se restauró a {{value}}.', + refuseDisable_one: 'Todavía existe {{count}} tumba. Vacíala primero, o usa forcedisable.', + refuseDisable_other: 'Todavía existen {{count}} tumbas. Vacíalas primero, o usa forcedisable.', + alreadyEnabled: 'Tumbas ya está activado.', + alreadyDisabled: 'Tumbas ya está desactivado.', + purged: 'Se purgaron {{total}} tumbas ({{removed}} eliminadas ahora, {{tombstoned}} pendientes — se eliminan cuando sus chunks vuelvan a cargarse).', + purgedNone: 'Nada que purgar.', + purgeStatus_one: '{{count}} tumba está pendiente esperando a que su chunk cargue.', + purgeStatus_other: '{{count}} tumbas están pendientes esperando a que sus chunks carguen.', + forcePurgeStart_one: 'Purgando a la fuerza {{count}} tumba con un ticking area — puede tardar.', + forcePurgeStart_other: 'Purgando a la fuerza {{count}} tumbas con un ticking area — puede tardar.', + forcePurgeDone: 'Purga forzada terminada: {{count}} tumbas eliminadas físicamente.', + forcePurgeBusy: 'Ya hay una purga forzada en curso.', + }, + warn: { + keepInventoryReasserted: 'Tumbas reactivó la regla keepInventory. Desactiva el addon con /bt_gc_graves:gravesadmin forcedisable si no lo deseas.', + }, +} as const; diff --git a/addons/files/gameplay_changes/graves/tsconfig.json b/addons/files/gameplay_changes/graves/tsconfig.json new file mode 100644 index 000000000..70bcbbb62 --- /dev/null +++ b/addons/files/gameplay_changes/graves/tsconfig.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "declaration": false, + "declarationMap": false, + "sourceMap": true, + "stripInternal": true, + "strict": true, + "moduleResolution": "bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + "noImplicitAny": true, + "removeComments": true, + "preserveConstEnums": true, + "noImplicitReturns": true, + "allowJs": true, + "resolveJsonModule": true, + "rootDir": ".", + "jsx": "react-jsx", + "jsxImportSource": "@bedrock-core/ui", + "paths": { + "@bedrock-core/generated/i18n": [ + "./packs/data/i18n/i18n.generated.json" + ], + "@bedrock-core/generated/guides": [ + "./packs/data/guides/guides.generated.json" + ] + } + }, + "files": [ + "packs/BP/scripts/main.ts" + ], + "include": [ + "packs/BP/scripts/**/*", + "packs/BP/blocks/**/*", + "packs/BP/entities/**/*", + "packs/BP/items/**/*", + "packs/data/**/*" + ], + "exclude": [ + "node_modules", + "**/__tests__/*", + "eslint.config.mjs" + ] +} \ No newline at end of file diff --git a/addons/packs.json b/addons/packs.json index efc04ec1b..e8ecfc4cf 100644 --- a/addons/packs.json +++ b/addons/packs.json @@ -1,10 +1,5 @@ { "section": "addons", - "version": [ - 1, - 21, - 0 - ], "categories": [ { "id": "gameplay_changes", @@ -13,8 +8,7 @@ { "id": "graves", "name": "Graves", - "description": "When you die, it places a grave to save all your drops. Hit a grave to retrieve your items. /function graves/config", - "version": "1.21 - 1.0.0" + "description": "When you die, it places a grave to save all your drops. Open a grave to retrieve your items. Type /bt_gc_graves:config to configure." } ] } diff --git a/yarn.lock b/yarn.lock index afd95c8b5..8dcecd9e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,85 +5,164 @@ __metadata: version: 9 cacheKey: 10c0 -"@bedrock-core/config@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/config@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/config@npm:0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/config@npm:0.1.0" + peerDependencies: + "@bedrock-core/guides": "*" + "@bedrock-core/i18n": "*" + "@bedrock-core/navigation": "*" + "@bedrock-core/ore-styled": "*" + "@bedrock-core/server-runtime": "*" + "@bedrock-core/ui-runtime": "*" + "@minecraft/server": "*" + checksum: 10c0/f24f69987881c71e72da009bddeb73c9b3401ba2c63e5c7818996df2d48dab2bf698e02c26734d67257d197d6c9719abc8dd6587cffa1baa378cf5abb2606e3f languageName: node - linkType: soft + linkType: hard -"@bedrock-core/flexbox@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/flexbox@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/flexbox@npm:1.0.0": + version: 1.0.0 + resolution: "@bedrock-core/flexbox@npm:1.0.0" + checksum: 10c0/4e58fa4578c94b4199c579011f37326e51ae37c861ddc610cadc4fa5a79b0793a65f890a397aba42532b669ac87a2187658371f8f975d8e65505533c2c9d1c92 languageName: node - linkType: soft + linkType: hard -"@bedrock-core/guides@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/guides@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/guides@npm:0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/guides@npm:0.1.0" + peerDependencies: + "@bedrock-core/navigation": "*" + "@bedrock-core/ore-styled": "*" + "@bedrock-core/ui-runtime": "*" + checksum: 10c0/c81ea5ca2d860b8531358238255a7cfbb7406eba9f94a0c26371dbe32d2d36a73a6add2690170e837dfc2577aa7d925eb09ee33ccb513f1d7821334c17b13e9e languageName: node - linkType: soft + linkType: hard -"@bedrock-core/navigation@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/navigation@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/i18n@npm:0.1.0, @bedrock-core/i18n@npm:^0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/i18n@npm:0.1.0" + peerDependencies: + "@minecraft/server": ">=2.8.0" + checksum: 10c0/b79f6d71a001831d7fc481a3a080e52cbd3d826646dd497907714dcf03750fbbf8f175508a2704502a01bf0523103016c76fd34527584b680bb7ee2e4a99a6d4 languageName: node - linkType: soft + linkType: hard -"@bedrock-core/ore-styled@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/ore-styled@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/navigation@npm:0.9.3": + version: 0.9.3 + resolution: "@bedrock-core/navigation@npm:0.9.3" + peerDependencies: + "@bedrock-core/ui-runtime": "*" + checksum: 10c0/6be869339c347db8ad4bcc934fe37fcb61353f8500ceca5e6e4cf35eb9b663d6d4e5b6cda2e7d9aed926fc94e8719c094da6e755ce85d6993534d6436f86ef9b languageName: node - linkType: soft + linkType: hard -"@bedrock-core/server-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/server-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/ore-styled@npm:0.10.0": + version: 0.10.0 + resolution: "@bedrock-core/ore-styled@npm:0.10.0" + dependencies: + "@bedrock-core/i18n": "npm:0.1.0" + peerDependencies: + "@bedrock-core/ui-runtime": "*" + checksum: 10c0/2ba08313ef13a2949e5b04014a44ea751619769270c5a986141c19d544c709cda50e440074463ab82551c1c6957889dd10bfef36decb3b3680d59ab9e2ca0606 languageName: node - linkType: soft + linkType: hard -"@bedrock-core/sync@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/sync@link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/server-runtime@npm:0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/server-runtime@npm:0.1.0" + dependencies: + "@bedrock-core/i18n": "npm:^0.1.0" + "@bedrock-core/sync": "npm:^0.1.0" + peerDependencies: + "@minecraft/server": ">=2.8.0" + checksum: 10c0/beddcb3b73340de82218ee8f0d6b913983f655bbcad7bc2a34eb7b06c65575046b788fafea5562ade02b53638ee082d307337e11561bf25d55f43d499c4b1d7d languageName: node - linkType: soft + linkType: hard -"@bedrock-core/ui-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves": - version: 0.0.0-use.local - resolution: "@bedrock-core/ui-runtime@link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime::locator=%40bedrock-tweaks%2Fgraves%40workspace%3Aaddons%2Ffiles%2Fgameplay_changes%2Fgraves" +"@bedrock-core/server@npm:^0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/server@npm:0.1.0" + dependencies: + "@bedrock-core/server-runtime": "npm:0.1.0" + "@bedrock-core/sync": "npm:0.1.0" + peerDependencies: + "@minecraft/server": ">=2.8.0" + checksum: 10c0/6c901e7adc14cfdf89043a986c6937ba426f48d6c4111edccb0db1f5e40dad23f84ef1b55acda0992e8ebd37648f2d19e7ebaeb1b47e6730d7377306927cca62 languageName: node - linkType: soft + linkType: hard + +"@bedrock-core/sync@npm:0.1.0, @bedrock-core/sync@npm:^0.1.0": + version: 0.1.0 + resolution: "@bedrock-core/sync@npm:0.1.0" + peerDependencies: + "@minecraft/server": ">=2.8.0" + checksum: 10c0/75e8238ea2af6e9b205f60925dabf34e41836f71bf7b20dd792314007fed7b15a4366625085e1abadc61a6cebb2acf88d1b536e4b85dd8f50b53db76e2351a65 + languageName: node + linkType: hard + +"@bedrock-core/ui-runtime@npm:0.10.1": + version: 0.10.1 + resolution: "@bedrock-core/ui-runtime@npm:0.10.1" + dependencies: + "@bedrock-core/flexbox": "npm:1.0.0" + "@bedrock-core/i18n": "npm:0.1.0" + peerDependencies: + "@minecraft/server": "*" + "@minecraft/server-ui": "*" + checksum: 10c0/b616eecb76345fb9d809fdec343fd6f01b49d62337e99d7d2e1495182ee4183cf9427c175c263e344831006d37b2d9f53d734e685ac1a1ed2c5d8a76f937e2fe + languageName: node + linkType: hard + +"@bedrock-core/ui@npm:^0.10.0": + version: 0.10.0 + resolution: "@bedrock-core/ui@npm:0.10.0" + dependencies: + "@bedrock-core/config": "npm:0.1.0" + "@bedrock-core/flexbox": "npm:1.0.0" + "@bedrock-core/guides": "npm:0.1.0" + "@bedrock-core/i18n": "npm:0.1.0" + "@bedrock-core/navigation": "npm:0.9.3" + "@bedrock-core/ore-styled": "npm:0.10.0" + "@bedrock-core/ui-runtime": "npm:0.10.1" + peerDependencies: + "@minecraft/server": "*" + "@minecraft/server-ui": "*" + checksum: 10c0/c324846676f5dc1dd37b3cee0c43b04c9021e3d279f767cd31659bf46b806fc77ce30f7262b68999c166b720bb8fac0c1a6f6f8e6b6323d5e2e90d428e170c2c + languageName: node + linkType: hard "@bedrock-tweaks/graves@workspace:addons/files/gameplay_changes/graves": version: 0.0.0-use.local resolution: "@bedrock-tweaks/graves@workspace:addons/files/gameplay_changes/graves" dependencies: - "@bedrock-core/config": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/config" - "@bedrock-core/flexbox": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/flexbox" - "@bedrock-core/guides": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/guides" - "@bedrock-core/navigation": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/navigation" - "@bedrock-core/ore-styled": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ore-styled" - "@bedrock-core/server-runtime": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/server-runtime" - "@bedrock-core/sync": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/server-public/packages/sync" - "@bedrock-core/ui-runtime": "link:../../../../../../Minecraft/Proyectos/@bedrock-core/ui/packages/ui-runtime" - "@eslint/js": "npm:^9.38.0" - "@eslint/json": "npm:^0.13.2" - "@minecraft/common": "npm:1.3.0" - "@minecraft/math": "npm:2.4.0" + "@bedrock-core/server": "npm:^0.1.0" + "@bedrock-core/ui": "npm:^0.10.0" + "@eslint/js": "npm:^10.0.1" + "@eslint/json": "npm:^2.0.0" + "@minecraft/common": "npm:1.2.0" "@minecraft/server": "npm:2.8.0" "@minecraft/server-ui": "npm:2.1.0" "@minecraft/vanilla-data": "npm:1.26.32" - "@stylistic/eslint-config": "npm:^1.1.0" - "@stylistic/eslint-plugin": "npm:^5.4.0" - "@typescript-eslint/eslint-plugin": "npm:^8.46.1" - "@typescript-eslint/parser": "npm:^8.46.1" - eslint: "npm:^9.38.0" - eslint-plugin-minecraft-linting: "npm:^2.0.10" - typescript: "npm:^5.9.3" - typescript-eslint: "npm:^8.46.1" + "@stylistic/eslint-plugin": "npm:^5.10.0" + eslint: "npm:^10.4.1" + eslint-plugin-minecraft-linting: "npm:^1.2.7" + typescript: "npm:^6.0.3" + typescript-eslint: "npm:^8.60.1" languageName: unknown linkType: soft -"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.7.0, @eslint-community/eslint-utils@npm:^4.8.0": +"@eslint-community/eslint-utils@npm:^4.2.0": + version: 4.10.1 + resolution: "@eslint-community/eslint-utils@npm:4.10.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/b514586655698bc6b74db72a496c77e813c78b63e36a83429845ac7057dd77113b0b6c31e6e590d5baaeee2abae6ea22363a41209bcafa078d895ab83ce83011 + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.8.0": version: 4.9.0 resolution: "@eslint-community/eslint-utils@npm:4.9.0" dependencies: @@ -105,181 +184,104 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.12.1": - version: 4.12.1 - resolution: "@eslint-community/regexpp@npm:4.12.1" - checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 - languageName: node - linkType: hard - -"@eslint-community/regexpp@npm:^4.12.2": +"@eslint-community/regexpp@npm:^4.12.2, @eslint-community/regexpp@npm:^4.6.1": version: 4.12.2 resolution: "@eslint-community/regexpp@npm:4.12.2" checksum: 10c0/fddcbc66851b308478d04e302a4d771d6917a0b3740dc351513c0da9ca2eab8a1adf99f5e0aa7ab8b13fa0df005c81adeee7e63a92f3effd7d367a163b721c2d languageName: node linkType: hard -"@eslint/config-array@npm:^0.21.1": - version: 0.21.1 - resolution: "@eslint/config-array@npm:0.21.1" - dependencies: - "@eslint/object-schema": "npm:^2.1.7" - debug: "npm:^4.3.1" - minimatch: "npm:^3.1.2" - checksum: 10c0/2f657d4edd6ddcb920579b72e7a5b127865d4c3fb4dda24f11d5c4f445a93ca481aebdbd6bf3291c536f5d034458dbcbb298ee3b698bc6c9dd02900fe87eec3c - languageName: node - linkType: hard - -"@eslint/config-array@npm:^0.21.2": - version: 0.21.2 - resolution: "@eslint/config-array@npm:0.21.2" +"@eslint/config-array@npm:^0.23.5": + version: 0.23.5 + resolution: "@eslint/config-array@npm:0.23.5" dependencies: - "@eslint/object-schema": "npm:^2.1.7" + "@eslint/object-schema": "npm:^3.0.5" debug: "npm:^4.3.1" - minimatch: "npm:^3.1.5" - checksum: 10c0/89dfe815d18456177c0a1f238daf4593107fd20298b3598e0103054360d3b8d09d967defd8318f031185d68df1f95cfa68becf1390a9c5c6887665f1475142e3 - languageName: node - linkType: hard - -"@eslint/config-helpers@npm:^0.4.1": - version: 0.4.1 - resolution: "@eslint/config-helpers@npm:0.4.1" - dependencies: - "@eslint/core": "npm:^0.16.0" - checksum: 10c0/bb7dd534019a975320ac0f8e0699b37433cee9a3731354c1ee941648e6651032386e7848792060fb53a0fd603ea6cf7a101ed3bd5b82ee2f641598986d1e080a - languageName: node - linkType: hard - -"@eslint/config-helpers@npm:^0.4.2": - version: 0.4.2 - resolution: "@eslint/config-helpers@npm:0.4.2" - dependencies: - "@eslint/core": "npm:^0.17.0" - checksum: 10c0/92efd7a527b2d17eb1a148409d71d80f9ac160b565ac73ee092252e8bf08ecd08670699f46b306b94f13d22e88ac88a612120e7847570dd7cdc72f234d50dcb4 + minimatch: "npm:^10.2.4" + checksum: 10c0/b24833c4c76e78ee075d306cd3f095db46b2db0f90cc13a6ee6e4275f9889731c05bf5403ab5fefb79c756e07ac9184ed0e04570341382f9eccbccc80e6d1a0c languageName: node linkType: hard -"@eslint/core@npm:^0.15.2": - version: 0.15.2 - resolution: "@eslint/core@npm:0.15.2" +"@eslint/config-helpers@npm:^0.7.0": + version: 0.7.0 + resolution: "@eslint/config-helpers@npm:0.7.0" dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/c17a6dc4f5a6006ecb60165cc38bcd21fefb4a10c7a2578a0cfe5813bbd442531a87ed741da5adab5eb678e8e693fda2e2b14555b035355537e32bcec367ea17 + "@eslint/core": "npm:^1.2.1" + checksum: 10c0/fd40d57d6f1db49f7b647048b88a433dc7f6522ef3edf855a43cb526ef4fc40622ceed0dc8de2e03d254f30f8e035370570de1d4bd8e7c2b1200131451e0d331 languageName: node linkType: hard -"@eslint/core@npm:^0.16.0": - version: 0.16.0 - resolution: "@eslint/core@npm:0.16.0" - dependencies: - "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/f27496a244ccfdca3e0fbc3331f9da3f603bdf1aa431af0045a3205826789a54493bc619ad6311a9090eaf7bc25798ff4e265dea1eccd2df9ce3b454f7e7da27 - languageName: node - linkType: hard - -"@eslint/core@npm:^0.17.0": - version: 0.17.0 - resolution: "@eslint/core@npm:0.17.0" +"@eslint/core@npm:^1.2.1": + version: 1.2.1 + resolution: "@eslint/core@npm:1.2.1" dependencies: "@types/json-schema": "npm:^7.0.15" - checksum: 10c0/9a580f2246633bc752298e7440dd942ec421860d1946d0801f0423830e67887e4aeba10ab9a23d281727a978eb93d053d1922a587d502942a713607f40ed704e + checksum: 10c0/10979b40588ecfef771fcb5013a542a35fb30692cc95a65f3481b0b36fbd89f5679efeb30d57f4eed35203d859aabace2a620177d6c536f71b299a1af2f3398f languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.1": - version: 3.3.1 - resolution: "@eslint/eslintrc@npm:3.3.1" +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: "npm:^6.12.4" debug: "npm:^4.3.2" - espree: "npm:^10.0.1" - globals: "npm:^14.0.0" + espree: "npm:^9.6.0" + globals: "npm:^13.19.0" ignore: "npm:^5.2.0" import-fresh: "npm:^3.2.1" js-yaml: "npm:^4.1.0" minimatch: "npm:^3.1.2" strip-json-comments: "npm:^3.1.1" - checksum: 10c0/b0e63f3bc5cce4555f791a4e487bf999173fcf27c65e1ab6e7d63634d8a43b33c3693e79f192cbff486d7df1be8ebb2bd2edc6e70ddd486cbfa84a359a3e3b41 + checksum: 10c0/32f67052b81768ae876c84569ffd562491ec5a5091b0c1e1ca1e0f3c24fb42f804952fdd0a137873bc64303ba368a71ba079a6f691cee25beee9722d94cc8573 languageName: node linkType: hard -"@eslint/eslintrc@npm:^3.3.5": - version: 3.3.5 - resolution: "@eslint/eslintrc@npm:3.3.5" - dependencies: - ajv: "npm:^6.14.0" - debug: "npm:^4.3.2" - espree: "npm:^10.0.1" - globals: "npm:^14.0.0" - ignore: "npm:^5.2.0" - import-fresh: "npm:^3.2.1" - js-yaml: "npm:^4.1.1" - minimatch: "npm:^3.1.5" - strip-json-comments: "npm:^3.1.1" - checksum: 10c0/9fb9f1ca65e46d6173966e3aaa5bd353e3a65d7f1f582bebf77f578fab7d7960a399fac1ecfb1e7d52bd61f5cefd6531087ca52a3a3c388f2e1b4f1ebd3da8b7 +"@eslint/js@npm:8.57.1": + version: 8.57.1 + resolution: "@eslint/js@npm:8.57.1" + checksum: 10c0/b489c474a3b5b54381c62e82b3f7f65f4b8a5eaaed126546520bf2fede5532a8ed53212919fed1e9048dcf7f37167c8561d58d0ba4492a4244004e7793805223 languageName: node linkType: hard -"@eslint/js@npm:9.38.0": - version: 9.38.0 - resolution: "@eslint/js@npm:9.38.0" - checksum: 10c0/b4a0d561ab93f0b1bc6a3f5e3f83764c9cccade59f2c54f1d718c1dcc71ac4d1be97bef7300cca641932d72e7555c79a7bf07e4e4ce1d0a1ddccc84d6440d2a6 - languageName: node - linkType: hard - -"@eslint/js@npm:9.39.4, @eslint/js@npm:^9.38.0": - version: 9.39.4 - resolution: "@eslint/js@npm:9.39.4" - checksum: 10c0/5aa7dea2cbc5decf7f5e3b0c6f86a084ccee0f792d288ca8e839f8bc1b64e03e227068968e49b26096e6f71fd857ab6e42691d1b993826b9a3883f1bdd7a0e46 +"@eslint/js@npm:^10.0.1": + version: 10.0.1 + resolution: "@eslint/js@npm:10.0.1" + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + checksum: 10c0/9f3fcaf71ba7fdf65d82e8faad6ecfe97e11801cc3c362b306a88ea1ed1344ae0d35330dddb0e8ad18f010f6687a70b75491b9e01c8af57acd7987cee6b3ec6c languageName: node linkType: hard -"@eslint/json@npm:^0.13.2": - version: 0.13.2 - resolution: "@eslint/json@npm:0.13.2" +"@eslint/json@npm:^2.0.0": + version: 2.0.1 + resolution: "@eslint/json@npm:2.0.1" dependencies: - "@eslint/core": "npm:^0.15.2" - "@eslint/plugin-kit": "npm:^0.3.5" - "@humanwhocodes/momoa": "npm:^3.3.9" + "@eslint/core": "npm:^1.2.1" + "@eslint/plugin-kit": "npm:^0.7.2" + "@humanwhocodes/momoa": "npm:^3.3.10" natural-compare: "npm:^1.4.0" - checksum: 10c0/1e024e33687a2cb26261856f0c5d459d06100b2e09718d6b1e8b63f6595e879fea6a7944f97abd07309b937773fc75c7dc4d61f4fc070422a0c8740c40334774 + checksum: 10c0/7dcf19d9dad8e74081a56987bde94579042811217d659fc5e13e87a67cdfd9a6c70e127dc797ddd925807d8e5377e97ce0f250639e29fbd4d427c6e8e5160044 languageName: node linkType: hard -"@eslint/object-schema@npm:^2.1.7": - version: 2.1.7 - resolution: "@eslint/object-schema@npm:2.1.7" - checksum: 10c0/936b6e499853d1335803f556d526c86f5fe2259ed241bc665000e1d6353828edd913feed43120d150adb75570cae162cf000b5b0dfc9596726761c36b82f4e87 - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.3.5": - version: 0.3.5 - resolution: "@eslint/plugin-kit@npm:0.3.5" - dependencies: - "@eslint/core": "npm:^0.15.2" - levn: "npm:^0.4.1" - checksum: 10c0/c178c1b58c574200c0fd125af3e4bc775daba7ce434ba6d1eeaf9bcb64b2e9fea75efabffb3ed3ab28858e55a016a5efa95f509994ee4341b341199ca630b89e - languageName: node - linkType: hard - -"@eslint/plugin-kit@npm:^0.4.0": - version: 0.4.0 - resolution: "@eslint/plugin-kit@npm:0.4.0" - dependencies: - "@eslint/core": "npm:^0.16.0" - levn: "npm:^0.4.1" - checksum: 10c0/125614e902bb34c041da859794c47ac2ec4a814f5d9e7c4d37fcd34b38d8ee5cf1f97020d38d168885d9bf4046a9a7decb86b4cee8dac9eedcc6ad08ebafe204 +"@eslint/object-schema@npm:^3.0.5": + version: 3.0.5 + resolution: "@eslint/object-schema@npm:3.0.5" + checksum: 10c0/1db337431f520b99e9edda64ef5fafd7ec6a029843eeb608753025125b6649d861d843cffafafd3c4e37926d7d5f9ec0c6a8e3665c13c3da2144e8132892e92e languageName: node linkType: hard -"@eslint/plugin-kit@npm:^0.4.1": - version: 0.4.1 - resolution: "@eslint/plugin-kit@npm:0.4.1" +"@eslint/plugin-kit@npm:^0.7.2": + version: 0.7.2 + resolution: "@eslint/plugin-kit@npm:0.7.2" dependencies: - "@eslint/core": "npm:^0.17.0" + "@eslint/core": "npm:^1.2.1" levn: "npm:^0.4.1" - checksum: 10c0/51600f78b798f172a9915dffb295e2ffb44840d583427bc732baf12ecb963eb841b253300e657da91d890f4b323d10a1bd12934bf293e3018d8bb66fdce5217b + checksum: 10c0/aafba08077bcd6d7dde6c2e21db18086046a88f914f29971a84cac9ad2d48952ded1b293e665e523805297eff756522dafa16f0062195e2c7143dcd1d47d11ed languageName: node linkType: hard @@ -300,6 +302,17 @@ __metadata: languageName: node linkType: hard +"@humanwhocodes/config-array@npm:^0.13.0": + version: 0.13.0 + resolution: "@humanwhocodes/config-array@npm:0.13.0" + dependencies: + "@humanwhocodes/object-schema": "npm:^2.0.3" + debug: "npm:^4.3.1" + minimatch: "npm:^3.0.5" + checksum: 10c0/205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e + languageName: node + linkType: hard + "@humanwhocodes/module-importer@npm:^1.0.1": version: 1.0.1 resolution: "@humanwhocodes/module-importer@npm:1.0.1" @@ -307,10 +320,17 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/momoa@npm:^3.3.9": - version: 3.3.9 - resolution: "@humanwhocodes/momoa@npm:3.3.9" - checksum: 10c0/591e1ef6b6e299a57006a052199a0b5f854dfbd0016c7862eb21fe7441a1426ef2ea75c006863693eb98fc46d4a60a82319546adb0277df2814b543a8dad7a7b +"@humanwhocodes/momoa@npm:^3.3.10": + version: 3.3.10 + resolution: "@humanwhocodes/momoa@npm:3.3.10" + checksum: 10c0/b80a99f517195ca11d9c3c19431ec5b1b9f4cd21437ed463b2db7e5244a0cdde70148b419e4721baa689b86b8bef2adbb6d1b2c1d140938f1af4640682e3b6d6 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^2.0.3": + version: 2.0.3 + resolution: "@humanwhocodes/object-schema@npm:2.0.3" + checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c languageName: node linkType: hard @@ -321,19 +341,10 @@ __metadata: languageName: node linkType: hard -"@minecraft/common@npm:1.3.0": - version: 1.3.0 - resolution: "@minecraft/common@npm:1.3.0" - checksum: 10c0/6f335029426b6bab2b0c22562863c6a0681d855767ffa8d8e3f0e579ce940505ad2175bd4c96bb5c35b90344ee758757d1d7807908623b6fe00f5f73ea596911 - languageName: node - linkType: hard - -"@minecraft/math@npm:2.4.0": - version: 2.4.0 - resolution: "@minecraft/math@npm:2.4.0" - peerDependencies: - "@minecraft/server": ^1.15.0 || ^2.0.0 - checksum: 10c0/b38640384285cef98d7757b1e2587e729dd2dd00cdd25716b0e594f12598d7dfca35ec9c0fc02631f246967d5276e0a8b2a312cc890a42fefa56c4e8429d87e8 +"@minecraft/common@npm:1.2.0": + version: 1.2.0 + resolution: "@minecraft/common@npm:1.2.0" + checksum: 10c0/597c3ff8ab275ba5d5fb3037e68970e59ac96e8793b7738178c95b17d22a8f48a4412425314ed494f664466dd9a342e2a9eff52af544e57689f103c2ae965e10 languageName: node linkType: hard @@ -381,7 +392,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -391,43 +402,7 @@ __metadata: languageName: node linkType: hard -"@stylistic/eslint-config@npm:^1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-config@npm:1.1.0" - dependencies: - "@stylistic/eslint-plugin-js": "npm:1.1.0" - "@stylistic/eslint-plugin-ts": "npm:1.1.0" - checksum: 10c0/bb19a26f0c329c6454416f5c170ef3ffde1ed924c448c2faa34aabffbe04c65f47b004ef812e7253c2325c49c064726ddfe35c6dab6c66cdce50390f06fec34c - languageName: node - linkType: hard - -"@stylistic/eslint-plugin-js@npm:1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-plugin-js@npm:1.1.0" - dependencies: - acorn: "npm:^8.11.2" - escape-string-regexp: "npm:^4.0.0" - eslint-visitor-keys: "npm:^3.4.3" - espree: "npm:^9.6.1" - graphemer: "npm:^1.4.0" - checksum: 10c0/15cb4cf4e47f0f63c06592b2c399c3a3795781f2efe099c55e3a29cb380b3043407e86f090e58e7e55419a3acfd7429195c752c9eb8d94e0358b45758efe64c1 - languageName: node - linkType: hard - -"@stylistic/eslint-plugin-ts@npm:1.1.0": - version: 1.1.0 - resolution: "@stylistic/eslint-plugin-ts@npm:1.1.0" - dependencies: - "@stylistic/eslint-plugin-js": "npm:1.1.0" - "@typescript-eslint/utils": "npm:^6.10.0" - graphemer: "npm:^1.4.0" - peerDependencies: - eslint: "*" - checksum: 10c0/6f89f7b73794e3f346c35008fbfc28a530a5ad66f03d8b94c4571220a720053298f1ab815909170c1df22fdd44353084dd6b6530f9d0f7a7bddda6f5a1e7b336 - languageName: node - linkType: hard - -"@stylistic/eslint-plugin@npm:^5.4.0": +"@stylistic/eslint-plugin@npm:^5.10.0": version: 5.10.0 resolution: "@stylistic/eslint-plugin@npm:5.10.0" dependencies: @@ -443,6 +418,13 @@ __metadata: languageName: node linkType: hard +"@types/esrecurse@npm:^4.3.1": + version: 4.3.1 + resolution: "@types/esrecurse@npm:4.3.1" + checksum: 10c0/90dad74d5da3ad27606d8e8e757322f33171cfeaa15ad558b615cf71bb2a516492d18f55f4816384685a3eb2412142e732bbae9a4a7cd2cf3deb7572aa4ebe03 + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -450,197 +432,143 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.15": - version: 7.0.15 - resolution: "@types/json-schema@npm:7.0.15" - checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db +"@types/estree@npm:^1.0.8": + version: 1.0.9 + resolution: "@types/estree@npm:1.0.9" + checksum: 10c0/3ad3286ca2988cd550dafb8f2ad599c8474868e954fa601a36655bdfefd8039f7c714b8c1c7f2ae219ffbd58bd4660e66fa7479a0120fc02d4777057d4865387 languageName: node linkType: hard -"@types/semver@npm:^7.5.0": - version: 7.7.1 - resolution: "@types/semver@npm:7.7.1" - checksum: 10c0/c938aef3bf79a73f0f3f6037c16e2e759ff40c54122ddf0b2583703393d8d3127130823facb880e694caa324eb6845628186aac1997ee8b31dc2d18fafe26268 +"@types/json-schema@npm:^7.0.15": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:8.59.2, @typescript-eslint/eslint-plugin@npm:^8.46.1": - version: 8.59.2 - resolution: "@typescript-eslint/eslint-plugin@npm:8.59.2" +"@typescript-eslint/eslint-plugin@npm:8.67.0, @typescript-eslint/eslint-plugin@npm:^8.16.0": + version: 8.67.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.67.0" dependencies: "@eslint-community/regexpp": "npm:^4.12.2" - "@typescript-eslint/scope-manager": "npm:8.59.2" - "@typescript-eslint/type-utils": "npm:8.59.2" - "@typescript-eslint/utils": "npm:8.59.2" - "@typescript-eslint/visitor-keys": "npm:8.59.2" + "@typescript-eslint/scope-manager": "npm:8.67.0" + "@typescript-eslint/type-utils": "npm:8.67.0" + "@typescript-eslint/utils": "npm:8.67.0" + "@typescript-eslint/visitor-keys": "npm:8.67.0" ignore: "npm:^7.0.5" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.5.0" peerDependencies: - "@typescript-eslint/parser": ^8.59.2 + "@typescript-eslint/parser": ^8.67.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/c4c2a67d0ae47ab27e47c46a4cd59eb9941592595d3440cd4dcbccc1983a08c5c9bd276304797964fa2c9276be4cd60077f3087efc6a5370d5b8869720759bc8 + checksum: 10c0/5962baaa764cd6350dfdf51c9a09fdd9ee6be65982ac562ee9b732f851744158e5006bf8ce61556a93534d831be8300fd89d7b79b6b4d7170dc2381d987dc8d9 languageName: node linkType: hard -"@typescript-eslint/parser@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/parser@npm:8.46.1" +"@typescript-eslint/parser@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/parser@npm:8.67.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - debug: "npm:^4.3.4" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/4d14e9dbd5b4ba6001d35ae8833b1b03588911d44b1e01a7e38b1883148c3b1d22e4d4de50e5c6a698a4697ef067e235524b521023d0f5a830767d54c8c5fff5 - languageName: node - linkType: hard - -"@typescript-eslint/parser@npm:8.59.2, @typescript-eslint/parser@npm:^8.46.1": - version: 8.59.2 - resolution: "@typescript-eslint/parser@npm:8.59.2" - dependencies: - "@typescript-eslint/scope-manager": "npm:8.59.2" - "@typescript-eslint/types": "npm:8.59.2" - "@typescript-eslint/typescript-estree": "npm:8.59.2" - "@typescript-eslint/visitor-keys": "npm:8.59.2" + "@typescript-eslint/scope-manager": "npm:8.67.0" + "@typescript-eslint/types": "npm:8.67.0" + "@typescript-eslint/typescript-estree": "npm:8.67.0" + "@typescript-eslint/visitor-keys": "npm:8.67.0" debug: "npm:^4.4.3" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/baf8da8ad233908f00bdfe5835c1e03fd7c9256f675a27b0d10d40d0245c158efc847dee1331c61992fbb152d55bfda3111795f8422e9d341f09ec9729364800 + checksum: 10c0/8f6f8fbea429509ca0c95c4d48a45da0c890172a590606d65587e75a3ca762c3e5678a20a63fc9e386b0b21b7aa643ba9724354ceaa75bc8f62d0b22cb0198c8 languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/project-service@npm:8.46.1" +"@typescript-eslint/parser@npm:^7.2.0": + version: 7.18.0 + resolution: "@typescript-eslint/parser@npm:7.18.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.46.1" - "@typescript-eslint/types": "npm:^8.46.1" + "@typescript-eslint/scope-manager": "npm:7.18.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/typescript-estree": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" debug: "npm:^4.3.4" peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/7218bb343eb371e468596947ef66f0ad5024a76f2787550e093af0fc2b34e1bba3e86840bdec719afd26368e9f75c1ea4ab09bdc84610a746acd89b66910cf8b + eslint: ^8.56.0 + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/370e73fca4278091bc1b657f85e7d74cd52b24257ea20c927a8e17546107ce04fbf313fec99aed0cc2a145ddbae1d3b12e9cc2c1320117636dc1281bcfd08059 languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/project-service@npm:8.59.2" +"@typescript-eslint/project-service@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/project-service@npm:8.67.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.59.2" - "@typescript-eslint/types": "npm:^8.59.2" + "@typescript-eslint/tsconfig-utils": "npm:^8.67.0" + "@typescript-eslint/types": "npm:^8.67.0" debug: "npm:^4.4.3" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/0228f01c61e5ef9022b06510f79f97e37daafd165c592927b640e3587d5cec65a8394a541e1fd21bf65df9f6f1948523569966c9e2181d9cfe911240969cebdb - languageName: node - linkType: hard - -"@typescript-eslint/rule-tester@npm:^8.18.1": - version: 8.46.1 - resolution: "@typescript-eslint/rule-tester@npm:8.46.1" - dependencies: - "@typescript-eslint/parser": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - "@typescript-eslint/utils": "npm:8.46.1" - ajv: "npm:^6.12.6" - json-stable-stringify-without-jsonify: "npm:^1.0.1" - lodash.merge: "npm:4.6.2" - semver: "npm:^7.6.0" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - checksum: 10c0/1528fe639881d842424525912811cc21ae970e2d807c8717f9152f3347e656b09f373d3441f5e966cf323a9de5065f9bc0a3770c9abc2d1d8c3fc061ca69d733 + checksum: 10c0/d8f89dc209f2186c04fb1c42c1a5c69c21696a7a57c5f2be2222682a6440b49ec32687ae85cbd1c1c164431635fbc37bb9404b336e5748966e270ee1347e028c languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/scope-manager@npm:6.21.0" +"@typescript-eslint/scope-manager@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/scope-manager@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" - checksum: 10c0/eaf868938d811cbbea33e97e44ba7050d2b6892202cea6a9622c486b85ab1cf801979edf78036179a8ba4ac26f1dfdf7fcc83a68c1ff66be0b3a8e9a9989b526 + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" + checksum: 10c0/038cd58c2271de146b3a594afe2c99290034033326d57ff1f902976022c8b0138ffd3cb893ae439ae41003b5e4bcc00cabf6b244ce40e8668f9412cc96d97b8e languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/scope-manager@npm:8.46.1" +"@typescript-eslint/scope-manager@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/scope-manager@npm:8.67.0" dependencies: - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - checksum: 10c0/5cff63677e90f3307fe924b739a3fe9f5239f74ec389fa06d6fa0a3fa51f592d8fb038c0c71088157b5b6fb426145bff1239aa3676c05c7d71d3b9be0f8c2cba + "@typescript-eslint/types": "npm:8.67.0" + "@typescript-eslint/visitor-keys": "npm:8.67.0" + checksum: 10c0/8f1fe7dffcb6929ad66dcdca77dd1e4a703f18ab3ab1d685458af74dad10b9be05795e64bd58ff60b45cda8d45737240c84874674d39140c2689e00e3ee82bb9 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/scope-manager@npm:8.59.2" - dependencies: - "@typescript-eslint/types": "npm:8.59.2" - "@typescript-eslint/visitor-keys": "npm:8.59.2" - checksum: 10c0/eec819a96442e98879a66d908a9a388502dc4398005af360dd177907f021ee12e4f2967413b123c4ad4567e7f294b177cf6ee559eed2e86d38ab1af3ed5588a5 - languageName: node - linkType: hard - -"@typescript-eslint/tsconfig-utils@npm:8.46.1, @typescript-eslint/tsconfig-utils@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.46.1" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/c373bd4e2f43e03d8d4dc91cacbc0acdb217809f0e7b23fb4dd349fdab2503489dd79a3adb394491763ec967fa1312c5c9aebdbc5799ad3ed773b036a6eddb9d - languageName: node - linkType: hard - -"@typescript-eslint/tsconfig-utils@npm:8.59.2, @typescript-eslint/tsconfig-utils@npm:^8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.59.2" +"@typescript-eslint/tsconfig-utils@npm:8.67.0, @typescript-eslint/tsconfig-utils@npm:^8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.67.0" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/ca7f386cdc9fa4b8c5de3622e2019b9729b8f5682292e01cc42b7cee4a7fc13005642b9dde733b429ad4e6f8600608930d9c33711d8c20c4f9d0af6520ba78d8 + checksum: 10c0/c19161347ea3d7a0081653c4d399275a3e0d66f87a24131fb5a358e6b55bc27d709781dbda16382f3f721d790338dcf42c65c9e6c26077123e9f3d076d37a894 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/type-utils@npm:8.59.2" +"@typescript-eslint/type-utils@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/type-utils@npm:8.67.0" dependencies: - "@typescript-eslint/types": "npm:8.59.2" - "@typescript-eslint/typescript-estree": "npm:8.59.2" - "@typescript-eslint/utils": "npm:8.59.2" + "@typescript-eslint/types": "npm:8.67.0" + "@typescript-eslint/typescript-estree": "npm:8.67.0" + "@typescript-eslint/utils": "npm:8.67.0" debug: "npm:^4.4.3" ts-api-utils: "npm:^2.5.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/373ea2b03363e0c16ba75946327f01a130820f8b65976e831294c9e931d22d88d40f67a651a2151f0fcc9c91744019311fddadb608fe63ff6c0a65130417c34e - languageName: node - linkType: hard - -"@typescript-eslint/types@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/types@npm:6.21.0" - checksum: 10c0/020631d3223bbcff8a0da3efbdf058220a8f48a3de221563996ad1dcc30d6c08dadc3f7608cc08830d21c0d565efd2db19b557b9528921c78aabb605eef2d74d + checksum: 10c0/0970ea126f9b63a6eb9ca3525a6bf3065dffb3f61a00bd0ee84967140e7ecffe45cafda0cd2c90233dcc06b19c8fb98f4cd90ba8ebbb12deaf9501b9f5b54d8c languageName: node linkType: hard -"@typescript-eslint/types@npm:8.46.1, @typescript-eslint/types@npm:^8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/types@npm:8.46.1" - checksum: 10c0/90887acaa5b33b45af20cf7f87ec4ae098c0daa88484245473e73903fa6e542f613247c22148132167891ca06af6549a60b9d2fd14a65b22871e016901ce3756 +"@typescript-eslint/types@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/types@npm:7.18.0" + checksum: 10c0/eb7371ac55ca77db8e59ba0310b41a74523f17e06f485a0ef819491bc3dd8909bb930120ff7d30aaf54e888167e0005aa1337011f3663dc90fb19203ce478054 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.59.2, @typescript-eslint/types@npm:^8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/types@npm:8.59.2" - checksum: 10c0/ee6889a22b237ef426c45f11dc167f7f220007c2a8f77c8b1ab9e57ac32a2b47fad29f53c4230847cbe49bcd30a35fc2f276e01611d0dab9918d7ef72334f423 +"@typescript-eslint/types@npm:8.67.0, @typescript-eslint/types@npm:^8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/types@npm:8.67.0" + checksum: 10c0/b892a00d4cbea9604a0abf6eedd0ea019b27df4220a1d90e0035101cb4f846722c9e3eeadce40b423c1e0240709bbc787c6ca49bcc4f8c25ba23b4bd0436492a languageName: node linkType: hard @@ -651,53 +579,33 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.21.0" +"@typescript-eslint/typescript-estree@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/visitor-keys": "npm:6.21.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" - minimatch: "npm:9.0.3" - semver: "npm:^7.5.4" - ts-api-utils: "npm:^1.0.1" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" peerDependenciesMeta: typescript: optional: true - checksum: 10c0/af1438c60f080045ebb330155a8c9bb90db345d5069cdd5d01b67de502abb7449d6c75500519df829f913a6b3f490ade3e8215279b6bdc63d0fb0ae61034df5f - languageName: node - linkType: hard - -"@typescript-eslint/typescript-estree@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.46.1" - dependencies: - "@typescript-eslint/project-service": "npm:8.46.1" - "@typescript-eslint/tsconfig-utils": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/visitor-keys": "npm:8.46.1" - debug: "npm:^4.3.4" - fast-glob: "npm:^3.3.2" - is-glob: "npm:^4.0.3" - minimatch: "npm:^9.0.4" - semver: "npm:^7.6.0" - ts-api-utils: "npm:^2.1.0" - peerDependencies: - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/610048f615d4487f3dc57b7440214a14614a9dca8783d142e7dd29e2948d9c8239773839a3bcdf509c266d5f8595ea9f3a20c53c38d7b3bf3cf2305de1491bd8 + checksum: 10c0/0c7f109a2e460ec8a1524339479cf78ff17814d23c83aa5112c77fb345e87b3642616291908dcddea1e671da63686403dfb712e4a4435104f92abdfddf9aba81 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/typescript-estree@npm:8.59.2" +"@typescript-eslint/typescript-estree@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.67.0" dependencies: - "@typescript-eslint/project-service": "npm:8.59.2" - "@typescript-eslint/tsconfig-utils": "npm:8.59.2" - "@typescript-eslint/types": "npm:8.59.2" - "@typescript-eslint/visitor-keys": "npm:8.59.2" + "@typescript-eslint/project-service": "npm:8.67.0" + "@typescript-eslint/tsconfig-utils": "npm:8.67.0" + "@typescript-eslint/types": "npm:8.67.0" + "@typescript-eslint/visitor-keys": "npm:8.67.0" debug: "npm:^4.4.3" minimatch: "npm:^10.2.2" semver: "npm:^7.7.3" @@ -705,84 +613,63 @@ __metadata: ts-api-utils: "npm:^2.5.0" peerDependencies: typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/8bde0e7d184e4e9d98b1ef32b8692b433c8dc5c54439f4a34ae49cf4cb6117435b86d8d891b58a73676fbcf1162717365945053d48139cafb265f775e6c8d2af - languageName: node - linkType: hard - -"@typescript-eslint/utils@npm:8.46.1, @typescript-eslint/utils@npm:^8.18.1": - version: 8.46.1 - resolution: "@typescript-eslint/utils@npm:8.46.1" - dependencies: - "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.46.1" - "@typescript-eslint/types": "npm:8.46.1" - "@typescript-eslint/typescript-estree": "npm:8.46.1" - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ">=4.8.4 <6.0.0" - checksum: 10c0/9089be6b88a934843fd4eead61739e43dc79ba3db3dbaebcd9908eed819765b6414da983254a7d619e89d28b441bd131f53c9f163c39ca5b2369b76cd6699121 + checksum: 10c0/449350fcf4f4ee55a6bb7a73302c4eef072a1282d366344cb625c0f17231eb4088251e6ce61fb48d0d536febb9a28f3725b9bd78ac8d0dbf9c161cf51745870c languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/utils@npm:8.59.2" +"@typescript-eslint/utils@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/utils@npm:8.67.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.9.1" - "@typescript-eslint/scope-manager": "npm:8.59.2" - "@typescript-eslint/types": "npm:8.59.2" - "@typescript-eslint/typescript-estree": "npm:8.59.2" + "@typescript-eslint/scope-manager": "npm:8.67.0" + "@typescript-eslint/types": "npm:8.67.0" + "@typescript-eslint/typescript-estree": "npm:8.67.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/d972d6f2ade6639088e3d2acf319c82cd981455a282d245dd715c5d77365647240d2cb34be00ab0d3a3c16593fb5050fcd698d399ff8451801825211d653ce64 + checksum: 10c0/cccaca1aeba52ec332ee95f3367b84eaf5dd9d60a0d1b4332ebe41b775fa7e8b8faf412de4d88b73a22f84b01f6ab48496c61863407b08fcbef9af429b6b89f1 languageName: node linkType: hard -"@typescript-eslint/utils@npm:^6.10.0": - version: 6.21.0 - resolution: "@typescript-eslint/utils@npm:6.21.0" +"@typescript-eslint/utils@npm:^7.2.0": + version: 7.18.0 + resolution: "@typescript-eslint/utils@npm:7.18.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@types/json-schema": "npm:^7.0.12" - "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.21.0" - "@typescript-eslint/types": "npm:6.21.0" - "@typescript-eslint/typescript-estree": "npm:6.21.0" - semver: "npm:^7.5.4" + "@typescript-eslint/scope-manager": "npm:7.18.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/typescript-estree": "npm:7.18.0" peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - checksum: 10c0/ab2df3833b2582d4e5467a484d08942b4f2f7208f8e09d67de510008eb8001a9b7460f2f9ba11c12086fd3cdcac0c626761c7995c2c6b5657d5fa6b82030a32d + eslint: ^8.56.0 + checksum: 10c0/a25a6d50eb45c514469a01ff01f215115a4725fb18401055a847ddf20d1b681409c4027f349033a95c4ff7138d28c3b0a70253dfe8262eb732df4b87c547bd1e languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.21.0": - version: 6.21.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.21.0" +"@typescript-eslint/visitor-keys@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:6.21.0" - eslint-visitor-keys: "npm:^3.4.1" - checksum: 10c0/7395f69739cfa1cb83c1fb2fad30afa2a814756367302fb4facd5893eff66abc807e8d8f63eba94ed3b0fe0c1c996ac9a1680bcbf0f83717acedc3f2bb724fbf + "@typescript-eslint/types": "npm:7.18.0" + eslint-visitor-keys: "npm:^3.4.3" + checksum: 10c0/538b645f8ff1d9debf264865c69a317074eaff0255e63d7407046176b0f6a6beba34a6c51d511f12444bae12a98c69891eb6f403c9f54c6c2e2849d1c1cb73c0 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.46.1": - version: 8.46.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.46.1" +"@typescript-eslint/visitor-keys@npm:8.67.0": + version: 8.67.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.67.0" dependencies: - "@typescript-eslint/types": "npm:8.46.1" - eslint-visitor-keys: "npm:^4.2.1" - checksum: 10c0/4139a8d78ad95e59fff2285beb623a530b7c2e6af89b994a92e9d8728d0c86eb8d86f64f2372aa874f9f24924253ba9887a2f77bec6bfc6028380b024c24e582 + "@typescript-eslint/types": "npm:8.67.0" + eslint-visitor-keys: "npm:^5.0.0" + checksum: 10c0/d43e6151cd1cdbcfb2f9fa54946198baaebaf0b7a9057ccb2f1aeba1aa3eb46708a97cd7773dcfd0eba3bd45642a5520f4607001cd27d8958e41aa56b2fe7a35 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.59.2": - version: 8.59.2 - resolution: "@typescript-eslint/visitor-keys@npm:8.59.2" - dependencies: - "@typescript-eslint/types": "npm:8.59.2" - eslint-visitor-keys: "npm:^5.0.0" - checksum: 10c0/5863ea98ac642d94fd100f0077c5cfcbe168c3e2ac9bd05da7fc9878c32c20b6d4fc6916df44ffe79c538c4f367a083489d70dab07aa09c51e4e7729716d106e +"@ungap/structured-clone@npm:^1.2.0": + version: 1.3.3 + resolution: "@ungap/structured-clone@npm:1.3.3" + checksum: 10c0/b199e280ee06e9c447e0ccd38df60a65c2b2c13d3c77af50b1e6d77230aee1ea7da309d7b80c5a4850c8e22e4eee9e4b2813c6a33f8c360560d7f2e99a9f6e8f languageName: node linkType: hard @@ -795,7 +682,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.11.2, acorn@npm:^8.15.0, acorn@npm:^8.9.0": +"acorn@npm:^8.15.0, acorn@npm:^8.9.0": version: 8.15.0 resolution: "acorn@npm:8.15.0" bin: @@ -804,7 +691,16 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.4, ajv@npm:^6.12.6": +"acorn@npm:^8.16.0": + version: 8.18.0 + resolution: "acorn@npm:8.18.0" + bin: + acorn: bin/acorn + checksum: 10c0/be771be2135cc07910cf76f444ad514d7dcfd6d4a8026e597e93155275abc8ef61eee12211d52146e9d962874269b634f397464942087be013c89d0c54c5f8e5 + languageName: node + linkType: hard + +"ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -828,6 +724,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + "ansi-styles@npm:^4.1.0": version: 4.3.0 resolution: "ansi-styles@npm:4.3.0" @@ -899,6 +802,15 @@ __metadata: languageName: node linkType: hard +"brace-expansion@npm:^5.0.8": + version: 5.0.9 + resolution: "brace-expansion@npm:5.0.9" + dependencies: + balanced-match: "npm:^4.0.2" + checksum: 10c0/3dea38884a1c3c8b1c9c44a7402a0c76fca460f70cffb3127242b0b4cbf4472019e022ade021eec44838ff19f1dac2625dfd11dd459d7e1e055b0698a8d52fec + languageName: node + linkType: hard + "braces@npm:^3.0.3": version: 3.0.3 resolution: "braces@npm:3.0.3" @@ -948,7 +860,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.6": +"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -987,6 +899,15 @@ __metadata: languageName: node linkType: hard +"doctrine@npm:^3.0.0": + version: 3.0.0 + resolution: "doctrine@npm:3.0.0" + dependencies: + esutils: "npm:^2.0.2" + checksum: 10c0/c96bdccabe9d62ab6fea9399fdff04a66e6563c1d6fb3a3a063e8d53c3bb136ba63e84250bbf63d00086a769ad53aef92d2bd483f03f837fc97b71cbee6b2520 + languageName: node + linkType: hard + "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -994,25 +915,37 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-minecraft-linting@npm:^2.0.10": - version: 2.0.12 - resolution: "eslint-plugin-minecraft-linting@npm:2.0.12" +"eslint-plugin-minecraft-linting@npm:^1.2.7": + version: 1.2.7 + resolution: "eslint-plugin-minecraft-linting@npm:1.2.7" dependencies: - "@typescript-eslint/rule-tester": "npm:^8.18.1" - "@typescript-eslint/utils": "npm:^8.18.1" - eslint: "npm:^9.17.0" - typescript: "npm:^5.6.0" - checksum: 10c0/2a81cb40b505136554ee3113d2b70f6f3c6c5a6b5a67fd7c7f8f17be192ce39c786452fba6b30fa4c59f2e8d121745b9a5310ffc7cc4093aaa4ec6838db81271 + "@typescript-eslint/eslint-plugin": "npm:^8.16.0" + "@typescript-eslint/parser": "npm:^7.2.0" + "@typescript-eslint/utils": "npm:^7.2.0" + eslint: "npm:^8.53.0" + checksum: 10c0/1ca68ccdd3adb3db08eb2c620664acf12b846e4b8d41d92cc567d27e5f1bc7eb6bc97513b41b2d223d649e5694590357ed8249b0bb49252a0bee357199a8028d languageName: node linkType: hard -"eslint-scope@npm:^8.4.0": - version: 8.4.0 - resolution: "eslint-scope@npm:8.4.0" +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 + checksum: 10c0/613c267aea34b5a6d6c00514e8545ef1f1433108097e857225fed40d397dd6b1809dffd11c2fde23b37ca53d7bf935fe04d2a18e6fc932b31837b6ad67e1c116 + languageName: node + linkType: hard + +"eslint-scope@npm:^9.1.2": + version: 9.1.2 + resolution: "eslint-scope@npm:9.1.2" + dependencies: + "@types/esrecurse": "npm:^4.3.1" + "@types/estree": "npm:^1.0.8" + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/9fb8bca5a73e5741efb6cec84467027b6cb6f4203ff9b43a938e272c5cd30800bde46a5c20dfd1609f840225f0b62b7673be391b20acadf8658ca9fa4729b3dd languageName: node linkType: hard @@ -1030,38 +963,35 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^5.0.0": +"eslint-visitor-keys@npm:^5.0.0, eslint-visitor-keys@npm:^5.0.1": version: 5.0.1 resolution: "eslint-visitor-keys@npm:5.0.1" checksum: 10c0/16190bdf2cbae40a1109384c94450c526a79b0b9c3cb21e544256ed85ac48a4b84db66b74a6561d20fe6ab77447f150d711c2ad5ad74df4fcc133736bce99678 languageName: node linkType: hard -"eslint@npm:^9.17.0": - version: 9.38.0 - resolution: "eslint@npm:9.38.0" +"eslint@npm:^10.4.1": + version: 10.8.1 + resolution: "eslint@npm:10.8.1" dependencies: "@eslint-community/eslint-utils": "npm:^4.8.0" - "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.1" - "@eslint/config-helpers": "npm:^0.4.1" - "@eslint/core": "npm:^0.16.0" - "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.38.0" - "@eslint/plugin-kit": "npm:^0.4.0" + "@eslint-community/regexpp": "npm:^4.12.2" + "@eslint/config-array": "npm:^0.23.5" + "@eslint/config-helpers": "npm:^0.7.0" + "@eslint/core": "npm:^1.2.1" + "@eslint/plugin-kit": "npm:^0.7.2" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" "@humanwhocodes/retry": "npm:^0.4.2" "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.12.4" - chalk: "npm:^4.0.0" + ajv: "npm:^6.14.0" cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.4.0" - eslint-visitor-keys: "npm:^4.2.1" - espree: "npm:^10.4.0" - esquery: "npm:^1.5.0" + eslint-scope: "npm:^9.1.2" + eslint-visitor-keys: "npm:^5.0.1" + espree: "npm:^11.2.0" + esquery: "npm:^1.7.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" file-entry-cache: "npm:^8.0.0" @@ -1071,8 +1001,7 @@ __metadata: imurmurhash: "npm:^0.1.4" is-glob: "npm:^4.0.0" json-stable-stringify-without-jsonify: "npm:^1.0.1" - lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.2" + minimatch: "npm:^10.2.5" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" peerDependencies: @@ -1082,60 +1011,59 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/51b0978dce04233580263fd4b5c4f128ecffdcde44fbddfedb5bced48a60d4fc619f5ae91800a1461a78a860b14c77a5081b0b2cf628b705580b70126a11e14b + checksum: 10c0/0c4720a43ef2052829db18e37aa79adb5f0a62137bdf4a4f8e9507bbbbf888ba0669fc03e4b7139d8f27b59b051bbbcd0a3988b068c30fa934a766d60a81f599 languageName: node linkType: hard -"eslint@npm:^9.38.0": - version: 9.39.4 - resolution: "eslint@npm:9.39.4" +"eslint@npm:^8.53.0": + version: 8.57.1 + resolution: "eslint@npm:8.57.1" dependencies: - "@eslint-community/eslint-utils": "npm:^4.8.0" - "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.2" - "@eslint/config-helpers": "npm:^0.4.2" - "@eslint/core": "npm:^0.17.0" - "@eslint/eslintrc": "npm:^3.3.5" - "@eslint/js": "npm:9.39.4" - "@eslint/plugin-kit": "npm:^0.4.1" - "@humanfs/node": "npm:^0.16.6" + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.6.1" + "@eslint/eslintrc": "npm:^2.1.4" + "@eslint/js": "npm:8.57.1" + "@humanwhocodes/config-array": "npm:^0.13.0" "@humanwhocodes/module-importer": "npm:^1.0.1" - "@humanwhocodes/retry": "npm:^0.4.2" - "@types/estree": "npm:^1.0.6" - ajv: "npm:^6.14.0" + "@nodelib/fs.walk": "npm:^1.2.8" + "@ungap/structured-clone": "npm:^1.2.0" + ajv: "npm:^6.12.4" chalk: "npm:^4.0.0" - cross-spawn: "npm:^7.0.6" + cross-spawn: "npm:^7.0.2" debug: "npm:^4.3.2" + doctrine: "npm:^3.0.0" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.4.0" - eslint-visitor-keys: "npm:^4.2.1" - espree: "npm:^10.4.0" - esquery: "npm:^1.5.0" + eslint-scope: "npm:^7.2.2" + eslint-visitor-keys: "npm:^3.4.3" + espree: "npm:^9.6.1" + esquery: "npm:^1.4.2" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" - file-entry-cache: "npm:^8.0.0" + file-entry-cache: "npm:^6.0.1" find-up: "npm:^5.0.0" glob-parent: "npm:^6.0.2" + globals: "npm:^13.19.0" + graphemer: "npm:^1.4.0" ignore: "npm:^5.2.0" imurmurhash: "npm:^0.1.4" is-glob: "npm:^4.0.0" + is-path-inside: "npm:^3.0.3" + js-yaml: "npm:^4.1.0" json-stable-stringify-without-jsonify: "npm:^1.0.1" + levn: "npm:^0.4.1" lodash.merge: "npm:^4.6.2" - minimatch: "npm:^3.1.5" + minimatch: "npm:^3.1.2" natural-compare: "npm:^1.4.0" optionator: "npm:^0.9.3" - peerDependencies: - jiti: "*" - peerDependenciesMeta: - jiti: - optional: true + strip-ansi: "npm:^6.0.1" + text-table: "npm:^0.2.0" bin: eslint: bin/eslint.js - checksum: 10c0/1955067c2d991f0c84f4c4abfafe31bb47fa3b717a7fd3e43fe1e511c6f859d7700cbca969f85661dc4c130f7aeced5e5444884314198a54428f5e5141db9337 + checksum: 10c0/1fd31533086c1b72f86770a4d9d7058ee8b4643fd1cfd10c7aac1ecb8725698e88352a87805cf4b2ce890aa35947df4b4da9655fb7fdfa60dbb448a43f6ebcf1 languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.4.0": +"espree@npm:^10.4.0": version: 10.4.0 resolution: "espree@npm:10.4.0" dependencies: @@ -1146,7 +1074,18 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.6.1": +"espree@npm:^11.2.0": + version: 11.2.0 + resolution: "espree@npm:11.2.0" + dependencies: + acorn: "npm:^8.16.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^5.0.1" + checksum: 10c0/cf87e18ffd9dc113eb8d16588e7757701bc10c9934a71cce8b89c2611d51672681a918307bd6b19ac3ccd0e7ba1cbccc2f815b36b52fa7e73097b251014c3d81 + languageName: node + linkType: hard + +"espree@npm:^9.6.0, espree@npm:^9.6.1": version: 9.6.1 resolution: "espree@npm:9.6.1" dependencies: @@ -1157,12 +1096,12 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.5.0": - version: 1.6.0 - resolution: "esquery@npm:1.6.0" +"esquery@npm:^1.4.2, esquery@npm:^1.7.0": + version: 1.7.0 + resolution: "esquery@npm:1.7.0" dependencies: estraverse: "npm:^5.1.0" - checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 + checksum: 10c0/77d5173db450b66f3bc685d11af4c90cffeedb340f34a39af96d43509a335ce39c894fd79233df32d38f5e4e219fa0f7076f6ec90bae8320170ba082c0db4793 languageName: node linkType: hard @@ -1196,7 +1135,7 @@ __metadata: languageName: node linkType: hard -"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.2": +"fast-glob@npm:^3.2.9": version: 3.3.3 resolution: "fast-glob@npm:3.3.3" dependencies: @@ -1244,6 +1183,15 @@ __metadata: languageName: node linkType: hard +"file-entry-cache@npm:^6.0.1": + version: 6.0.1 + resolution: "file-entry-cache@npm:6.0.1" + dependencies: + flat-cache: "npm:^3.0.4" + checksum: 10c0/58473e8a82794d01b38e5e435f6feaf648e3f36fdb3a56e98f417f4efae71ad1c0d4ebd8a9a7c50c3ad085820a93fc7494ad721e0e4ebc1da3573f4e1c3c7cdd + languageName: node + linkType: hard + "file-entry-cache@npm:^8.0.0": version: 8.0.0 resolution: "file-entry-cache@npm:8.0.0" @@ -1272,6 +1220,17 @@ __metadata: languageName: node linkType: hard +"flat-cache@npm:^3.0.4": + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.3" + rimraf: "npm:^3.0.2" + checksum: 10c0/b76f611bd5f5d68f7ae632e3ae503e678d205cf97a17c6ab5b12f6ca61188b5f1f7464503efae6dc18683ed8f0b41460beb48ac4b9ac63fe6201296a91ba2f75 + languageName: node + linkType: hard + "flat-cache@npm:^4.0.0": version: 4.0.1 resolution: "flat-cache@npm:4.0.1" @@ -1289,6 +1248,13 @@ __metadata: languageName: node linkType: hard +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 + languageName: node + linkType: hard + "gameplay_changes-b20603@workspace:addons/files/gameplay_changes": version: 0.0.0-use.local resolution: "gameplay_changes-b20603@workspace:addons/files/gameplay_changes" @@ -1313,10 +1279,26 @@ __metadata: languageName: node linkType: hard -"globals@npm:^14.0.0": - version: 14.0.0 - resolution: "globals@npm:14.0.0" - checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe + languageName: node + linkType: hard + +"globals@npm:^13.19.0": + version: 13.24.0 + resolution: "globals@npm:13.24.0" + dependencies: + type-fest: "npm:^0.20.2" + checksum: 10c0/d3c11aeea898eb83d5ec7a99508600fbe8f83d2cf00cbb77f873dbf2bcb39428eff1b538e4915c993d8a3b3473fa71eeebfe22c9bb3a3003d1e26b1f2c8a42cd languageName: node linkType: hard @@ -1379,6 +1361,23 @@ __metadata: languageName: node linkType: hard +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: "npm:^1.3.0" + wrappy: "npm:1" + checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 + languageName: node + linkType: hard + +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -1402,6 +1401,13 @@ __metadata: languageName: node linkType: hard +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: 10c0/cf7d4ac35fb96bab6a1d2c3598fe5ebb29aafb52c0aaa482b5a3ed9d8ba3edc11631e3ec2637660c44b3ce0e61a08d54946e8af30dec0b60a7c27296c68ffd05 + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -1420,17 +1426,6 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:^4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" - dependencies: - argparse: "npm:^2.0.1" - bin: - js-yaml: bin/js-yaml.js - checksum: 10c0/561c7d7088c40a9bb53cc75becbfb1df6ae49b34b5e6e5a81744b14ae8667ec564ad2527709d1a6e7d5e5fa6d483aa0f373a50ad98d42fde368ec4a190d4fae7 - languageName: node - linkType: hard - "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -1452,7 +1447,7 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.5.4": +"keyv@npm:^4.5.3, keyv@npm:^4.5.4": version: 4.5.4 resolution: "keyv@npm:4.5.4" dependencies: @@ -1480,7 +1475,7 @@ __metadata: languageName: node linkType: hard -"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.2": +"lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 @@ -1504,15 +1499,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac - languageName: node - linkType: hard - "minimatch@npm:^10.2.2": version: 10.2.5 resolution: "minimatch@npm:10.2.5" @@ -1522,16 +1508,16 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^3.1.2": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" +"minimatch@npm:^10.2.4, minimatch@npm:^10.2.5": + version: 10.2.6 + resolution: "minimatch@npm:10.2.6" dependencies: - brace-expansion: "npm:^1.1.7" - checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + brace-expansion: "npm:^5.0.8" + checksum: 10c0/4559a836243b98bd4d17ea9f7edae698717c76399eea7be374f3737f33164e4907f19e9726891ddeb122f750a5a7fa80d2ac43e851d6e5984dc4ff42ec127d3a languageName: node linkType: hard -"minimatch@npm:^3.1.5": +"minimatch@npm:^3.0.5, minimatch@npm:^3.1.1": version: 3.1.5 resolution: "minimatch@npm:3.1.5" dependencies: @@ -1540,6 +1526,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + "minimatch@npm:^9.0.4": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -1563,6 +1558,15 @@ __metadata: languageName: node linkType: hard +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.4 resolution: "optionator@npm:0.9.4" @@ -1611,6 +1615,13 @@ __metadata: languageName: node linkType: hard +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 + languageName: node + linkType: hard + "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -1681,6 +1692,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: bin.js + checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -1690,7 +1712,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.5.4, semver@npm:^7.6.0": +"semver@npm:^7.6.0": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -1731,6 +1753,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + "strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" @@ -1747,6 +1778,13 @@ __metadata: languageName: node linkType: hard +"text-table@npm:^0.2.0": + version: 0.2.0 + resolution: "text-table@npm:0.2.0" + checksum: 10c0/02805740c12851ea5982686810702e2f14369a5f4c5c40a836821e3eefc65ffeec3131ba324692a37608294b0fd8c1e55a2dd571ffed4909822787668ddbee5c + languageName: node + linkType: hard + "tinyglobby@npm:^0.2.15": version: 0.2.16 resolution: "tinyglobby@npm:0.2.16" @@ -1766,7 +1804,7 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^1.0.1": +"ts-api-utils@npm:^1.3.0": version: 1.4.3 resolution: "ts-api-utils@npm:1.4.3" peerDependencies: @@ -1775,15 +1813,6 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^2.1.0": - version: 2.1.0 - resolution: "ts-api-utils@npm:2.1.0" - peerDependencies: - typescript: ">=4.8.4" - checksum: 10c0/9806a38adea2db0f6aa217ccc6bc9c391ddba338a9fe3080676d0d50ed806d305bb90e8cef0276e793d28c8a929f400abb184ddd7ff83a416959c0f4d2ce754f - languageName: node - linkType: hard - "ts-api-utils@npm:^2.5.0": version: 2.5.0 resolution: "ts-api-utils@npm:2.5.0" @@ -1802,38 +1831,45 @@ __metadata: languageName: node linkType: hard -"typescript-eslint@npm:^8.46.1": - version: 8.59.2 - resolution: "typescript-eslint@npm:8.59.2" +"type-fest@npm:^0.20.2": + version: 0.20.2 + resolution: "type-fest@npm:0.20.2" + checksum: 10c0/dea9df45ea1f0aaa4e2d3bed3f9a0bfe9e5b2592bddb92eb1bf06e50bcf98dbb78189668cd8bc31a0511d3fc25539b4cd5c704497e53e93e2d40ca764b10bfc3 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.60.1": + version: 8.67.0 + resolution: "typescript-eslint@npm:8.67.0" dependencies: - "@typescript-eslint/eslint-plugin": "npm:8.59.2" - "@typescript-eslint/parser": "npm:8.59.2" - "@typescript-eslint/typescript-estree": "npm:8.59.2" - "@typescript-eslint/utils": "npm:8.59.2" + "@typescript-eslint/eslint-plugin": "npm:8.67.0" + "@typescript-eslint/parser": "npm:8.67.0" + "@typescript-eslint/typescript-estree": "npm:8.67.0" + "@typescript-eslint/utils": "npm:8.67.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: ">=4.8.4 <6.1.0" - checksum: 10c0/c2736b839fa268da75df0ca8a1b8cfd658a6c6210c676412ed902c7ff8fea716f36a7e135133000a8f72091255f395d7c85783c4d6a9b1be79e8989abde1cd43 + checksum: 10c0/6ec860f6763be8a5fabf143ff8b6d4e43b98be0c781afc4725eda578ba0e0cecfcc1f06bd1b56fcccc41fa3fc15e4661ec32120d28f2e7abe4953e91ee346de1 languageName: node linkType: hard -"typescript@npm:^5.6.0, typescript@npm:^5.9.3": - version: 5.9.3 - resolution: "typescript@npm:5.9.3" +"typescript@npm:^6.0.3": + version: 6.0.3 + resolution: "typescript@npm:6.0.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/6bd7552ce39f97e711db5aa048f6f9995b53f1c52f7d8667c1abdc1700c68a76a308f579cd309ce6b53646deb4e9a1be7c813a93baaf0a28ccd536a30270e1c5 + checksum: 10c0/4a25ff5045b984370f48f196b3a0120779b1b343d40b9a68d114ea5e5fff099809b2bb777576991a63a5cd59cf7bffd96ff6fe10afcefbcb8bd6fb96ad4b6606 languageName: node linkType: hard -"typescript@patch:typescript@npm%3A^5.6.0#optional!builtin, typescript@patch:typescript@npm%3A^5.9.3#optional!builtin": - version: 5.9.3 - resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" +"typescript@patch:typescript@npm%3A^6.0.3#optional!builtin": + version: 6.0.3 + resolution: "typescript@patch:typescript@npm%3A6.0.3#optional!builtin::version=6.0.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10c0/ad09fdf7a756814dce65bc60c1657b40d44451346858eea230e10f2e95a289d9183b6e32e5c11e95acc0ccc214b4f36289dcad4bf1886b0adb84d711d336a430 + checksum: 10c0/2f25c74e65663c248fa1ade2b8459d9ce5372ff9dad07067310f132966ebec1d93f6c42f0baf77a6b6a7a91460463f708e6887013aaade22111037457c6b25df languageName: node linkType: hard @@ -1864,6 +1900,13 @@ __metadata: languageName: node linkType: hard +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" From 56a58c9b183888d7049c2670d8ed455dfcf9ef9b Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 02:06:21 +0200 Subject: [PATCH 04/11] chore(files): documentation update - Documented the `yarn create-addon` scaffolder in place of the removed addon template and its key replacement list - Fixed the watch command (`yarn run watch`) and clarified that versions are bumped in the addon package.json and BP/RP manifests, not in addons/packs.json - Updated the packs.json interfaces: addons no longer carry a pack version or a root version, both are added by the server from the pack manifests - Dropped the JSON UI restriction for addons and refreshed the technical details to prefer custom commands and UI --- CONTRIBUTING.md | 66 ++++++++++++++++++------------------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8d1ac31c..d9113cf8b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -126,11 +126,13 @@ right of the IDE. **Creating a new addon:** -1. Copy the template from `templates/addon/` to `addons///` -2. Update `addons//package.json` to include the new addon in workspaces -3. Update `addons///package.json` using the graves example as reference: -4. Update UUIDs, pack name, and descriptions in BP/RP manifest.json and config.json -5. Update module versions in config.json and package.json as needed +Addons are built on the [bedrock-core](https://bedrock-core.drav.dev/) stack. Scaffold them with the repo wrapper around its CLI: + +1. Run `yarn create-addon [--author ] [description...]` from the repo root (example: `yarn create-addon gameplay_changes graves When you die, a grave saves all your drops.`) +2. It scaffolds `addons/files///` with creator `bt` and namespace `bt__` (example: `bt_gc_graves`), credits you as the author (your git user.name, or `--author`) in `config.json`, the manifests and the bedrock-core addon list (` · Bedrock Tweaks`), adapts the package to the monorepo (`@bedrock-tweaks/`, private, no addon-local yarn files) and registers it in the category workspaces +3. Run `yarn install` at the root, then build the addon from its directory +4. Add the pack entry to `addons/packs.json` with id `` under its category (the `bt__` namespace is only for commands, tags and identifiers inside the addon) +5. Use the latest stable Minecraft module versions and `min_engine_version`; experimental/beta APIs are not accepted **Development workflow:** @@ -138,7 +140,7 @@ After installing the monorepo with `yarn install`, run `yarn regolith-install` o Then, **open each addon as a standalone VSCode instance** and run commands from that directory: -- **Watch mode** (live recompilation): `yarn run dev` (runs `regolith watch`) +- **Watch mode** (live recompilation): `yarn run watch` (runs `regolith watch`) - **Build once**: `yarn run build` (runs `regolith run build`) - **Lint addon**: `yarn run lint` (runs `eslint .`) @@ -152,7 +154,7 @@ From the root directory, use these commands to manage the entire monorepo: **Before submitting PR:** - Lint the addon from its directory: `yarn run lint` -- Bump versions in addon `package.json` and `addons/packs.json` +- Bump the addon version in its `package.json` and BP/RP `manifest.json` (addons do not set versions in `addons/packs.json` — the version is inferred from the pack itself) - Test the addon in-game on at least 1 device **Monorepo structure:** @@ -163,34 +165,11 @@ From the root directory, use these commands to manage the entire monorepo: Regarding regolith filters, currently it is only accepted filters which run on node. -Resource Pack JSON UI modifications for addons are not accepted at this moment. - #### Technical Details -- Addons should not have functions +- Addons should not have functions, prefer custom commands - All settings and interactions should be in-game or in server forms -- Addons should have a basic `/bt: config` (TBD specifics discuss in discord) base command which should open a config server form -- Addon could have extra commands for quick access if necessary for commodity (for example tpa) but prefer server forms, easier for normal users -- The code in the template is an example it could be removed and changed as long as it follows the structure -- Prefer interfaces to types. -- Prefer functional programming over object-oriented programming. -- Prefer `const` and `let` over `var`. - -#### Keys to change - -When making an addon from the template you should look for these keys and replace them - -```md - - - - - - - - - -``` +- Addon could have extra commands for quick access if necessary for commodity (for example tpa) but prefer ui, easier for normal users If you notice any files not following the Style Guide feel free to open a PR. @@ -333,7 +312,9 @@ export interface PacksJSON { section: Section; // Global pack version, this will be the header.min_engine_version in the manifest.json // example: [1, 21, 0] - version: number[]; + // * Not set for Addons: the server adds it when generating, as the min of the + // minimum engine versions of all addons + version?: number[]; categories: Category[]; combinations: Combination[]; deepMergeFiles: DeepMergeFile[]; @@ -364,22 +345,23 @@ export interface Pack { name: string; description: string; message?: Message; - version?: string; // * only Addons and CT + version?: string; // * only CT — addons do not carry a version here: it is inferred from the pack itself and added by the server, which builds the dependency tree and downloads all selected packs priority?: number; // Higher number, higher priority disabled?: boolean; } /** - * Pack Version is a string as follows: " - " for addons - * and just "" for crafting tweaks - * minecraft_version is the minimum version of the game the pack is compatible with + * Pack Version is a string as follows: "" for crafting tweaks * pack_version is the version of the pack for that minecraft update, each mc update it resets * example: - * (version update) "1.21.50 - 1.0.0" - * (bug fix) "1.21.50 - 1.0.1" - * (pack major revamp) "1.21.50 - 2.0.0" - * (pack improvements) "1.21.50 - 2.1.0" - * (version update) "1.22.0 - 1.0.0" + * (version update) "1.0.0" + * (bug fix) "1.0.1" + * (pack major revamp) "2.0.0" + * (pack improvements) "2.1.0" + * + * Addons never author versions in packs.json: each addon is its own pack, its + * version and minimum engine version come from the pack manifest, and the + * server adds pack versions and the root version when generating. */ export interface Combination { From 50ed378ccff09e71f9d13647d4a1d022d6fe6755 Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 10:09:34 +0200 Subject: [PATCH 05/11] update(files): Graves - Rebuilt the grave model as a stone brick wall with east/west connections and a raised centre post, using the vanilla stone brick texture - Reworked the grave list, admin panel and detail screens onto the bedrock-core screen shell: card parent, fixed header and a scroll region below it, with the detail actions pinned under the scroll - Added a settings gear to the list and admin headers which opens the addon config through the shared bedrock-core UI - Replaced the grave shake with a damped spring around the vertical axis, driven by an animation controller so every hit restarts it - Updated the addon to @bedrock-core/ui 0.11.0 and its core UI pack dependency --- .../gameplay_changes/graves/package.json | 2 +- .../graves/packs/BP/manifest.json | 2 +- .../graves/packs/BP/scripts/UI/AdminPanel.tsx | 35 ++-- .../packs/BP/scripts/UI/GraveDetail.tsx | 64 ++++---- .../graves/packs/BP/scripts/UI/GraveList.tsx | 37 +++-- .../packs/BP/scripts/UI/SettingsButton.tsx | 37 +++++ .../RP/animation_controllers/grave.ac.json | 25 +++ .../packs/RP/animations/grave.animation.json | 4 +- .../graves/packs/RP/entity/grave.entity.json | 7 +- .../packs/RP/models/entity/grave.geo.json | 152 +++++++++++++----- .../graves/packs/RP/textures/entity/grave.png | 4 +- .../graves/packs/data/i18n/en_US.ts | 4 +- .../graves/packs/data/i18n/es_ES.ts | 4 +- yarn.lock | 30 ++-- 14 files changed, 283 insertions(+), 124 deletions(-) create mode 100644 addons/files/gameplay_changes/graves/packs/BP/scripts/UI/SettingsButton.tsx create mode 100644 addons/files/gameplay_changes/graves/packs/RP/animation_controllers/grave.ac.json diff --git a/addons/files/gameplay_changes/graves/package.json b/addons/files/gameplay_changes/graves/package.json index 37fca6f28..1e96adb85 100644 --- a/addons/files/gameplay_changes/graves/package.json +++ b/addons/files/gameplay_changes/graves/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@bedrock-core/server": "^0.1.0", - "@bedrock-core/ui": "^0.10.0", + "@bedrock-core/ui": "^0.11.0", "@minecraft/common": "1.2.0", "@minecraft/server": "2.8.0", "@minecraft/server-ui": "2.1.0", diff --git a/addons/files/gameplay_changes/graves/packs/BP/manifest.json b/addons/files/gameplay_changes/graves/packs/BP/manifest.json index 526b785c9..f511ec81c 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/manifest.json +++ b/addons/files/gameplay_changes/graves/packs/BP/manifest.json @@ -29,7 +29,7 @@ }, { "uuid": "761ecd37-ad1c-4a64-862a-d6cc38767426", - "version": [1, 10, 0] + "version": [1, 11, 0] }, { "module_name": "@minecraft/server", diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx index e85c1f22f..b7a473a85 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/AdminPanel.tsx @@ -1,11 +1,13 @@ /** * Every grave in the world, straight from the index — no chunk needs to be * loaded (§6b). Rows navigate into the same detail screen the list uses. + * Same core screen shell as the list: Card parent, scroll below the header. */ -import { Panel, Text, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; -import { Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; +import { Panel, Scroll, Text, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Card, Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; import type { ScreenProps } from '@bedrock-core/ui/navigation'; import { i18n } from './i18n'; +import { SettingsButton } from './SettingsButton'; import { allRecords } from '../index/store'; import { agoStr, dimName, posStr } from '../util'; import type { GravesRoutes } from './GravesApp'; @@ -20,19 +22,24 @@ export function AdminPanel({ navigation }: ScreenProps): const records = [...allRecords()].sort((a, b) => b.diedAt - a.diedAt); return ( - +
$.admin.title)} onClose={exit} /> - - {records.length === 0 - ? {t($ => $.admin.empty)} - : records.map(record => ( - $.list.row, { count: record.items, xp: record.xp })} · ${agoStr(bound, record.diedAt)}`} - onPress={() => navigation.navigate('Detail', { graveId: record.id })} - /> - ))} + + + + + {records.length === 0 + ? {t($ => $.admin.empty)} + : records.map(record => ( + $.list.row, { count: record.items, xp: record.xp })} — ${agoStr(bound, record.diedAt)}`} + onPress={() => navigation.navigate('Detail', { graveId: record.id })} + /> + ))} + + - + ); } diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx index d8a5053b8..b74c3e0ff 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx @@ -1,9 +1,10 @@ /** * One grave: text summary only — no item icons by design (§4: ItemRenderer * aux ids are unreliable in multi-addon worlds). Operators get teleport and - * purge actions. + * purge actions. Core screen shell: Card parent, Header flush on top, info + * scrolls below it, actions pinned under the scroll. */ -import { Panel, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Panel, Scroll, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; import { Button, Card, Divider, Header, theme } from '@bedrock-core/ui/ore-styled'; import { isOperator } from '@bedrock-core/server'; import { world } from '@minecraft/server'; @@ -27,10 +28,12 @@ export function GraveDetail({ navigation, route }: ScreenProps +
$.detail.title)} onBack={() => navigation.goBack()} onClose={exit} /> - {t($ => $.list.empty)} - + + {t($ => $.list.empty)} + + ); } @@ -58,29 +61,36 @@ export function GraveDetail({ navigation, route }: ScreenProps +
$.detail.title)} breadcrumbs={[record.ownerName]} onBack={() => navigation.goBack()} onClose={exit} /> - - {`${fontColor.muted}${t($ => $.detail.owner)}: §r${record.ownerName}`} - {`${fontColor.muted}${t($ => $.detail.dimension)}: §r${dimName(bound, record.dim)}`} - {`${fontColor.muted}${t($ => $.detail.position)}: §r${posStr(record.x, record.y, record.z)}`} - - {t($ => $.detail.items, { count: record.items })} - {t($ => $.detail.xp, { amount: record.xp })} - {`${t($ => $.detail.when)}: ${agoStr(bound, record.diedAt)}`} - {cause ? {cause} : undefined} - {record.floating ? {`§6${t($ => $.list.floating)}`} : undefined} - {record.purge ? {`§c${t($ => $.detail.pendingPurge)}`} : undefined} - - {admin - ? ( - - - + + + + + {`${fontColor.muted}${t($ => $.detail.owner)}: §r${record.ownerName}`} + {`${fontColor.muted}${t($ => $.detail.dimension)}: §r${dimName(bound, record.dim)}`} + {`${fontColor.muted}${t($ => $.detail.position)}: §r${posStr(record.x, record.y, record.z)}`} + + {t($ => $.detail.items, { count: record.items })} + {t($ => $.detail.xp, { amount: record.xp })} + {`${t($ => $.detail.when)}: ${agoStr(bound, record.diedAt)}`} + {cause ? {cause} : undefined} + {record.floating ? {`§6${t($ => $.list.floating)}`} : undefined} + {record.purge ? {`§c${t($ => $.detail.pendingPurge)}`} : undefined} - ) - : undefined} - - + + + + {admin + ? ( + + + + + ) + : undefined} + + + ); } diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx index 98f53034a..b051e538b 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx @@ -1,11 +1,13 @@ /** * The player's own graves — one MenuRow per grave, newest first. Replaces the - * Java pack's paginated tellraw list; the root scroll handles overflow. + * Java pack's paginated tellraw list; the scroll region below the header + * handles overflow, matching the core `ui(core)` screen shell. */ -import { Panel, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; -import { Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; +import { Panel, Scroll, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Card, Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; import type { ScreenProps } from '@bedrock-core/ui/navigation'; import { i18n } from './i18n'; +import { SettingsButton } from './SettingsButton'; import { visibleRecordsOf } from '../index/store'; import { agoStr, dimName, posStr } from '../util'; import type { GravesRoutes } from './GravesApp'; @@ -21,19 +23,24 @@ export function GraveList({ navigation }: ScreenProps): JS const records = [...visibleRecordsOf(player.id)].sort((a, b) => b.diedAt - a.diedAt); return ( - +
$.list.title)} onClose={exit} /> - - {records.length === 0 - ? {t($ => $.list.empty)} - : records.map(record => ( - $.list.row, { count: record.items, xp: record.xp })} - subtitle={`${dimName(bound, record.dim)} ${posStr(record.x, record.y, record.z)} · ${agoStr(bound, record.diedAt)}${record.floating ? ` · ${t($ => $.list.floating)}` : ''}`} - onPress={() => navigation.navigate('Detail', { graveId: record.id })} - /> - ))} + + + + + {records.length === 0 + ? {t($ => $.list.empty)} + : records.map(record => ( + $.list.row, { count: record.items, xp: record.xp })} + subtitle={`${dimName(bound, record.dim)} ${posStr(record.x, record.y, record.z)} — ${agoStr(bound, record.diedAt)}${record.floating ? ` — ${t($ => $.list.floating)}` : ''}`} + onPress={() => navigation.navigate('Detail', { graveId: record.id })} + /> + ))} + + - + ); } diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/SettingsButton.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/SettingsButton.tsx new file mode 100644 index 000000000..fe675414a --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/SettingsButton.tsx @@ -0,0 +1,37 @@ +/** + * Gear button seated in the header's empty back slot — an absolute overlay + * with the same metrics as the ore back control (header margins 1 + padding, + * iconSize square). Opens this addon's settings through the shared `ui(core)` + * config UI. The main list and the admin panel wear it; detail keeps back. + */ +import { usePlayer, type JSX } from '@bedrock-core/ui'; +import { openUi } from '@bedrock-core/ui/config'; +import { Button, theme } from '@bedrock-core/ui/ore-styled'; +import { core } from '../registration'; + +/** Vanilla dark gear glyph — reads on the light ore header like the §0 title. */ +const GEAR = 'textures/ui/settings_glyph_color_2x'; + +export function SettingsButton(): JSX.Element { + const player = usePlayer(); + const h = theme.components.header; + + return ( + - - + + + + + + + ) : undefined} - ); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx index b051e538b..cbaf643bf 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveList.tsx @@ -1,18 +1,24 @@ /** - * The player's own graves — one MenuRow per grave, newest first. Replaces the + * The player's own graves — one row per grave, newest first. Replaces the * Java pack's paginated tellraw list; the scroll region below the header * handles overflow, matching the core `ui(core)` screen shell. + * + * Each row carries its own locator-bar checkbox. This screen is the ONLY place + * that control exists, because it is the only screen that shows graves the + * viewer owns — the admin panel lists everyone's, and a waypoint onto someone + * else's death pile is not something this addon offers from anywhere. */ -import { Panel, Scroll, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; -import { Card, Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; +import { Panel, Scroll, Text, usePlayer, useExit, useState, useTranslation, type JSX } from '@bedrock-core/ui'; +import { Card, Checkbox, Header, MenuRow, theme } from '@bedrock-core/ui/ore-styled'; import type { ScreenProps } from '@bedrock-core/ui/navigation'; import { i18n } from './i18n'; import { SettingsButton } from './SettingsButton'; -import { visibleRecordsOf } from '../index/store'; +import { updateRecord, visibleRecordsOf } from '../index/store'; import { agoStr, dimName, posStr } from '../util'; +import type { GraveRecord } from '../types'; import type { GravesRoutes } from './GravesApp'; -const { spacing } = theme.tokens; +const { fontColor, spacing } = theme.tokens; export function GraveList({ navigation }: ScreenProps): JSX.Element { const player = usePlayer(); @@ -20,26 +26,51 @@ export function GraveList({ navigation }: ScreenProps): JS const exit = useExit(); const { t, key } = bound; + // Re-read on every toggle: the checkbox writes to the index, and the index — + // not this component — is what the locator bar and the next open both read. + const [revision, setRevision] = useState(0); const records = [...visibleRecordsOf(player.id)].sort((a, b) => b.diedAt - a.diedAt); + const toggleWaypoint = (record: GraveRecord, show: boolean): void => { + // `undefined` rather than a false flag: the index is JSON, so the key + // simply stops being written, and "absent means shown" stays the one rule. + updateRecord({ ...record, noWaypoint: show ? undefined : true }); + setRevision(revision + 1); + }; + return (
$.list.title)} onClose={exit} /> - - - - {records.length === 0 - ? {t($ => $.list.empty)} - : records.map(record => ( - $.list.row, { count: record.items, xp: record.xp })} - subtitle={`${dimName(bound, record.dim)} ${posStr(record.x, record.y, record.z)} — ${agoStr(bound, record.diedAt)}${record.floating ? ` — ${t($ => $.list.floating)}` : ''}`} - onPress={() => navigation.navigate('Detail', { graveId: record.id })} - /> - ))} - - + + {records.length === 0 + ? {t($ => $.list.empty)} + : ( + + {`${fontColor.muted}${t($ => $.list.waypointHint)}`} + + + + {records.map(record => ( + + + $.list.row, { count: record.items, xp: record.xp })} + subtitle={`${dimName(bound, record.dim)} ${posStr(record.x, record.y, record.z)} · ${agoStr(bound, record.diedAt)}${record.floating ? ` · ${t($ => $.list.floating)}` : ''}`} + onPress={() => navigation.navigate('Detail', { graveId: record.id })} + /> + + toggleWaypoint(record, show)} + /> + + ))} + + + + + )} ); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts index c79b4de1a..e2872d705 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/config.ts @@ -23,6 +23,12 @@ export const configDef = { label: i18n.key($ => $.config.graveKeyEnabled.label), description: i18n.key($ => $.config.graveKeyEnabled.desc), }, + allowLocating: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.allowLocating.label), + description: i18n.key($ => $.config.allowLocating.desc), + }, }, contents: { $label: i18n.key($ => $.config.groups.contents.label), @@ -142,6 +148,12 @@ export const configDef = { label: i18n.key($ => $.config.showDeathToast.label), description: i18n.key($ => $.config.showDeathToast.desc), }, + showOnLocatorBar: { + type: 'boolean', + default: true, + label: i18n.key($ => $.config.showOnLocatorBar.label), + description: i18n.key($ => $.config.showOnLocatorBar.desc), + }, graveNameStyle: { type: 'enum', default: 'name', diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts index 6c11c03f0..5b7d1a8c8 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/constants.ts @@ -30,6 +30,10 @@ export const PROP_PREV_KEEP_INVENTORY = `${NAMESPACE}:prevKeepInventory`; export const GRAVE_INVENTORY_SIZE = 41; export const PLAYER_CONTAINER_SLOTS = 36; +/** Locator bar (§8) — the grave icon shipped in the RP, and its bar tint. */ +export const WAYPOINT_TEXTURE = 'textures/ui/grave_waypoint'; +export const WAYPOINT_COLOR = { red: 0.62, green: 0.62, blue: 0.66 }; + /** Ticks the grave keeps shaking after the first hit — the second-hit window. */ export const SHAKE_WINDOW_TICKS = 10; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts new file mode 100644 index 000000000..53168b171 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts @@ -0,0 +1,248 @@ +/** + * §8 — the locator bar. Each of a player's own graves becomes a waypoint on + * their bar, the Bedrock answer to the Java pack's `/graves list` coordinates + * hunt: the direction is on the HUD, so nobody has to copy numbers out of chat. + * + * Waypoints are OWNER-ONLY, on purpose. `allowRobbing` opens someone else's + * grave to you, but it does not put a marker on it — a server-wide bar of + * everyone's death piles would be a loot map, not a convenience. + * + * The set is reconciled, never incrementally patched: `sync()` diffs what the + * index says against what we put on the bar and applies the difference. Every + * trigger — index write, respawn, either config toggle — runs the same path, + * so there is no ordering between them to get wrong. + */ +import type { LocatorBar, Player } from '@minecraft/server'; +import { LocationWaypoint, system, WaypointTexture, world } from '@minecraft/server'; +import { WAYPOINT_COLOR, WAYPOINT_TEXTURE } from './constants'; +import { visibleRecordsOf } from './index/store'; +import { config, core } from './registration'; +import type { GraveRecord } from './types'; + +/** One tracked waypoint and the record position it was built from. */ +interface Tracked { + waypoint: LocationWaypoint; + dim: string; + x: number; + y: number; + z: number; +} + +/** player.id → graveId → tracked waypoint. Rebuilt from scratch on rejoin. */ +const tracked = new Map>(); + +/** Per-player unsubscribe for the player-scope toggle, dropped on leave. */ +const unsubscribes = new Map void>(); + +/** + * The custom grave icon, with the vanilla square as the fallback shape. + * + * A texture selector is validated when the waypoint is built, and a rejected + * one takes the whole waypoint with it — so a bad path must not be able to + * cost the player their marker. `buildWaypoint` catches that and retries with + * the built-in texture, which cannot fail. + */ +const selectorFor = (custom: boolean): { textureBoundsList: [{ lowerBound: number; texture: WaypointTexture | { path: string; iconWidth: number; iconHeight: number } }] } => ({ + textureBoundsList: [ + { + lowerBound: 0, + texture: custom + ? { path: WAYPOINT_TEXTURE, iconWidth: 1, iconHeight: 1 } + : WaypointTexture.Square, + }, + ], +}); + +/** Whether the RP icon has already proven itself unusable in this session. */ +let customTextureBroken = false; + +const buildWaypoint = (record: GraveRecord): LocationWaypoint | undefined => { + const dimension = world.getDimension(record.dim); + + if (!dimension) { + return undefined; + } + + const at = { dimension, x: record.x + 0.5, y: record.y, z: record.z + 0.5 }; + + if (!customTextureBroken) { + try { + return new LocationWaypoint(at, selectorFor(true), WAYPOINT_COLOR); + } catch (error) { + customTextureBroken = true; + console.warn(`[graves] locator icon '${WAYPOINT_TEXTURE}' rejected, falling back to the vanilla square: ${String(error)}`); + } + } + + try { + return new LocationWaypoint(at, selectorFor(false), WAYPOINT_COLOR); + } catch (error) { + console.error(`[graves] could not build a waypoint for grave ${record.id}: ${String(error)}`); + + return undefined; + } +}; + +const drop = (bar: LocatorBar, entry: Tracked): void => { + try { + bar.removeWaypoint(entry.waypoint); + } catch { + // Already gone: the engine drops invalid waypoints on its own next tick. + } + + try { + entry.waypoint.remove(); + } catch { + // Same — nothing to do, and nothing that should reach the player. + } +}; + +/** Which graves this player should currently see marked. */ +const wanted = (player: Player): GraveRecord[] => { + if (!config.server.access.allowLocating.get()) { + return []; + } + + if (!config.player.for(player).showOnLocatorBar.get()) { + return []; + } + + // visibleRecordsOf is keyed by owner: a player is only ever handed their own + // graves, so no waypoint can point at someone else's death pile — not with + // robbing on, not from the admin panel, not at all. + // Newest first, so the cap below keeps the graves a player is most likely + // to still want rather than an arbitrary slice. + return [...visibleRecordsOf(player.id)] + .filter(record => !record.noWaypoint) + .sort((a, b) => b.diedAt - a.diedAt); +}; + +/** + * Reconcile one player's bar with the index. + * + * Cheap enough to run on every index write: it reads one dynamic property and + * walks a handful of records, and it only touches the bar where the two sides + * actually disagree. + */ +export const sync = (player: Player): void => { + let mine = tracked.get(player.id); + + if (!mine) { + mine = new Map(); + tracked.set(player.id, mine); + } + + const bar = player.locatorBar; + const records = wanted(player); + const live = new Set(records.map(r => r.id)); + + for (const [graveId, entry] of mine) { + if (!live.has(graveId)) { + drop(bar, entry); + mine.delete(graveId); + } + } + + // Our own share of the bar is what we may fill; other packs' waypoints are + // counted by `count` but are neither ours to read nor ours to evict. + const room = Math.max(0, bar.maxCount - (bar.count - mine.size)); + + for (const record of records) { + const existing = mine.get(record.id); + + if (existing) { + // A grave that drifted (reconcile refreshed its position) moves its + // marker rather than rebuilding it — the handle stays valid. + if (existing.dim !== record.dim || existing.x !== record.x || existing.y !== record.y || existing.z !== record.z) { + const dimension = world.getDimension(record.dim); + + if (dimension) { + try { + existing.waypoint.setDimensionLocation({ dimension, x: record.x + 0.5, y: record.y, z: record.z + 0.5 }); + existing.dim = record.dim; + existing.x = record.x; + existing.y = record.y; + existing.z = record.z; + } catch (error) { + console.warn(`[graves] could not move waypoint for grave ${record.id}: ${String(error)}`); + } + } + } + + continue; + } + + if (mine.size >= room) { + break; + } + + const waypoint = buildWaypoint(record); + + if (!waypoint) { + continue; + } + + try { + bar.addWaypoint(waypoint); + mine.set(record.id, { waypoint, dim: record.dim, x: record.x, y: record.y, z: record.z }); + } catch (error) { + // A full bar or a racing removal — the next sync tries again. + waypoint.remove(); + console.warn(`[graves] could not add waypoint for grave ${record.id}: ${String(error)}`); + } + } +}; + +const syncAll = (): void => { + for (const player of world.getAllPlayers()) { + sync(player); + } +}; + +/** Forget a player's bookkeeping — their bar goes with them. */ +const forget = (playerId: string): void => { + unsubscribes.get(playerId)?.(); + unsubscribes.delete(playerId); + tracked.delete(playerId); +}; + +export function initLocator(): void { + // Spawn covers both halves of the lifecycle: the join that has to build the + // set from nothing, and the respawn right after the death that created a + // grave. Bookkeeping is dropped first — a rejoining player gets a fresh bar + // from the engine, so last session's handles are stale by definition. + world.afterEvents.playerSpawn.subscribe(({ player, initialSpawn }) => { + if (initialSpawn) { + forget(player.id); + + const unsubscribe = config.player.for(player).showOnLocatorBar.subscribe(() => { + sync(player); + }); + + unsubscribes.set(player.id, unsubscribe); + } + + sync(player); + }); + + world.afterEvents.playerLeave.subscribe(({ playerId }) => { + forget(playerId); + }); + + // Every index write already republishes the grave summary to `core.state` + // (see store.ts), and a set() always emits — even when the summary value is + // unchanged, as a position-only update leaves it. So this one subscription + // covers a grave created, looted, purged, despawned or moved, with no + // second notification channel to keep in step with the first. + // + // Index writes happen inside read-only-ish handlers (entityDie, entityLoad); + // the bar is a privileged API, so touch it next tick. + core.state.subscribe(() => { + system.run(syncAll); + }); + + // The server-wide switch moves every bar at once. + config.server.access.allowLocating.subscribe(() => { + system.run(syncAll); + }); +} diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts index 370aee041..ad0cb0026 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/main.ts @@ -13,6 +13,7 @@ import { initGate } from './open/gate'; import { initAttack } from './open/attack'; import { initReconcile } from './index/reconcile'; import { initDespawn } from './despawn'; +import { initLocator } from './locator'; import { initCommands } from './commands'; import { initRpc } from './rpc'; @@ -27,4 +28,5 @@ initGate(); initAttack(); initReconcile(); initDespawn(); +initLocator(); initRpc(); diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts index 352b2b8bc..4b08958ab 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/open/gate.ts @@ -47,7 +47,7 @@ export function initGate(): void { // Native container path: let the engine open the screen, then watch it. system.run(() => { - if (!target.isValid) { + if (!target.isValid || !player.isValid) { return; } diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts index 4671cba45..b7b25b0b2 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/types.ts @@ -25,6 +25,8 @@ export interface GraveRecord { killer?: string; /** §3b row 11 — hovering over the void (no platform placed). */ floating?: true; + /** Owner ticked this grave off their locator bar (§8). Absent means shown. */ + noWaypoint?: true; /** Tombstone — remove the entity the moment its chunk loads (§6a). */ purge?: true; } diff --git a/addons/files/gameplay_changes/graves/packs/RP/manifest.json b/addons/files/gameplay_changes/graves/packs/RP/manifest.json index 1f125eb37..32d1c014f 100644 --- a/addons/files/gameplay_changes/graves/packs/RP/manifest.json +++ b/addons/files/gameplay_changes/graves/packs/RP/manifest.json @@ -6,7 +6,7 @@ "uuid": "49af505d-e4b5-43b7-9e6e-847a41228ece", "pack_scope": "world", "version": [1, 0, 0], - "min_engine_version": [1, 26, 30] + "min_engine_version": [1, 26, 40] }, "modules": [ { diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png b/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png index d554d36bb..6af7ee20e 100644 --- a/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/entity/grave.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24415444c9e4815aad4a3a349ecbfc0e027b96be25ac5b7c273c06f16c4fdd2f -size 188 +oid sha256:584baddfbb51d44b048fd0755831074ced43400f3e04995195b87663945e806b +size 486 diff --git a/addons/files/gameplay_changes/graves/packs/RP/textures/ui/grave_waypoint.png b/addons/files/gameplay_changes/graves/packs/RP/textures/ui/grave_waypoint.png new file mode 100644 index 000000000..869f2c3d8 --- /dev/null +++ b/addons/files/gameplay_changes/graves/packs/RP/textures/ui/grave_waypoint.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b96e7d7e784cdd21bdeb86dc14f475309506001d1225c2c574be8b449b850c8d +size 416 diff --git a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx index 9d0577fe6..9c8c4ed36 100644 --- a/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx +++ b/addons/files/gameplay_changes/graves/packs/data/guides/en_US/configuration.mdx @@ -11,8 +11,13 @@ yours alone — they only change how Graves behaves for you. - **Show Grave Location On Death** — the coordinates message in chat when you die. +- **My Graves On Locator Bar** — show your own graves as waypoints on the + locator bar, so you can walk back to them without reading coordinates. Only + you ever see your graves there. - **Grave Name Tag** — what the floating name over your graves shows: your name, your name with the death time, or nothing. Server-wide behaviour (robbing, despawn times, XP, the void platform...) is -configured by operators. +configured by operators. An operator can turn the locator bar markers off for +the whole server with **Show Graves On Locator Bar**, which overrides your own +setting. diff --git a/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts b/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts index b6c444018..1edb02444 100644 --- a/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts +++ b/addons/files/gameplay_changes/graves/packs/data/i18n/en_US.ts @@ -36,6 +36,10 @@ export default { label: 'Allow Grave Robbing', desc: 'When enabled, anyone can open anyone else\'s grave without a key.', }, + allowLocating: { + label: 'Show Graves On Locator Bar', + desc: 'When enabled, each player sees their own graves as waypoints on the locator bar.', + }, graveKeyEnabled: { label: 'Grave Key', desc: 'When enabled, a grave key in either hand opens any grave (one key is consumed).', @@ -96,6 +100,10 @@ export default { label: 'Show Grave Location On Death', desc: 'Get a chat message with your grave\'s coordinates when you die.', }, + showOnLocatorBar: { + label: 'My Graves On Locator Bar', + desc: 'Show your own graves as waypoints on your locator bar.', + }, graveNameStyle: { label: 'Grave Name Tag', desc: 'What the floating name over your graves shows.', @@ -144,6 +152,7 @@ export default { days: '{{d}}d {{h}}h ago', }, list: { + waypointHint: 'Tick a grave to show it on your locator bar.', title: 'Your Graves', empty: 'You have no graves. Lucky you!', row_one: '{{count}} item — {{xp}} XP', @@ -164,7 +173,6 @@ export default { pendingPurge: 'Marked for deletion — it disappears when its chunk next loads.', teleport: 'Teleport', purge: 'Purge', - close: 'Close', }, admin: { title: 'All Graves', diff --git a/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts b/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts index 17a638b49..1bc01676f 100644 --- a/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts +++ b/addons/files/gameplay_changes/graves/packs/data/i18n/es_ES.ts @@ -35,6 +35,10 @@ export default { label: 'Permitir Saqueo De Tumbas', desc: 'Si está activado, cualquiera puede abrir la tumba de otro jugador sin llave.', }, + allowLocating: { + label: 'Mostrar Tumbas En La Barra De Localización', + desc: 'Si está activado, cada jugador ve sus propias tumbas como puntos en la barra de localización.', + }, graveKeyEnabled: { label: 'Llave De Tumba', desc: 'Si está activada, una llave de tumba en cualquier mano abre cualquier tumba (se consume una llave).', @@ -95,6 +99,10 @@ export default { label: 'Mostrar Ubicación Al Morir', desc: 'Recibe un mensaje con las coordenadas de tu tumba cuando mueres.', }, + showOnLocatorBar: { + label: 'Mis Tumbas En La Barra De Localización', + desc: 'Muestra tus propias tumbas como puntos en tu barra de localización.', + }, graveNameStyle: { label: 'Nombre De La Tumba', desc: 'Qué muestra el nombre flotante sobre tus tumbas.', @@ -143,6 +151,7 @@ export default { days: 'hace {{d}}d {{h}}h', }, list: { + waypointHint: 'Marca una tumba para mostrarla en tu barra de localización.', title: 'Tus Tumbas', empty: 'No tienes tumbas. ¡Qué suerte!', row_one: '{{count}} objeto — {{xp}} XP', @@ -163,7 +172,6 @@ export default { pendingPurge: 'Marcada para eliminación — desaparecerá cuando su chunk vuelva a cargarse.', teleport: 'Teletransportar', purge: 'Purgar', - close: 'Cerrar', }, admin: { title: 'Todas Las Tumbas', diff --git a/yarn.lock b/yarn.lock index a41843cbd..383304d67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -139,10 +139,10 @@ __metadata: "@bedrock-core/ui": "npm:^0.11.0" "@eslint/js": "npm:^10.0.1" "@eslint/json": "npm:^2.0.0" - "@minecraft/common": "npm:1.2.0" - "@minecraft/server": "npm:2.8.0" + "@minecraft/common": "npm:1.3.0" + "@minecraft/server": "npm:2.9.0" "@minecraft/server-ui": "npm:2.1.0" - "@minecraft/vanilla-data": "npm:1.26.32" + "@minecraft/vanilla-data": "npm:1.26.44" "@stylistic/eslint-plugin": "npm:^5.10.0" eslint: "npm:^10.4.1" eslint-plugin-minecraft-linting: "npm:^1.2.7" @@ -341,10 +341,10 @@ __metadata: languageName: node linkType: hard -"@minecraft/common@npm:1.2.0": - version: 1.2.0 - resolution: "@minecraft/common@npm:1.2.0" - checksum: 10c0/597c3ff8ab275ba5d5fb3037e68970e59ac96e8793b7738178c95b17d22a8f48a4412425314ed494f664466dd9a342e2a9eff52af544e57689f103c2ae965e10 +"@minecraft/common@npm:1.3.0": + version: 1.3.0 + resolution: "@minecraft/common@npm:1.3.0" + checksum: 10c0/6f335029426b6bab2b0c22562863c6a0681d855767ffa8d8e3f0e579ce940505ad2175bd4c96bb5c35b90344ee758757d1d7807908623b6fe00f5f73ea596911 languageName: node linkType: hard @@ -358,20 +358,20 @@ __metadata: languageName: node linkType: hard -"@minecraft/server@npm:2.8.0": - version: 2.8.0 - resolution: "@minecraft/server@npm:2.8.0" +"@minecraft/server@npm:2.9.0": + version: 2.9.0 + resolution: "@minecraft/server@npm:2.9.0" peerDependencies: "@minecraft/common": ^1.2.0 "@minecraft/vanilla-data": ">=1.20.70" - checksum: 10c0/bb4f8a8a90b52831786f51ce320bbf2a8494f7d71d4d6329c22c9710a10a36ed7096acd4f1ff9d2c9971d95c391b8ba36ef674bda47629f9c671c9134994786d + checksum: 10c0/0a88cd6ad7c5f8a626c3e386a485bd4aab57984dca7f3e35544885f9be25bda94f953efec9fb799a08190920e1676629509934e7ab275add9946c9f4331b8562 languageName: node linkType: hard -"@minecraft/vanilla-data@npm:1.26.32": - version: 1.26.32 - resolution: "@minecraft/vanilla-data@npm:1.26.32" - checksum: 10c0/7e17dc24e0c588194f6f196e3f69ea0589e23f55187e456e635ecc2e28bc3cd68960e67e56cd53ff8d122f44c0c85c3f32686aab86da0d7a860dd736e8f210fc +"@minecraft/vanilla-data@npm:1.26.44": + version: 1.26.44 + resolution: "@minecraft/vanilla-data@npm:1.26.44" + checksum: 10c0/fec0637bdc0860acf3c91f8bb0cadfe960e0178985aed4c3c6dc5b2d3f5bc44af95c1bf9879e33bc8f30fe8ec617298f0a05dda0ef1f4706cc32216fe18bfe5d languageName: node linkType: hard From d397b9607fe0d10d2e550aa41bd83943939b37f6 Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 11:13:58 +0200 Subject: [PATCH 08/11] chore(files): addon scaffolding - Fixed a bare --author silently falling back to the git name and consuming the next argument, which turned the addon name into the description - Fixed the category initials producing "undefined" when a segment held consecutive underscores --- tools/create-addon.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/create-addon.mjs b/tools/create-addon.mjs index cd89355e8..90a4a635b 100644 --- a/tools/create-addon.mjs +++ b/tools/create-addon.mjs @@ -44,6 +44,13 @@ let authorArg; if (authorFlag !== -1) { authorArg = rawArgs[authorFlag + 1]; + + // A bare `--author` used to fall through to the git name AND eat the next + // argument, silently turning the addon name into the description. + if (!authorArg || authorArg.startsWith('--')) { + fail('--author needs a name, e.g. --author "Your Name".'); + } + rawArgs.splice(authorFlag, 2); } @@ -71,7 +78,7 @@ if (!NAME_RE.test(name)) { } // gameplay_changes -> gc; nested categories join their initials with '_'. -const initials = (segment) => segment.split('_').map(word => word[0]).join(''); +const initials = (segment) => segment.split('_').filter(Boolean).map(word => word[0]).join(''); const prefix = segments.map(initials).join('_'); const projectName = `${prefix}_${name}`; const categoryDir = path.join(root, 'addons', 'files', ...segments); From 1e49b84a5e05e9f8965274e6ab120c75c4572498 Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Thu, 20 Aug 2026 11:14:03 +0200 Subject: [PATCH 09/11] chore(files): documentation update - Noted that an addon may override the repository indentation for its own JS/TS when its ESLint config matches --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9c80b363a..bc798fd6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -113,6 +113,8 @@ If there is need to scale the template use closest neighbor instead of other sam JSON, JS and TS files should be formatted using 1 tab with size 4 for indentation. +An addon may override this for its own JS/TS as long as its ESLint config is configured to match, so the linter and the editor agree. + ### Addons (Yarn Monorepo Packages) Addons are managed as Yarn workspace packages. To start working in addons you will need to have the following: From a1378b313ddae9fa9c2d02c3d626bf2fb79bb427 Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Fri, 21 Aug 2026 00:33:08 +0200 Subject: [PATCH 10/11] chore(files): addon scaffolding - Run the bedrock-core CLI with an argument array instead of a shell string, so a description containing $(...), backticks, & or %VAR% is passed through as text - Resolve npm's npx-cli.js next to the running Node binary, since Windows ships npx as a .cmd shim that cannot be spawned without a shell Co-Authored-By: Claude Opus 5 (1M context) --- tools/create-addon.mjs | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/tools/create-addon.mjs b/tools/create-addon.mjs index 90a4a635b..3212f8a91 100644 --- a/tools/create-addon.mjs +++ b/tools/create-addon.mjs @@ -100,14 +100,44 @@ mkdirSync(categoryDir, { recursive: true }); // The CLI author must be `bt` — it becomes the creator id half of the addon // namespace. The real author is patched in below; that split is a BT thing, // not a bedrock-core one. -const quote = (value) => `"${value.replace(/"/g, '\\"')}"`; const cliArgs = [projectName, '--author', 'bt', '--description', description]; -const command = process.env.BEDROCK_CORE_CLI - ? `node ${quote(process.env.BEDROCK_CORE_CLI)} ${cliArgs.map(quote).join(' ')}` - : `npx @bedrock-core/cli ${cliArgs.map(quote).join(' ')}`; -console.info(`▶ ${command}`); -const result = spawnSync(command, { cwd: categoryDir, stdio: 'inherit', shell: true }); +// npm ships `npx` as a .cmd shim on Windows, and Node refuses to spawn those +// without a shell (CVE-2024-27980). Running npm's own CLI entry through this +// Node binary skips the shim, so the scaffold never needs a shell at all. +const npxCli = () => { + const nodeDir = path.dirname(process.execPath); + + return [ + path.join(nodeDir, 'node_modules', 'npm', 'bin', 'npx-cli.js'), + path.join(nodeDir, '..', 'lib', 'node_modules', 'npm', 'bin', 'npx-cli.js'), + ].find(existsSync); +}; + +// No shell, ever: the description is free text, and `$(...)`, backticks, `&` +// or `%VAR%` in it would be read as syntax rather than as characters. +const resolveCommand = () => { + if (process.env.BEDROCK_CORE_CLI) { + return [process.execPath, [process.env.BEDROCK_CORE_CLI, ...cliArgs]]; + } + + const npx = npxCli(); + + if (npx) { + return [process.execPath, [npx, '@bedrock-core/cli', ...cliArgs]]; + } + + if (process.platform === 'win32') { + fail("Could not find npm's npx-cli.js next to this Node install. Set BEDROCK_CORE_CLI to a checked-out @bedrock-core/cli entry point."); + } + + return ['npx', ['@bedrock-core/cli', ...cliArgs]]; +}; + +const [command, commandArgs] = resolveCommand(); + +console.info(`▶ ${command} ${commandArgs.join(' ')}`); +const result = spawnSync(command, commandArgs, { cwd: categoryDir, stdio: 'inherit' }); if (result.status !== 0 || !existsSync(scaffoldDir)) { fail('The bedrock-core CLI did not produce a project.'); From fe33735ac24cb61a7b302967298b2c205729dcfb Mon Sep 17 00:00:00 2001 From: DrAv0011 Date: Fri, 21 Aug 2026 00:33:20 +0200 Subject: [PATCH 11/11] update(files): Graves - Add util.dimensionOf(), a guarded world.getDimension() for stored record.dim ids, which throws rather than returning undefined when the dimension no longer resolves - Route the locator bar (waypoint build + drift move) and the admin teleport action through it, so a stale dimension id skips the work instead of crashing the handler - Drop the trailing comma from the addon .vscode/settings.json Co-Authored-By: Claude Opus 5 (1M context) --- .../graves/.vscode/settings.json | 2 +- .../packs/BP/scripts/UI/GraveDetail.tsx | 18 +++++++++++------ .../graves/packs/BP/scripts/locator.ts | 5 +++-- .../graves/packs/BP/scripts/util.ts | 20 ++++++++++++++++++- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/addons/files/gameplay_changes/graves/.vscode/settings.json b/addons/files/gameplay_changes/graves/.vscode/settings.json index 06b133e2d..c690145ac 100644 --- a/addons/files/gameplay_changes/graves/.vscode/settings.json +++ b/addons/files/gameplay_changes/graves/.vscode/settings.json @@ -43,5 +43,5 @@ { "mode": "auto" } - ], + ] } \ No newline at end of file diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx index ed54a329e..fd79576a2 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/UI/GraveDetail.tsx @@ -7,12 +7,11 @@ import { Fragment, Panel, Scroll, Text, usePlayer, useExit, useTranslation, type JSX } from '@bedrock-core/ui'; import { Button, Card, Divider, Header, theme } from '@bedrock-core/ui/ore-styled'; import { isOperator } from '@bedrock-core/server'; -import { world } from '@minecraft/server'; import type { ScreenProps } from '@bedrock-core/ui/navigation'; import { i18n } from './i18n'; import { byEntityId, markPurge } from '../index/store'; import { graveEntity, removeGrave } from '../lifecycle'; -import { agoStr, causeText, dimName, posStr } from '../util'; +import { agoStr, causeText, dimensionOf, dimName, posStr } from '../util'; import type { GravesRoutes } from './GravesApp'; const { fontColor, spacing } = theme.tokens; @@ -41,10 +40,17 @@ export function GraveDetail({ navigation, route }: ScreenProps { - player.teleport( - { x: record.x + 0.5, y: record.y, z: record.z + 0.5 }, - { dimension: world.getDimension(record.dim) }, - ); + const dimension = dimensionOf(record.dim); + + if (!dimension) { + // The record outlived its dimension: nothing to teleport to, and the + // screen stays open rather than dropping the operator somewhere else. + console.warn(`[graves] cannot teleport to grave ${record.id}: dimension '${record.dim}' is gone`); + + return; + } + + player.teleport({ x: record.x + 0.5, y: record.y, z: record.z + 0.5 }, { dimension }); exit(); }; diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts index 53168b171..9a413dabc 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/locator.ts @@ -18,6 +18,7 @@ import { WAYPOINT_COLOR, WAYPOINT_TEXTURE } from './constants'; import { visibleRecordsOf } from './index/store'; import { config, core } from './registration'; import type { GraveRecord } from './types'; +import { dimensionOf } from './util'; /** One tracked waypoint and the record position it was built from. */ interface Tracked { @@ -57,7 +58,7 @@ const selectorFor = (custom: boolean): { textureBoundsList: [{ lowerBound: numbe let customTextureBroken = false; const buildWaypoint = (record: GraveRecord): LocationWaypoint | undefined => { - const dimension = world.getDimension(record.dim); + const dimension = dimensionOf(record.dim); if (!dimension) { return undefined; @@ -154,7 +155,7 @@ export const sync = (player: Player): void => { // A grave that drifted (reconcile refreshed its position) moves its // marker rather than rebuilding it — the handle stays valid. if (existing.dim !== record.dim || existing.x !== record.x || existing.y !== record.y || existing.z !== record.z) { - const dimension = world.getDimension(record.dim); + const dimension = dimensionOf(record.dim); if (dimension) { try { diff --git a/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts b/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts index 713da340e..9f9e2e198 100644 --- a/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts +++ b/addons/files/gameplay_changes/graves/packs/BP/scripts/util.ts @@ -1,10 +1,28 @@ /** Small formatting helpers and guards shared across the addon. */ -import type { Entity, Player, RawMessage } from '@minecraft/server'; +import type { Dimension, Entity, Player, RawMessage } from '@minecraft/server'; +import { world } from '@minecraft/server'; import { MinecraftEntityTypes } from '@minecraft/vanilla-data'; import { i18n } from './UI/i18n'; export const isPlayer = (entity: Entity): entity is Player => entity.typeId === MinecraftEntityTypes.Player; +/** + * A stored `record.dim` resolved back to a live dimension, or undefined. + * + * `world.getDimension` throws on an id it does not know rather than returning + * undefined, and records outlive the session that wrote them: an id from a + * custom-dimension pack survives that pack being removed. Every read of + * `record.dim` goes through here so a stale id skips work instead of taking + * down the caller (purge.ts guards its own, inside the ticking-area loop). + */ +export const dimensionOf = (dimId: string): Dimension | undefined => { + try { + return world.getDimension(dimId); + } catch { + return undefined; + } +}; + /** The verb set i18n.forPlayer()/useTranslation() hand out. */ export type Bound = ReturnType;