From a8046a772983c89f7e1a1e3f3db1501e44e48878 Mon Sep 17 00:00:00 2001 From: Mulverine Date: Sun, 5 Mar 2023 22:17:14 -0700 Subject: [PATCH] Resource packs working! --- README.md | 2 +- package-lock.json | 47 ++++-- package.json | 5 +- .../datapack/criteria/ItemCriterion.ts | 2 +- src/arguments/resources/datapack/recipe.ts | 2 +- src/arguments/resources/datapack/structure.ts | 2 +- .../resources/datapack/trimMaterial.ts | 6 +- .../resources/datapack/trimPattern.ts | 6 +- .../implementations/server/datapack.ts | 8 +- src/commands/implementations/server/reload.ts | 4 +- src/core/mcmeta.ts | 28 ++-- src/core/resources/custom.ts | 2 +- src/core/resources/datapack/advancement.ts | 2 +- src/core/resources/datapack/damageType.ts | 2 +- src/core/resources/datapack/itemModifier.ts | 2 +- src/core/resources/datapack/lootTable.ts | 2 +- src/core/resources/datapack/mcfunction.ts | 6 +- src/core/resources/datapack/predicate.ts | 2 +- src/core/resources/datapack/recipe.ts | 2 +- src/core/resources/datapack/structure.ts | 4 +- src/core/resources/datapack/tag.ts | 2 +- src/core/resources/datapack/trimMaterial.ts | 2 +- src/core/resources/datapack/trimPattern.ts | 2 +- src/core/resources/dependency.ts | 51 +++++++ src/core/resources/resourcepack/atlas.ts | 10 +- src/core/resources/resourcepack/blockstate.ts | 4 +- src/core/resources/resourcepack/font.ts | 4 +- src/core/resources/resourcepack/language.ts | 4 +- src/core/resources/resourcepack/model.ts | 129 ++++++++++++++-- src/core/resources/resourcepack/sound.ts | 78 +++++++++- src/core/resources/resourcepack/text.ts | 5 +- src/core/resources/resourcepack/texture.ts | 6 +- src/core/resources/smithed.ts | 48 ------ src/core/sandstoneCore.ts | 56 ++++++- src/core/smithed.ts | 141 ++++++++++++++++++ src/index.ts | 106 ++++++++++++- src/pack/dependencies.ts | 8 +- src/pack/index.ts | 2 + src/pack/pack.ts | 120 ++++++++++++--- src/utils.ts | 105 +++++++++++++ src/variables/Selector.ts | 4 +- src/variables/Sleep.ts | 2 +- src/variables/UtilityChunk.ts | 6 +- 43 files changed, 856 insertions(+), 175 deletions(-) create mode 100644 src/core/resources/dependency.ts delete mode 100644 src/core/resources/smithed.ts create mode 100644 src/core/smithed.ts diff --git a/README.md b/README.md index d020c2d1..0bc0a3cb 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ You don't need to remember commands syntax anymore. This autocompletion works for all resources: commands, predicates, loot tables, advancements... ## 📂 Better organisation of resources -You can have multiple functions, advancements, loot tables per files - or you can keep the vanilla organisation, and have only 1 per file. Sandstone allows you to organise your data pack as you prefer, without sticking to Mojang's conventions. +You can have multiple functions, advancements, loot tables per files - or you can keep the vanilla organisation, and have only 1 per file. Sandstone allows you to organise your datapack as you prefer, without sticking to Mojang's conventions. You also benefit from all the capabilities of a real programming language: multiline comments, indentation, documentation... diff --git a/package-lock.json b/package-lock.json index dd6f10a2..1b897b52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,12 @@ "version": "0.14.0-alpha.19", "license": "MIT", "dependencies": { + "@sinclair/typebox": "^0.25.24", + "@smithed-mc/data-types": "^0.1.2", "@types/adm-zip": "^0.5.0", "@types/fs-extra": "^11.0.1", "@types/lodash": "^4.14.191", + "@types/semver": "^7.3.13", "@typescript/analyze-trace": "^0.4.0", "adm-zip": "^0.5.10", "chalk": "^4", @@ -163,6 +166,20 @@ "node": ">= 8" } }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" + }, + "node_modules/@smithed-mc/data-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@smithed-mc/data-types/-/data-types-0.1.2.tgz", + "integrity": "sha512-V03jQcBosFrJ5ei74leYiqUIDLcb3GA++Equzw7wAukQ3vB9zgJgX00ypSjmIgpoSDkJmz5T4suC/YM/N2Wnqw==", + "dependencies": { + "@sinclair/typebox": "^0.25.20", + "semver": "^7.3.8" + } + }, "node_modules/@swc/core": { "version": "1.2.164", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.164.tgz", @@ -509,8 +526,7 @@ "node_modules/@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.50.0", @@ -2857,7 +2873,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -3556,7 +3571,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -4167,8 +4181,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.2", @@ -4301,6 +4314,20 @@ "fastq": "^1.6.0" } }, + "@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" + }, + "@smithed-mc/data-types": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@smithed-mc/data-types/-/data-types-0.1.2.tgz", + "integrity": "sha512-V03jQcBosFrJ5ei74leYiqUIDLcb3GA++Equzw7wAukQ3vB9zgJgX00ypSjmIgpoSDkJmz5T4suC/YM/N2Wnqw==", + "requires": { + "@sinclair/typebox": "^0.25.20", + "semver": "^7.3.8" + } + }, "@swc/core": { "version": "1.2.164", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.2.164.tgz", @@ -4520,8 +4547,7 @@ "@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", - "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", - "dev": true + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==" }, "@typescript-eslint/eslint-plugin": { "version": "5.50.0", @@ -6107,7 +6133,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -6571,7 +6596,6 @@ "version": "7.3.8", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, "requires": { "lru-cache": "^6.0.0" } @@ -7002,8 +7026,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 4a9d1880..b3b62216 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sandstone", "description": "Sandstone, a Typescript library for Minecraft datapacks.", - "version": "0.14.0-alpha.19", + "version": "0.14.0-alpha.20", "contributors": [ { "name": "TheMrZZ - Florian ERNST", @@ -17,9 +17,12 @@ "url": "https://github.com/sandstone-mc/sandstone/issues" }, "dependencies": { + "@sinclair/typebox": "^0.25.24", + "@smithed-mc/data-types": "^0.1.2", "@types/adm-zip": "^0.5.0", "@types/fs-extra": "^11.0.1", "@types/lodash": "^4.14.191", + "@types/semver": "^7.3.13", "@typescript/analyze-trace": "^0.4.0", "adm-zip": "^0.5.10", "chalk": "^4", diff --git a/src/arguments/resources/datapack/criteria/ItemCriterion.ts b/src/arguments/resources/datapack/criteria/ItemCriterion.ts index beaa576a..c5f69055 100644 --- a/src/arguments/resources/datapack/criteria/ItemCriterion.ts +++ b/src/arguments/resources/datapack/criteria/ItemCriterion.ts @@ -26,6 +26,6 @@ export type ItemCriterion = Partial<{ /** A brewed potion ID. */ potion: string - /** An item data pack tag. */ + /** An item datapack tag. */ tag: string | TagClass<'items'> }> diff --git a/src/arguments/resources/datapack/recipe.ts b/src/arguments/resources/datapack/recipe.ts index 240ca0ff..cd25634e 100644 --- a/src/arguments/resources/datapack/recipe.ts +++ b/src/arguments/resources/datapack/recipe.ts @@ -71,7 +71,7 @@ type RecipeKind | un * - `crafting_shape`: Represents a shaped crafting recipe in a crafting table. * - `crafting_shapeless`: Represents a shapeless crafting recipe in a crafting table. * - `crafting_special_*`: Represents a crafting recipe in a crafting table that is handled with builtin logic instead of being data-driven. - * When the "vanilla" data pack is disabled, they can be used to reenable desired builtin crafting recipes. + * When the "vanilla" datapack is disabled, they can be used to reenable desired builtin crafting recipes. * - `smelting`: Represents a recipe in a furnace. * - `smithing`: Represents a recipe in a smithing table. * - `smoking`: Represents a recipe in a smoker. diff --git a/src/arguments/resources/datapack/structure.ts b/src/arguments/resources/datapack/structure.ts index de40b9a0..2053cd20 100644 --- a/src/arguments/resources/datapack/structure.ts +++ b/src/arguments/resources/datapack/structure.ts @@ -1,5 +1,5 @@ import type { LiteralUnion } from 'sandstone/utils' -import type { BLOCKS, RootNBT } from '../index' +import type { BLOCKS, RootNBT } from '../../index' type Vec3 = [number, number, number] diff --git a/src/arguments/resources/datapack/trimMaterial.ts b/src/arguments/resources/datapack/trimMaterial.ts index c2d780f0..a751eb20 100644 --- a/src/arguments/resources/datapack/trimMaterial.ts +++ b/src/arguments/resources/datapack/trimMaterial.ts @@ -1,6 +1,6 @@ -import type { LiteralUnion } from 'sandstone/utils' -import type { JSONTextComponent } from '../jsonTextComponent' -import type { ITEMS } from '#arguments/generated' +import type { ITEMS } from '../../generated' +import type { JSONTextComponent } from '../../jsonTextComponent' +import type { LiteralUnion } from '#utils' /** Key is armor material, value is a string which will be used in the resource pack. */ export type OverrideArmorMaterials = Record, string> diff --git a/src/arguments/resources/datapack/trimPattern.ts b/src/arguments/resources/datapack/trimPattern.ts index 204a39d3..79411ed0 100644 --- a/src/arguments/resources/datapack/trimPattern.ts +++ b/src/arguments/resources/datapack/trimPattern.ts @@ -1,6 +1,6 @@ -import type { LiteralUnion } from 'sandstone/utils' -import type { JSONTextComponent } from '../jsonTextComponent' -import type { ITEMS } from '#arguments/generated' +import type { ITEMS } from '../../generated' +import type { JSONTextComponent } from '../../jsonTextComponent' +import type { LiteralUnion } from '#utils' export type TrimPatternJSON = { /** A resource location (In the resource pack) of the pattern that will be used as an overlay on the armor. */ diff --git a/src/commands/implementations/server/datapack.ts b/src/commands/implementations/server/datapack.ts index acb7dbf9..c82f7f33 100644 --- a/src/commands/implementations/server/datapack.ts +++ b/src/commands/implementations/server/datapack.ts @@ -40,14 +40,14 @@ export class DataPackCommand extends CommandArguments { /** * Disable the specified pack. * - * @param name Specifies the name of the data pack. + * @param name Specifies the name of the datapack. */ disable = (name: string) => this.finalCommand([name]) /** * Enable the specified pack. * - * @param name Specifies the name of the data pack. + * @param name Specifies the name of the datapack. * * @example * @@ -61,9 +61,9 @@ export class DataPackCommand extends CommandArguments { enable = (name: string) => this.subCommand(['enable', name], DataPackEnableCommand, false) /** - * List all data packs, or list only the available/enabled ones. + * List all datapacks, or list only the available/enabled ones. * - * Hovering over the data packs in the chat output shows their description defined in their pack.mcmeta. + * Hovering over the datapacks in the chat output shows their description defined in their pack.mcmeta. * * @param typ `"available"` to only show available datapacks, `"enabled"` to only show enabled ones. */ diff --git a/src/commands/implementations/server/reload.ts b/src/commands/implementations/server/reload.ts index d22eb68d..de0cb6b0 100644 --- a/src/commands/implementations/server/reload.ts +++ b/src/commands/implementations/server/reload.ts @@ -10,9 +10,9 @@ export class ReloadCommand extends CommandArguments { protected NodeType = ReloadCommandNode /** - * Reloads the current data packs. + * Reloads the current datapacks. * - * If a data pack has invalid data (such as an invalid recipe format), + * If a datapack has invalid data (such as an invalid recipe format), * changes are not applied and the game continues using the previous data. */ reload = () => this.finalCommand([]) diff --git a/src/core/mcmeta.ts b/src/core/mcmeta.ts index 94aee455..67179798 100644 --- a/src/core/mcmeta.ts +++ b/src/core/mcmeta.ts @@ -11,11 +11,11 @@ const fetch = import('node-fetch') export type MCMetaBranches = 'assets' | 'atlas' | 'data' | 'registries' | 'summary' export class MCMetaCache { - readonly baseURL = 'https://raw.githubusercontent.com/misode/mcmeta/' + readonly base = '/misode/mcmeta/' readonly path = path.join(process.env.WORKING_DIR as string, 'resources', 'cache', 'mcmeta') - readonly manifest = path.join(this.path, '..', 'mcmeta.json') + readonly manifest = path.join(this.path, '..', '..', 'mcmeta.json') readonly lockFile = path.join(this.path, '..', 'lock-mcmeta.json') @@ -32,7 +32,7 @@ export class MCMetaCache { async load() { this.loaded = true - if (await fs.pathExists(this.manifest)) { + if (!(await fs.pathExists(this.manifest))) { return } @@ -42,7 +42,7 @@ export class MCMetaCache { this.version = manifest.version - const getZip = async (branch: string) => new AdmZip(await (await (await fetch).default(`https://github.com/misode/mcmeta/archive/refs/heads/${branch}.zip`)).buffer()) + const getZip = async (branch: string) => new AdmZip(await (await (await fetch).default(`https://github.com${this.base}archive/refs/heads/${branch}.zip`)).buffer()) if (!(await fs.pathExists(this.lockFile))) { if (manifest.files.length > 10) { @@ -172,10 +172,18 @@ export class MCMetaCache { } async save() { - await fs.writeFile(this.manifest, JSON.stringify({ - branches: this.branches, - files: iterateEntries(this.files, (val) => val.text), - })) + if (this.files.size !== 0) { + await fs.writeFile(this.manifest, JSON.stringify({ + branches: this.branches, + files: iterateEntries(this.files, (val) => val.text), + })) + + await fs.writeFile(this.lockFile, JSON.stringify({ + files: this.files, + version: this.version, + versionDate: this.versionDate, + })) + } } async get(branch: MCMetaBranches, relativePath: string): Promise @@ -200,7 +208,7 @@ export class MCMetaCache { return existing } - const req = await (await fetch).default(`${this.baseURL}/${branch}/${relativePath}`) + const req = await (await fetch).default(`https://raw.githubusercontent.com${this.base}${branch}/${relativePath}`) const file = await (text ? req.text() : req.buffer()) @@ -210,7 +218,7 @@ export class MCMetaCache { } async getVersion(lockFile?: any) { - const fetchVersion = async () => JSON.parse(await (await (await fetch).default('https://api.github.com/repos/misode/mcmeta/commits?sha=summary')).text()).sha as string + const fetchVersion = async () => JSON.parse(await (await (await fetch).default(`https://api.github.com/repos${this.base}commits?sha=summary`)).text()).sha as string const currentDate = Date() diff --git a/src/core/resources/custom.ts b/src/core/resources/custom.ts index a1663e18..c6c45549 100644 --- a/src/core/resources/custom.ts +++ b/src/core/resources/custom.ts @@ -47,7 +47,7 @@ export abstract class CustomResourceClass extends ResourceClass extends Res public advancementJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: AdvancementClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, AdvancementNode, sandstoneCore.pack.resourceToPath(name, ['advancements']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, AdvancementNode, sandstoneCore.pack.resourceToPath(name, ['advancements']), args) this.advancementJSON = args.advancement as AdvancementJSON diff --git a/src/core/resources/datapack/damageType.ts b/src/core/resources/datapack/damageType.ts index c5e488a7..9b5424a3 100644 --- a/src/core/resources/datapack/damageType.ts +++ b/src/core/resources/datapack/damageType.ts @@ -41,7 +41,7 @@ export class DamageTypeClass extends ResourceClass implements Co public damageTypeJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: DamageTypeClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, DamageTypeNode, sandstoneCore.pack.resourceToPath(name, ['trim_materials']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, DamageTypeNode, sandstoneCore.pack.resourceToPath(name, ['trim_materials']), args) this.damageTypeJSON = args.damageType as DamageTypeJSON diff --git a/src/core/resources/datapack/itemModifier.ts b/src/core/resources/datapack/itemModifier.ts index bb7232fd..86465a9e 100644 --- a/src/core/resources/datapack/itemModifier.ts +++ b/src/core/resources/datapack/itemModifier.ts @@ -33,7 +33,7 @@ export class ItemModifierClass extends ResourceClass implement public itemModifierJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: ItemModifierClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, ItemModifierNode, sandstoneCore.pack.resourceToPath(name, ['item_modifiers']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, ItemModifierNode, sandstoneCore.pack.resourceToPath(name, ['item_modifiers']), args) this.itemModifierJSON = args.itemModifier as ItemModifierJSON diff --git a/src/core/resources/datapack/lootTable.ts b/src/core/resources/datapack/lootTable.ts index 0dba3bc1..f6da5721 100644 --- a/src/core/resources/datapack/lootTable.ts +++ b/src/core/resources/datapack/lootTable.ts @@ -30,7 +30,7 @@ export class LootTableClass extends ResourceClass { public lootTableJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: LootTableClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, LootTableNode, sandstoneCore.pack.resourceToPath(name, ['loot_tables']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, LootTableNode, sandstoneCore.pack.resourceToPath(name, ['loot_tables']), args) this.lootTableJSON = args.lootTable as LootTableJSON diff --git a/src/core/resources/datapack/mcfunction.ts b/src/core/resources/datapack/mcfunction.ts index bb5a5dc2..d71e4506 100644 --- a/src/core/resources/datapack/mcfunction.ts +++ b/src/core/resources/datapack/mcfunction.ts @@ -156,12 +156,12 @@ export type MCFunctionClassArguments = ({ * * @example * - * // Run every 5 ticks, including on data pack load. + * // Run every 5 ticks, including on datapack load. * { * runEvery: 5, * } * - * // Run every 5 ticks, but wait 5 ticks before data pack loads for 1st execution. + * // Run every 5 ticks, but wait 5 ticks before datapack loads for 1st execution. * { * runEvery: 5, * runOnLoad: false, @@ -199,7 +199,7 @@ export class _RawMCFunctionClass extends CallableResourceClass { protected lazy: boolean constructor(core: SandstoneCore, name: string, args: MCFunctionClassArguments) { - super(core, { packType: core.pack.dataPack, extension: 'mcfunction' }, MCFunctionNode, core.pack.resourceToPath(name, ['functions']), { + super(core, { packType: core.pack.dataPack(), extension: 'mcfunction' }, MCFunctionNode, core.pack.resourceToPath(name, ['functions']), { ...args, addToSandstoneCore: args.lazy ? false : args.addToSandstoneCore, }) diff --git a/src/core/resources/datapack/predicate.ts b/src/core/resources/datapack/predicate.ts index 93c78cfa..6cf0cce3 100644 --- a/src/core/resources/datapack/predicate.ts +++ b/src/core/resources/datapack/predicate.ts @@ -30,7 +30,7 @@ export class PredicateClass extends ResourceClass implements List public predicateJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: PredicateClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, PredicateNode, sandstoneCore.pack.resourceToPath(name, ['predicates']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, PredicateNode, sandstoneCore.pack.resourceToPath(name, ['predicates']), args) this.predicateJSON = args.predicate as PredicateJSON diff --git a/src/core/resources/datapack/recipe.ts b/src/core/resources/datapack/recipe.ts index 691a2f39..d950bcae 100644 --- a/src/core/resources/datapack/recipe.ts +++ b/src/core/resources/datapack/recipe.ts @@ -27,7 +27,7 @@ export class RecipeClass extends ResourceClass { public recipeJSON: NonNullable constructor(sandstoneCore: SandstoneCore, name: string, args: RecipeClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, RecipeNode, sandstoneCore.pack.resourceToPath(name, ['recipes']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, RecipeNode, sandstoneCore.pack.resourceToPath(name, ['recipes']), args) this.recipeJSON = args.recipe diff --git a/src/core/resources/datapack/structure.ts b/src/core/resources/datapack/structure.ts index 6fc32d1a..cc9f75da 100644 --- a/src/core/resources/datapack/structure.ts +++ b/src/core/resources/datapack/structure.ts @@ -66,10 +66,10 @@ export class StructureClass extends ResourceClass { structureNBT?: StructureNBT constructor(sandstoneCore: SandstoneCore, name: string, args: StructureClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'nbt', encoding: false }, StructureNode, sandstoneCore.pack.resourceToPath(name, ['structures']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'nbt', encoding: false }, StructureNode, sandstoneCore.pack.resourceToPath(name, ['structures']), args) if (args.structure === undefined) { - this.structureBuffer = sandstoneCore.getExistingResource(this) as Promise + this.structureBuffer = sandstoneCore.getExistingResource(this, false) } else if (typeof args.structure === 'string') { this.structureBuffer = sandstoneCore.getExistingResource(args.structure, false) } else if (args.structure instanceof StructureClass) { diff --git a/src/core/resources/datapack/tag.ts b/src/core/resources/datapack/tag.ts index f9a0b239..9390261d 100644 --- a/src/core/resources/datapack/tag.ts +++ b/src/core/resources/datapack/tag.ts @@ -89,7 +89,7 @@ export class TagClass> extends Resourc readonly tagJSON: NonNullable> constructor(sandstoneCore: SandstoneCore, type: REGISTRY, name: string, args: TagClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, TagNode, sandstoneCore.pack.resourceToPath(name, ['tags', type]), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, TagNode, sandstoneCore.pack.resourceToPath(name, ['tags', type]), args) this.type = type diff --git a/src/core/resources/datapack/trimMaterial.ts b/src/core/resources/datapack/trimMaterial.ts index 88f20a53..266bfab4 100644 --- a/src/core/resources/datapack/trimMaterial.ts +++ b/src/core/resources/datapack/trimMaterial.ts @@ -44,7 +44,7 @@ export class TrimMaterialClass extends ResourceClass implement readonly equipmentCheck constructor(sandstoneCore: SandstoneCore, name: string, args: TrimMaterialClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, TrimMaterialNode, sandstoneCore.pack.resourceToPath(name, ['trim_materials']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, TrimMaterialNode, sandstoneCore.pack.resourceToPath(name, ['trim_materials']), args) this.trimMaterialJSON = args.trimMaterial as TrimMaterialJSON diff --git a/src/core/resources/datapack/trimPattern.ts b/src/core/resources/datapack/trimPattern.ts index 6c04a949..fd3c96bc 100644 --- a/src/core/resources/datapack/trimPattern.ts +++ b/src/core/resources/datapack/trimPattern.ts @@ -41,7 +41,7 @@ export class TrimPatternClass extends ResourceClass implements readonly equipmentCheck constructor(sandstoneCore: SandstoneCore, name: string, args: TrimPatternClassArguments) { - super(sandstoneCore, { packType: sandstoneCore.pack.dataPack, extension: 'json' }, TrimPatternNode, sandstoneCore.pack.resourceToPath(name, ['trim_patterns']), args) + super(sandstoneCore, { packType: sandstoneCore.pack.dataPack(), extension: 'json' }, TrimPatternNode, sandstoneCore.pack.resourceToPath(name, ['trim_patterns']), args) this.trimPatternJSON = args.trimPattern as TrimPatternJSON diff --git a/src/core/resources/dependency.ts b/src/core/resources/dependency.ts new file mode 100644 index 00000000..2608f7ee --- /dev/null +++ b/src/core/resources/dependency.ts @@ -0,0 +1,51 @@ +import { ContainerNode } from '../nodes' +import { ResourceClass } from './resource' + +import type { SandstoneCore } from '../sandstoneCore' +import type { Dependency } from '../smithed' +import type { ResourceNode } from './resource' +import type { PackType } from '#pack' + +/** + * A node representing a custom resource. + */ +export class DependencyNode extends ContainerNode implements ResourceNode { + constructor(sandstoneCore: SandstoneCore, public resource: DependencyClass) { + super(sandstoneCore) + } + + getValue = () => this.resource.getValue() +} + +export class DependencyClass extends ResourceClass { + side: 'client' | 'server' + + constructor(sandstoneCore: SandstoneCore, public dependency: Dependency, side: 'client' | 'server') { + super( + sandstoneCore, + { + packType: (side === 'client' ? sandstoneCore.pack.packTypes.get('resourcepack-dependencies') : sandstoneCore.pack.packTypes.get('datapack-dependencies')) as PackType, + extension: 'zip', + encoding: false, + }, + DependencyNode, + ['/'], + { + addToSandstoneCore: true, + creator: 'sandstone', + }, + ) + + this.side = side + + this.handleConflicts() + } + + getValue() { + if (this.side === 'client') { + return this.dependency.datapack + } + + return this.dependency.resourcepack as Buffer + } +} diff --git a/src/core/resources/resourcepack/atlas.ts b/src/core/resources/resourcepack/atlas.ts index 94f20674..9b8f48a3 100644 --- a/src/core/resources/resourcepack/atlas.ts +++ b/src/core/resources/resourcepack/atlas.ts @@ -16,7 +16,7 @@ export class AtlasNode extends ContainerNode implements ResourceNode getValue = () => JSON.stringify(this.resource.atlasJSON) } -export type AtlasArguments = { +export type AtlasClassArguments = { /** * The block state's JSON. */ @@ -25,12 +25,14 @@ export type AtlasArguments = { } & ResourceClassArguments<'list'> export class AtlasClass extends ResourceClass implements ListResource { - atlasJSON: NonNullable + atlasJSON: NonNullable - constructor(core: SandstoneCore, name: string, args: AtlasArguments) { - super(core, { packType: core.pack.resourcePack }, AtlasNode, core.pack.resourceToPath(name, ['atlases']), args) + constructor(core: SandstoneCore, name: string, args: AtlasClassArguments) { + super(core, { packType: core.pack.resourcePack() }, AtlasNode, core.pack.resourceToPath(name, ['atlases']), args) this.atlasJSON = args.atlas || { sources: [] } + + this.handleConflicts() } push(...sources: AtlasSpriteSource[] | AtlasClass[]) { diff --git a/src/core/resources/resourcepack/blockstate.ts b/src/core/resources/resourcepack/blockstate.ts index 26de3d25..0b4e797e 100644 --- a/src/core/resources/resourcepack/blockstate.ts +++ b/src/core/resources/resourcepack/blockstate.ts @@ -28,10 +28,12 @@ export class BlockStateClass extends ResourceClass< blockStateJSON: NonNullable['blockState']> constructor(core: SandstoneCore, name: string, public type: Type, args: BlockStateArguments) { - super(core, { packType: core.pack.resourcePack }, BlockStateNode, core.pack.resourceToPath(name, ['blockstates']), args) + super(core, { packType: core.pack.resourcePack() }, BlockStateNode, core.pack.resourceToPath(name, ['blockstates']), args) /** @ts-ignore */ this.blockStateJSON = args.blockState || (type === 'variant' ? { variants: {} } : { multipart: [] }) + + this.handleConflicts() } push(...states: BlockStateClass[] | BlockStateDefinition[]) { diff --git a/src/core/resources/resourcepack/font.ts b/src/core/resources/resourcepack/font.ts index ef63260a..86a45faa 100644 --- a/src/core/resources/resourcepack/font.ts +++ b/src/core/resources/resourcepack/font.ts @@ -28,9 +28,11 @@ export class FontClass extends ResourceClass implements ListResource { fontJSON: { providers: FontArguments['providers'] } constructor(core: SandstoneCore, name: string, args: FontArguments) { - super(core, { packType: core.pack.resourcePack }, FontNode, core.pack.resourceToPath(name, ['font']), args) + super(core, { packType: core.pack.resourcePack() }, FontNode, core.pack.resourceToPath(name, ['font']), args) this.fontJSON = { providers: args.providers } + + this.handleConflicts() } push(...providers: FontProvider[] | FontClass[]) { diff --git a/src/core/resources/resourcepack/language.ts b/src/core/resources/resourcepack/language.ts index e44162e7..ca3f5a74 100644 --- a/src/core/resources/resourcepack/language.ts +++ b/src/core/resources/resourcepack/language.ts @@ -28,9 +28,11 @@ export class LanguageClass extends ResourceClass implements ListRe languageJSON: NonNullable constructor(core: SandstoneCore, name: string, args: LanguageArguments) { - super(core, { packType: core.pack.resourcePack }, LanguageNode, core.pack.resourceToPath(name, ['lang']), args) + super(core, { packType: core.pack.resourcePack() }, LanguageNode, core.pack.resourceToPath(name, ['lang']), args) this.languageJSON = args.language || {} + + this.handleConflicts() } push(...translations: NonNullable[] | LanguageClass[]) { diff --git a/src/core/resources/resourcepack/model.ts b/src/core/resources/resourcepack/model.ts index d03a705c..3e0181de 100644 --- a/src/core/resources/resourcepack/model.ts +++ b/src/core/resources/resourcepack/model.ts @@ -105,8 +105,6 @@ export class ModelNode extends ContainerNode implements ResourceNode getValue = () => JSON.stringify(this.resource.toJSON()) } -type MODEL_TYPES = 'block' | 'entity' - export type ModelClassArguments = { /** * The model's path or its JSON. @@ -134,8 +132,8 @@ export class ModelClass extends ResourceClass { // TODO: Helper methods overrides: ModelData['overrides'] = [] - constructor(core: SandstoneCore, public type: MODEL_TYPES, name: string, args: ModelClassArguments) { - super(core, { packType: core.pack.resourcePack }, ModelNode, core.pack.resourceToPath(name, ['models', type]), args) + constructor(core: SandstoneCore, public type: 'block' | 'item', name: string, args: ModelClassArguments) { + super(core, { packType: core.pack.resourcePack() }, ModelNode, core.pack.resourceToPath(name, ['models', type]), args) const data = args.model if (data) { @@ -145,6 +143,8 @@ export class ModelClass extends ResourceClass { this.fromJSON(data) } } + + this.handleConflicts() } /** @@ -217,11 +217,8 @@ export class ModelClass extends ResourceClass { } async load() { - if (this.path[0] === 'minecraft') { - this.fromJSON(JSON.parse(await this.core.getVanillaResource(this.name))) - } else { - this.fromJSON(JSON.parse(await this.core.getExistingResource(this.existingPath!))) - } + this.fromJSON(JSON.parse(await this.core.getExistingResource(this))) + return this } } @@ -746,3 +743,117 @@ function vecN(...args: ArrayOfLength | [ArrayOfLeng if (typeof (args as unknown[])[0] === 'number') { return new Vector(args as ArrayOfLength) } return new Vector((args as unknown[])[0] as ArrayOfLength | Vector>) } + +export type BlockFaceName = 'north' | 'east' | 'south' | 'west' | 'up' | 'down'; +export type BlockAxisName = 'x' | 'y' | 'z'; + +type Unconst = + A['length'] extends _S['length'] + ? _S + : Unconst; + +export class BlockFace< + Name extends BlockFaceName = BlockFaceName, + Idx extends EnumerateInt<6> = EnumerateInt<6>, + Normal extends Readonly> = Readonly>, + OppositeName extends 'North' | 'East' | 'South' | 'West' | 'Up' | 'Down' = 'North' | 'East' | 'South' | 'West' | 'Up' | 'Down' + > { + public static readonly North = new BlockFace('north' as const, 0, [0, 0, -1] as const, 'South' as const) + + public static readonly East = new BlockFace('east' as const, 1, [1, 0, 0] as const, 'West' as const) + + public static readonly South = new BlockFace('south' as const, 2, [0, 0, 1] as const, 'North' as const) + + public static readonly West = new BlockFace('west' as const, 3, [-1, 0, 0] as const, 'East' as const) + + public static readonly Up = new BlockFace('up' as const, 4, [0, 1, 0] as const, 'Down' as const) + + public static readonly Down = new BlockFace('down' as const, 5, [0, -1, 0] as const, 'Up' as const) + + public static fromName(name: 'north'): (typeof BlockFace)['North']; + + public static fromName(name: 'east'): (typeof BlockFace)['East']; + + public static fromName(name: 'south'): (typeof BlockFace)['South']; + + public static fromName(name: 'west'): (typeof BlockFace)['West']; + + public static fromName(name: 'up'): (typeof BlockFace)['Up']; + + public static fromName(name: 'down'): (typeof BlockFace)['Down']; + + public static fromName(name: BlockFaceName): BlockFace; + + public static fromName(name: string): BlockFace | null; + + public static fromName(name: string): BlockFace | null { + switch (name) { + case 'north': return this.North + case 'east': return this.East + case 'south': return this.South + case 'west': return this.West + case 'up': return this.Up + case 'down': return this.Down + default: return null + } + } + + public static [Symbol.iterator]() { + return BLOCK_FACES[Symbol.iterator]() + } + + public static get filter() { + return BLOCK_FACES.filter + } + + public static forEach(callback: (face: BlockFace, id: number) => void) { + BLOCK_FACES.forEach((face, id) => callback(face, id)) + } + + public readonly opposite!: (typeof BlockFace)[OppositeName] + + /* @ts-ignore */ + public readonly normal: Vector> + + public readonly isSide: Name extends 'north' | 'east' | 'south' | 'west' ? true : false + + public readonly axis: Name extends 'east' | 'west' ? 'x' : Name extends 'up' | 'down' ? 'y' : 'z' + + private constructor( + public readonly name: Name, + public readonly id: Idx, + normal: Normal, + opposite: OppositeName, + ) { + /* @ts-ignore */ + this.normal = new Vector(normal as Unconst) + if (normal[0] !== 0) this.axis = 'x' as any + else if (normal[1] !== 0) this.axis = 'y' as any + else this.axis = 'z' as any + this.isSide = (this.axis !== 'y') as any; + // is set correctly later + (this as any).opposite = opposite + } + + public toString(): Name { + return this.name + } + + public toJSON(): Name { + return this.name + } +} + +const BLOCK_FACES = Object.freeze([ + BlockFace.North, + BlockFace.East, + BlockFace.South, + BlockFace.West, + BlockFace.Up, + BlockFace.Down, +] as const) + +BlockFace.forEach((face) => { + const opp: keyof typeof BlockFace = face.opposite as any; + (face as any).opposite = BlockFace[opp] +}) diff --git a/src/core/resources/resourcepack/sound.ts b/src/core/resources/resourcepack/sound.ts index 98da133a..6325ecc8 100644 --- a/src/core/resources/resourcepack/sound.ts +++ b/src/core/resources/resourcepack/sound.ts @@ -4,9 +4,11 @@ import { ContainerNode } from '../../nodes' import { ResourceClass } from '../resource' import type { SandstoneCore } from '../../sandstoneCore' -import type { ResourceClassArguments, ResourceNode } from '../resource' +import type { ListResource, ResourceClassArguments, ResourceNode } from '../resource' import type { SOUND_TYPES, SoundsDefinitions } from '#arguments' +const sounds: Map = new Map() + /** * A node representing a Minecraft sound. */ @@ -24,7 +26,10 @@ export type SoundEventArguments = { */ sound?: string | Promise | Buffer - id?: string + /** + * Whether to automatically add this to a sounds.json file. Defaults to false. + */ + addToSounds?: boolean } & ResourceClassArguments<'default'> @@ -32,7 +37,7 @@ export class SoundEventClass extends ResourceClass | Buffer constructor(core: SandstoneCore, public type: Type, name: string, args: SoundEventArguments) { - super(core, { packType: core.pack.resourcePack, extension: 'ogg', encoding: false }, SoundEventNode, core.pack.resourceToPath(name, ['sounds', type]), args) + super(core, { packType: core.pack.resourcePack(), extension: 'ogg', encoding: false }, SoundEventNode, core.pack.resourceToPath(name, ['sounds', type]), args) if (args.addToSandstoneCore && args.sound !== undefined) { if (typeof args.sound === 'string') { @@ -40,7 +45,22 @@ export class SoundEventClass extends ResourceClass -export class SoundsClass extends ResourceClass { +export class SoundsClass extends ResourceClass implements ListResource { soundsJSON: SoundsDefinitions | Promise - constructor(core: SandstoneCore, name: string, args: SoundsArguments) { - super(core, { packType: core.pack.resourcePack }, SoundsNode, core.pack.resourceToPath(name, ['sounds']), args) + constructor(core: SandstoneCore, namespace: string, args: SoundsArguments) { + super(core, { packType: core.pack.resourcePack() }, SoundsNode, core.pack.resourceToPath(`${namespace}:sounds`, []), args) if (args.definitions) { this.soundsJSON = args.definitions - } else if (this.path[0] === 'minecraft') { - this.soundsJSON = (async () => JSON.parse(await (core.getVanillaResource(this) as Promise)))() } else { this.soundsJSON = (async () => JSON.parse(await (core.getExistingResource(this) as Promise)))() } } + + async push(...soundEvents: SoundsClass[] | SoundEventClass[]) { + if (soundEvents[0] instanceof SoundsClass) { + for await (const _sounds of soundEvents) { + const def = _sounds as SoundsClass + const s = await this.soundsJSON + + // TODO: Implement sound event options + this.soundsJSON = { ...s, ...def } + } + } else { + for await (const _sound of soundEvents) { + const sound = _sound as SoundEventClass + const s = await this.soundsJSON + + // TODO: Implement sound event options + s[`${sound.type}.${sound.name}`] = { + sounds: [`${sound.type}.${sound.name}`], + } + } + } + } + + async unshift(...soundEvents: SoundsClass[] | SoundEventClass[]) { + if (soundEvents[0] instanceof SoundsClass) { + for await (const _sounds of soundEvents) { + const def = _sounds as SoundsClass + const s = await this.soundsJSON + + // TODO: Implement sound event options + this.soundsJSON = { ...def, ...s } + } + } else { + for await (const _sound of soundEvents) { + const sound = _sound as SoundEventClass + const s = await this.soundsJSON + + // TODO: Implement sound event options + s[`${sound.type}.${sound.name}`] = { + sounds: [`${sound.type}.${sound.name}`], + } + } + } + } } diff --git a/src/core/resources/resourcepack/text.ts b/src/core/resources/resourcepack/text.ts index e2bed8f9..c63a9278 100644 --- a/src/core/resources/resourcepack/text.ts +++ b/src/core/resources/resourcepack/text.ts @@ -31,9 +31,8 @@ export class PlainTextClass extends ResourceClass implements List texts: NonNullable = '' constructor(core: SandstoneCore, name: string, args: PlainTextArguments) { - super(core, { packType: core.pack.resourcePack, extension: 'txt' }, PlainTextNode, core.pack.resourceToPath(name, ['texts']), args) + super(core, { packType: core.pack.resourcePack(), extension: 'txt' }, PlainTextNode, core.pack.resourceToPath(name, ['texts']), args) - // TODO this.texts = '' if (args.text) { if (Array.isArray(args.text)) { @@ -42,6 +41,8 @@ export class PlainTextClass extends ResourceClass implements List this.push(args.text) } } + + this.handleConflicts() } componentToPlainText(__text: JSONTextComponent): string { diff --git a/src/core/resources/resourcepack/texture.ts b/src/core/resources/resourcepack/texture.ts index 882094b9..554f13f5 100644 --- a/src/core/resources/resourcepack/texture.ts +++ b/src/core/resources/resourcepack/texture.ts @@ -43,7 +43,7 @@ export class TextureClass extends ResourceClass['texture'] constructor(core: SandstoneCore, type: Type, name: string, args: TextureArguments) { - super(core, { packType: core.pack.resourcePack, extension: 'png', encoding: false }, TextureNode, core.pack.resourceToPath(name, ['textures', type]), args) + super(core, { packType: core.pack.resourcePack(), extension: 'png', encoding: false }, TextureNode, core.pack.resourceToPath(name, ['textures', type]), args) this.type = type @@ -56,9 +56,11 @@ export class TextureClass extends ResourceClass { - constructor(sandstoneCore: SandstoneCore, public resource: DependencyClass) { - super(sandstoneCore) - } - - getValue = () => this.resource.getValue() -} - -export type DependencyClassArguments = { - /** Whether this is a resource pack (client) or a datapack (server) dependency. Defaults to the datapack (server). */ - side?: 'client' | 'server', -} & ResourceClassArguments<'default'> - -export abstract class DependencyClass extends ResourceClass { - constructor(sandstoneCore: SandstoneCore, name: string, args: DependencyClassArguments) { - super( - sandstoneCore, - { - packType: (args.side === 'client' ? sandstoneCore.pack.packTypes.get('resourcepack-dependencies') : sandstoneCore.pack.packTypes.get('datapack-dependencies')) as PackType, - extension: 'zip', - encoding: false, - }, - DependencyNode, - ['/'], - args, - ) - - this.handleConflicts() - } - - getValue() { - // implement - } -} diff --git a/src/core/sandstoneCore.ts b/src/core/sandstoneCore.ts index e36e7151..ed9e498d 100644 --- a/src/core/sandstoneCore.ts +++ b/src/core/sandstoneCore.ts @@ -2,8 +2,10 @@ import fs from 'fs-extra' import path from 'path' -import { handleDependencies } from '../pack/dependencies' +import { DataPackDependencies, ResourcePackDependencies } from '../pack/dependencies' import { MCMetaCache } from './mcmeta' +import { DependencyClass } from './resources/dependency' +import { SmithedDependencyCache } from './smithed' import type { SandstonePack } from 'sandstone' import type { MCMetaBranches } from './mcmeta' @@ -22,6 +24,10 @@ export class SandstoneCore { mcmetaCache = new MCMetaCache() + smithed = new SmithedDependencyCache() + + dependencies: Promise[] = [] + constructor(public pack: SandstonePack) { this.resourceNodes = new Set() this.mcfunctionStack = [] @@ -75,7 +81,9 @@ export class SandstoneCore { async getExistingResource(relativePath: string, encoding: fs.EncodingOption): Promise - async getExistingResource(resource: ResourceClass): Promise + async getExistingResource(resource: ResourceClass, encoding?: 'utf-8'): Promise + + async getExistingResource(resource: ResourceClass, encoding: false | fs.EncodingOption): Promise async getExistingResource(pathOrResource: string | ResourceClass, encoding: false | fs.EncodingOption = 'utf-8'): Promise { if (typeof pathOrResource === 'string') { @@ -99,7 +107,41 @@ export class SandstoneCore { return fs.readFile(fullPath, pathOrResource.fileEncoding) } - async getVanillaResource() { + async getVanillaResource(relativePath: string): Promise + + async getVanillaResource(relativePath: string, text: true, type: 'client' | 'server'): Promise + + async getVanillaResource(relativePath: string, text: false, type: 'client' | 'server'): Promise + + async getVanillaResource(relativePath: string, text = true, type: 'client' | 'server' = 'server'): Promise { + return this.mcmetaCache.get(type === 'server' ? 'data' : 'assets', relativePath, text as true) + } + + /** + * Add a dependency for a Smithed Library + */ + depend(dependency: string, version = 'latest') { + if (!this.smithed.has(dependency)) { + this.dependencies.push((async () => { + const _depend = await this.smithed.get(dependency, version) + + if (!this.pack.packTypes.has('datapack-dependencies')) { + this.pack.packTypes.set('datapack-dependencies', new DataPackDependencies()) + } + + // eslint-disable-next-line no-new + new DependencyClass(this, _depend, 'server') + + if (_depend.resourcepack) { + if (!this.pack.packTypes.has('resourcepack-dependencies')) { + this.pack.packTypes.set('resourcepack-dependencies', new ResourcePackDependencies()) + } + + // eslint-disable-next-line no-new + new DependencyClass(this, _depend, 'client') + } + })()) + } } generateResources = (opts: { visitors: GenericCoreVisitor[] }) => { @@ -129,6 +171,12 @@ export class SandstoneCore { } save = async (cliOptions: { fileHandler: (relativePath: string, content: any) => Promise, dry: boolean, verbose: boolean }, opts: { visitors: GenericCoreVisitor[] }) => { + await Promise.allSettled(this.dependencies) + + await this.mcmetaCache.save() + + await this.smithed.save() + const resources = this.generateResources(opts) for await (const node of resources) { @@ -154,7 +202,5 @@ export class SandstoneCore { await cliOptions.fileHandler(`${resourcePath}.${fileExtension}`, value) } } - - handleDependencies(this.pack.dependencies) } } diff --git a/src/core/smithed.ts b/src/core/smithed.ts new file mode 100644 index 00000000..69cd91b2 --- /dev/null +++ b/src/core/smithed.ts @@ -0,0 +1,141 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +import AdmZip from 'adm-zip' +import fs from 'fs-extra' +import path from 'path' +import { iterateEntries } from '#utils' + +import type { PackData } from '#utils' + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fetch = import('node-fetch') + +type Manifest = Record + +export type Dependency = { version: string, date: number, datapack: Buffer, resourcepack?: Buffer | false } + +export class SmithedDependencyCache { + readonly baseURL = 'https://api.smithed.dev/v2/' + + readonly path = path.join(process.env.WORKING_DIR as string, 'resources', 'cache', 'smithed') + + readonly manifest = path.join(this.path, '..', '..', 'smithed.json') + + readonly lockFile = path.join(this.path, '..', 'lock-smithed.json') + + dependencies: Map = new Map() + + loaded = false + + async load() { + this.loaded = true + + if (!(await fs.pathExists(this.manifest))) { + return + } + + const manifest = JSON.parse(await fs.readFile(this.manifest, 'utf-8')) as Manifest + + if (!(await fs.pathExists(this.lockFile))) { + for await (const [id, version] of Object.entries(manifest)) { + await this.get(id, version) + } + + await fs.writeFile(this.lockFile, JSON.stringify(manifest)) + } else { + const lockFile = JSON.parse(await fs.readFile(this.lockFile, 'utf-8')) as Record + + for await (const [id, version] of Object.entries(manifest)) { + if (!lockFile[id]) { + await this.get(id, version) + } else if (lockFile[id].version !== version) { + await this.get(id, version) + } else { + const date = Number(Date()) + + if (version === 'latest' && ((lockFile[id].date - date) / 36e5) > 1) { + lockFile[id].date = date + + await this.get(id, version) + } else { + this.dependencies.set(id, { + version, + date: lockFile[id].date, + datapack: await fs.readFile(path.join(this.path, id, 'datapack.zip')), + resourcepack: lockFile[id].resourcepack ? await fs.readFile(path.join(this.path, id, 'resourcepack.zip')) : false, + }) + } + } + } + + await fs.writeFile(this.lockFile, JSON.stringify(lockFile)) + } + } + + async save() { + if (this.dependencies.size !== 0) { + await fs.writeFile(this.manifest, JSON.stringify(iterateEntries(this.dependencies, (val) => val.version))) + } + } + + async get(dependency: string, version: string) { + if (!this.loaded) { + await this.load() + } + + const existing = this.dependencies.get(dependency) + + if (existing && existing.version === version) { + return existing + } + + let url = `${this.baseURL}download?pack=${dependency}` + + let hasResourcePack = false + + const pack = JSON.parse(await (await (await fetch).default(`${this.baseURL}packs/${dependency}`)).text()) as PackData + + if (version === 'latest') { + const { versions } = pack + + const ver = versions[versions.length - 1] + + if (ver.downloads.resourcepack) { + hasResourcePack = true + } + } else { + const ver = pack.versions.find((_ver) => _ver.name === version) as PackData['versions'][0] + + if (ver.downloads.resourcepack) { + hasResourcePack = true + } + + url += `@${version}` + } + + const files = (new AdmZip(await (await (await fetch).default(url)).buffer())).getEntries() + + const datapack: Buffer = await new Promise((res) => { + files[0].getDataAsync((data) => res(data as Buffer)) + }) + + await fs.writeFile(path.join(this.path, dependency, 'datapack.zip'), datapack) + + let resourcepack: Buffer | false = false + + if (hasResourcePack) { + resourcepack = await new Promise((res) => { + files[1].getDataAsync((data) => res(data as Buffer)) + }) + + await fs.writeFile(path.join(this.path, dependency, 'resourcepack.zip'), datapack) + } + + return (this.dependencies.set(dependency, { + version, date: version === 'latest' ? 0 : Number(Date()), datapack, resourcepack, + }).get(dependency)!) + } + + has(dependency: string) { + return this.dependencies.has(dependency) + } +} diff --git a/src/index.ts b/src/index.ts index 13d46e3f..c5e20ee3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { SandstonePack } from './pack' import type { JSONTextComponent } from './arguments/jsonTextComponent' import type { // eslint-disable-next-line max-len - AdvancementClassArguments, DamageTypeClassArguments, ItemModifierClassArguments, LootTableClassArguments, MCFunctionClassArguments, PredicateClassArguments, RecipeClassArguments, TagClassArguments, TrimMaterialClassArguments, TrimPatternClassArguments, + AdvancementClassArguments, AtlasClassArguments, BlockStateArguments, DamageTypeClassArguments, FontArguments, ItemModifierClassArguments, LanguageArguments, LootTableClassArguments, MCFunctionClassArguments, ModelClassArguments, PlainTextArguments, PredicateClassArguments, RecipeClassArguments, SoundEventArguments, TagClassArguments, TextureArguments, TrimMaterialClassArguments, TrimPatternClassArguments, } from './core/index' import type { BASIC_CONFLICT_STRATEGIES, LiteralUnion } from './utils' @@ -78,6 +78,8 @@ export const { export const { // Resources + RawResource, + MCFunction, Advancement, DamageType, @@ -88,7 +90,15 @@ export const { Tag, TrimMaterial, TrimPattern, - RawResource, + + Atlas, + BlockState, + Font, + Language, + Model, + SoundEvent, + PlainText, // TODO: text type is any when in a workspace, w h y + Texture, // Variables packTypes, @@ -111,6 +121,14 @@ export const { sleep, } = sandstonePack +export const { + getVanillaResource, + getExistingResource, + mcmetaCache, + depend, + +} = sandstonePack.core + export type DatapackConfig = { /** * The description of the datapack. @@ -120,7 +138,7 @@ export type DatapackConfig = { description: JSONTextComponent /** - * The format version of the data pack. + * The format version of the datapack. * Can change depending on the versions of Minecraft. * * @see [https://minecraft.gamepedia.com/Data_Pack#pack.mcmeta](https://minecraft.gamepedia.com/Data_Pack#pack.mcmeta) @@ -131,7 +149,7 @@ export type DatapackConfig = { features?: string[], /** - * Section for filtering out files from data packs applied below this one. Any file that matches one of the patterns inside `block` will be treated as if it was not present in the pack at all. + * Section for filtering out files from datapacks applied below this one. Any file that matches one of the patterns inside `block` will be treated as if it was not present in the pack at all. */ filter?: { /** List of patterns */ @@ -142,14 +160,40 @@ export type DatapackConfig = { path?: string }[] } +} +export type ResourcePackConfig = { /** - * The strategy to use when 2 resources of the same type (Advancement, MCFunctions...) have the same name. + * The description of the resource pack. + * Can be a single string or a JSON Text Component + * (like in /tellraw or /title). + */ + description: JSONTextComponent + + /** + * The format version of the resource pack. + * Can change depending on the versions of Minecraft. + * + * @see [https://minecraft.fandom.com/wiki/Resource_pack#Contents](https://minecraft.fandom.com/wiki/Resource_pack#Contents) + */ + packFormat: number + + /** + * Section for filtering out files from resource packs applied below this one. Any file that matches one of the patterns inside `block` will be treated as if it was not present in the pack at all. */ - onConflict?: OnConflict + filter?: { + /** List of patterns */ + block: { + /** A regular expression for the namespace of files to be filtered out. If unspecified, it applies to every namespace. */ + namespace?: string + /** A regular expression for the paths of files to be filtered out. If unspecified, it applies to every file. */ + path?: string + }[] + } } -type PackConfigs> = Record +// eslint-disable-next-line max-len +type PackConfigs> = Record export interface SandstoneConfig { /** @@ -165,6 +209,11 @@ export interface SandstoneConfig { packs: PackConfigs> + /** + * The strategy to use when 2 resources of the same type (Advancement, MCFunctions...) have the same name. + */ + onConflict?: Partial> // TODO: Types are still screwy with this, fix. + /** * A unique identifier that is used to distinguish your variables from other Sandstone pack variables. * @@ -301,7 +350,48 @@ type ContentStrategy = ( * The conflict strategy to use for Trim patterns. * Will override the defined `default` strategy. */ - ContentStrategyKind<'trim_patterns', NonNullable> + ContentStrategyKind<'trim_patterns', NonNullable> | + + /** + * The conflict strategy to use for Atlases. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'atlass', NonNullable> | // atlass is not a typo. feel free to PR :trolley: + /** + * The conflict strategy to use for Block states. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'block_states', NonNullable['onConflict']>> | + /** + * The conflict strategy to use for Fonts. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'fonts', NonNullable> | + /** + * The conflict strategy to use for Languages. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'languages', NonNullable> | + /** + * The conflict strategy to use for Models. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'models', NonNullable> | + /** + * The conflict strategy to use for Sound Events. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'sound_events', NonNullable> | + /** + * The conflict strategy to use for Plain Text files. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'texts', NonNullable> | + /** + * The conflict strategy to use for Textures. + * Will override the defined `default` strategy. + */ + ContentStrategyKind<'texture', NonNullable['onConflict']>> ) type OnConflict = Record diff --git a/src/pack/dependencies.ts b/src/pack/dependencies.ts index cc73066c..05b10b18 100644 --- a/src/pack/dependencies.ts +++ b/src/pack/dependencies.ts @@ -1,13 +1,11 @@ import { PackType } from './packType' -import type { handlerReadFile, handlerWriteFile } from './packType' - export class DataPackDependencies extends PackType { constructor() { super('datapack_dependencies', 'saves/$worldName$/datapacks', 'world/datapacks', 'datapacks', 'server', false, undefined, false) } - handleOutput = async (type: 'output' | 'client' | 'server', readFile: handlerReadFile, writeFile: handlerWriteFile) => { + handleOutput = async (type: 'output' | 'client' | 'server') => { if (type === 'output') { // TODO: run weld } @@ -16,10 +14,10 @@ export class DataPackDependencies extends PackType { export class ResourcePackDependencies extends PackType { constructor() { - super('resource_pack_dependencies', 'saves/$worldName$/resources', 'resources', 'resourcepacks', 'client', true, undefined, false) + super('resourcepack_dependencies', 'saves/$worldName$/resources', 'resources', 'resourcepacks/$packName$_dependencies', 'client', true, undefined, false) } - handleOutput = async (type: 'output' | 'client' | 'server', readFile: handlerReadFile, writeFile: handlerWriteFile) => { + handleOutput = async (type: 'output' | 'client' | 'server') => { if (type === 'output') { // TODO: run weld } diff --git a/src/pack/index.ts b/src/pack/index.ts index 98dec4bf..900f73f5 100644 --- a/src/pack/index.ts +++ b/src/pack/index.ts @@ -1,2 +1,4 @@ +export * from './dependencies' export * from './pack' +export * from './packType' export * from './visitors' diff --git a/src/pack/pack.ts b/src/pack/pack.ts index b91bbf8e..4fa9550c 100644 --- a/src/pack/pack.ts +++ b/src/pack/pack.ts @@ -7,7 +7,7 @@ import { DimensionChunkClass, RootChunkClass, UtilityChunkClass } from 'sandston import { UUIDClass } from 'sandstone/variables/UUID' import { SandstoneCommands } from '#commands' import { - AdvancementClass, DamageTypeClass, ItemModifierClass, LootTableClass, MCFunctionClass, PredicateClass, RecipeClass, SandstoneCore, TagClass, TrimMaterialClass, TrimPatternClass, + AdvancementClass, AtlasClass, BlockStateClass, DamageTypeClass, FontClass, ItemModifierClass, LanguageClass, LootTableClass, MCFunctionClass, ModelClass, PlainTextClass, PredicateClass, RecipeClass, SandstoneCore, SoundEventClass, TagClass, TextureClass, TrimMaterialClass, TrimPatternClass, } from '#core' import { Flow, SandstoneConditions } from '#flow' import { randomUUID } from '#utils' @@ -18,7 +18,6 @@ import { } from '#variables' import { Score } from '#variables/Score' -import { DataPackDependencies, ResourcePackDependencies } from './dependencies' import { PackType } from './packType' import { ContainerCommandsToMCFunctionVisitor, GenerateLazyMCFunction, IfElseTransformationVisitor, InitConstantsVisitor, InitObjectivesVisitor, @@ -34,13 +33,13 @@ import type { import type { handlerReadFile, handlerWriteFile } from './packType' import type { // eslint-disable-next-line max-len - AdvancementJSON, Coordinates, DamageTypeJSON, DIMENSIONS, ItemModifierJSON, JSONTextComponent, LootTableJSON, NBTObject, OBJECTIVE_CRITERION, PredicateJSON, RecipeJSON, REGISTRIES, SingleEntityArgument, TagValuesJSON, TimeArgument, TrimMaterialJSON, TrimPatternJSON, + AdvancementJSON, AtlasDefinition, BlockStateDefinition, BlockStateType, Coordinates, DamageTypeJSON, DIMENSIONS, FontProvider, ItemModifierJSON, JSONTextComponent, LootTableJSON, NBTObject, OBJECTIVE_CRITERION, PredicateJSON, RecipeJSON, REGISTRIES, SingleEntityArgument, SOUND_TYPES, TagValuesJSON, TEXTURE_TYPES, TimeArgument, TrimMaterialJSON, TrimPatternJSON, } from '#arguments' import type { StoreType } from '#commands' import type { _RawMCFunctionClass, // eslint-disable-next-line max-len - AdvancementClassArguments, DamageTypeClassArguments, ItemModifierClassArguments, LootTableClassArguments, MCFunctionClassArguments, Node, PredicateClassArguments, RecipeClassArguments, TagClassArguments, TrimMaterialClassArguments, TrimPatternClassArguments, + AdvancementClassArguments, AtlasClassArguments, BlockStateArguments, DamageTypeClassArguments, FontArguments, ItemModifierClassArguments, LanguageArguments, LootTableClassArguments, MCFunctionClassArguments, ModelClassArguments, Node, PlainTextArguments, PredicateClassArguments, RecipeClassArguments, SoundEventArguments, TagClassArguments, TextureArguments, TrimMaterialClassArguments, TrimPatternClassArguments, } from '#core' import type { LiteralUnion, MakeInstanceCallable } from '#utils' import type { @@ -88,8 +87,8 @@ export class ResourcePack extends PackType { // TODO: typing. low priority readonly packMcmeta: any - constructor(archiveOutput: boolean, options: { packFormat: number, description: JSONTextComponent, features?: string[], filter?: { namespace?: string, path?: string }[] }) { - super('datapack', 'saves/$worldName$/resources', 'resource_pack', 'resource_packs/$packName$', 'client', true, 'assets', true) + constructor(options: { packFormat: number, description: JSONTextComponent, features?: string[], filter?: { namespace?: string, path?: string }[] }) { + super('resourcepack', 'saves/$worldName$/resources', 'resource_pack', 'resourcepacks/$packName$', 'client', true, 'assets', true) this.packMcmeta = { pack: { @@ -134,12 +133,24 @@ export class SandstonePack { packOptions = JSON.parse(process.env.PACK_OPTIONS as string) - get dataPack() { - return this.packTypes.get('datapack') as DataPack + dataPack() { + let pack = this.packTypes.get('datapack') as DataPack + + if (!pack) { + pack = this.packTypes.set('datapack', new DataPack(false, this.packOptions.datapack)).get('datapack') as DataPack + } + + return pack } - get resourcePack() { - return this.packTypes.get('resourcepack') as ResourcePack + resourcePack() { + let pack = this.packTypes.get('resourcepack') as ResourcePack + + if (!pack) { + pack = this.packTypes.set('resourcepack', new ResourcePack(this.packOptions.resourcepack)).get('resourcepack') as ResourcePack + } + + return pack } // Smithed Pack IDs @@ -169,10 +180,6 @@ export class SandstonePack { this.core = new SandstoneCore(this) this.packTypes = new Map() - this.packTypes.set('datapack', new DataPack(false, this.packOptions.datapack)) - this.packTypes.set('datapack-dependencies', new DataPackDependencies()) - this.packTypes.set('resourcepack', new ResourcePack(false, this.packOptions.datapack)) - this.packTypes.set('resourcepack-dependencies', new ResourcePackDependencies()) this.commands = new SandstoneCommands(this) @@ -527,13 +534,14 @@ export class SandstonePack { UUID(source: UUIDSource = randomUUID(), holderState: 1 | Omit | Score | 'permanent' = 'permanent', options?: UUIDOptions) { return new UUIDClass(this.core, source, holderState as number, options) } /** **Waiting on Smithed Dimensions to be functional.** */ - get rootChunk() { - if (this.utilityChunks.get('0,0;smithed:void')) { - return this.utilityChunks.get('0,0;smithed:void') as RootChunkClass + rootChunk() { + const root = this.utilityChunks.get('0,0;smithed:void') + + if (root) { + return root as RootChunkClass } - this.utilityChunks.set('0,0;smithed:void', new RootChunkClass(this)) - return this.utilityChunks.get('0,0;smithed:void') as RootChunkClass + return this.utilityChunks.set('0,0;smithed:void', new RootChunkClass(this)).get('0,0;smithed:void') as RootChunkClass } __dimensionEntryPoints: { @@ -567,7 +575,7 @@ export class SandstonePack { dimensionMarker() { // TODO: Set dimension target to current dimension some how - return this.rootChunk.armorStand.execute.on('passengers').if.score(this.dimensionID('@s'), '=', this.dimensionTarget).on('origin') + return this.rootChunk().armorStand.execute.on('passengers').if.score(this.dimensionID('@s'), '=', this.dimensionTarget).on('origin') } UtilityChunk>(id: IDString, chunk: ChunkLoc, marker?: UUIDClass<'known', 'permanent'>) { @@ -708,7 +716,7 @@ export class SandstonePack { class RawResource extends this.makeCustomResource[1] { constructor() { super(core, path.join('/'), { - type: `${Math.random()}`, packType: dataPack, extension, addToSandstoneCore: true, creator: 'user', + type: `${Math.random()}`, packType: dataPack(), extension, addToSandstoneCore: true, creator: 'user', }) } @@ -791,6 +799,76 @@ export class SandstonePack { ...options, }) + Atlas = (name: string, atlas: AtlasDefinition, options?: AtlasClassArguments) => new AtlasClass(this.core, name, { + atlas, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('atlas') as AtlasClassArguments['onConflict'], + ...options, + }) + + BlockState(type: Type, name: string, blockState: BlockStateDefinition, options?: BlockStateArguments) { + return new BlockStateClass(this.core, name, type, { + blockState, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('blockstate') as BlockStateArguments['onConflict'], + ...options, + }) + } + + Font = (name: string, providers: FontProvider[], options?: FontArguments) => new FontClass(this.core, name, { + providers, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('font') as FontArguments['onConflict'], + ...options, + }) + + Language = (name: string, language: LanguageArguments['language'], options?: LanguageArguments) => new LanguageClass(this.core, name, { + language, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('language') as LanguageArguments['onConflict'], + ...options, + }) + + Model = (type: 'block' | 'item', name: string, model: ModelClassArguments['model'], options?: ModelClassArguments) => new ModelClass(this.core, type, name, { + model, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('model') as ModelClassArguments['onConflict'], + ...options, + }) + + SoundEvent(type: Type, name: string, sound: SoundEventArguments['sound'], options?: SoundEventArguments) { + return new SoundEventClass(this.core, type, name, { + sound, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('sound_event') as SoundEventArguments['onConflict'], + ...options, + }) + } + + PlainText = (name: string, text: PlainTextArguments['text'], options?: PlainTextArguments) => new PlainTextClass(this.core, name, { + text, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('atlas') as PlainTextArguments['onConflict'], + ...options, + }) + + Texture(type: Type, name: string, texture: TextureArguments['texture'], options?: TextureArguments) { + return new TextureClass(this.core, type, name, { + texture, + creator: 'user', + addToSandstoneCore: true, + onConflict: conflictDefaults('sound_event') as TextureArguments['onConflict'], + ...options, + }) + } + save = async (cliOptions: { fileHandler: (relativePath: string, content: any) => Promise, dry: boolean, verbose: boolean }) => { await this.core.save(cliOptions, { visitors: [ diff --git a/src/utils.ts b/src/utils.ts index 2f8cec33..c4894045 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,10 @@ +import { coerce } from 'semver' import * as util from 'util' +import { Type } from '@sinclair/typebox' +import { Format } from '@sinclair/typebox/format' + +import type { Static } from '@sinclair/typebox' import type { UUIDinNumber } from './variables/UUID' /** @@ -221,3 +226,103 @@ export function iterateEntries(node: Map, fn: (val: any) => any) { node.forEach(([key, val]) => (newNode[key] = fn(val))) return newNode } + +export interface PackEntry { + added: number, + downloads: { [key: string]: number } + updated: number + owner: string +} + +Format.Set('semver', (v) => coerce(v) != null) + +export const supportedMinecraftVersions = [ + '1.18', + '1.18.1', + '1.18.2', + '1.19', +] +export const latestMinecraftVersion = '1.19' + +export const MinecraftVersionSchema = Type.Union(supportedMinecraftVersions.map((v) => Type.Literal(v))) + +export const packCategories = [ + 'Extensive', + 'Lightweight', + 'QoL', + 'Vanilla+', + 'Tech', + 'Magic', + 'Library', + 'Exploration', + 'World Overhaul', + 'No Resource Pack', +] + +const PackDependencySchema = Type.Object({ + id: Type.String(), + version: Type.String(), +}) + +export const PackVersionSchema = Type.Object({ + name: Type.String({ minLength: 1 }), + downloads: Type.Object({ + datapack: Type.String({ minLength: 1 }), + resourcepack: Type.String(), + }), + supports: Type.Array(MinecraftVersionSchema, { minItems: 1 }), + dependencies: Type.Array(PackDependencySchema), +}) + +export const PackDataSchema = Type.Object({ + id: Type.String({ minLength: 3 }), + display: Type.Object({ + name: Type.String({ minLength: 3 }), + description: Type.String({ minLength: 3 }), + icon: Type.String({ default: '' }), + hidden: Type.Boolean({ default: false }), + webPage: Type.Optional(Type.String()), + }), + versions: Type.Array(PackVersionSchema, { minItems: 1 }), + categories: Type.Array(Type.Union(packCategories.map((c) => Type.Literal(c)))), +}) + +export const MetaDataSchema = Type.Object({ + docId: Type.String(), + rawId: Type.String(), + stats: Type.Object({ + updated: Type.Optional(Type.Number()), + added: Type.Number(), + downloads: Type.Object({ + total: Type.Number(), + today: Type.Number(), + }), + }), + owner: Type.String(), + contributors: Type.Array(Type.String(), { default: [] }), +}) + +export type PackMetaData = Static +export type MinecraftVersion = Static +export type PackDependency = Static +export type PackVersion = Static +export type PackData = Static + +export interface UserData { + displayName: string +} + +// eslint-disable-next-line no-shadow +export enum HTTPResponses { + OK = 200, + CREATED = 201, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + FORBIDDEN = 403, + NOT_FOUND = 404, + CONFLICT = 409, + SERVER_ERROR = 500 + +} + +export type ReviewState = 'verified'|'pending'|'unsubmitted'|'rejected' diff --git a/src/variables/Selector.ts b/src/variables/Selector.ts index d96dc12d..1081fa8c 100644 --- a/src/variables/Selector.ts +++ b/src/variables/Selector.ts @@ -7,7 +7,7 @@ import type { LiteralUnion } from '../utils' import type { ConditionTextComponentClass, SelectorPickClass } from './abstractClasses' import type { NotNBT } from './nbt/NBTs' import type { - ENTITY_TYPES, GAMEMODES, Range, RootNBT, TextComponentObject, + ENTITY_TYPES, GAMEMODES, JSONTextComponent, Range, RootNBT, } from '#arguments' import type { PredicateClass } from '#core' import type { SandstonePack } from '#pack' @@ -309,7 +309,7 @@ export class SelectorClass = undefined as unknown as DataPointClass<'storage'> core.pack.initMCFunction.push(() => { - core.pack.rootChunk.createMember('armor_stand', { UUID: new NBTIntArray(stack.known) }) + core.pack.rootChunk().createMember('armor_stand', { UUID: new NBTIntArray(stack.known) }) current = core.pack.DataVariable({ Duration, Tags: [`${core.pack.defaultNamespace}.__asyncTimer`] }) }) diff --git a/src/variables/UtilityChunk.ts b/src/variables/UtilityChunk.ts index 5dab8410..522a614e 100644 --- a/src/variables/UtilityChunk.ts +++ b/src/variables/UtilityChunk.ts @@ -86,7 +86,7 @@ export class DimensionChunkClass extends UtilityChunkCla } /** Partial execute command executing as (not at) the per-dimension marker */ - execute = () => this.pack.commands.execute.as(this.pack.rootChunk.armorStand).on('passengers').if.score(this.pack.dimensionID('@s'), '=', this.pack.dimensionTarget) + execute = () => this.pack.commands.execute.as(this.pack.rootChunk().armorStand).on('passengers').if.score(this.pack.dimensionID('@s'), '=', this.pack.dimensionTarget) __predicate?: PredicateClass @@ -173,8 +173,8 @@ export class RootChunkClass extends UtilityChunkClass<[0, 0], ['smithed', 'void' constructor(pack: SandstonePack) { super(pack, ['smithed', 'void'], [0, 0], pack.UUID('000000fe-0000-0000-0000-000000000000')) - // TODO - pack.dependencies.set('smithed.forceload', true) + console.log('Function of Utility Chunks is delayed until the Smithed Forceload library is released.') + // pack.core.depend('forceload') } /**