diff --git a/package-lock.json b/package-lock.json index 0641b63d..1ef731fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,22 +10,6 @@ "license": "MIT", "dependencies": { "@giscus/react": "^2.2.3", - "@mcschema/core": "^0.13.0", - "@mcschema/java-1.15": "^0.2.13", - "@mcschema/java-1.16": "^0.6.20", - "@mcschema/java-1.17": "^0.2.40", - "@mcschema/java-1.18": "^0.3.16", - "@mcschema/java-1.18.2": "^0.1.26", - "@mcschema/java-1.19": "^0.1.54", - "@mcschema/java-1.19.3": "^0.0.17", - "@mcschema/java-1.19.4": "^0.1.21", - "@mcschema/java-1.20": "^0.0.24", - "@mcschema/java-1.20.2": "^0.0.15", - "@mcschema/java-1.20.3": "^0.0.16", - "@mcschema/java-1.20.5": "^0.0.42", - "@mcschema/java-1.21": "^0.0.27", - "@mcschema/java-1.21.2": "^0.0.13", - "@mcschema/locales": "^0.1.104", "@zip.js/zip.js": "^2.4.5", "brace": "^0.11.1", "buffer": "^6.0.3", @@ -68,9 +52,6 @@ "vite-plugin-static-copy": "^0.12.0" } }, - "@mcschema/java-1.18@0.3.0-beta": { - "extraneous": true - }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -629,128 +610,6 @@ "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz", "integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w==" }, - "node_modules/@mcschema/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.13.0.tgz", - "integrity": "sha512-nJRDvdEI2Z7Yw7eWKcbkuLFKtPIyeCc1d04E7EeUOpwxfHffgL5a0VBD2PX692z3igfTHGNXgPkzrleV91Q/ww==" - }, - "node_modules/@mcschema/java-1.15": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.15/-/java-1.15-0.2.13.tgz", - "integrity": "sha512-f3NanEsd48svxZfqquSylXrHZDd5vjvi8D6r15blrCi2J2C42032Ir0dMPD38hLxu9ey0eIrgucDApn/GXO1uA==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.16": { - "version": "0.6.20", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.6.20.tgz", - "integrity": "sha512-pJihtCYPloVAk6Tq2JVvfk666rPLaNmXkHhD6/a1kk/AUaP2wk/3YuYdXyRqz7/lmxkBpJK/BvhAO4ZtsQfNtg==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.17": { - "version": "0.2.40", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.17/-/java-1.17-0.2.40.tgz", - "integrity": "sha512-aFlH9APa4JEMsz0y3BKjYGILXn6QQcQDOCHA+qgdXDjc0xsrHffO1l/TVVDi5GQCCqEgGA15guqfLVV+7Vtb2A==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.18": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.18/-/java-1.18-0.3.16.tgz", - "integrity": "sha512-crxXl0OW6BYlD+Cjaa8cJEsoehnBa6whrE6JBG37nZ1nQBl0e9vun4fbaZi0buzFjeJGZ0st7Lm0LsFJRe7FEw==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.18.2": { - "version": "0.1.26", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.18.2/-/java-1.18.2-0.1.26.tgz", - "integrity": "sha512-GN6gCyEyTTJTiv0vn4cTVMYVgG2BJpDXJh2KPrg5cT/7gCpHUrt4PJAyzEaYLkDZoW17p2gWuY12gIM/yfuiDA==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.19": { - "version": "0.1.54", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19/-/java-1.19-0.1.54.tgz", - "integrity": "sha512-eh8kTBs3EAGlWzTbmGzoIRtePd2dUkttooGsmXmjadvwf/yT19Ew+P+U4Y6LLFLnNoWJxruCVhznS1KxzoT0wA==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.19.3": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19.3/-/java-1.19.3-0.0.17.tgz", - "integrity": "sha512-8V3UpdmVPb2JZJ5A3Mi6J5Lf5dicmY9w11dq8VQm9jSEPwY7IXSnE4l9J3AmFkIgAhiaNTQ+/lbmliR7LNUAlQ==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.19.4": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19.4/-/java-1.19.4-0.1.21.tgz", - "integrity": "sha512-QZhe7L6t7/Uf6vK0pXWSJYoGA0D+VAv10wrMrwG6jTHoCI1iPkFhet9JqUnOwOv0Ql+jwh7zDNQjTHJIkHwfMQ==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.20": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20/-/java-1.20-0.0.24.tgz", - "integrity": "sha512-Bcm4n7YTXQ/6mGmj7l9KcRXl8ta9nokNZDyrgEeexsrE9jzj1ef6TsNra2bI9kCka9l4s4XzZ1VDuM/Ag/Ud0g==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.20.2": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.2/-/java-1.20.2-0.0.15.tgz", - "integrity": "sha512-crGOWodFnWT5XGjp2b8kZeXKsHpdQHDaES09NYf/VNWUIdGNYD7hXlIVvQEsqbZt1ABuisIKWvNYZJwH7Fhdew==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.20.3": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.3/-/java-1.20.3-0.0.16.tgz", - "integrity": "sha512-XNX02G7RHB8u/ibwU0GSo+lsz/5rcduHnWKk/BJcGH6Q2NswLKJDiyt8Ow5KrcmtCBxprCLjg5pJaj/Ql6aKoQ==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.20.5": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.5/-/java-1.20.5-0.0.42.tgz", - "integrity": "sha512-LgVeCvHQPMQUMPPiJ5tkm8RG7EKowvw6eHxu8RPakeX/Del5OYkQI252hkP2RC3AA46NHE+iKqtsk8A+lvCuow==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.21": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.21/-/java-1.21-0.0.27.tgz", - "integrity": "sha512-X2o8VJouEv5ZixLrpngyq0srwYB8Bp0lmIBKRJvJ+7/5RB6Mrf9I/Z4y7BkPjpKjw8TME1r5Tw/g7sjrYXvEUQ==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/java-1.21.2": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.21.2/-/java-1.21.2-0.0.13.tgz", - "integrity": "sha512-qGDOczhRd/KY+75oXxoKwZSuJS8k038mkiSqCbe+TFapXCZSv87TOnXCUD+LFK0v51ejwFM6mErv8ZJinuKq9g==", - "dependencies": { - "@mcschema/core": "^0.13.0" - } - }, - "node_modules/@mcschema/locales": { - "version": "0.1.104", - "resolved": "https://registry.npmjs.org/@mcschema/locales/-/locales-0.1.104.tgz", - "integrity": "sha512-fZ9zzb4OvMnxRFtVYpPuFDugd2LGYMfGRsYzl+RG6wFZAUuB6pe+igNmK5o8VfX1ldO2W5FWkddgh/MjtemFOA==" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -4775,128 +4634,6 @@ "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.2.tgz", "integrity": "sha512-VMOxsWh/QDwrxPsgkSQnuZ+8mfNy1OTjzzUdLBvvZtpahwPTHTeVZ51RZRqO4xfKVrR+btIPA8D01IL3xeG66w==" }, - "@mcschema/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.13.0.tgz", - "integrity": "sha512-nJRDvdEI2Z7Yw7eWKcbkuLFKtPIyeCc1d04E7EeUOpwxfHffgL5a0VBD2PX692z3igfTHGNXgPkzrleV91Q/ww==" - }, - "@mcschema/java-1.15": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.15/-/java-1.15-0.2.13.tgz", - "integrity": "sha512-f3NanEsd48svxZfqquSylXrHZDd5vjvi8D6r15blrCi2J2C42032Ir0dMPD38hLxu9ey0eIrgucDApn/GXO1uA==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.16": { - "version": "0.6.20", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.16/-/java-1.16-0.6.20.tgz", - "integrity": "sha512-pJihtCYPloVAk6Tq2JVvfk666rPLaNmXkHhD6/a1kk/AUaP2wk/3YuYdXyRqz7/lmxkBpJK/BvhAO4ZtsQfNtg==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.17": { - "version": "0.2.40", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.17/-/java-1.17-0.2.40.tgz", - "integrity": "sha512-aFlH9APa4JEMsz0y3BKjYGILXn6QQcQDOCHA+qgdXDjc0xsrHffO1l/TVVDi5GQCCqEgGA15guqfLVV+7Vtb2A==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.18": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.18/-/java-1.18-0.3.16.tgz", - "integrity": "sha512-crxXl0OW6BYlD+Cjaa8cJEsoehnBa6whrE6JBG37nZ1nQBl0e9vun4fbaZi0buzFjeJGZ0st7Lm0LsFJRe7FEw==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.18.2": { - "version": "0.1.26", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.18.2/-/java-1.18.2-0.1.26.tgz", - "integrity": "sha512-GN6gCyEyTTJTiv0vn4cTVMYVgG2BJpDXJh2KPrg5cT/7gCpHUrt4PJAyzEaYLkDZoW17p2gWuY12gIM/yfuiDA==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.19": { - "version": "0.1.54", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19/-/java-1.19-0.1.54.tgz", - "integrity": "sha512-eh8kTBs3EAGlWzTbmGzoIRtePd2dUkttooGsmXmjadvwf/yT19Ew+P+U4Y6LLFLnNoWJxruCVhznS1KxzoT0wA==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.19.3": { - "version": "0.0.17", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19.3/-/java-1.19.3-0.0.17.tgz", - "integrity": "sha512-8V3UpdmVPb2JZJ5A3Mi6J5Lf5dicmY9w11dq8VQm9jSEPwY7IXSnE4l9J3AmFkIgAhiaNTQ+/lbmliR7LNUAlQ==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.19.4": { - "version": "0.1.21", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.19.4/-/java-1.19.4-0.1.21.tgz", - "integrity": "sha512-QZhe7L6t7/Uf6vK0pXWSJYoGA0D+VAv10wrMrwG6jTHoCI1iPkFhet9JqUnOwOv0Ql+jwh7zDNQjTHJIkHwfMQ==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.20": { - "version": "0.0.24", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20/-/java-1.20-0.0.24.tgz", - "integrity": "sha512-Bcm4n7YTXQ/6mGmj7l9KcRXl8ta9nokNZDyrgEeexsrE9jzj1ef6TsNra2bI9kCka9l4s4XzZ1VDuM/Ag/Ud0g==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.20.2": { - "version": "0.0.15", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.2/-/java-1.20.2-0.0.15.tgz", - "integrity": "sha512-crGOWodFnWT5XGjp2b8kZeXKsHpdQHDaES09NYf/VNWUIdGNYD7hXlIVvQEsqbZt1ABuisIKWvNYZJwH7Fhdew==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.20.3": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.3/-/java-1.20.3-0.0.16.tgz", - "integrity": "sha512-XNX02G7RHB8u/ibwU0GSo+lsz/5rcduHnWKk/BJcGH6Q2NswLKJDiyt8Ow5KrcmtCBxprCLjg5pJaj/Ql6aKoQ==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.20.5": { - "version": "0.0.42", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.20.5/-/java-1.20.5-0.0.42.tgz", - "integrity": "sha512-LgVeCvHQPMQUMPPiJ5tkm8RG7EKowvw6eHxu8RPakeX/Del5OYkQI252hkP2RC3AA46NHE+iKqtsk8A+lvCuow==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.21": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.21/-/java-1.21-0.0.27.tgz", - "integrity": "sha512-X2o8VJouEv5ZixLrpngyq0srwYB8Bp0lmIBKRJvJ+7/5RB6Mrf9I/Z4y7BkPjpKjw8TME1r5Tw/g7sjrYXvEUQ==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/java-1.21.2": { - "version": "0.0.13", - "resolved": "https://registry.npmjs.org/@mcschema/java-1.21.2/-/java-1.21.2-0.0.13.tgz", - "integrity": "sha512-qGDOczhRd/KY+75oXxoKwZSuJS8k038mkiSqCbe+TFapXCZSv87TOnXCUD+LFK0v51ejwFM6mErv8ZJinuKq9g==", - "requires": { - "@mcschema/core": "^0.13.0" - } - }, - "@mcschema/locales": { - "version": "0.1.104", - "resolved": "https://registry.npmjs.org/@mcschema/locales/-/locales-0.1.104.tgz", - "integrity": "sha512-fZ9zzb4OvMnxRFtVYpPuFDugd2LGYMfGRsYzl+RG6wFZAUuB6pe+igNmK5o8VfX1ldO2W5FWkddgh/MjtemFOA==" - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index e6d291c6..e1485b94 100644 --- a/package.json +++ b/package.json @@ -16,22 +16,6 @@ "license": "MIT", "dependencies": { "@giscus/react": "^2.2.3", - "@mcschema/core": "^0.13.0", - "@mcschema/java-1.15": "^0.2.13", - "@mcschema/java-1.16": "^0.6.20", - "@mcschema/java-1.17": "^0.2.40", - "@mcschema/java-1.18": "^0.3.16", - "@mcschema/java-1.18.2": "^0.1.26", - "@mcschema/java-1.19": "^0.1.54", - "@mcschema/java-1.19.3": "^0.0.17", - "@mcschema/java-1.19.4": "^0.1.21", - "@mcschema/java-1.20": "^0.0.24", - "@mcschema/java-1.20.2": "^0.0.15", - "@mcschema/java-1.20.3": "^0.0.16", - "@mcschema/java-1.20.5": "^0.0.42", - "@mcschema/java-1.21": "^0.0.27", - "@mcschema/java-1.21.2": "^0.0.13", - "@mcschema/locales": "^0.1.104", "@zip.js/zip.js": "^2.4.5", "brace": "^0.11.1", "buffer": "^6.0.3", diff --git a/src/app/Utils.ts b/src/app/Utils.ts index 578d6b33..29757e85 100644 --- a/src/app/Utils.ts +++ b/src/app/Utils.ts @@ -1,5 +1,3 @@ -import type { DataModel } from '@mcschema/core' -import { Path } from '@mcschema/core' import * as zip from '@zip.js/zip.js' import type { Identifier, NbtTag, Random } from 'deepslate' import { Matrix3, Matrix4, NbtByte, NbtCompound, NbtDouble, NbtInt, NbtList, NbtString, Vector } from 'deepslate' @@ -47,21 +45,6 @@ export function generateColor() { return Math.floor(Math.random() * 16777215) } -export function newSeed(model: DataModel) { - const seed = Math.floor(Math.random() * (4294967296)) - 2147483648 - const dimensions = model.get(new Path(['dimensions'])) - model.set(new Path(['seed']), seed, true) - if (isObject(dimensions)) { - Object.keys(dimensions).forEach(id => { - model.set(new Path(['dimensions', id, 'generator', 'seed']), seed, true) - model.set(new Path(['dimensions', id, 'generator', 'biome_source', 'seed']), seed, true) - }) - } - model.set(new Path(['placement', 'salt']), Math.abs(seed), true) - model.set(new Path(['generator', 'seed']), seed, true) - model.set(new Path(['generator', 'biome_source', 'seed']), seed) -} - export function htmlEncode(str: string) { return str.replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/') diff --git a/src/app/components/ItemDisplay.tsx b/src/app/components/ItemDisplay.tsx index 17f8e1ae..0b46068e 100644 --- a/src/app/components/ItemDisplay.tsx +++ b/src/app/components/ItemDisplay.tsx @@ -3,10 +3,9 @@ import { Identifier } from 'deepslate/core' import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' -import { fetchItemComponents } from '../services/index.js' +import { fetchItemComponents, fetchRegistries } from '../services/index.js' import { ResolvedItem } from '../services/ResolvedItem.js' import { renderItem } from '../services/Resources.js' -import { getCollections } from '../services/Schemas.js' import { jsonToNbt } from '../Utils.js' import { ItemTooltip } from './ItemTooltip.jsx' import { Octicon } from './Octicon.jsx' @@ -83,14 +82,17 @@ function ItemItself({ item }: ResolvedProps) { return Octicon.package } - const { value: collections } = useAsync(() => getCollections(version), []) + const { value: allModels, loading: loadingModels } = useAsync(async () => { + const registries = await fetchRegistries(version) + return registries.get('model') + }, [version]) - if (collections === undefined) { + if (loadingModels || allModels === undefined) { return null } const modelPath = `item/${item.id.path}` - if (collections.get('model').includes('minecraft:' + modelPath)) { + if (allModels && allModels.includes('minecraft:' + modelPath)) { return } diff --git a/src/app/components/ItemDisplay1204.tsx b/src/app/components/ItemDisplay1204.tsx index 05b5d959..f8bf9ce7 100644 --- a/src/app/components/ItemDisplay1204.tsx +++ b/src/app/components/ItemDisplay1204.tsx @@ -3,8 +3,8 @@ import { Identifier } from 'deepslate-1.20.4/core' import { useEffect, useRef, useState } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' +import { fetchRegistries } from '../services/index.js' import { renderItem } from '../services/Resources1204.js' -import { getCollections } from '../services/Schemas.js' import { ItemTooltip1204 } from './ItemTooltip1204.jsx' import { Octicon } from './Octicon.jsx' import { itemHasGlint } from './previews/LootTable1204.js' @@ -69,14 +69,17 @@ function ItemItself({ item }: Props) { return Octicon.package } - const { value: collections } = useAsync(() => getCollections(version), []) + const { value: allModels, loading: loadingModels } = useAsync(async () => { + const registries = await fetchRegistries(version) + return registries.get('model') + }, [version]) - if (collections === undefined) { + if (loadingModels || allModels === undefined) { return null } const modelPath = `item/${item.id.path}` - if (collections.get('model').includes('minecraft:' + modelPath)) { + if (allModels && allModels.includes('minecraft:' + modelPath)) { return } diff --git a/src/app/components/customized/CustomizedSlider.tsx b/src/app/components/customized/CustomizedSlider.tsx index 3a15c447..4ecdd33b 100644 --- a/src/app/components/customized/CustomizedSlider.tsx +++ b/src/app/components/customized/CustomizedSlider.tsx @@ -1,4 +1,3 @@ -import type { NodeChildren } from '@mcschema/core' import { NumberInput, RangeInput } from '../index.js' import { CustomizedInput } from './CustomizedInput.jsx' @@ -12,7 +11,6 @@ interface Props { initial?: number, error?: string, onChange: (value: number) => void, - children?: NodeChildren, } export function CustomizedSlider(props: Props) { const isInteger = (props.step ?? 1) >= 1 diff --git a/src/app/components/generator/FileCreation.tsx b/src/app/components/generator/FileCreation.tsx index 83252fd2..740bbab8 100644 --- a/src/app/components/generator/FileCreation.tsx +++ b/src/app/components/generator/FileCreation.tsx @@ -1,13 +1,13 @@ -import { DataModel } from '@mcschema/core' import { useState } from 'preact/hooks' import { Analytics } from '../../Analytics.js' import { useLocale, useProject } from '../../contexts/index.js' +import type { FileModel } from '../../services/index.js' import { Btn } from '../Btn.js' import { TextInput } from '../forms/index.js' import { Modal } from '../Modal.js' interface Props { - model: DataModel, + model: FileModel, id: string, method: string, onClose: () => void, @@ -29,7 +29,7 @@ export function FileCreation({ model, id, method, onClose }: Props) { return } Analytics.saveProjectFile(id, projects.length, project.files.length, method as any) - updateFile(id, undefined, { type: id, id: fileId, data: DataModel.unwrapLists(model.data) }) + updateFile(id, undefined, { type: id, id: fileId, data: model.data }) onClose() } diff --git a/src/app/components/generator/PreviewPanel.tsx b/src/app/components/generator/PreviewPanel.tsx index 5cc5f8e7..c2576361 100644 --- a/src/app/components/generator/PreviewPanel.tsx +++ b/src/app/components/generator/PreviewPanel.tsx @@ -1,69 +1,59 @@ -import type { DataModel } from '@mcschema/core' -import { Path } from '@mcschema/core' -import { useState } from 'preact/hooks' -import { useModel } from '../../hooks/index.js' -import type { VersionId } from '../../services/index.js' +import { useVersion } from '../../contexts/Version.jsx' +import type { FileModel } from '../../services/index.js' import { checkVersion } from '../../services/index.js' import { BiomeSourcePreview, BlockStatePreview, DecoratorPreview, DensityFunctionPreview, LootTablePreview, ModelPreview, NoisePreview, NoiseSettingsPreview, RecipePreview, StructureSetPreview } from '../previews/index.js' export const HasPreview = ['loot_table', 'recipe', 'dimension', 'worldgen/density_function', 'worldgen/noise', 'worldgen/noise_settings', 'worldgen/configured_feature', 'worldgen/placed_feature', 'worldgen/structure_set', 'block_definition', 'model'] type PreviewPanelProps = { - model: DataModel | undefined, - version: VersionId, + model: FileModel | undefined, id: string, shown: boolean, onError: (message: string) => unknown, } -export function PreviewPanel({ model, version, id, shown }: PreviewPanelProps) { - const [, setCount] = useState(0) - - useModel(model, () => { - setCount(count => count + 1) - }) +export function PreviewPanel({ model, id, shown }: PreviewPanelProps) { + const { version } = useVersion() if (!model) return <>> - const data = model.get(new Path([])) - if (!data) return <>> if (id === 'loot_table') { - return + return } if (id === 'recipe') { - return + return } - if (id === 'dimension' && model.get(new Path(['generator', 'type']))?.endsWith('noise')) { - return + if (id === 'dimension' && model.data.generator?.type?.endsWith('noise')) { + return } if (id === 'worldgen/density_function') { - return + return } if (id === 'worldgen/noise') { - return + return } if (id === 'worldgen/noise_settings' && checkVersion(version, '1.18')) { - return + return } if ((id === 'worldgen/placed_feature' || (id === 'worldgen/configured_feature' && checkVersion(version, '1.16', '1.17')))) { - return + return } if (id === 'worldgen/structure_set' && checkVersion(version, '1.19')) { - return + return } if (id === 'block_definition') { - return + return } if (id === 'model') { - return + return } return <>> diff --git a/src/app/components/generator/ProjectPanel.tsx b/src/app/components/generator/ProjectPanel.tsx index a602809d..b468db59 100644 --- a/src/app/components/generator/ProjectPanel.tsx +++ b/src/app/components/generator/ProjectPanel.tsx @@ -1,10 +1,8 @@ -import type { DataModel } from '@mcschema/core' import { useCallback, useMemo, useRef, useState } from 'preact/hooks' import { Analytics } from '../../Analytics.js' import config from '../../Config.js' import { disectFilePath, DRAFT_PROJECT, getFilePath, useLocale, useProject, useVersion } from '../../contexts/index.js' import { useFocus } from '../../hooks/useFocus.js' -import type { VersionId } from '../../services/index.js' import { stringifySource } from '../../services/index.js' import { Store } from '../../Store.js' import { writeZip } from '../../Utils.js' @@ -15,9 +13,6 @@ import type { TreeViewGroupRenderer, TreeViewLeafRenderer } from '../TreeView.js import { TreeView } from '../TreeView.js' interface Props { - model: DataModel | undefined, - version: VersionId, - id: string, onError: (message: string) => unknown, onRename: (file: { type: string, id: string }) => unknown, onCreate: () => unknown, diff --git a/src/app/components/generator/SchemaGenerator.tsx b/src/app/components/generator/SchemaGenerator.tsx index 540af753..a1014a8f 100644 --- a/src/app/components/generator/SchemaGenerator.tsx +++ b/src/app/components/generator/SchemaGenerator.tsx @@ -1,14 +1,12 @@ -import { DataModel, Path } from '@mcschema/core' import { route } from 'preact-router' -import { useCallback, useEffect, useErrorBoundary, useMemo, useRef, useState } from 'preact/hooks' +import { useCallback, useEffect, useErrorBoundary, useMemo, useState } from 'preact/hooks' import { Analytics } from '../../Analytics.js' import type { ConfigGenerator } from '../../Config.js' import config from '../../Config.js' import { DRAFT_PROJECT, useLocale, useProject, useVersion } from '../../contexts/index.js' -import { AsyncCancel, useActiveTimeout, useAsync, useModel, useSearchParam } from '../../hooks/index.js' -import { getOutput } from '../../schema/transformOutput.js' -import type { VersionId } from '../../services/index.js' -import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet } from '../../services/index.js' +import { AsyncCancel, useActiveTimeout, useAsync, useSearchParam } from '../../hooks/index.js' +import type { FileModel, VersionId } from '../../services/index.js' +import { checkVersion, createMockFileModel, fetchPreset, fetchRegistries, getSnippet, shareSnippet } from '../../services/index.js' import { Store } from '../../Store.js' import { cleanUrl, deepEqual, genPath } from '../../Utils.js' import { Ad, Btn, BtnMenu, ErrorPanel, FileCreation, FileRenaming, Footer, HasPreview, Octicon, PreviewPanel, ProjectCreation, ProjectDeletion, ProjectPanel, SearchList, SourcePanel, TextInput, Tree, VersionSwitcher } from '../index.js' @@ -22,7 +20,7 @@ interface Props { export function SchemaGenerator({ gen, allowedVersions }: Props) { const { locale } = useLocale() const { version, changeVersion, changeTargetVersion } = useVersion() - const { projects, project, file, updateProject, updateFile, closeFile } = useProject() + const { projects, project, file, updateProject, closeFile } = useProject() const [error, setError] = useState(null) const [errorBoundary, errorRetry] = useErrorBoundary() if (errorBoundary) { @@ -34,16 +32,15 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { const [currentPreset, setCurrentPreset] = useSearchParam('preset') const [sharedSnippetId, setSharedSnippetId] = useSearchParam(SHARE_KEY) - const ignoreChange = useRef(false) const backup = useMemo(() => Store.getBackup(gen.id), [gen.id]) const loadBackup = () => { if (backup !== undefined) { - model?.reset(DataModel.wrapLists(backup), false) + // TODO: implement } } - const { value } = useAsync(async () => { + const {} = useAsync(async () => { let data: unknown = undefined if (currentPreset && sharedSnippetId) { setSharedSnippetId(undefined) @@ -81,81 +78,65 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { } data = file.data } - const [model, blockStates] = await Promise.all([ - getModel(version, gen.id), - getBlockStates(version), - ]) if (data) { - ignoreChange.current = true - model.reset(DataModel.wrapLists(data), false) + // TODO: set file contents to data } Analytics.setGenerator(gen.id) - return { model, blockStates } + return {} }, [gen.id, version, sharedSnippetId, currentPreset, project.name, file?.id]) - const model = value?.model - const blockStates = value?.blockStates + const model: FileModel = createMockFileModel() - useModel(model, model => { - if (!ignoreChange.current) { - setCurrentPreset(undefined, true) - setSharedSnippetId(undefined, true) - } - if (file && model && blockStates) { - const data = getOutput(model, blockStates) - updateFile(gen.id, file.id, { id: file.id, data }) - } - ignoreChange.current = false - Store.setBackup(gen.id, DataModel.unwrapLists(model.data)) - setError(null) - }, [gen.id, setCurrentPreset, setSharedSnippetId, blockStates, file?.id]) + // TODO: when contents of file change: + // - remove preset and share id from url + // - update project + // - store backup const reset = () => { - Analytics.resetGenerator(gen.id, model?.historyIndex ?? 1, 'menu') - model?.reset(DataModel.wrapLists(model.schema.default()), true) + Analytics.resetGenerator(gen.id, 1, 'menu') + // TODO } const undo = (e: MouseEvent) => { e.stopPropagation() - Analytics.undoGenerator(gen.id, model?.historyIndex ?? 1, 'menu') - model?.undo() + Analytics.undoGenerator(gen.id, 1, 'menu') + // TODO } const redo = (e: MouseEvent) => { e.stopPropagation() - Analytics.redoGenerator(gen.id, model?.historyIndex ?? 1, 'menu') - model?.redo() + Analytics.redoGenerator(gen.id, 1, 'menu') + // TODO } - const onKeyUp = (e: KeyboardEvent) => { - if (e.ctrlKey && e.key === 'z') { - Analytics.undoGenerator(gen.id, model?.historyIndex ?? 1, 'hotkey') - model?.undo() - } else if (e.ctrlKey && e.key === 'y') { - Analytics.redoGenerator(gen.id, model?.historyIndex ?? 1, 'hotkey') - model?.redo() - } - } - const onKeyDown = (e: KeyboardEvent) => { - if (e.ctrlKey && e.key === 's') { - setFileSaving('hotkey') - e.preventDefault() - e.stopPropagation() - } - } useEffect(() => { + const onKeyUp = (e: KeyboardEvent) => { + if (e.ctrlKey && e.key === 'z') { + Analytics.undoGenerator(gen.id, 1, 'hotkey') + // TODO + } else if (e.ctrlKey && e.key === 'y') { + Analytics.redoGenerator(gen.id, 1, 'hotkey') + // TODO + } + } + const onKeyDown = (e: KeyboardEvent) => { + if (e.ctrlKey && e.key === 's') { + setFileSaving('hotkey') + e.preventDefault() + e.stopPropagation() + } + } + document.addEventListener('keyup', onKeyUp) document.addEventListener('keydown', onKeyDown) return () => { document.removeEventListener('keyup', onKeyUp) document.removeEventListener('keydown', onKeyDown) } - }, [model, blockStates, file]) + }, [gen.id]) - const [presets, setPresets] = useState([]) - useEffect(() => { - getCollections(version).then(collections => { - setPresets(collections.get(gen.id).map(p => p.startsWith('minecraft:') ? p.slice(10) : p)) - }) - .catch(e => { console.error(e); setError(e) }) + const { value: presets } = useAsync(async () => { + const registries = await fetchRegistries(version) + const entries = registries.get(gen.id) ?? [] + return entries.map(e => e.startsWith('minecraft:') ? e.slice(10) : e) }, [version, gen.id]) const selectPreset = (id: string) => { @@ -168,13 +149,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { const loadPreset = async (id: string) => { try { const preset = await fetchPreset(version, genPath(gen, version), id) - const seed = model?.get(new Path(['generator', 'seed'])) - if (preset?.generator?.seed !== undefined && seed !== undefined) { - preset.generator.seed = seed - if (preset.generator.biome_source?.seed !== undefined) { - preset.generator.biome_source.seed = seed - } - } + // TODO: sync random seed return preset } catch (e) { setError(`Cannot load preset ${id} in ${version}`) @@ -203,14 +178,14 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { setShareUrl(`${location.origin}/${gen.url}/?version=${version}&preset=${currentPreset}`) setShareShown(true) copySharedId() - } else if (model && blockStates) { - const output = getOutput(model, blockStates) - if (deepEqual(output, model.schema.default())) { + } else { + // TODO: get contents from file, and compare to default of type + if (deepEqual(model.data, {})) { setShareUrl(`${location.origin}/${gen.url}/?version=${version}`) setShareShown(true) } else { setShareLoading(true) - shareSnippet(gen.id, version, output, previewShown) + shareSnippet(gen.id, version, model.data, previewShown) .then(({ id, length, compressed, rate }) => { Analytics.createSnippet(gen.id, id, version, length, compressed, rate) const url = `${location.origin}/${gen.url}/?${SHARE_KEY}=${id}` @@ -298,21 +273,12 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { const [projectDeleting, setprojectDeleting] = useState(false) const [fileSaving, setFileSaving] = useState(undefined) const [fileRenaming, setFileRenaming] = useState<{ type: string, id: string } | undefined>(undefined) - const [newFileQueued, setNewFileQueued] = useState(false) const onNewFile = useCallback(() => { closeFile() - // Need to queue reset because otherwise the useModel hook will update the old file - setNewFileQueued(true) + // TODO: create new file with default contents }, [closeFile]) - useEffect(() => { - if (file === undefined && newFileQueued) { - model?.reset(DataModel.wrapLists(model.schema.default()), true) - setNewFileQueued(false) - } - }, [model, newFileQueued, file]) - return <> {!gen.tags?.includes('partners') && } @@ -335,7 +301,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { {error && setError(null)} />} - + @@ -356,10 +322,10 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { - + - + @@ -371,7 +337,7 @@ export function SchemaGenerator({ gen, allowedVersions }: Props) { - setprojectDeleting(true)} onRename={setFileRenaming} onCreate={() => setProjectCreating(true)} /> + setprojectDeleting(true)} onRename={setFileRenaming} onCreate={() => setProjectCreating(true)} /> {projectCreating && setProjectCreating(false)} />} {projectDeleting && setprojectDeleting(false)} />} diff --git a/src/app/components/generator/SourcePanel.tsx b/src/app/components/generator/SourcePanel.tsx index 643618b1..809866ff 100644 --- a/src/app/components/generator/SourcePanel.tsx +++ b/src/app/components/generator/SourcePanel.tsx @@ -1,9 +1,7 @@ -import { DataModel } from '@mcschema/core' import { useCallback, useEffect, useRef, useState } from 'preact/hooks' import { useLocale } from '../../contexts/index.js' -import { useLocalStorage, useModel } from '../../hooks/index.js' -import { getOutput } from '../../schema/transformOutput.js' -import type { BlockStateRegistry } from '../../services/index.js' +import { useLocalStorage } from '../../hooks/index.js' +import type { FileModel } from '../../services/index.js' import { getSourceFormats, getSourceIndent, getSourceIndents, parseSource, sortData, stringifySource } from '../../services/index.js' import { Store } from '../../Store.js' import { message } from '../../Utils.js' @@ -18,15 +16,14 @@ interface Editor { type SourcePanelProps = { name: string, - model: DataModel | undefined, - blockStates: BlockStateRegistry | undefined, + model: FileModel | undefined, doCopy?: number, doDownload?: number, doImport?: number, copySuccess: () => unknown, onError: (message: string | Error) => unknown, } -export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) { +export function SourcePanel({ name, model, doCopy, doDownload, doImport, copySuccess, onError }: SourcePanelProps) { const { locale } = useLocale() const [indent, setIndent] = useState(Store.getIndent()) const [format, setFormat] = useState(Store.getFormat()) @@ -40,8 +37,8 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm const textarea = useRef(null) const editor = useRef() - const getSerializedOutput = useCallback((model: DataModel, blockStates: BlockStateRegistry) => { - let data = getOutput(model, blockStates) + const getSerializedOutput = useCallback((model: FileModel) => { + let data = model.data if (sort === 'alphabetically') { data = sortData(data) } @@ -51,9 +48,9 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm useEffect(() => { retransform.current = () => { if (!editor.current) return - if (!model || !blockStates) return + if (!model) return try { - const output = getSerializedOutput(model, blockStates) + const output = getSerializedOutput(model) editor.current.setValue(output) } catch (e) { if (e instanceof Error) { @@ -72,8 +69,8 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm const value = editor.current.getValue() if (value.length === 0) return try { - const data = await parseSource(value, format) - model?.reset(DataModel.wrapLists(data), false) + await parseSource(value, format) + // TODO: import } catch (e) { if (e instanceof Error) { e.message = `Error importing: ${e.message}` @@ -84,7 +81,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm console.error(e) } } - }, [model, blockStates, indent, format, sort, highlighting]) + }, [model, indent, format, sort, highlighting]) useEffect(() => { if (highlighting) { @@ -145,10 +142,7 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm } }, [highlighting]) - useModel(model, () => { - if (!retransform.current) return - retransform.current() - }) + // TODO: when file contents change, retransform useEffect(() => { if (!retransform.current) return if (model) retransform.current() @@ -163,16 +157,16 @@ export function SourcePanel({ name, model, blockStates, doCopy, doDownload, doIm }, [indent, format, sort, highlighting, braceLoaded]) useEffect(() => { - if (doCopy && model && blockStates) { - navigator.clipboard.writeText(getSerializedOutput(model, blockStates)).then(() => { + if (doCopy && model) { + navigator.clipboard.writeText(getSerializedOutput(model)).then(() => { copySuccess() }) } }, [doCopy]) useEffect(() => { - if (doDownload && model && blockStates && download.current) { - const content = encodeURIComponent(getSerializedOutput(model, blockStates)) + if (doDownload && model && download.current) { + const content = encodeURIComponent(getSerializedOutput(model)) download.current.setAttribute('href', `data:text/json;charset=utf-8,${content}`) const fileName = name === 'pack_mcmeta' ? 'pack.mcmeta' : `${name}.${format}` download.current.setAttribute('download', fileName) diff --git a/src/app/components/generator/Tree.tsx b/src/app/components/generator/Tree.tsx index 3f955c98..3b9c662f 100644 --- a/src/app/components/generator/Tree.tsx +++ b/src/app/components/generator/Tree.tsx @@ -1,19 +1,14 @@ -import type { DataModel } from '@mcschema/core' -import { useErrorBoundary, useState } from 'preact/hooks' +import { useErrorBoundary } from 'preact/hooks' import { useLocale } from '../../contexts/index.js' -import { useModel } from '../../hooks/index.js' -import { FullNode } from '../../schema/renderHtml.js' -import type { BlockStateRegistry, VersionId } from '../../services/index.js' +import type { FileModel } from '../../services/index.js' type TreePanelProps = { - version: VersionId, - model: DataModel | undefined, - blockStates: BlockStateRegistry | undefined, + model: FileModel | undefined, onError: (message: string) => unknown, } -export function Tree({ version, model, blockStates, onError }: TreePanelProps) { +export function Tree({ model, onError }: TreePanelProps) { const { lang } = useLocale() - if (!model || !blockStates || lang === 'none') return <>> + if (!model || lang === 'none') return <>> const [error] = useErrorBoundary(e => { onError(`Error rendering the tree: ${e.message}`) @@ -21,12 +16,7 @@ export function Tree({ version, model, blockStates, onError }: TreePanelProps) { }) if (error) return <>> - const [, setState] = useState(0) - useModel(model, () => { - setState(state => state + 1) - }) - return - + {/* TODO: render tree */} } diff --git a/src/app/components/previews/BiomeSourcePreview.tsx b/src/app/components/previews/BiomeSourcePreview.tsx index 63275888..4fe0bed8 100644 --- a/src/app/components/previews/BiomeSourcePreview.tsx +++ b/src/app/components/previews/BiomeSourcePreview.tsx @@ -1,8 +1,7 @@ -import { DataModel } from '@mcschema/core' import { clampedMap } from 'deepslate' import { mat3 } from 'gl-matrix' import { useCallback, useRef, useState } from 'preact/hooks' -import { getProjectData, useLocale, useProject, useStore } from '../../contexts/index.js' +import { getProjectData, useLocale, useProject, useStore, useVersion } from '../../contexts/index.js' import { useAsync } from '../../hooks/index.js' import { checkVersion } from '../../services/Schemas.js' import { Store } from '../../Store.js' @@ -21,8 +20,9 @@ type Layer = typeof LAYERS[number] const DETAIL_DELAY = 300 const DETAIL_SCALE = 2 -export const BiomeSourcePreview = ({ data, shown, version }: PreviewProps) => { +export const BiomeSourcePreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() + const { version } = useVersion() const { project } = useProject() const { biomeColors } = useStore() const [seed, setSeed] = useState(randomSeed()) @@ -31,13 +31,13 @@ export const BiomeSourcePreview = ({ data, shown, version }: PreviewProps) => { const [focused, setFocused] = useState([]) const [focused2, setFocused2] = useState([]) - const state = JSON.stringify(data) - const type: string = data?.generator?.biome_source?.type?.replace(/^minecraft:/, '') ?? '' + const state = JSON.stringify(model.data) + const type: string = model.data?.generator?.biome_source?.type?.replace(/^minecraft:/, '') ?? '' const hasRandomness = type === 'multi_noise' || type === 'the_end' const { value } = useAsync(async function loadBiomeSource() { await DEEPSLATE.loadVersion(version, getProjectData(project)) - await DEEPSLATE.loadChunkGenerator(DataModel.unwrapLists(data?.generator?.settings), DataModel.unwrapLists(data?.generator?.biome_source), seed) + await DEEPSLATE.loadChunkGenerator(model.data?.generator?.settings, model.data?.generator?.biome_source, seed) return { biomeSource: { loaded: true }, noiseRouter: checkVersion(version, '1.19') ? DEEPSLATE.getNoiseRouter() : undefined, diff --git a/src/app/components/previews/BlockStatePreview.tsx b/src/app/components/previews/BlockStatePreview.tsx index 89575fb5..adb99d4b 100644 --- a/src/app/components/previews/BlockStatePreview.tsx +++ b/src/app/components/previews/BlockStatePreview.tsx @@ -1,4 +1,3 @@ -import { DataModel } from '@mcschema/core' import { BlockDefinition, Identifier, Structure, StructureRenderer } from 'deepslate/render' import type { mat4 } from 'gl-matrix' import { useCallback, useRef } from 'preact/hooks' @@ -11,14 +10,14 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx' const PREVIEW_ID = Identifier.parse('misode:preview') -export const BlockStatePreview = ({ data, shown }: PreviewProps) => { +export const BlockStatePreview = ({ model, shown }: PreviewProps) => { const { version } = useVersion() - const serializedData = JSON.stringify(data) + const serializedData = JSON.stringify(model.data) const { value: resources } = useAsync(async () => { if (!shown) return AsyncCancel const resources = await getResources(version) - const definition = BlockDefinition.fromJson(DataModel.unwrapLists(data)) + const definition = BlockDefinition.fromJson(model.data) const wrapper = new ResourceWrapper(resources, { getBlockDefinition(id) { if (id.equals(PREVIEW_ID)) return definition diff --git a/src/app/components/previews/Decorator.ts b/src/app/components/previews/Decorator.ts index 23a26de4..f65b4c78 100644 --- a/src/app/components/previews/Decorator.ts +++ b/src/app/components/previews/Decorator.ts @@ -1,9 +1,8 @@ -import { DataModel } from '@mcschema/core' import type { BlockPos, ChunkPos, PerlinNoise, Random } from 'deepslate/worldgen' -import type { Color } from '../../Utils.js' -import { clamp, isObject, stringToColor } from '../../Utils.js' import type { VersionId } from '../../services/index.js' import { checkVersion } from '../../services/index.js' +import type { Color } from '../../Utils.js' +import { clamp, isObject, stringToColor } from '../../Utils.js' export type Placement = [BlockPos, number] @@ -38,9 +37,9 @@ export const featureColors: Color[] = [ export function decorateChunk(pos: ChunkPos, state: any, ctx: PlacementContext): PlacedFeature[] { if (checkVersion(ctx.version, undefined, '1.17')) { - getPlacements([pos[0] * 16, 0, pos[1] * 16], DataModel.unwrapLists(state), ctx) + getPlacements([pos[0] * 16, 0, pos[1] * 16], state, ctx) } else { - modifyPlacement([pos[0] * 16, 0, pos[1] * 16], DataModel.unwrapLists(state.placement), ctx) + modifyPlacement([pos[0] * 16, 0, pos[1] * 16], state.placement, ctx) } return ctx.placements.map(([pos, i]) => { diff --git a/src/app/components/previews/DecoratorPreview.tsx b/src/app/components/previews/DecoratorPreview.tsx index 9b038e0d..86031dff 100644 --- a/src/app/components/previews/DecoratorPreview.tsx +++ b/src/app/components/previews/DecoratorPreview.tsx @@ -1,7 +1,7 @@ import { BlockPos, ChunkPos, LegacyRandom, PerlinNoise } from 'deepslate' import type { mat3 } from 'gl-matrix' import { useCallback, useMemo, useRef, useState } from 'preact/hooks' -import { useLocale } from '../../contexts/index.js' +import { useLocale, useVersion } from '../../contexts/index.js' import { computeIfAbsent, iterateWorld2D, randomSeed } from '../../Utils.js' import { Btn } from '../index.js' import type { PlacedFeature, PlacementContext } from './Decorator.js' @@ -9,10 +9,11 @@ import { decorateChunk } from './Decorator.js' import type { PreviewProps } from './index.js' import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' -export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => { +export const DecoratorPreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() + const { version } = useVersion() const [seed, setSeed] = useState(randomSeed()) - const state = JSON.stringify(data) + const state = JSON.stringify(model.data) const { context, chunkFeatures } = useMemo(() => { const random = new LegacyRandom(seed) @@ -51,7 +52,7 @@ export const DecoratorPreview = ({ data, version, shown }: PreviewProps) => { iterateWorld2D(imageData.current, transform, (x, y) => { const pos = ChunkPos.create(Math.floor(x / 16), Math.floor(-y / 16)) - const features = computeIfAbsent(chunkFeatures, `${pos[0]} ${pos[1]}`, () => decorateChunk(pos, data, context)) + const features = computeIfAbsent(chunkFeatures, `${pos[0]} ${pos[1]}`, () => decorateChunk(pos, model.data, context)) return features.find(f => f.pos[0] === x && f.pos[2] == -y) ?? { pos: BlockPos.create(x, 0, -y) } }, (feature) => { if ('color' in feature) { diff --git a/src/app/components/previews/DensityFunctionPreview.tsx b/src/app/components/previews/DensityFunctionPreview.tsx index 4742e0dc..886a8541 100644 --- a/src/app/components/previews/DensityFunctionPreview.tsx +++ b/src/app/components/previews/DensityFunctionPreview.tsx @@ -1,4 +1,3 @@ -import { DataModel } from '@mcschema/core' import type { Voxel } from 'deepslate/render' import { clampedMap, VoxelRenderer } from 'deepslate/render' import type { mat3, mat4 } from 'gl-matrix' @@ -19,7 +18,7 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx' const MODES = ['side', 'top', '3d'] as const -export const DensityFunctionPreview = ({ data, shown }: PreviewProps) => { +export const DensityFunctionPreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() const { project } = useProject() const { version } = useVersion() @@ -29,11 +28,11 @@ export const DensityFunctionPreview = ({ data, shown }: PreviewProps) => { const [seed, setSeed] = useState(randomSeed()) const [minY] = useState(0) const [height] = useState(256) - const serializedData = JSON.stringify(data) + const serializedData = JSON.stringify(model.data) const { value: df } = useAsync(async () => { await DEEPSLATE.loadVersion(version, getProjectData(project)) - const df = DEEPSLATE.loadDensityFunction(DataModel.unwrapLists(data), minY, height, seed) + const df = DEEPSLATE.loadDensityFunction(model.data, minY, height, seed) return df }, [version, project, minY, height, seed, serializedData]) diff --git a/src/app/components/previews/LootTablePreview.tsx b/src/app/components/previews/LootTablePreview.tsx index 60c68fe5..1205ba96 100644 --- a/src/app/components/previews/LootTablePreview.tsx +++ b/src/app/components/previews/LootTablePreview.tsx @@ -1,4 +1,3 @@ -import { DataModel } from '@mcschema/core' import { Identifier } from 'deepslate' import { useMemo, useRef, useState } from 'preact/hooks' import { useLocale, useVersion } from '../../contexts/index.js' @@ -12,7 +11,7 @@ import type { PreviewProps } from './index.js' import { generateLootTable } from './LootTable.js' import { generateLootTable as generateLootTable1204 } from './LootTable1204.js' -export const LootTablePreview = ({ data }: PreviewProps) => { +export const LootTablePreview = ({ model }: PreviewProps) => { const { locale } = useLocale() const { version } = useVersion() const use1204 = !checkVersion(version, '1.20.5') @@ -35,7 +34,7 @@ export const LootTablePreview = ({ data }: PreviewProps) => { ]) }, [version]) - const table = DataModel.unwrapLists(data) + const table = model.data const state = JSON.stringify(table) const items = useMemo(() => { if (dependencies === undefined || loading) { diff --git a/src/app/components/previews/ModelPreview.tsx b/src/app/components/previews/ModelPreview.tsx index 9c440f32..0b7b31c4 100644 --- a/src/app/components/previews/ModelPreview.tsx +++ b/src/app/components/previews/ModelPreview.tsx @@ -1,4 +1,3 @@ -import { DataModel } from '@mcschema/core' import { BlockDefinition, BlockModel, Identifier, Structure, StructureRenderer } from 'deepslate/render' import type { mat4 } from 'gl-matrix' import { useCallback, useRef } from 'preact/hooks' @@ -12,22 +11,22 @@ import { InteractiveCanvas3D } from './InteractiveCanvas3D.jsx' const PREVIEW_ID = Identifier.parse('misode:preview') const PREVIEW_DEFINITION = new BlockDefinition({ '': { model: PREVIEW_ID.toString() }}, undefined) -export const ModelPreview = ({ data, shown }: PreviewProps) => { +export const ModelPreview = ({ model, shown }: PreviewProps) => { const { version } = useVersion() - const serializedData = JSON.stringify(data) + const serializedData = JSON.stringify(model.data) const { value: resources } = useAsync(async () => { if (!shown) return AsyncCancel const resources = await getResources(version) - const model = BlockModel.fromJson(DataModel.unwrapLists(data)) - model.flatten(resources) + const blockModel = BlockModel.fromJson(model.data) + blockModel.flatten(resources) const wrapper = new ResourceWrapper(resources, { getBlockDefinition(id) { if (id.equals(PREVIEW_ID)) return PREVIEW_DEFINITION return null }, getBlockModel(id) { - if (id.equals(PREVIEW_ID)) return model + if (id.equals(PREVIEW_ID)) return blockModel return null }, }) diff --git a/src/app/components/previews/NoisePreview.tsx b/src/app/components/previews/NoisePreview.tsx index 097d83f2..7d12be0b 100644 --- a/src/app/components/previews/NoisePreview.tsx +++ b/src/app/components/previews/NoisePreview.tsx @@ -1,4 +1,3 @@ -import { DataModel } from '@mcschema/core' import { clampedMap, NoiseParameters, NormalNoise, XoroshiroRandom } from 'deepslate' import type { mat3 } from 'gl-matrix' import { useCallback, useMemo, useRef, useState } from 'preact/hooks' @@ -12,14 +11,14 @@ import { ColormapSelector } from './ColormapSelector.jsx' import type { PreviewProps } from './index.js' import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' -export const NoisePreview = ({ data, shown }: PreviewProps) => { +export const NoisePreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() const [seed, setSeed] = useState(randomSeed()) - const state = JSON.stringify(data) + const state = JSON.stringify(model.data) const noise = useMemo(() => { const random = XoroshiroRandom.create(seed) - const params = NoiseParameters.fromJson(DataModel.unwrapLists(data)) + const params = NoiseParameters.fromJson(model.data) return new NormalNoise(random, params) }, [state, seed]) diff --git a/src/app/components/previews/NoiseSettingsPreview.tsx b/src/app/components/previews/NoiseSettingsPreview.tsx index a75a25e3..e8752b69 100644 --- a/src/app/components/previews/NoiseSettingsPreview.tsx +++ b/src/app/components/previews/NoiseSettingsPreview.tsx @@ -1,31 +1,31 @@ -import { DataModel } from '@mcschema/core' import { clampedMap } from 'deepslate' import type { mat3 } from 'gl-matrix' import { vec2 } from 'gl-matrix' -import { useCallback, useMemo, useRef, useState } from 'preact/hooks' +import { useCallback, useRef, useState } from 'preact/hooks' +import { getProjectData, useLocale, useProject, useVersion } from '../../contexts/index.js' +import { useAsync } from '../../hooks/index.js' +import { fetchRegistries } from '../../services/index.js' import { Store } from '../../Store.js' import { iterateWorld2D, randomSeed } from '../../Utils.js' -import { getProjectData, useLocale, useProject } from '../../contexts/index.js' -import { useAsync } from '../../hooks/index.js' -import { CachedCollections } from '../../services/index.js' import { Btn, BtnInput, BtnMenu, ErrorPanel } from '../index.js' import type { ColormapType } from './Colormap.js' import { getColormap } from './Colormap.js' import { ColormapSelector } from './ColormapSelector.jsx' import { DEEPSLATE } from './Deepslate.js' -import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' import type { PreviewProps } from './index.js' +import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' -export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => { +export const NoiseSettingsPreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() + const { version } = useVersion() const { project } = useProject() const [seed, setSeed] = useState(randomSeed()) const [biome, setBiome] = useState('minecraft:plains') const [layer, setLayer] = useState('terrain') - const state = JSON.stringify(data) + const state = JSON.stringify(model.data) const { value, error } = useAsync(async () => { - const unwrapped = DataModel.unwrapLists(data) + const unwrapped = model.data await DEEPSLATE.loadVersion(version, getProjectData(project)) const biomeSource = { type: 'fixed', biome } await DEEPSLATE.loadChunkGenerator(unwrapped, biomeSource, seed) @@ -86,7 +86,10 @@ export const NoiseSettingsPreview = ({ data, shown, version }: PreviewProps) => } }, [noiseSettings, finalDensity]) - const allBiomes = useMemo(() => CachedCollections?.get('worldgen/biome') ?? [], [version]) + const { value: allBiomes } = useAsync(async () => { + const registries = await fetchRegistries(version) + return registries.get('worldgen/biome') + }, [version]) if (error) { return diff --git a/src/app/components/previews/RecipePreview.tsx b/src/app/components/previews/RecipePreview.tsx index 9eaccba8..2451ca1b 100644 --- a/src/app/components/previews/RecipePreview.tsx +++ b/src/app/components/previews/RecipePreview.tsx @@ -1,7 +1,6 @@ -import { DataModel } from '@mcschema/core' import { Identifier, ItemStack } from 'deepslate' import { useEffect, useMemo, useRef, useState } from 'preact/hooks' -import { useLocale } from '../../contexts/index.js' +import { useLocale, useVersion } from '../../contexts/index.js' import { useAsync } from '../../hooks/useAsync.js' import type { VersionId } from '../../services/index.js' import { checkVersion, fetchAllPresets } from '../../services/index.js' @@ -12,8 +11,9 @@ import type { PreviewProps } from './index.js' const ANIMATION_TIME = 1000 -export const RecipePreview = ({ data, version }: PreviewProps) => { +export const RecipePreview = ({ model }: PreviewProps) => { const { locale } = useLocale() + const { version } = useVersion() const [advancedTooltips, setAdvancedTooltips] = useState(true) const [animation, setAnimation] = useState(0) const overlay = useRef(null) @@ -29,7 +29,7 @@ export const RecipePreview = ({ data, version }: PreviewProps) => { return () => clearInterval(interval) }, []) - const recipe = DataModel.unwrapLists(data) + const recipe = model.data const state = JSON.stringify(recipe) const items = useMemo>(() => { return placeItems(version, recipe, animation, itemTags ?? new Map()) diff --git a/src/app/components/previews/StructureSetPreview.tsx b/src/app/components/previews/StructureSetPreview.tsx index 451d563c..80f10b56 100644 --- a/src/app/components/previews/StructureSetPreview.tsx +++ b/src/app/components/previews/StructureSetPreview.tsx @@ -1,26 +1,26 @@ -import { DataModel } from '@mcschema/core' import type { Identifier } from 'deepslate' import { ChunkPos } from 'deepslate' import type { mat3 } from 'gl-matrix' import { useCallback, useMemo, useRef, useState } from 'preact/hooks' +import { useLocale, useVersion } from '../../contexts/index.js' +import { useAsync } from '../../hooks/useAsync.js' import type { Color } from '../../Utils.js' import { computeIfAbsent, iterateWorld2D, randomSeed, stringToColor } from '../../Utils.js' -import { useLocale } from '../../contexts/index.js' -import { useAsync } from '../../hooks/useAsync.js' import { Btn } from '../index.js' import { featureColors } from './Decorator.js' import { DEEPSLATE } from './Deepslate.js' -import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' import type { PreviewProps } from './index.js' +import { InteractiveCanvas2D } from './InteractiveCanvas2D.jsx' -export const StructureSetPreview = ({ data, version, shown }: PreviewProps) => { +export const StructureSetPreview = ({ model, shown }: PreviewProps) => { const { locale } = useLocale() + const { version } = useVersion() const [seed, setSeed] = useState(randomSeed()) - const state = JSON.stringify(data) + const state = JSON.stringify(model.data) const { value: structureSet } = useAsync(async () => { await DEEPSLATE.loadVersion(version) - const structureSet = DEEPSLATE.loadStructureSet(DataModel.unwrapLists(data), seed) + const structureSet = DEEPSLATE.loadStructureSet(model.data, seed) return structureSet }, [state, version, seed]) diff --git a/src/app/components/previews/index.ts b/src/app/components/previews/index.ts index abadf129..b139e825 100644 --- a/src/app/components/previews/index.ts +++ b/src/app/components/previews/index.ts @@ -1,5 +1,4 @@ -import type { DataModel } from '@mcschema/core' -import type { VersionId } from '../../services/index.js' +import type { FileModel } from '../../services/index.js' export * from './BiomeSourcePreview.js' export * from './BlockStatePreview.jsx' @@ -12,9 +11,7 @@ export * from './NoiseSettingsPreview.js' export * from './RecipePreview.jsx' export * from './StructureSetPreview.jsx' -export type PreviewProps = { - model: DataModel, - data: any, - shown: boolean, - version: VersionId, +export interface PreviewProps { + model: FileModel + shown: boolean } diff --git a/src/app/contexts/Locale.tsx b/src/app/contexts/Locale.tsx index 1b40eb5f..596253a5 100644 --- a/src/app/contexts/Locale.tsx +++ b/src/app/contexts/Locale.tsx @@ -46,13 +46,7 @@ async function loadLocale(language: string) { const langConfig = config.languages.find(lang => lang.code === language) if (!langConfig) return const data = await import(`../../locales/${language}.json`) - const schema = langConfig.schemas !== false - && await import(`../../../node_modules/@mcschema/locales/src/${language}.json`) - let partners = { default: {} } - if (language === 'en') { - partners = await import('../partners/locales/en.json') - } - Locales[language] = { ...data.default, ...schema.default, ...partners.default } + Locales[language] = data.default } export function useLocale() { diff --git a/src/app/hooks/index.ts b/src/app/hooks/index.ts index ce5d2c4b..449eb0dd 100644 --- a/src/app/hooks/index.ts +++ b/src/app/hooks/index.ts @@ -5,6 +5,5 @@ export * from './useFocus.js' export * from './useHash.js' export * from './useLocalStorage.js' export * from './useMediaQuery.js' -export * from './useModel.js' export * from './useSearchParam.js' export * from './useTags.js' diff --git a/src/app/hooks/useModel.ts b/src/app/hooks/useModel.ts deleted file mode 100644 index 4567deb8..00000000 --- a/src/app/hooks/useModel.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { DataModel } from '@mcschema/core' -import type { Inputs } from 'preact/hooks' -import { useEffect } from 'preact/hooks' - -export function useModel(model: DataModel | undefined | null, invalidated: (model: DataModel) => unknown, inputs?: Inputs) { - const listener = { - invalidated() { - if (model) { - invalidated(model) - } - }, - } - - useEffect(() => { - model?.addListener(listener) - return () => { - model?.removeListener(listener) - } - }, [model, ...inputs ?? []]) -} diff --git a/src/app/partners/ImmersiveWeathering.ts b/src/app/partners/ImmersiveWeathering.ts deleted file mode 100644 index ac3da960..00000000 --- a/src/app/partners/ImmersiveWeathering.ts +++ /dev/null @@ -1,210 +0,0 @@ -import type { CollectionRegistry, ResourceType, SchemaRegistry } from '@mcschema/core' -import { BooleanNode, Case, ChoiceNode, ListNode, MapNode, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core' - -const ID = 'immersive_weathering' - -export function initImmersiveWeathering(schemas: SchemaRegistry, collections: CollectionRegistry) { - const Reference = RawReference.bind(undefined, schemas) - const StringNode = RawStringNode.bind(undefined, collections) - - const Tag = (id: Exclude) => ChoiceNode([ - { - type: 'string', - node: StringNode({ validator: 'resource', params: { pool: id, allowTag: true } }), - change: (v: unknown) => { - if (Array.isArray(v) && typeof v[0] === 'string' && !v[0].startsWith('#')) { - return v[0] - } - return undefined - }, - }, - { - type: 'list', - node: ListNode( - StringNode({ validator: 'resource', params: { pool: id } }) - ), - change: (v: unknown) => { - if (typeof v === 'string' && !v.startsWith('#')) { - return [v] - } - return [] - }, - }, - ], { choiceContext: 'tag' }) - - schemas.register(`${ID}:block_growth`, ObjectNode({ - area_condition: Reference(`${ID}:area_condition`), - position_predicates: Opt(ListNode( - Reference(`${ID}:position_test`) - )), - growth_chance: NumberNode({ min: 0, max: 1 }), - growth_for_face: ListNode( - ObjectNode({ - direction: Opt(StringNode({ enum: 'direction' })), - weight: Opt(NumberNode({ integer: true })), - growth: ListNode( - ObjectNode({ - data: Reference(`${ID}:block_pair`), - weight: NumberNode({ integer: true }), - }) - ), - }, { category: 'pool' }) - ), - owners: ListNode( - StringNode({ validator: 'resource', params: { pool: 'block' } }) - ), - replacing_target: Reference(`${ID}:rule_test`), - target_self: Opt(BooleanNode()), - destroy_target: Opt(BooleanNode()), - }, { context: `${ID}.block_growth` })) - - schemas.register(`${ID}:area_condition`, ObjectNode({ - type: StringNode({ enum: ['generate_if_not_too_many', 'neighbor_based_generation'] }), - [Switch]: [{ push: 'type' }], - [Case]: { - generate_if_not_too_many: { - radiusX: NumberNode({ integer: true }), - radiusY: NumberNode({ integer: true }), - radiusZ: NumberNode({ integer: true }), - requiredAmount: NumberNode({ integer: true }), - yOffset: Opt(NumberNode({ integer: true })), - must_have: Opt(Reference(`${ID}:rule_test`)), - must_not_have: Opt(Reference(`${ID}:rule_test`)), - includes: Opt(Tag('block')), - }, - neighbor_based_generation: { - must_have: Reference(`${ID}:rule_test`), - must_not_have: Opt(Reference(`${ID}:rule_test`)), - required_amount: Opt(NumberNode({ integer: true })), - directions: ListNode( - StringNode({ enum: 'direction' }) - ), - }, - }, - }, { context: `${ID}.area_condition` })) - - schemas.register(`${ID}:block_pair`, ObjectNode({ - block: Reference(`${ID}:block_state`), - above_block: Opt(Reference(`${ID}:block_state`)), - }, { context: `${ID}.block_pair` })) - - schemas.register(`${ID}:block_state`, ObjectNode({ - Name: StringNode({ validator: 'resource', params: { pool: 'block' } }), - Properties: Opt(MapNode( - StringNode(), - StringNode(), - )), - }, { context: 'block_state' })) - - schemas.register(`${ID}:position_test`, ObjectNode({ - predicate_type: StringNode({ enum: ['biome_match', 'day_test', 'nand', 'precipitation_test', 'temperature_range'] }), - [Switch]: [{ push: 'predicate_type' }], - [Case]: { - biome_match: { - biomes: Tag('$worldgen/biome'), - }, - day_test: { - day: BooleanNode(), - }, - nand: { - predicates: ListNode( - Reference(`${ID}:position_test`) - ), - }, - precipitation_test: { - precipitation: StringNode({ enum: ['none', 'rain', 'snow']}), - }, - temperature_range: { - min: NumberNode(), - max: NumberNode(), - use_local_pos: Opt(BooleanNode()), - }, - }, - }, { context: `${ID}.position_test`, category: 'predicate' })) - - collections.register(`${ID}:rule_test`, [ - ...collections.get('rule_test'), - 'immersive_weathering:block_set_match', - 'immersive_weathering:fluid_match', - 'immersive_weathering:tree_log', - ]) - - schemas.register(`${ID}:rule_test`, ObjectNode({ - predicate_type: StringNode({ validator: 'resource', params: { pool: `${ID}:rule_test` as any } }), - [Switch]: [{ push: 'predicate_type' }], - [Case]: { - 'minecraft:block_match': { - block: StringNode({ validator: 'resource', params: { pool: 'block' } }), - }, - 'minecraft:blockstate_match': { - block_state: Reference('block_state'), - }, - 'minecraft:random_block_match': { - block: StringNode({ validator: 'resource', params: { pool: 'block' } }), - probability: NumberNode({ min: 0, max: 1 }), - }, - 'minecraft:random_blockstate_match': { - block_state: Reference('block_state'), - probability: NumberNode({ min: 0, max: 1 }), - }, - 'minecraft:tag_match': { - tag: StringNode({ validator: 'resource', params: { pool: '$tag/block' }}), - }, - 'immersive_weathering:block_set_match': { - blocks: Tag('block'), - probability: Opt(NumberNode({ min: 0, max: 1 })), - }, - 'immersive_weathering:fluid_match': { - fluid: StringNode({ validator: 'resource', params: { pool: 'fluid' } }), - }, - }, - }, { context: 'rule_test', disableSwitchContext: true })) - - collections.register('block_growth', [ - 'immersive_weathering:brain_coral', - 'immersive_weathering:bubble_coral', - 'immersive_weathering:cracked_mud_rivers', - 'immersive_weathering:crimson_nylium', - 'immersive_weathering:cryosol', - 'immersive_weathering:farmland_rare_weeds', - 'immersive_weathering:farmland_weeds', - 'immersive_weathering:fire_coral', - 'immersive_weathering:fire_soot', - 'immersive_weathering:fluvisol', - 'immersive_weathering:grass_base', - 'immersive_weathering:grass_block_badlands', - 'immersive_weathering:grass_block_bamboo_jungle', - 'immersive_weathering:grass_block_birch_forest', - 'immersive_weathering:grass_block_dark_forest', - 'immersive_weathering:grass_block_flower_forest', - 'immersive_weathering:grass_block_forest', - 'immersive_weathering:grass_block_jungle', - 'immersive_weathering:grass_block_lush_caves', - 'immersive_weathering:grass_block_old_growth_spruce', - 'immersive_weathering:grass_block_plains', - 'immersive_weathering:grass_block_sunflower_plains', - 'immersive_weathering:grass_block_swamp', - 'immersive_weathering:grass_block_taiga', - 'immersive_weathering:grass_block_wooded_badlands', - 'immersive_weathering:hanging_roots', - 'immersive_weathering:horn_coral', - 'immersive_weathering:humus', - 'immersive_weathering:icicle_growth', - 'immersive_weathering:large_fern', - 'immersive_weathering:magma', - 'immersive_weathering:mycelium', - 'immersive_weathering:podzol', - 'immersive_weathering:red_sand_weathering', - 'immersive_weathering:rooted_dirt', - 'immersive_weathering:rooted_grass', - 'immersive_weathering:sand_weathering', - 'immersive_weathering:sapling', - 'immersive_weathering:sapling_nether', - 'immersive_weathering:silt', - 'immersive_weathering:tall_grass', - 'immersive_weathering:tall_seagrass', - 'immersive_weathering:tube_coral', - 'immersive_weathering:vertisol', - 'immersive_weathering:warped_nylium', - ]) -} diff --git a/src/app/partners/Lithostitched.ts b/src/app/partners/Lithostitched.ts deleted file mode 100644 index 675ee272..00000000 --- a/src/app/partners/Lithostitched.ts +++ /dev/null @@ -1,239 +0,0 @@ -import type { CollectionRegistry, ResourceType, SchemaRegistry } from '@mcschema/core' -import { BooleanNode, Case, ChoiceNode, ListNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core' -import type { VersionId } from '../services/Schemas.js' - - -const ID = 'lithostitched' - -export function initLithostitched(schemas: SchemaRegistry, collections: CollectionRegistry, version: VersionId) { - const Reference = RawReference.bind(undefined, schemas) - const StringNode = RawStringNode.bind(undefined, collections) - - const Tag = (id: Exclude) => - ChoiceNode( - [ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: id, allowTag: true }, - }), - change: (v: unknown) => { - if ( - Array.isArray(v) && - typeof v[0] === 'string' && - !v[0].startsWith('#') - ) { - return v[0] - } - return undefined - }, - }, - { - type: 'list', - node: ListNode( - StringNode({ validator: 'resource', params: { pool: id } }) - ), - change: (v: unknown) => { - if (typeof v === 'string' && !v.startsWith('#')) { - return [v] - } - return [] - }, - }, - ], - { choiceContext: 'tag' } - ) - - // Worldgen Modifiers - const MobCategorySpawnSettings = Mod( - ObjectNode({ - type: StringNode({ - validator: 'resource', - params: { pool: 'entity_type' }, - }), - weight: NumberNode({ integer: true }), - minCount: NumberNode({ integer: true }), - maxCount: NumberNode({ integer: true }), - }), - { - category: () => 'pool', - default: () => [ - { - type: 'minecraft:bat', - weight: 1, - }, - ], - } - ) - - collections.register(`${ID}:modifier_type`, [ - 'lithostitched:add_biome_spawns', - 'lithostitched:add_features', - ...(version === '1.20.5' || version === '1.21') - ? ['lithostitched:add_pool_aliases'] - : [], - 'lithostitched:add_structure_set_entries', - 'lithostitched:add_surface_rule', - 'lithostitched:add_template_pool_elements', - 'lithostitched:no_op', - 'lithostitched:redirect_feature', - 'lithostitched:remove_biome_spawns', - 'lithostitched:remove_features', - 'lithostitched:remove_structures_from_structure_set', - 'lithostitched:replace_climate', - 'lithostitched:replace_effects', - ]) - - collections.register(`${ID}:modifier_predicate_type`, [ - 'lithostitched:all_of', - 'lithostitched:any_of', - 'lithostitched:mod_loaded', - 'lithostitched:not', - 'lithostitched:true', - ]) - - schemas.register(`${ID}:worldgen_modifier`, Mod(ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: `${ID}:modifier_type` as any } }), - predicate: Mod(Opt(Reference(`${ID}:modifier_predicate`)), { - enabled: () => version !== '1.21', - }), - [Switch]: [{ push: 'type' }], - [Case]: { - 'lithostitched:add_biome_spawns': { - biomes: Tag('$worldgen/biome'), - spawners: ChoiceNode([ - { - type: 'object', - node: MobCategorySpawnSettings, - change: (v: any) => v[0], - }, - { - type: 'list', - node: ListNode(MobCategorySpawnSettings), - change: (v: any) => Array(v), - }, - ]), - }, - 'lithostitched:add_features': { - biomes: Tag('$worldgen/biome'), - features: Tag('$worldgen/configured_feature'), - step: StringNode({ enum: 'decoration_step' }), - }, - 'lithostitched:add_pool_aliases': { - structure: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } }), - pool_aliases: Reference('pool_alias_binding'), - }, - 'lithostitched:add_structure_set_entries': { - structure_set: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure_set' } }), - entries: ListNode( - ObjectNode({ - structure: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } }), - weight: NumberNode({ integer: true, min: 1 }), - }) - ), - }, - 'lithostitched:add_surface_rule': { - levels: ListNode(StringNode({ validator: 'resource', params: { pool: '$dimension' } })), - surface_rule: Reference('material_rule'), - }, - 'lithostitched:add_template_pool_elements': { - template_pool: StringNode({ validator: 'resource', params: { pool: '$worldgen/template_pool' } }), - elements: ListNode( - Reference('template_weighted_element') - ), - }, - 'lithostitched:redirect_feature': { - placed_feature: StringNode({ validator: 'resource', params: { pool: '$worldgen/placed_feature' } }), - redirect_to: StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_feature' } }), - }, - 'lithostitched:remove_biome_spawns': { - biomes: Tag('$worldgen/biome'), - mobs: Tag('entity_type'), - }, - 'lithostitched:remove_features': { - biomes: Tag('$worldgen/biome'), - features: Tag('$worldgen/configured_feature'), - step: StringNode({ enum: 'decoration_step' }), - }, - 'lithostitched:remove_structures_from_structure_set': { - structure_set: StringNode({ validator: 'resource', params: { pool: '$worldgen/structure_set' } }), - structures: ListNode( - StringNode({ validator: 'resource', params: { pool: '$worldgen/structure' } }) - ), - }, - 'lithostitched:replace_climate': { - biomes: Tag('$worldgen/biome'), - climate: ObjectNode({ - temperature: NumberNode(), - downfall: NumberNode(), - has_precipitation: BooleanNode(), - temperature_modifier: Opt(StringNode({ enum: ['none', 'frozen'] })), - }), - }, - 'lithostitched:replace_effects': { - biomes: Tag('$worldgen/biome'), - effects: ObjectNode({ - sky_color: Opt(NumberNode({ color: true })), - fog_color: Opt(NumberNode({ color: true })), - water_color: Opt(NumberNode({ color: true })), - water_fog_color: Opt(NumberNode({ color: true })), - grass_color: Opt(NumberNode({ color: true })), - foliage_color: Opt(NumberNode({ color: true })), - grass_color_modifier: Opt(StringNode({ enum: ['none', 'dark_forest', 'swamp'] })), - ambient_sound: Opt(StringNode()), - mood_sound: Opt(ObjectNode({ - sound: StringNode(), - tick_delay: NumberNode({ integer: true }), - block_search_extent: NumberNode({ integer: true }), - offset: NumberNode(), - })), - additions_sound: Opt(ObjectNode({ - sound: StringNode(), - tick_chance: NumberNode({ min: 0, max: 1 }), - })), - music: Opt(ObjectNode({ - sound: StringNode(), - min_delay: NumberNode({ integer: true, min: 0 }), - max_delay: NumberNode({ integer: true, min: 0 }), - replace_current_music: BooleanNode(), - })), - particle: Opt(ObjectNode({ - options: ObjectNode({ - type: StringNode(), - }), - probability: NumberNode({ min: 0, max: 1 }), - })), - }), - }, - }, - }, { context: `${ID}.worldgen_modifier`, disableSwitchContext: true }), { - default: () => ({ - type: `${ID}:add_features`, - biomes: '#minecraft:is_overworld', - features: 'example:ore_ruby', - step: 'underground_ores', - }), - })) - - schemas.register(`${ID}:modifier_predicate`, ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: `${ID}:modifier_predicate_type` as any } }), - [Switch]: [{ push: 'type' }], - [Case]: { - 'lithostitched:all_of': { - predicates: ListNode(Reference(`${ID}:modifier_predicate`)), - }, - 'lithostitched:any_of': { - predicates: ListNode(Reference(`${ID}:modifier_predicate`)), - }, - 'lithostitched:mod_loaded': { - mod_id: StringNode(), - }, - 'lithostitched:not': { - predicate: Reference(`${ID}:modifier_predicate`), - }, - }, - }, { - context: `${ID}.modifier_predicate`, disableSwitchContext: true, - })) -} diff --git a/src/app/partners/NeoForge.ts b/src/app/partners/NeoForge.ts deleted file mode 100644 index 5048df7c..00000000 --- a/src/app/partners/NeoForge.ts +++ /dev/null @@ -1,492 +0,0 @@ -import type { CollectionRegistry, INode, ResourceType, SchemaRegistry } from '@mcschema/core' -import { BooleanNode, Case, ChoiceNode, ListNode, MapNode, Mod, NumberNode, ObjectNode, Opt, StringNode as RawStringNode, Switch } from '@mcschema/core' -import type { VersionId } from '../services/Schemas.js' - -const ID = 'neoforge' - -export function initNeoForge(schemas: SchemaRegistry, collections: CollectionRegistry, _version: VersionId) { - const StringNode = RawStringNode.bind(undefined, collections) - - // Homogenous list (ref, list of refs, tag) - const Tag = (id: Exclude) => - ChoiceNode( - [ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: id, allowTag: true }, - }), - change: (v: unknown) => { - if ( - Array.isArray(v) && - typeof v[0] === 'string' && - !v[0].startsWith('#') - ) { - return v[0] - } - return undefined - }, - }, - { - type: 'list', - node: ListNode( - StringNode({ validator: 'resource', params: { pool: id } }) - ), - change: (v: unknown) => { - if (typeof v === 'string' && !v.startsWith('#')) { - return [v] - } - return [] - }, - }, - ], - { choiceContext: 'tag' } - ) - - // Spawner data - const MobCategorySpawnSettings = Mod( - ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: 'entity_type' }}), - weight: NumberNode({ integer: true, min: 0 }), - minCount: NumberNode({ integer: true, min: 1 }), - maxCount: NumberNode({ integer: true, min: 1 }), - }), - { - category: () => 'pool', - default: () => [ - { - type: 'minecraft:bat', - weight: 1, - }, - ], - } - ) - - // Generation step carving - const CarvingStep = StringNode({ enum: [ 'air', 'liquid' ] }) - - // Mob category - const MobCategory = StringNode({ enum: [ 'monster', 'creature', 'ambient', 'axolotls', 'underground_water_creature', 'water_creature', 'water_ambient', 'misc' ], additional: true }) - - // Biome modifier types - collections.register(`${ID}:biome_modifier_type`, [ - 'neoforge:none', - 'neoforge:add_features', - 'neoforge:remove_features', - 'neoforge:add_spawns', - 'neoforge:remove_spawns', - 'neoforge:add_carvers', - 'neoforge:remove_carvers', - 'neoforge:add_spawn_costs', - 'neoforge:remove_spawn_costs', - ]) - - // Biome modifiers - schemas.register(`${ID}:biome_modifier`, Mod( - ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: `${ID}:biome_modifier_type` as any }}), - [Switch]: [{ push: 'type' }], - [Case]: { - 'neoforge:none': {}, - 'neoforge:add_features': { - biomes: Tag('$worldgen/biome'), - features: Tag('$worldgen/placed_feature'), - step: StringNode({ enum: 'decoration_step' }), - }, - 'neoforge:remove_features': { - biomes: Tag('$worldgen/biome'), - features: Tag('$worldgen/placed_feature'), - steps: Opt(ChoiceNode([ - { - type: 'string', - node: StringNode({ enum: 'decoration_step' }), - change: (v: any) => v[0], - }, - { - type: 'list', - node: ListNode(StringNode({ enum: 'decoration_step' })), - change: (v: any) => Array(v), - }, - ])), - }, - 'neoforge:add_spawns': { - biomes: Tag('$worldgen/biome'), - spawners: ChoiceNode([ - { - type: 'object', - node: MobCategorySpawnSettings, - change: (v: any) => v[0], - }, - { - type: 'list', - node: MobCategorySpawnSettings, - change: (v: any) => Array(v), - }, - ]), - }, - 'neoforge:remove_spawns': { - biomes: Tag('$worldgen/biome'), - entity_types: Tag('entity_type'), - }, - 'neoforge:add_carvers': { - biomes: Tag('$worldgen/biome'), - carvers: Tag('$worldgen/configured_carver'), - step: CarvingStep, - }, - 'neoforge:remove_carvers': { - biomes: Tag('$worldgen/biome'), - carvers: Tag('$worldgen/configured_carver'), - steps: Opt(ChoiceNode([ - { - type: 'string', - node: CarvingStep, - change: (v: any) => v[0], - }, - { - type: 'list', - node: ListNode(CarvingStep), - change: (v: any) => Array(v), - }, - ])), - }, - 'neoforge:add_spawn_costs': { - biomes: Tag('$worldgen/biome'), - entity_types: Tag('entity_type'), - spawn_cost: ObjectNode({ - energy_budget: NumberNode(), - charge: NumberNode(), - }), - }, - 'neoforge:remove_spawn_costs': { - biomes: Tag('$worldgen/biome'), - entity_types: Tag('entity_type'), - }, - }, - }, { context: `${ID}.biome_modifier`, disableSwitchContext: true }), - { - default: () => ({ - type: `${ID}:add_features`, - biomes: '#minecraft:is_overworld', - features: 'minecraft:ore_iron_small', - step: 'underground_ores', - }), - } - )) - - // Structure modifier types - collections.register(`${ID}:structure_modifier_type`, [ - 'neoforge:none', - 'neoforge:add_spawns', - 'neoforge:remove_spawns', - 'neoforge:clear_spawns', - ]) - - // Structure modifiers - schemas.register(`${ID}:structure_modifier`, Mod( - ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: `${ID}:structure_modifier_type` as any }}), - [Switch]: [{ push: 'type' }], - [Case]: { - 'neoforge:none': {}, - 'neoforge:add_spawns': { - structures: Tag('$worldgen/structure'), - spawners: ChoiceNode([ - { - type: 'object', - node: MobCategorySpawnSettings, - change: (v: any) => v[0], - }, - { - type: 'list', - node: MobCategorySpawnSettings, - change: (v: any) => Array(v), - }, - ]), - }, - 'neoforge:remove_spawns': { - structures: Tag('$worldgen/structure'), - entity_types: Tag('entity_type'), - }, - 'neoforge:clear_spawns': { - structures: Tag('$worldgen/structure'), - categories: ChoiceNode([ - { - type: 'string', - node: MobCategory, - change: (v: any) => v[0], - }, - { - type: 'list', - node: MobCategory, - change: (v: any) => Array(v), - }, - ]), - }, - }, - }, { context: `${ID}.structure_modifier`, disableSwitchContext: true }), - { - default: () => ({ - type: `${ID}:add_spawns`, - structures: '#minecraft:village', - spawners: { - type: 'minecraft:bat', - weight: 1, - }, - }), - } - )) - - // Data maps - createDataMap(schemas, collections, 'compostables', 'item', ChoiceNode([ - { - type: 'number', - node: NumberNode({ - min: 0, - max: 1, - }), - change: (v: any) => v?.chance, - }, - { - type: 'object', - node: ObjectNode({ - chance: NumberNode({ - min: 0, - max: 1, - }), - can_villager_compost: Opt(BooleanNode()), - }), - change: (v: any) => ({ - chance: v, - can_villager_compost: false, - }), - }, - ]), (values) => values['minecraft:apple'] = { - chance: 1, - can_villager_compost: true, - } - ) - createDataMap(schemas, collections, 'furnace_fuels', 'item', ChoiceNode([ - { - type: 'number', - node: NumberNode({ - min: 1, - integer: true, - }), - change: (v: any) => v?.burn_time, - }, - { - type: 'object', - node: ObjectNode({ - burn_time: NumberNode({ - min: 1, - integer: true, - }), - }), - change: (v: any) => ({ - burn_time: v, - }), - }, - ]), (values) => values['minecraft:chest'] = { - burn_time: 300, - } - ) - createDataMap(schemas, collections, 'monster_room_mobs', 'entity_type', ChoiceNode([ - { - type: 'number', - node: NumberNode({ - min: 0, - integer: true, - }), - change: (v: any) => v?.weight, - }, - { - type: 'object', - node: ObjectNode({ - weight: NumberNode({ - min: 0, - integer: true, - }), - }), - change: (v: any) => ({ - weight: v, - }), - }, - ]), (values) => values['minecraft:bat'] = { - weight: 5, - }) - createDataMap(schemas, collections, 'oxidizables', 'block', ChoiceNode([ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: 'block' }, - }), - change: (v: any) => v?.next_oxidation_stage, - }, - { - type: 'object', - node: ObjectNode({ - next_oxidation_stage: StringNode({ - validator: 'resource', - params: { pool: 'block' }, - }), - }), - change: (v: any) => ({ - next_oxidation_stage: v, - }), - }, - ]), (values) => values['minecraft:grass_block'] = { - next_oxidation_stage: 'minecraft:dirt', - }) - createDataMap(schemas, collections, 'parrot_imitations', 'entity_type', ChoiceNode([ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: 'sound_event' as any }, - }), - change: (v: any) => v?.sound, - }, - { - type: 'object', - node: ObjectNode({ - sound: StringNode({ - validator: 'resource', - params: { pool: 'sound_event' as any }, - }), - }), - change: (v: any) => ({ - sound: v, - }), - }, - ]), (values) => values['minecraft:allay'] = { - sound: 'minecraft:entity.allay.ambient_without_item', - }) - createDataMap(schemas, collections, 'raid_hero_gifts', 'villager_profession', ChoiceNode([ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: '$loot_table' }, - }), - change: (v: any) => v?.loot_table, - }, - { - type: 'object', - node: ObjectNode({ - loot_table: StringNode({ - validator: 'resource', - params: { pool: '$loot_table' }, - }), - }), - change: (v: any) => ({ - loot_table: v, - }), - }, - ]), (values) => values['minecraft:cleric'] = { - loot_table: 'minecraft:empty', - }) - createDataMap(schemas, collections, 'vibration_frequencies', 'game_event', ChoiceNode([ - { - type: 'number', - node: NumberNode({ - min: 1, - max: 15, - integer: true, - }), - change: (v: any) => v?.frequency, - }, - { - type: 'object', - node: ObjectNode({ - frequency: NumberNode({ - min: 1, - max: 15, - integer: true, - }), - }), - change: (v: any) => ({ - frequency: v, - }), - }, - ]), (values) => values['minecraft:block_change'] = { - frequency: 5, - }) - createDataMap(schemas, collections, 'waxables', 'block', ChoiceNode([ - { - type: 'string', - node: StringNode({ - validator: 'resource', - params: { pool: 'block' }, - }), - change: (v: any) => v?.waxed, - }, - { - type: 'object', - node: ObjectNode({ - waxed: StringNode({ - validator: 'resource', - params: { pool: 'block' }, - }), - }), - change: (v: any) => ({ - waxed: v, - }), - }, - ]), (values) => values['minecraft:dirt'] = { - waxed: 'minecraft:coarse_dirt', - }) -} - -function createDataMap(schemas: SchemaRegistry, collections: CollectionRegistry, dataMap: string, registry: ResourceType, valueNode: INode, def: (values: any) => void) { - const StringNode = RawStringNode.bind(undefined, collections) - - // Ref or tag - const Tag = StringNode({ - validator: 'resource', - params: { pool: registry, allowTag: true }, - }) - - // Create data map - schemas.register(`${ID}:data_map_${dataMap}`, Mod( - ObjectNode({ - replace: Opt(BooleanNode()), - values: MapNode( - Tag, - ChoiceNode([ - { - type: 'direct', - match: () => true, - node: valueNode, - change: (v: any) => v?.value, - }, - { - type: 'replaceable', - match: (v: any) => typeof v === 'object' && v?.value !== undefined, - priority: 1, - node: ObjectNode({ - replace: Opt(BooleanNode()), - value: valueNode, - }), - change: (v: any) => ({ - replace: true, - value: v, - }), - }, - ]), - ), - remove: Opt(ListNode(Tag)), - }, {context: `${ID}.data_map_${dataMap}`, disableSwitchContext: true}), - { - default: () => { - const result = { - values: {}, - } - def(result.values) - - return result - }, - } - )) -} diff --git a/src/app/partners/Obsidian.ts b/src/app/partners/Obsidian.ts deleted file mode 100644 index ca6f59e3..00000000 --- a/src/app/partners/Obsidian.ts +++ /dev/null @@ -1,442 +0,0 @@ -import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core' -import { BooleanNode, Case, ListNode, MapNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core' - -const ID = 'obsidian' - -export function initObsidian(schemas: SchemaRegistry, collections: CollectionRegistry) { - const Reference = RawReference.bind(undefined, schemas) - const StringNode = RawStringNode.bind(undefined, collections) - - // ITEMS - schemas.register(`${ID}:item`, Mod(ObjectNode({ - information: Opt(Reference(`${ID}:item_information`)), - display: Opt(ObjectNode({ - model: Opt(Reference(`${ID}:model`)), - item_model: Opt(Reference(`${ID}:model`)), - lore: Opt(ListNode( - ObjectNode({ - text: Reference(`${ID}:name_information`), - }), - )), - })), - use_action: Opt(ObjectNode({ - action: Opt(StringNode({ enum: ['none', 'eat', 'drink', 'block', 'bow', 'spear', 'crossbow', 'spyglass'] })), - right_click_action: Opt(StringNode({ enum: ['open_gui', 'run_command', 'open_url']})) , - command: Opt(StringNode()), - url: Opt(StringNode()), - gui_size: Opt(NumberNode({ integer: true, min: 1, max: 6 })), - gui_title: Opt(Reference(`${ID}:name_information`)), - })), - }, { context: `${ID}:item` }), { - default: () => ({}), - })) - - schemas.register(`${ID}:item_information`, ObjectNode({ - rarity: Opt(StringNode({ enum: ['common', 'uncommon', 'rare', 'epic']})), - creative_tab: Opt(StringNode()), - max_stack_size: Opt(NumberNode({ integer: true, min: 1 })), - name: Opt(Reference(`${ID}:name_information`)), - has_enchantment_glint: Opt(BooleanNode()), - is_enchantable: Opt(BooleanNode()), - enchantability: Opt(NumberNode({ integer: true })), - use_duration: Opt(NumberNode({ integer: true })), - can_place_block: Opt(BooleanNode()), - placable_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })), - wearable: Opt(BooleanNode()), - default_color: Opt(NumberNode({ color: true })), - wearable_slot: Opt(StringNode()), - custom_render_mode: Opt(BooleanNode()), - render_mode_models: Opt(ListNode( - ObjectNode({ - model: Reference('model_identifier'), - modes: ListNode(StringNode()), - }) - )), - }, { context: `${ID}:item_information` })) - - schemas.register(`${ID}:item_model`, ObjectNode({ - textures: Opt(MapNode( - StringNode(), - StringNode({ validator: 'resource', params: { pool: '$texture' } }), - )), - parent: StringNode({ validator: 'resource', params: { pool: '$model'} }), - }, { context: `${ID}:item_model` })) - - schemas.register(`${ID}:block`, Mod(ObjectNode({ - block_type: Opt(StringNode({ enum: `${ID}:block_type`})), - information: Opt(Reference(`${ID}:block_information`)), - display: Opt(ObjectNode({ - model: Opt(Reference(`${ID}:model`)), - item_model: Opt(Reference(`${ID}:model`)), - block_model: Opt(Reference(`${ID}:model`)), - lore: ListNode( - ObjectNode({ - text: Reference(`${ID}:item_name_information`), - }), - ), - })), - additional_information: Opt(ObjectNode({ - extraBlocksName: Opt(StringNode()), - slab: Opt(BooleanNode()), - stairs: Opt(BooleanNode()), - walls: Opt(BooleanNode()), - fence: Opt(BooleanNode()), - fenceGate: Opt(BooleanNode()), - button: Opt(BooleanNode()), - pressurePlate: Opt(BooleanNode()), - door: Opt(BooleanNode()), - trapdoor: Opt(BooleanNode()), - path: Opt(BooleanNode()), - lantern: Opt(BooleanNode()), - barrel: Opt(BooleanNode()), - leaves: Opt(BooleanNode()), - plant: Opt(BooleanNode()), - chains: Opt(BooleanNode()), - cake_like: Opt(BooleanNode()), - waterloggable: Opt(BooleanNode()), - dyable: Opt(BooleanNode()), - defaultColor: Opt(NumberNode({ color: true })), - sittable: Opt(BooleanNode()), - isConvertible: Opt(BooleanNode()), - convertible: Opt(ObjectNode({ - drops_item: Opt(BooleanNode()), - reversible: Opt(BooleanNode()), - parent_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })), - transformed_block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' } })), - dropped_item: Opt(StringNode({ validator: 'resource', params: { pool: 'item' } })), - sound: Opt(StringNode()), - conversionItem: Opt(ObjectNode({ - item: StringNode({ validator: 'resource', params: { pool: 'item' } }), - tag: StringNode({ validator: 'resource', params: { pool: '$tag/item' } }), - })), - reversalItem: Opt(ObjectNode({ - item: StringNode({ validator: 'resource', params: { pool: 'item' } }), - tag: StringNode({ validator: 'resource', params: { pool: '$tag/item' } }), - })), - })), - })), - functions: Opt(ObjectNode({ - random_tick: Opt(Reference(`${ID}:function`)), - scheduled_tick: Opt(Reference(`${ID}:function`)), - random_display_tick: Opt(Reference(`${ID}:function`)), - place: Opt(Reference(`${ID}:function`)), - break: Opt(Reference(`${ID}:function`)), - use: Opt(Reference(`${ID}:function`)), - walk_on: Opt(Reference(`${ID}:function`)), - })), - ore_information: Opt(ObjectNode({ - test_type: Opt(StringNode({ enum: ['tag', 'always', 'block_match', 'block_state_match', 'random_block_match', 'random_block_state_match']})), - target_state: Opt(ObjectNode({ - block: Opt(StringNode({ validator: 'resource', params: { pool: 'block' }})), - tag: Opt(StringNode({ validator: 'resource', params: { pool: '$tag/block' } })), - properties: Opt(MapNode( - StringNode(), - StringNode(), - )), - probability: Opt(NumberNode({ min: 0, max: 1 })), - })), - triangleRange: Opt(BooleanNode()), - plateau: Opt(NumberNode({ integer: true, min: 0 })), - spawnPredicate: Opt(StringNode({ enum: ['built_in', 'vanilla', 'overworld', 'the_nether', 'the_end', 'categories', 'biomes'] })), - biomeCategories: ListNode(StringNode()), - biomes: ListNode( - StringNode({ validator: 'resource', params: { pool: '$worldgen/biome' }}) - ), - size: Opt(NumberNode({ integer: true })), - chance: Opt(NumberNode({ integer: true, min: 1 })), - discardOnAirChance: Opt(NumberNode({ min: 0, max: 1 })), - topOffset: Reference(`${ID}:block_y_offset`), - bottomOffset: Reference(`${ID}:block_y_offset`), - })), - food_information: Opt(ObjectNode({ - hunger: Opt(NumberNode({ integer: true, min: 0 })), - saturation: Opt(NumberNode({ integer: true, min: 0 })), - effects: ListNode( - ObjectNode({ - effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), - duration: Opt(NumberNode({ integer: true, min: 0 })), - amplifier: Opt(NumberNode({ integer: true, min: 0 })), - chance: Opt(NumberNode({ integer: true, min: 0, max: 1 })), - }) - ), - })), - cake_slices: Opt(NumberNode({integer: true, min: 1})), - campfire_properties: Opt(ObjectNode({ - emits_particles: Opt(BooleanNode()), - fire_damage: Opt(NumberNode({ integer: true })), - luminance: Opt(NumberNode({ integer: true })), - })), - particle_type: Opt(StringNode()), - can_plant_on: Opt(ListNode( - StringNode({ validator: 'resource', params: { pool: 'block' } }) - )), - growable: Opt(ObjectNode({ - min_age: Opt(NumberNode({ integer: true, min: 1 })), - max_age: Opt(NumberNode({ integer: true })), - })), - oxidizable_properties: Opt(ObjectNode({ - stages: Opt(ListNode( - ObjectNode({ - can_be_waxed: Opt(BooleanNode()), - stairs: Opt(BooleanNode()), - slab: Opt(BooleanNode()), - blocks: Opt(ListNode( - ObjectNode({ - name: Opt(Reference(`${ID}:name_information`)), - display: Opt(Reference(`${ID}:model`)), - }) - )), - }) - )), - })), - events: Opt(ListNode(Reference(`${ID}:event`))), - drop_information: Opt(ObjectNode({ - drops: Opt(ListNode( - ObjectNode({ - name: StringNode({ validator: 'resource', params: { pool: 'item' } }), - drops_if_silk_touch: Opt(BooleanNode()), - }) - )), - survives_explosion: Opt(BooleanNode()), - xp_drop_amount: Opt(NumberNode({ integer: true })), - })), - is_multi_block: Opt(BooleanNode()), - multiblock_information: Opt(ObjectNode({ - width: Opt(NumberNode({ integer: true })), - height: Opt(NumberNode({ integer: true })), - })), - placable_feature: Opt(StringNode({ validator: 'resource', params: { pool: '$worldgen/configured_feature' } })), - }, { context: `${ID}:block` }), { - default: () => ({}), - })) - - schemas.register(`${ID}:block_information`, ObjectNode({ - rarity: Opt(StringNode({ enum: ['common', 'uncommon', 'rare', 'epic']})), - creative_tab: Opt(StringNode()), - collidable: Opt(BooleanNode()), - max_stack_size: Opt(NumberNode({ integer: true, min: 1 })), - name: Opt(Reference(`${ID}:name_information`)), - vanilla_sound_group: Opt(StringNode()), - custom_sound_group: Opt(Reference(`${ID}:sound_group`)), - vanilla_material: Opt(StringNode()), - custom_material: Opt(Reference(`${ID}:material`)), - has_glint: Opt(BooleanNode()), - is_enchantable: Opt(BooleanNode()), - enchantability: Opt(NumberNode({ integer: true })), - fireproof: Opt(BooleanNode()), - translucent: Opt(BooleanNode()), - dynamic_boundaries: Opt(BooleanNode()), - has_item: Opt(BooleanNode()), - dyeable: Opt(BooleanNode()), - defaultColor: Opt(NumberNode({ color: true })), - wearable: Opt(BooleanNode()), - wearble_slot: Opt(StringNode()), - custom_render_mode: Opt(BooleanNode()), - render_mode_models: Opt(ListNode( - ObjectNode({ - model: Reference('model_identifier'), - modes: ListNode(StringNode()), - }) - )), - }, { context: `${ID}:block_information` })) - - schemas.register(`${ID}:block_y_offset`, ObjectNode({ - type: Opt(StringNode({enum: ['fixed', 'above_bottom', 'below_top', 'bottom', 'top']})), - offset: Opt(NumberNode({ integer: true })), - })) - - schemas.register(`${ID}:function`, ObjectNode({ - predicate: Opt(Reference(`${ID}:predicate`)), - function_type: StringNode({ enum: ['NONE', 'REQUIRES_SHIFTING', 'REQUIRES_ITEM', 'REQUIRES_SHIFTING_AND_ITEM'] }), - [Switch]: [{ push: 'function_type' }], - [Case]: { - NONE: {}, - REQUIRES_SHIFTING: {}, - REQUIRES_ITEM: { item: StringNode({ validator: 'resource', params: { pool: 'item' } }) }, - REQUIRES_SHIFTING_AND_ITEM: { item: StringNode({ validator: 'resource', params: { pool: 'item' } }) }, - }, - function_file: StringNode(), - }, { context: `${ID}:function` })) - - schemas.register(`${ID}:predicate`, ObjectNode({ - predicate_type: Opt(StringNode({ enum: ['ALWAYS', 'EQUALS', 'NOT_EQUALS', 'CONTAINS', 'NOT_CONTAINS', 'BEGINS_WITH', 'ENDS_WITH', 'REGEX'] })), - [Switch]: [{ push: 'predicate_type' }], - [Case]: { - ALWAYS: {}, - EQUALS: { left: StringNode(), right: StringNode() }, - NOT_EQUALS: { left: StringNode(), right: StringNode() }, - CONTAINS: { left: StringNode(), right: StringNode() }, - NOT_CONTAINS: { left: StringNode(), right: StringNode() }, - BEGINS_WITH: { left: StringNode(), right: StringNode() }, - ENDS_WITH: { left: StringNode(), right: StringNode() }, - REGEX: { left: StringNode(), right: StringNode() }, - }, - }, { context: `${ID}:predicate` })) - - schemas.register(`${ID}:model`, ObjectNode({ - textures: Opt(MapNode( - StringNode(), - StringNode({ validator: 'resource', params: { pool: '$texture' } }), - )), - parent: StringNode({ validator: 'resource', params: { pool: '$model'} }), - }, { context: `${ID}:model` })) - - schemas.register(`${ID}:sound_group`, ObjectNode({ - id: Opt(StringNode()), - break_sound: Opt(StringNode()), - step_sound: Opt(StringNode()), - place_sound: Opt(StringNode()), - hit_sound: Opt(StringNode()), - }, { context: `${ID}:sound_group` })) - - schemas.register(`${ID}:material`, ObjectNode({ - id: Opt(StringNode()), - map_color: Opt(StringNode()), - allows_movement: Opt(BooleanNode()), - burnable: Opt(BooleanNode()), - liquid: Opt(BooleanNode()), - allows_light: Opt(BooleanNode()), - replacable: Opt(BooleanNode()), - solid: Opt(BooleanNode()), - piston_behaviour: Opt(StringNode({ enum: ['NORMAL', 'DESTROY', 'BLOCK', 'IGNORE', 'PUSH_ONLY'] })), - }, { context: `${ID}:material` })) - - // COMMON - schemas.register(`${ID}:name_information`, ObjectNode({ - id: StringNode(), - text: Opt(StringNode()), - type: Opt(StringNode({ enum: ['literal', 'translatable'] })), - translated: Opt(MapNode( - StringNode(), - StringNode(), - )), - color: Opt(StringNode()), - formatting: Opt(ListNode(StringNode())), - })) - - schemas.register(`${ID}:display_information`, ObjectNode({ - // TODO - })) - - schemas.register(`${ID}:event`, ObjectNode({ - activations: StringNode({enum: ['use', 'shift_use', 'collide', 'walk_on']}), - predicate: Opt(StringNode({ validator: 'resource', params: { pool: '$predicate' } })), - type: StringNode({ enum: ['give_effect', 'damage', 'decrement_stack', 'kill', 'play_sound', 'remove_effect', 'run_command', 'set_block', 'set_block_at_pos', 'set_block_property', 'spawn_loot', 'spawn_entity'] }), - [Switch]: [{ push: 'type' }], - [Case]: { - give_effect: { - amplifier: NumberNode({ integer: true }), - duration: NumberNode(), - effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), - target: StringNode(), - }, - damage: { - amount: NumberNode({ integer: true }), - target: StringNode(), - damage_type: StringNode(), - }, - decrement_stack: { - amount: NumberNode({ integer: true }), - }, - kill: { - target: StringNode(), - }, - remove_effect: { - effect: StringNode({ validator: 'resource', params: { pool: 'mob_effect' } }), - target: StringNode(), - }, - run_command: { - command: StringNode(), - target: StringNode(), - }, - set_block: { - block: StringNode({ validator: 'resource', params: { pool: 'block' } }), - x_pos: NumberNode(), - y_pos: NumberNode(), - z_pos: NumberNode(), - }, - set_block_at_pos: { - block: StringNode({ validator: 'resource', params: { pool: 'block' } }), - x_pos: NumberNode(), - y_pos: NumberNode(), - z_pos: NumberNode(), - }, - set_block_property: { - block: StringNode({ validator: 'resource', params: { pool: 'block' } }), - property: StringNode(), - value: StringNode(), - }, - spawn_loot: { - loot_table: StringNode({ validator: 'resource', params: { pool: '$loot_table' } }), - x_pos: NumberNode(), - y_pos: NumberNode(), - z_pos: NumberNode(), - }, - spawn_entity: { - entity_type: StringNode({ validator: 'resource', params: { pool: 'entity_type' } }), - x_pos: NumberNode(), - y_pos: NumberNode(), - z_pos: NumberNode(), - amount: NumberNode({ integer: true }), - }, - }, - })) - - - // COLLECTIONS - collections.register(`${ID}:block_type`, [ - 'BLOCK', - 'HORIZONTAL_FACING_BLOCK', - 'ROTATABLE_BLOCK', - 'CAMPFIRE', - 'STAIRS', - 'SLAB', - 'WALL', - 'FENCE', - 'FENCE_GATE', - 'CAKE', - 'BED', - 'TRAPDOOR', - 'METAL_DOOR', - 'WOODEN_DOOR', - 'LOG', - 'STEM', - 'WOOD', - 'OXIDIZING_BLOCK', - 'PLANT', - 'PILLAR', - 'HORIZONTAL_FACING_PLANT', - 'SAPLING', - 'TORCH', - 'BEEHIVE', - 'LEAVES', - 'LADDER', - 'PATH', - 'WOODEN_BUTTON', - 'STONE_BUTTON', - 'DOUBLE_PLANT', - 'HORIZONTAL_FACING_DOUBLE_PLANT', - 'HANGING_DOUBLE_LEAVES', - 'EIGHT_DIRECTIONAL_BLOCK', - 'LANTERN', - 'CHAIN', - 'PANE', - 'DYEABLE', - 'LOOM', - 'GRINDSTONE', - 'CRAFTING_TABLE', - 'PISTON', - 'NOTEBLOCK', - 'JUKEBOX', - 'SMOKER', - 'FURNACE', - 'BLAST_FURNACE', - 'LECTERN', - 'FLETCHING_TABLE', - 'BARREL', - 'COMPOSTER', - 'RAILS', - 'CARTOGRAPHY_TABLE', - 'CARPET', - ]) -} diff --git a/src/app/partners/OhTheTreesYoullGrow.ts b/src/app/partners/OhTheTreesYoullGrow.ts deleted file mode 100644 index e33ba726..00000000 --- a/src/app/partners/OhTheTreesYoullGrow.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core' -import { Case, ListNode, Mod, NumberNode, ObjectNode, Opt, Reference as RawReference, StringNode as RawStringNode, Switch } from '@mcschema/core' - -export function initOhTheTreesYoullGrow(schemas: SchemaRegistry, collections: CollectionRegistry) { - const Reference = RawReference.bind(undefined, schemas) - const StringNode = RawStringNode.bind(undefined, collections) - - collections.register('ohthetreesyoullgrow:feature', [ - 'ohthetreesyoullgrow:tree_from_nbt_v1', - ]) - - const BlockSet = ListNode( - StringNode({ validator: 'resource', params: { pool: 'block' } }) - ) - - schemas.register('ohthetreesyoullgrow:configured_feature', Mod(ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: 'ohthetreesyoullgrow:feature' as any } }), - config: ObjectNode({ - [Switch]: ['pop', { push: 'type' }], - [Case]: { - 'ohthetreesyoullgrow:tree_from_nbt_v1': { - base_location: StringNode({ validator: 'resource', params: { pool: '$structure' } }), - canopy_location: StringNode({ validator: 'resource', params: { pool: '$structure' } }), - can_grow_on_filter: Reference('block_predicate_worldgen'), - can_leaves_place_filter: Reference('block_predicate_worldgen'), - decorators: Opt(ListNode( - ObjectNode({ - type: StringNode({ validator: 'resource', params: { pool: 'worldgen/tree_decorator_type' } }), - [Switch]: [{ push: 'type' }], - [Case]: { - 'minecraft:alter_ground': { - provider: Reference('block_state_provider'), - }, - 'minecraft:attached_to_leaves': { - probability: NumberNode({ min: 0, max: 1 }), - exclusion_radius_xz: NumberNode({ integer: true, min: 0, max: 16 }), - exclusion_radius_y: NumberNode({ integer: true, min: 0, max: 16 }), - required_empty_blocks: NumberNode({ integer: true, min: 1, max: 16 }), - block_provider: Reference('block_state_provider'), - directions: ListNode( - StringNode({ enum: 'direction' }) - ), - }, - 'minecraft:beehive': { - probability: NumberNode({ min: 0, max: 1 }), - }, - 'minecraft:cocoa': { - probability: NumberNode({ min: 0, max: 1 }), - }, - 'minecraft:leave_vine': { - probability: NumberNode({ min: 0, max: 1 }), - }, - }, - }, { context: 'tree_decorator' }) - )), - height: Reference('int_provider'), - leaves_provider: Reference('block_state_provider'), - leaves_target: BlockSet, - log_provider: Reference('block_state_provider'), - log_target: BlockSet, - max_log_depth: Opt(NumberNode({ integer: true })), - place_from_nbt: BlockSet, - }, - }, - }, { disableSwitchContext: true }), - }, { context: 'ohthetreesyoullgrow.configured_feature' }), { - default: () => ({ - type: 'ohthetreesyoullgrow:tree_from_nbt_v1', - config: { - can_grow_on_filter: { - type: 'minecraft:matching_block_tag', - tag: 'minecraft:dirt', - }, - can_leaves_place_filter: { - type: 'minecraft:replaceable', - }, - height: { - type: 'minecraft:uniform', - value: { - min_inclusive: 5, - max_inclusive: 10, - }, - }, - leaves_provider: { - type: 'minecraft:simple_state_provider', - state: { - Name: 'minecraft:acacia_leaves', - Properties: { - distance: '7', - persistent: 'false', - waterlogged: 'false', - }, - }, - }, - leaves_target: [ - 'minecraft:oak_leaves', - ], - log_provider: { - type: 'minecraft:simple_state_provider', - state: { - Name: 'minecraft:acacia_log', - Properties: { - axis: 'y', - }, - }, - }, - log_target: [ - 'minecraft:oak_log', - ], - place_from_nbt: [], - }, - }), - })) -} diff --git a/src/app/partners/index.ts b/src/app/partners/index.ts deleted file mode 100644 index 40686fa9..00000000 --- a/src/app/partners/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { CollectionRegistry, SchemaRegistry } from '@mcschema/core' -import type { VersionId } from '../services/Schemas.js' -import { initImmersiveWeathering } from './ImmersiveWeathering.js' -import { initLithostitched } from './Lithostitched.js' -import { initNeoForge } from './NeoForge.js' -import { initObsidian } from './Obsidian.js' -import { initOhTheTreesYoullGrow } from './OhTheTreesYoullGrow.js' - -export * from './ImmersiveWeathering.js' -export * from './Lithostitched.js' - -export function initPartners(schemas: SchemaRegistry, collections: CollectionRegistry, version: VersionId) { - initImmersiveWeathering(schemas, collections) - initLithostitched(schemas, collections, version) - initNeoForge(schemas, collections, version) - initObsidian(schemas, collections) - initOhTheTreesYoullGrow(schemas, collections) -} diff --git a/src/app/partners/locales/en.json b/src/app/partners/locales/en.json deleted file mode 100644 index c5cb767e..00000000 --- a/src/app/partners/locales/en.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "immersive_weathering.area_condition.type": "Type", - "immersive_weathering.area_condition.type.generate_if_not_too_many": "Generate if not too many", - "immersive_weathering.area_condition.type.neighbor_based_generation": "Neighbor based generation", - "immersive_weathering.area_condition.generate_if_not_too_many.radiusX": "Radius X", - "immersive_weathering.area_condition.generate_if_not_too_many.radiusY": "Radius Y", - "immersive_weathering.area_condition.generate_if_not_too_many.radiusZ": "Radius Z", - "immersive_weathering.area_condition.generate_if_not_too_many.requiredAmount": "Required amount", - "immersive_weathering.area_condition.generate_if_not_too_many.yOffset": "Y offset", - "immersive_weathering.area_condition.generate_if_not_too_many.must_have": "Must have", - "immersive_weathering.area_condition.generate_if_not_too_many.must_not_have": "Must not have", - "immersive_weathering.area_condition.generate_if_not_too_many.includes": "Includes", - "immersive_weathering.area_condition.neighbor_based_generation.must_have": "Must have", - "immersive_weathering.area_condition.neighbor_based_generation.must_not_have": "Must not have", - "immersive_weathering.area_condition.neighbor_based_generation.required_amount": "Required amount", - "immersive_weathering.area_condition.neighbor_based_generation.directions": "Directions", - "immersive_weathering.area_condition.neighbor_based_generation.directions.entry": "Direction", - "immersive_weathering.block_growth.area_condition": "Area conditions", - "immersive_weathering.block_growth.position_predicates": "Position predicates", - "immersive_weathering.block_growth.position_predicates.entry": "Position test", - "immersive_weathering.block_growth.growth_chance": "Growth chance", - "immersive_weathering.block_growth.growth_for_face": "Growth for face", - "immersive_weathering.block_growth.growth_for_face.entry": "Face", - "immersive_weathering.block_growth.growth_for_face.entry.direction": "Direction", - "immersive_weathering.block_growth.growth_for_face.entry.weight": "Weight", - "immersive_weathering.block_growth.growth_for_face.entry.growth": "Growth", - "immersive_weathering.block_growth.growth_for_face.entry.growth.entry.data": "Block pair", - "immersive_weathering.block_growth.growth_for_face.entry.growth.entry.weight": "Weight", - "immersive_weathering.block_growth.owners": "Owners", - "immersive_weathering.block_growth.owners.entry": "Block", - "immersive_weathering.block_growth.replacing_target": "Replacing target", - "immersive_weathering.block_growth.target_self": "Target self", - "immersive_weathering.block_growth.destroy_target": "Destroy target", - "immersive_weathering.block_pair.block": "Block", - "immersive_weathering.block_pair.above_block": "Above block", - "immersive_weathering.position_test.predicate_type": "Predicate type", - "immersive_weathering.position_test.predicate_type.biome_match": "Biome match", - "immersive_weathering.position_test.predicate_type.day_test": "Day test", - "immersive_weathering.position_test.predicate_type.nand": "NAND", - "immersive_weathering.position_test.predicate_type.precipitation_test": "Precipitation test", - "immersive_weathering.position_test.predicate_type.temperature_range": "Temperature range", - "immersive_weathering.position_test.biome_match.biomes": "Biomes", - "immersive_weathering.position_test.day_test.day": "Day", - "immersive_weathering.position_test.nand.predicates": "Predicates", - "immersive_weathering.position_test.precipitation_test.precipitation": "Precipitation", - "immersive_weathering.position_test.temperature_range.min": "Min", - "immersive_weathering.position_test.temperature_range.max": "Max", - "immersive_weathering.position_test.temperature_range.use_local_pos": "Use local pos", - "immersive_weathering:rule_test.always_true": "Always true", - "immersive_weathering:rule_test.block_match": "Block match", - "immersive_weathering:rule_test.blockstate_match": "Block state match", - "immersive_weathering:rule_test.random_block_match": "Random block match", - "immersive_weathering:rule_test.random_blockstate_match": "Random block state match", - "immersive_weathering:rule_test.tag_match": "Tag match", - "immersive_weathering:rule_test.immersive_weathering:block_set_match": "Block set match", - "immersive_weathering:rule_test.immersive_weathering:fluid_match": "Fluid match", - "immersive_weathering:rule_test.immersive_weathering:tree_log": "Tree log", - - "lithostitched:modifier_type.lithostitched:add_biome_spawns": "Add biome spawns", - "lithostitched:modifier_type.lithostitched:add_features": "Add features", - "lithostitched:modifier_type.lithostitched:add_pool_aliases": "Add pool aliases", - "lithostitched:modifier_type.lithostitched:add_structure_set_entries": "Add structure set entries", - "lithostitched:modifier_type.lithostitched:add_surface_rule": "Add surface rule", - "lithostitched:modifier_type.lithostitched:add_template_pool_elements": "Add template pool elements", - "lithostitched:modifier_type.lithostitched:no_op": "Nothing", - "lithostitched:modifier_type.lithostitched:redirect_feature": "Redirect feature", - "lithostitched:modifier_type.lithostitched:remove_biome_spawns": "Remove biome spawns", - "lithostitched:modifier_type.lithostitched:remove_features": "Remove features", - "lithostitched:modifier_type.lithostitched:remove_structures_from_structure_set": "Remove structures from set", - "lithostitched:modifier_type.lithostitched:replace_climate": "Replace climate", - "lithostitched:modifier_type.lithostitched:replace_effects": "Replace effects", - "lithostitched:modifier_predicate_type.lithostitched:all_of": "All of", - "lithostitched:modifier_predicate_type.lithostitched:any_of": "Any of", - "lithostitched:modifier_predicate_type.lithostitched:mod_loaded": "Mod loaded", - "lithostitched:modifier_predicate_type.lithostitched:not": "Not", - "lithostitched:modifier_predicate_type.lithostitched:true": "True", - - "neoforge:biome_modifier_type.neoforge:none": "Disable Biome Modifier", - "neoforge:biome_modifier_type.neoforge:add_features": "Add Features", - "neoforge:biome_modifier_type.neoforge:remove_features": "Remove Features", - "neoforge:biome_modifier_type.neoforge:add_spawns": "Add Mob Spawns", - "neoforge:biome_modifier_type.neoforge:remove_spawns": "Remove Mob Spawns", - "neoforge:biome_modifier_type.neoforge:add_carvers": "Add World Carvers", - "neoforge:biome_modifier_type.neoforge:remove_carvers": "Remove World Carvers", - "neoforge:biome_modifier_type.neoforge:add_spawn_costs": "Add Mob Spawn Costs", - "neoforge:biome_modifier_type.neoforge:remove_spawn_costs": "Remove Mob Spawn Costs", - "neoforge:structure_modifier_type.neoforge:none": "Disable Structure Modifier", - "neoforge:structure_modifier_type.neoforge:add_spawns": "Add Mob Spawns", - "neoforge:structure_modifier_type.neoforge:remove_spawns": "Remove Mob Spawns", - "neoforge:structure_modifier_type.neoforge:clear_spawns": "Clear Mob Spawns", - - "obsidian:item.information": "Item Information", - "obsidian:item_information.rarity": "Rarity", - "obsidian:item_information.creative_tab": "Creative Tab", - "obsidian:item_information.max_stack_size": "Max Stack Size", - "obsidian:item_information.name": "Item Name", - "obsidian:item_information.has_enchantment_glint": "Has Enchantment Glint", - "obsidian:item_information.is_enchantable": "Is Enchantable", - "obsidian:item_information.enchantability": "Enchantability", - "obsidian:item_information.use_duration": "Item Duration", - "obsidian:item_information.can_place_block": "Can Place Block", - "obsidian:item_information.placable_block": "Placable Block", - "obsidian:item_information.wearable": "Wearable", - "obsidian:item_information.default_color": "Default Color", - "obsidian:item_information.wearable_slot": "Wearable Slot", - "obsidian:item_information.custom_render_mode": "Has display-based models", - "obsidian:item_information.render_mode_models": "Display-based Models", - "obsidian:item.display": "Display", - "obsidian:item.display.model": "Model", - "obsidian:item.display.item_model": "Item Model", - "obsidian:item.display.lore": "Lore", - "obsidian:item.use_action": "Use Action", - "obsidian:item.use_action.action": "Action", - "obsidian:item.use_action.right_click_action": "Right Click Action", - "obsidian:item.use_action.command": "Command", - "obsidian:item.use_action.url": "URL", - "obsidian:item.use_action.gui_size": "GUI Size", - "obsidian:item.use_action.gui_title": "GUI Title", - - "obsidian:block.block_type": "Block Type", - "obsidian:block.information": "Block Information", - "obsidian:block_information.rarity": "Rarity", - "obsidian:block_information.creative_tab": "Creative Tab", - "obsidian:block_information.max_stack_size": "Max Stack Size", - "obsidian:block_information.name": "Item Name", - "obsidian:block_information.sound_group_type": "Sound Group Type", - "obsidian:block_information.vanilla_sound_group": "Vanilla Sound Group", - "obsidian:block_information.custom_sound_group": "Custom Sound Group", - "obsidian:block_information.material_type": "Material Type", - "obsidian:block_information.vanilla_material": "Vanilla Material", - "obsidian:block_information.custom_material": "Custom Material", - "obsidian:block.display": "Display", - "obsidian:block.additional_information": "Additional Information", - "obsidian:block.ore_information": "Ore Information", - "obsidian:block.food_information": "Food Information", - "obsidian:block.block_type.CAMPFIRE.campfire_properties": "Campfire Properties", - "obsidian:block.can_plant_on": "Can Plant On", - "obsidian:block.particle_type": "Particle Type", - "obsidian:block.growable": "Growable", - "obsidian:block.oxidizable_properties": "Oxidizable Properties", - "obsidian:block.events": "Events", - "obsidian:block.drop_information": "Drop Information", - "obsidian:block.is_multi_block": "Is Multi-Block", - "obsidian:block.multiblock_information": "Multiblock Information", - "obsidian:block.placable_feature": "Placable Feature", - "obsidian:block.display.model": "Model", - "obsidian:block.display.item_model": "Item Model", - "obsidian:block.display.block_model": "Block Model", - "obsidian:block.display.lore": "Lore", - - "ohthetreesyoullgrow:feature.ohthetreesyoullgrow:tree_from_nbt_v1": "Tree from NBT v1", - "ohthetreesyoullgrow.configured_feature.type": "Type", - "ohthetreesyoullgrow.configured_feature.config": "Config", - "ohthetreesyoullgrow.configured_feature.config.base_location": "Base location", - "ohthetreesyoullgrow.configured_feature.config.base_location.help": "The path to the trunk structure piece.", - "ohthetreesyoullgrow.configured_feature.config.canopy_location": "Canopy location", - "ohthetreesyoullgrow.configured_feature.config.canopy_location.help": "The path to the canopy structure piece.", - "ohthetreesyoullgrow.configured_feature.config.can_grow_on_filter": "Can grow on filter", - "ohthetreesyoullgrow.configured_feature.config.can_grow_on_filter.help": "Block filter for which this tree is allowed to grow on. Checks all of the red wool positions defined by the trunk.", - "ohthetreesyoullgrow.configured_feature.config.can_leaves_place_filter": "Can leaves place filter", - "ohthetreesyoullgrow.configured_feature.config.can_leaves_place_filter.help": "Block filter for which this tree's leaves are allowed to place.", - "ohthetreesyoullgrow.configured_feature.config.decorators": "Decorators", - "ohthetreesyoullgrow.configured_feature.config.decorators.entry": "Decorator", - "ohthetreesyoullgrow.configured_feature.config.height": "Height", - "ohthetreesyoullgrow.configured_feature.config.height.help": "Int provider defining the height of the tree.", - "ohthetreesyoullgrow.configured_feature.config.leaves_provider": "Leaves provider", - "ohthetreesyoullgrow.configured_feature.config.leaves_target": "Leaves target", - "ohthetreesyoullgrow.configured_feature.config.leaves_target.entry": "Block", - "ohthetreesyoullgrow.configured_feature.config.log_provider": "Log provider", - "ohthetreesyoullgrow.configured_feature.config.log_target": "Log target", - "ohthetreesyoullgrow.configured_feature.config.log_target.entry": "Block", - "ohthetreesyoullgrow.configured_feature.config.max_log_depth": "Max log depth", - "ohthetreesyoullgrow.configured_feature.config.place_from_nbt": "Place from NBT", - "ohthetreesyoullgrow.configured_feature.config.place_from_nbt.help": "Additional blocks from the structure pieces that should be placed in the world.", - "ohthetreesyoullgrow.configured_feature.config.place_from_nbt.entry": "Block" -} diff --git a/src/app/schema/ModelWrapper.ts b/src/app/schema/ModelWrapper.ts deleted file mode 100644 index ffe811d6..00000000 --- a/src/app/schema/ModelWrapper.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { INode, Path } from '@mcschema/core' -import { DataModel } from '@mcschema/core' - -export class ModelWrapper extends DataModel { - constructor( - schema: INode, - private readonly mapper: (path: Path) => Path, - private readonly getter: (path: Path) => any, - private readonly setter: (path: Path, value: any, silent?: boolean) => any, - ) { - super(schema) - } - - map(path: Path) { - return this.mapper(path) - } - - get(path: Path) { - return this.getter(path) - } - - set(path: Path, value: any, silent?: boolean) { - return this.setter(path, value, silent) - } -} diff --git a/src/app/schema/renderHtml.tsx b/src/app/schema/renderHtml.tsx deleted file mode 100644 index 67b068c1..00000000 --- a/src/app/schema/renderHtml.tsx +++ /dev/null @@ -1,719 +0,0 @@ -import type { BooleanHookParams, EnumOption, Hook, INode, ListHookParams, NodeChildren, NumberHookParams, StringHookParams, ValidationOption } from '@mcschema/core' -import { DataModel, ListNode, MapNode, ModelPath, ObjectNode, Path, relativePath, StringNode } from '@mcschema/core' -import { Identifier, ItemStack } from 'deepslate/core' -import type { ComponentChildren, JSX } from 'preact' -import { memo } from 'preact/compat' -import { useState } from 'preact/hooks' -import { Btn, Octicon } from '../components/index.js' -import { ItemDisplay } from '../components/ItemDisplay.jsx' -import { VanillaColors } from '../components/previews/BiomeSourcePreview.jsx' -import config from '../Config.js' -import { localize, useLocale, useStore } from '../contexts/index.js' -import { useFocus } from '../hooks/index.js' -import type { BlockStateRegistry, VersionId } from '../services/index.js' -import { CachedDecorator, CachedFeature, checkVersion } from '../services/index.js' -import { deepClone, deepEqual, generateColor, generateUUID, hexId, hexToRgb, isObject, newSeed, rgbToHex, stringToColor } from '../Utils.js' -import { ModelWrapper } from './ModelWrapper.js' - -const selectRegistries = ['loot_table.type', 'loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'recipe.type', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'dimension.generator.biome_source.preset', 'carver.type', 'feature.type', 'decorator.type', 'feature.tree.minimum_size.type', 'block_state_provider.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'structure_feature.type', 'surface_builder.type', 'processor.processor_type', 'rule_test.predicate_type', 'pos_rule_test.predicate_type', 'template_element.element_type', 'block_placer.type', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'structure_placement.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.cat.variant', 'entity.type_specific.frog.variant', 'rule_block_entity_modifier.type', 'pool_alias_binding.type', 'lithostitched.worldgen_modifier.type', 'lithostitched.modifier_predicate.type', 'ohthetreesyoullgrow.configured_feature.type', 'enchantment_provider.type', 'enchantment_value_effect.type', 'level_based_value.type', 'neoforge.biome_modifier.type', 'neoforge.structure_modifier.type'] -const datalistEnums = ['item_stack.components', 'function.set_components.components'] -const hiddenFields = ['number_provider.type', 'score_provider.type', 'nbt_provider.type', 'int_provider.type', 'float_provider.type', 'height_provider.type', 'level_based_value.type'] -const flattenedFields = ['feature.config', 'decorator.config', 'int_provider.value', 'float_provider.value', 'block_state_provider.simple_state_provider.state', 'block_state_provider.rotated_block_provider.state', 'block_state_provider.weighted_state_provider.entries.entry.data', 'rule_test.block_state', 'structure_feature.config', 'surface_builder.config', 'template_pool.elements.entry.element', 'decorator.block_survives_filter.state', 'material_rule.block.result_state', 'enchantment.effects.entry.effect'] -const inlineFields = ['loot_entry.type', 'function.function', 'condition.condition', 'criterion.trigger', 'dimension.generator.type', 'dimension.generator.biome_source.type', 'feature.type', 'decorator.type', 'block_state_provider.type', 'feature.tree.minimum_size.type', 'trunk_placer.type', 'foliage_placer.type', 'tree_decorator.type', 'block_placer.type', 'rule_test.predicate_type', 'processor.processor_type', 'template_element.element_type', 'nbt_operation.op', 'number_provider.value', 'score_provider.name', 'score_provider.target', 'nbt_provider.source', 'nbt_provider.target', 'generator_biome.biome', 'block_predicate.type', 'material_rule.type', 'material_condition.type', 'density_function.type', 'root_placer.type', 'entity.type_specific.type', 'glyph_provider.type', 'sprite_source.type', 'rule_block_entity_modifier.type', 'immersive_weathering.area_condition.type', 'immersive_weathering.block_growth.growth_for_face.entry.direction', 'immersive_weathering.position_test.predicate_type', 'pool_alias_binding.type', 'item_stack.id', 'data_component.container.entry.slot', 'map_decoration.type', 'suspicious_stew_effect_instance.id', 'enchantment_value_effect.type', 'enchantment_effect.type', 'particle.type'] -const nbtFields = ['function.set_nbt.tag', 'advancement.display.icon.nbt', 'text_component_object.nbt', 'entity.nbt', 'block.nbt', 'item.nbt'] -const fixedLists = ['generator_biome.parameters.temperature', 'generator_biome.parameters.humidity', 'generator_biome.parameters.continentalness', 'generator_biome.parameters.erosion', 'generator_biome.parameters.depth', 'generator_biome.parameters.weirdness', 'feature.end_spike.crystal_beam_target', 'feature.end_gateway.exit', 'decorator.block_filter.offset', 'block_predicate.has_sturdy_face.offset', 'block_predicate.inside_world_bounds.offset', 'block_predicate.matching_block_tag.offset', 'block_predicate.matching_blocks.offset', 'block_predicate.matching_fluids.offset', 'block_predicate.would_survive.offset', 'model_element.from', 'model_element.to', 'model_element.rotation.origin', 'model_element.faces.uv', 'item_transform.rotation', 'item_transform.translation', 'item_transform.scale', 'generator_structure.random_spread.locate_offset', 'pack_overlay.formats', 'data_component.profile.id', 'data_component.lodestone_tracker.tracker.pos', 'attribute_modifier.uuid'] -const collapsedFields = ['noise_settings.surface_rule', 'noise_settings.noise.terrain_shaper'] -const collapsableFields = ['density_function.argument', 'density_function.argument1', 'density_function.argument2', 'density_function.input', 'density_function.when_in_range', 'density_function.when_out_of_range'] -const itemPreviewFields = ['loot_pool.entries.entry', 'loot_entry.alternatives.children.entry', 'loot_entry.group.children.entry', 'loot_entry.sequence.children.entry', 'function.set_contents.entries.entry'] -const forceEnumContexts: Record = { 'loot_table.type': 'loot_table.type', 'condition.condition': 'loot_condition_type', 'function.function': 'loot_function_type' } - -const findGenerator = (id: string) => { - return config.generators.find(g => g.id === id.replace(/^\$/, '')) -} - -/** - * Secondary model used to remember the keys of a map - */ -const keysModel = new DataModel(MapNode( - StringNode(), - StringNode() -), { historyMax: 0 }) - -type JSXTriple = [JSX.Element | null, JSX.Element | null, JSX.Element | null] -type RenderHook = Hook<[any, string, VersionId, BlockStateRegistry, Record], JSXTriple> - -type NodeProps = T & { - node: INode, - path: ModelPath, - value: any, - lang: string, - version: VersionId, - states: BlockStateRegistry, - ctx: Record, -} - -export function FullNode({ model, lang, version, blockStates }: { model: DataModel, lang: string, version: VersionId, blockStates: BlockStateRegistry }) { - const path = new ModelPath(model) - const [prefix, suffix, body] = model.schema.hook(renderHtml, path, deepClone(model.data), lang, version, blockStates, {}) - return suffix?.props?.children.some((c: any) => c) ? - {prefix}{suffix} - {body} - : body -} - -const renderHtml: RenderHook = { - base() { - return [null, null, null] - }, - - boolean(params, path, value, lang, version, states, ctx) { - return [null, , null] - }, - - choice({ choices, config, switchNode }, path, value, lang, version, states, ctx) { - const choice = switchNode.activeCase(path, true) as typeof choices[number] - const contextPath = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path - const [prefix, suffix, body] = choice.node.hook(this, contextPath, value, lang, version, states, ctx) - if (choices.length === 1) { - return [prefix, suffix, body] - } - const choiceContextPath = config?.choiceContext ? new Path([], [config.choiceContext]) : config?.context ? new Path([], [config.context]) : path - const set = (type: string) => { - const c = choices.find(c => c.type === type) ?? choice - const def = c.node.default() - const newValue = c.change - ? c.change(DataModel.unwrapLists(value)) - : config.choiceContext === 'feature' && def?.type === 'minecraft:decorated' ? def.config.feature : def - path.model.set(path, DataModel.wrapLists(newValue)) - } - const inject = set((e.target as HTMLSelectElement).value)}> - {choices.map(c => - {pathLocale(lang, choiceContextPath.contextPush(c.type))} - )} - - return [prefix, <>{inject}{suffix}>, body] - }, - - list({ children, config, node }, path, value, lang, version, states, ctx) { - const context = path.getContext().join('.') - if (fixedLists.includes(context)) { - const prefix = <> - {[...Array(config.maxLength!)].map((_, i) => - )} - - > - const suffix = <>{[...Array(config.maxLength)].map((_, i) => { - const child = children.hook(this, path.modelPush(i), value?.[i]?.node, lang, version, states, ctx) - return child[1] - })}> - return [prefix, suffix, null] - } - - const onAdd = () => { - if (!Array.isArray(value)) value = [] - const node = DataModel.wrapLists(children.default()) - path.model.set(path, [{ node, id: hexId() }, ...value]) - } - const suffix = {Octicon.plus_circle} - return [null, suffix, ] - }, - - map({ children, keys, config }, path, value, lang, version, states, ctx) { - const { expand, collapse, isToggled } = useToggles() - - const keyPath = new ModelPath(keysModel, new Path([hashString(path.toString())], path.contextArr)) - const onAdd = () => { - const key = keyPath.get() - if (path.model.get(path.push(key)) === undefined) { - path.model.set(path.push(key), DataModel.wrapLists(children.default())) - } - keyPath.set('') - } - const blockState = config.validation?.validator === 'block_state_map' ? states?.[relativePath(path, config.validation.params.id).get()] : null - const keysSchema = blockState?.properties - ? StringNode(null!, { enum: Object.keys(blockState.properties ?? {}) }) - : keys - if (blockState && path.last() === 'Properties') { - if (typeof value !== 'object') value = {} - const properties = Object.entries(blockState.properties ?? {}) - .map(([key, values]) => [key, StringNode(null!, { enum: values })]) - Object.entries(blockState.properties ?? {}).forEach(([key, values]) => { - if (typeof value[key] !== 'string') { - path.model.errors.add(path.push(key), 'error.expected_string') - } else if (!values.includes(value[key])) { - path.model.errors.add(path.push(key), 'error.invalid_enum_option', value[key]) - } - }) - return ObjectNode(Object.fromEntries(properties)).hook(this, path, value, lang, version, states, ctx) - } - const suffix = <> - {keysSchema.hook(this, keyPath, keyPath.get() ?? '', lang, version, states, ctx)[1]} - {Octicon.plus_circle} - > - const body = <> - {typeof value === 'object' && Object.entries(value).map(([key, cValue]) => { - const pathWithContext = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path - const cPath = pathWithContext.modelPush(key) - const canToggle = children.type(cPath) === 'object' - const toggle = isToggled(key) - if (canToggle && (toggle === false || (toggle === undefined && value.length > 20))) { - return - - {Octicon.chevron_right} - {key} - - - } - const cSchema = blockState - ? StringNode(null!, { enum: blockState.properties?.[key] ?? [] }) - : children - if (blockState?.properties?.[key] && typeof cValue === 'string' - && !blockState.properties?.[key].includes(cValue)) { - path.model.errors.add(cPath, 'error.invalid_enum_option', cValue) - } - const onRemove = () => cPath.set(undefined) - return - {canToggle && {Octicon.chevron_down}} - {Octicon.trashcan} - - })} - > - return [null, suffix, body] - }, - - number(params, path, value, lang, version, states, ctx) { - return [null, , null] - }, - - object({ node, config, getActiveFields, getChildModelPath }, path, value, lang, version, states, ctx) { - const { expand, collapse, isToggled } = useToggles() - - if (path.getArray().length == 0 && isDecorated(config.context, value)) { - const { wrapper, fields } = createDecoratorsWrapper(getActiveFields(path), path, value) - value = wrapper.data - getActiveFields = () => fields - getChildModelPath = (path, key) => new ModelPath(wrapper, new Path(path.getArray(), ['feature'])).push(key) - } - - let prefix: JSX.Element | null = null - let suffix: JSX.Element | null = null - let body: JSX.Element | null = null - if (node.optional()) { - if (value === undefined) { - const onExpand = () => path.set(DataModel.wrapLists(node.default())) - suffix = {Octicon.plus_circle} - } else if (typeof value === 'object' && value !== null){ - const onCollapse = () => path.set(undefined) - suffix = {Octicon.trashcan} - } - } - const context = path.getContext().join('.') - if (collapsableFields.includes(context) || collapsedFields.includes(context)) { - const toggled = isToggled('') - const expanded = collapsedFields.includes(context) ? toggled : !toggled - prefix = <> - {expanded ? Octicon.chevron_down : Octicon.chevron_right} - > - if (!expanded) { - return [prefix, suffix, null] - } - } - if (!(node.optional() && value === undefined)) { - if (typeof value === 'object' && value !== null) { - const newCtx = (typeof value === 'object' && value !== null && node.default()?.pools) - ? { ...ctx, loot: value?.type } : ctx - body = <>{Object.entries(getActiveFields(path)) - .filter(([_, child]) => child.enabled(path)) - .map(([key, child]) => { - const cPath = getChildModelPath(path, key) - const context = cPath.getContext().join('.') - if (hiddenFields.includes(context)) return null - const [cPrefix, cSuffix, cBody] = child.hook(this, cPath, value[key], lang, version, states, newCtx) - const isFlattened = child.type(cPath) === 'object' && flattenedFields.includes(context) - const isInlined = inlineFields.includes(context) - if (isFlattened || isInlined) { - prefix = <>{prefix}{cPrefix}> - suffix = <>{suffix}{cSuffix}> - return isFlattened ? cBody : null - } - return - })}> - } else { - const onReset = () => path.set(DataModel.wrapLists(node.default())) - suffix = <>{suffix}{Octicon.history}> - } - } - return [prefix, suffix, body] - }, - - string(params, path, value, lang, version, states, ctx) { - return [null, , null] - }, -} - -function Collapsed({ path, value }: { path: ModelPath, value: any, schema: INode }) { - const { locale } = useLocale() - const context = path.getContext().join('.') - switch (context) { - case 'loot_table.pools.entry': - const count = value?.entries?.length ?? 0 - return {count} {count == 1 ? 'entry' : 'entries'} - case 'function.set_contents.entries.entry': - case 'loot_pool.entries.entry': - const name = value?.name?.replace(/^minecraft:/, '') ?? value?.type?.replace(/^minecraft:/, '') - const weight = value?.weight || undefined - return <> - {name} - {weight !== undefined && {weight}} - > - } - for (const child of Object.values(value ?? {})) { - if (typeof child === 'string') { - return {child.replace(/^minecraft:/, '')} - } - } - return null -} - -function useToggles() { - const [toggleState, setToggleState] = useState(new Map()) - const [toggleAll, setToggleAll] = useState(undefined) - - const expand = (key: string) => (evt: MouseEvent) => { - if (evt.ctrlKey) { - setToggleState(new Map()) - setToggleAll(true) - } else { - setToggleState(state => new Map(state.set(key, true))) - } - } - const collapse = (key: string) => (evt: MouseEvent) => { - if (evt.ctrlKey) { - setToggleState(new Map()) - setToggleAll(false) - } else { - setToggleState(state => new Map(state.set(key, false))) - } - } - - const isToggled = (key: string) => { - if (!(toggleState instanceof Map)) return false - return toggleState.get(key) ?? toggleAll - } - - return { expand, collapse, isToggled } -} - -function ListBody({ path, value, lang, config, children, version, states, ctx }: NodeProps) { - const { expand, collapse, isToggled } = useToggles() - const [maxShown, setMaxShown] = useState(50) - const onAddBottom = () => { - if (!Array.isArray(value)) value = [] - const node = DataModel.wrapLists(children.default()) - path.model.set(path, [...value, { node, id: hexId() }]) - } - return <> - {(value && Array.isArray(value)) && value.map(({ node: cValue, id: cId }, index) => { - if (index === maxShown) { - return - {localize(lang, 'entries_hidden', `${value.length - maxShown}`)} - setMaxShown(Math.min(maxShown + 50, value.length))}>{localize(lang, 'entries_hidden.more', '50')} - setMaxShown(value.length)}>{localize(lang, 'entries_hidden.all')} - - } - if (index > maxShown) { - return null - } - const pathWithContext = (config?.context) ? new ModelPath(path.getModel(), new Path(path.getArray(), [config.context])) : path - const cPath = pathWithContext.push(index).contextPush('entry') - const canToggle = children.type(cPath) === 'object' - const toggle = isToggled(cId) - - let label: undefined | string | JSX.Element - if (itemPreviewFields.includes(cPath.getContext().join('.'))) { - if (isObject(cValue) && typeof cValue.type === 'string' && cValue.type.replace(/^minecraft:/, '') === 'item' && typeof cValue.name === 'string') { - let itemStack: ItemStack | undefined - try { - itemStack = new ItemStack(Identifier.parse(cValue.name), 1) - } catch (e) {} - if (itemStack !== undefined) { - label = - } - } - } - - if (canToggle && (toggle === false || (toggle === undefined && value.length > 20))) { - return - - {Octicon.chevron_right} - {label ?? pathLocale(lang, cPath, `${index}`)} - - - } - - const onRemove = () => cPath.set(undefined) - const onMoveUp = () => { - const v = [...path.get()]; - [v[index - 1], v[index]] = [v[index], v[index - 1]] - path.model.set(path, v) - } - const onMoveDown = () => { - const v = [...path.get()]; - [v[index + 1], v[index]] = [v[index], v[index + 1]] - path.model.set(path, v) - } - const actions: MenuAction[] = [ - { - icon: 'duplicate', - label: 'duplicate', - onSelect: () => { - const v = [...path.get()] - v.splice(index, 0, { id: hexId(), node: deepClone(cValue) }) - path.model.set(path, v) - }, - }, - ] - return - {canToggle && {Octicon.chevron_down}} - {Octicon.trashcan} - {value.length > 1 && - {Octicon.chevron_up} - {Octicon.chevron_down} - } - - })} - {(value && value.length > 0 && value.length <= maxShown) && - {Octicon.plus_circle} - } - > -} - -function BooleanSuffix({ path, node, value, lang }: NodeProps) { - const set = (target: boolean) => { - path.model.set(path, node.optional() && value === target ? undefined : target) - } - return <> - set(false)}>{localize(lang, 'false')} - set(true)}>{localize(lang, 'true')} - > -} - -function NumberSuffix({ path, config, integer, value, lang }: NodeProps) { - const onChange = (evt: Event) => { - const value = (evt.target as HTMLInputElement).value - const parsed = integer ? parseInt(value) : parseFloat(value) - path.model.set(path, parsed) - } - const onColor = (evt: Event) => { - const value = (evt.target as HTMLInputElement).value - const parsed = parseInt(value.slice(1), 16) - path.model.set(path, parsed) - } - return <> - {if (evt.key === 'Enter') onChange(evt)}} /> - {config?.color && } - {config?.color && path.set(generateColor())} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_color')}>{Octicon.sync}} - {['dimension.generator.seed', 'dimension.generator.biome_source.seed', 'world_settings.seed', 'structure_placement.salt'].includes(path.getContext().join('.')) && newSeed(path.model)} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_seed')}>{Octicon.sync}} - > -} - -function StringSuffix({ path, getValues, config, node, value, lang, version, states }: NodeProps) { - const context = path.getContext().join('.') - const onChange = (evt: Event) => { - evt.stopPropagation() - const newValue = (evt.target as HTMLSelectElement).value - if (newValue === value) return - // Hackfix to support switching between checkerboard and multi_noise biome sources - if (context === 'dimension.generator.biome_source.type') { - const biomeSourceType = newValue.replace(/^minecraft:/, '') - const biomePath = path.pop().push('biomes') - const biomes = biomePath.get() - if (biomeSourceType === 'multi_noise') { - const newBiomes = Array.isArray(biomes) - ? biomes.flatMap((b: any) => { - if (typeof b.node !== 'string') return [] - return [{ node: { biome: b.node }}] - }) - : [{ node: { biome: 'minecraft:plains' } }] - path.model.set(biomePath, newBiomes, true) - } else if (biomeSourceType === 'checkerboard') { - const newBiomes = typeof biomes === 'string' - ? biomes - : Array.isArray(biomes) - ? biomes.flatMap((b: any) => { - if (typeof b.node !== 'object' || b.node === null || typeof b.node.biome !== 'string') return [] - return [{ node: b.node.biome }] - }) - : [{ node: 'minecraft:plains' }] - path.model.set(biomePath, newBiomes, true) - } - } - path.model.set(path, newValue.length === 0 ? undefined : newValue) - } - const values = getValues() - const id = !isEnum(config) && config?.validator === 'resource' && typeof config.params.pool === 'string' ? config.params.pool : undefined - - if (nbtFields.includes(context)) { - return - } else if ((isEnum(config) && !config.additional && !datalistEnums.includes(context)) || selectRegistries.includes(context)) { - let childPath = new Path([]) - if (isEnum(config) && typeof config.enum === 'string') { - childPath = childPath.contextPush(config.enum) - } else if (id) { - childPath = childPath.contextPush(id) - } else if (isEnum(config)) { - childPath = path - } else if (Object.hasOwn(forceEnumContexts, context)) { - childPath = childPath.contextPush(forceEnumContexts[context]) - } - return - {node.optional() && {localize(lang, 'unset')}} - {values.map(v => - {pathLocale(lang, childPath.contextPush(v.replace(/^minecraft:/, '')))} - )} - - } else if (!isEnum(config) && config?.validator === 'block_state_key') { - const blockState = states?.[relativePath(path, config.params.id).get()] - const values = Object.keys(blockState?.properties ?? {}) - return - {values.map(v => {v})} - - } else { - const { biomeColors, setBiomeColor } = useStore() - const fullId = typeof value === 'string' ? value.includes(':') ? value : 'minecraft:' + value : 'unknown' - const datalistId = hexId() - const gen = id ? findGenerator(id) : undefined - return <> - {if (evt.key === 'Enter') onChange(evt)}} - list={values.length > 0 ? datalistId : ''} /> - {values.length > 0 && - {values.map(v => )} - } - {['generator_biome.biome'].includes(context) && setBiomeColor(fullId, hexToRgb(v.currentTarget.value))}>} - {(['text_component_object.hoverEvent.show_entity.contents.id', 'enchantment.effects.entry.uuid'].includes(context) || ('attribute_modifier.id' === context && !checkVersion(version, '1.21'))) && path.set(generateUUID())} class="tooltipped tip-se" aria-label={localize(lang, 'generate_new_uuid')}>{Octicon.sync}} - {gen && values.includes(value) && value.startsWith('minecraft:') && - {Octicon.link_external}} - > - } -} - -type MenuAction = { - label: string, - description?: string, - icon: keyof typeof Octicon, - onSelect: () => unknown, -} - -type TreeNodeProps = { - schema: INode, - path: ModelPath, - value: any, - lang: string, - version: VersionId, - states: BlockStateRegistry, - ctx: Record, - compare?: any, - label?: string | ComponentChildren, - actions?: MenuAction[], - children?: ComponentChildren, -} -function TreeNode({ label, schema, path, value, lang, version, states, ctx, actions, children }: TreeNodeProps) { - const type = schema.type(path) - const category = schema.category(path) - const context = path.getContext().join('.') - - const [active, setActive] = useFocus() - const onContextMenu = (evt: MouseEvent) => { - evt.preventDefault() - setActive() - } - - const newCtx: Record = { ...ctx, depth: (ctx.depth ?? 0) + 1 } - delete newCtx.index - const [prefix, suffix, body] = schema.hook(renderHtml, path, value, lang, version, states, newCtx) - return - - - - {children} - {prefix} - - {label ?? pathLocale(lang, path, `${path.last()}`)} - {active && - {actions?.map(a => - a.onSelect()}/> - {a.description ?? localize(lang, a.label)} - )} - - navigator.clipboard.writeText(context)} /> - {context} - - } - - {suffix} - - {body && {body}} - -} - -const MemoedTreeNode = memo(TreeNode, (prev, next) => { - return prev.schema === next.schema - && prev.lang === next.lang - && prev.path.equals(next.path) - && deepEqual(prev.ctx, next.ctx) - && deepEqual(prev.value, next.value) -}) - -function isEnum(value?: ValidationOption | EnumOption): value is EnumOption { - return !!(value as any)?.enum -} - -function hashString(str: string) { - var hash = 0, i, chr - for (i = 0; i < str.length; i++) { - chr = str.charCodeAt(i) - hash = ((hash << 5) - hash) + chr - hash |= 0 - } - return hash -} - -function pathLocale(lang: string, path: Path, ...params: string[]) { - const ctx = path.getContext() - for (let i = 0; i < ctx.length; i += 1) { - const key = ctx.slice(i).join('.') - const result = localize(lang, key, ...params) - if (key !== result) { - return result - } - } - return ctx[ctx.length - 1] -} - -function ErrorPopup({ lang, path, nested }: { lang: string, path: ModelPath, nested?: boolean }) { - if (path.model instanceof ModelWrapper) { - path = path.model.map(path).withModel(path.model) - } - const e = nested - ? path.model.errors.getAll().filter(e => e.path.startsWith(path)) - : path.model.errors.get(path, true) - if (e.length === 0) return null - const message = localize(lang, e[0].error, ...(e[0].params ?? [])) - return popupIcon('node-error', 'issue_opened', message) -} - -function HelpPopup({ lang, path }: { lang: string, path: Path }) { - const key = path.contextPush('help').getContext().join('.') - const message = localize(lang, key) - if (message === key) return null - return popupIcon('node-help', 'info', message) -} - -const popupIcon = (type: string, icon: keyof typeof Octicon, popup: string) => { - const [active, setActive] = useFocus() - - return setActive()}> - {Octicon[icon]} - {popup} - -} - -function isDecorated(context: string | undefined, value: any) { - return context === 'feature' - && value?.type?.replace(/^minecraft:/, '') === 'decorated' - && isObject(value?.config) -} - -function createDecoratorsWrapper(originalFields: NodeChildren, path: ModelPath, value: any) { - const decorators: any[] = [] - const feature = iterateNestedDecorators(value, decorators) - const fields = { - type: originalFields.type, - config: ObjectNode({ - decorators: ListNode(CachedDecorator), - feature: CachedFeature, - }, { context: 'feature.decorated' }), - } - const schema = ObjectNode(fields, { context: 'feature' }) - const featurePath = new Path(['config', 'feature']) - const decoratorsPath = new Path(['config', 'decorators']) - const model = path.getModel() - const wrapper: ModelWrapper = new ModelWrapper(schema, path => { - if (path.startsWith(featurePath)) { - return new Path([...[...Array(decorators.length - 1)].flatMap(() => ['config', 'feature']), ...path.modelArr]) - } else if (path.startsWith(decoratorsPath)) { - if (path.modelArr.length === 2) { - return new Path([]) - } - const index = path.modelArr[2] - if (typeof index === 'number') { - return new Path([...[...Array(index)].flatMap(() => ['config', 'feature']), 'config', 'decorator', ...path.modelArr.slice(3)]) - } - } - return path - }, path => { - if (path.equals(decoratorsPath)) { - const newDecorators: any[] = [] - iterateNestedDecorators(model.data, newDecorators) - return newDecorators - } - return model.get(wrapper.map(path)) - }, (path, value, silent) => { - if (path.startsWith(featurePath)) { - const newDecorators: any[] = [] - iterateNestedDecorators(model.data, newDecorators) - const newPath =new Path([...[...Array(newDecorators.length - 1)].flatMap(() => ['config', 'feature']), ...path.modelArr]) - return model.set(newPath, value, silent) - } else if (path.startsWith(decoratorsPath)) { - const index = path.modelArr[2] - if (path.modelArr.length === 2) { - const feature = wrapper.get(featurePath) - return model.set(new Path(), produceNestedDecorators(feature, value), silent) - } else if (typeof index === 'number') { - if (path.modelArr.length === 3 && value === undefined) { - const feature = wrapper.get(featurePath) - const newDecorators: any[] = [] - iterateNestedDecorators(model.data, newDecorators) - newDecorators.splice(index, 1) - const newValue = produceNestedDecorators(feature, newDecorators) - return model.set(new Path(), newValue, silent) - } else { - const newPath = new Path([...[...Array(index)].flatMap(() => ['config', 'feature']), 'config', 'decorator', ...path.modelArr.slice(3)]) - return model.set(newPath, value, silent) - } - } - } - model.set(path, value, silent) - }) - wrapper.data = { - type: model.data.type, - config: { - decorators, - feature, - }, - } - wrapper.errors = model.errors - return { fields, wrapper } -} - -function iterateNestedDecorators(value: any, decorators: any[]): any { - if (value?.type?.replace(/^minecraft:/, '') !== 'decorated') { - return value - } - if (!isObject(value?.config)) { - return value - } - decorators.push({ id: decorators.length, node: value.config.decorator }) - return iterateNestedDecorators(value.config.feature ?? '', decorators) -} - -function produceNestedDecorators(feature: any, decorators: any[]): any { - if (decorators.length === 0) return feature - return { - type: 'minecraft:decorated', - config: { - decorator: decorators.shift().node, - feature: produceNestedDecorators(feature, decorators), - }, - } -} diff --git a/src/app/schema/transformOutput.ts b/src/app/schema/transformOutput.ts deleted file mode 100644 index b88aca7f..00000000 --- a/src/app/schema/transformOutput.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { DataModel, Hook } from '@mcschema/core' -import { ModelPath, relativePath } from '@mcschema/core' -import type { BlockStateRegistry } from '../services/index.js' - -export function getOutput(model: DataModel, blockStates: BlockStateRegistry): any { - return model.schema.hook(transformOutput, new ModelPath(model), model.data, { blockStates }) -} - -export type OutputProps = { - blockStates: BlockStateRegistry, -} -export const transformOutput: Hook<[any, OutputProps], any> = { - base({}, _, value) { - return value - }, - - choice({ switchNode }, path, value, props) { - return switchNode.hook(this, path, value, props) - }, - - list({ children }, path, value, props) { - if (!Array.isArray(value)) return value - const res = value.map((obj, index) => - children.hook(this, path.push(index), obj.node, props) - ) - for (const a of Object.getOwnPropertySymbols(value)) { - res[a as any] = value[a as any] - } - return res - }, - - map({ children, config }, path, value, props) { - if (value === undefined) return undefined - const blockState = config.validation?.validator === 'block_state_map'? props.blockStates?.[relativePath(path, config.validation.params.id).get()] : null - const res: any = {} - Object.keys(value).forEach(f => { - if (blockState) { - if (!Object.keys(blockState.properties ?? {}).includes(f)) return - } - res[f] = children.hook(this, path.push(f), value[f], props) - }) - for (const a of Object.getOwnPropertySymbols(value)) { - res[a as any] = value[a] - } - return res - }, - - object({ getActiveFields }, path, value, props) { - if (value === undefined || value === null || typeof value !== 'object') { - return value - } - const res: any = {} - const activeFields = getActiveFields(path) - Object.keys(activeFields) - .filter(k => activeFields[k].enabled(path)) - .forEach(f => { - const out = activeFields[f].hook(this, path.push(f), value[f], props) - if (out !== undefined && out !== null) { - res[f] = out - } - }) - for (const a of Object.getOwnPropertySymbols(value)) { - res[a as any] = value[a] - } - return res - }, -} diff --git a/src/app/services/DataFetcher.ts b/src/app/services/DataFetcher.ts index 830c811e..bf5059f3 100644 --- a/src/app/services/DataFetcher.ts +++ b/src/app/services/DataFetcher.ts @@ -1,20 +1,13 @@ -import type { CollectionRegistry } from '@mcschema/core' import config from '../Config.js' import { Store } from '../Store.js' import { message } from '../Utils.js' -import type { BlockStateRegistry, VersionId } from './Schemas.js' +import type { VersionId } from './Schemas.js' import { checkVersion } from './Schemas.js' const CACHE_NAME = 'misode-v2' const CACHE_LATEST_VERSION = 'cached_latest_version' const CACHE_PATCH = 'misode_cache_patch' -type Version = { - id: string, - ref?: string, - dynamic?: boolean, -} - declare var __LATEST_VERSION__: string export const latestVersion = __LATEST_VERSION__ ?? '' const mcmetaUrl = 'https://raw.githubusercontent.com/misode/mcmeta' @@ -46,51 +39,10 @@ async function validateCache(version: RefInfo) { } } -export async function fetchData(versionId: string, collectionTarget: CollectionRegistry, blockStateTarget: BlockStateRegistry) { - const version = config.versions.find(v => v.id === versionId) as Version | undefined - if (!version) { - console.error(`[fetchData] Unknown version ${version} in ${JSON.stringify(config.versions)}`) - return - } - - await validateCache(version) - - await Promise.all([ - _fetchRegistries(version, collectionTarget), - _fetchBlockStateMap(version, blockStateTarget), - ]) -} - -async function _fetchRegistries(version: Version, target: CollectionRegistry) { - console.debug(`[fetchRegistries] ${version.id}`) - try { - const data = await cachedFetch(`${mcmeta(version, 'summary')}/registries/data.min.json`) - for (const id in data) { - target.register(id, data[id].map((e: string) => 'minecraft:' + e)) - } - } catch (e) { - console.warn('Error occurred while fetching registries:', message(e)) - } -} - -async function _fetchBlockStateMap(version: Version, target: BlockStateRegistry) { - console.debug(`[fetchBlockStateMap] ${version.id}`) - try { - const data = await cachedFetch(`${mcmeta(version, 'summary')}/blocks/data.min.json`) - for (const id in data) { - target['minecraft:' + id] = { - properties: data[id][0], - default: data[id][1], - } - } - } catch (e) { - console.warn('Error occurred while fetching block state map:', message(e)) - } -} - export async function fetchRegistries(versionId: VersionId) { console.debug(`[fetchRegistries] ${versionId}`) const version = config.versions.find(v => v.id === versionId)! + await validateCache(version) try { const data = await cachedFetch(`${mcmeta(version, 'summary')}/registries/data.min.json`) const result = new Map() @@ -103,10 +55,16 @@ export async function fetchRegistries(versionId: VersionId) { } } +export interface BlockStateData { + properties: Record + default: Record +} + export async function fetchBlockStates(versionId: VersionId) { console.debug(`[fetchBlockStates] ${versionId}`) const version = config.versions.find(v => v.id === versionId)! - const result = new Map, default: Record}>() + const result = new Map() + await validateCache(version) try { const data = await cachedFetch(`${mcmeta(version, 'summary')}/blocks/data.min.json`) for (const id in data) { @@ -128,6 +86,7 @@ export async function fetchItemComponents(versionId: VersionId) { if (!checkVersion(versionId, '1.20.5')) { return result } + await validateCache(version) try { const data = await cachedFetch>>(`${mcmeta(version, 'summary')}/item_components/data.min.json`) for (const [id, components] of Object.entries(data)) { @@ -152,6 +111,7 @@ export async function fetchItemComponents(versionId: VersionId) { export async function fetchPreset(versionId: VersionId, registry: string, id: string) { console.debug(`[fetchPreset] ${versionId} ${registry} ${id}`) const version = config.versions.find(v => v.id === versionId)! + await validateCache(version) try { let url if (id.startsWith('immersive_weathering:')) { diff --git a/src/app/services/Schemas.ts b/src/app/services/Schemas.ts index f56ae22c..752ddb04 100644 --- a/src/app/services/Schemas.ts +++ b/src/app/services/Schemas.ts @@ -1,77 +1,27 @@ -import type { CollectionRegistry, INode, SchemaRegistry } from '@mcschema/core' -import { ChoiceNode, DataModel, Reference, StringNode } from '@mcschema/core' import config from '../Config.js' -import { initPartners } from '../partners/index.js' import { message } from '../Utils.js' -import { fetchData } from './DataFetcher.js' +import type { BlockStateData } from './DataFetcher.js' +import { fetchBlockStates, fetchRegistries } from './DataFetcher.js' export const VersionIds = ['1.15', '1.16', '1.17', '1.18', '1.18.2', '1.19', '1.19.3', '1.19.4', '1.20', '1.20.2', '1.20.3', '1.20.5', '1.21', '1.21.2'] as const export type VersionId = typeof VersionIds[number] export const DEFAULT_VERSION: VersionId = '1.21' -export type BlockStateRegistry = { - [block: string]: { - properties?: { - [key: string]: string[], - }, - default?: { - [key: string]: string, - }, - }, +interface VersionData { + registries: Map + blockStates: Map } -type VersionData = { - collections: CollectionRegistry, - schemas: SchemaRegistry, - blockStates: BlockStateRegistry, -} const Versions: Record> = {} -type ModelData = { - model: DataModel, - version: VersionId, -} -const Models: Record = {} - -const versionGetter: { - [versionId in VersionId]: () => Promise<{ - getCollections: () => CollectionRegistry, - getSchemas: (collections: CollectionRegistry) => SchemaRegistry, - }> -} = { - 1.15: () => import('@mcschema/java-1.15'), - 1.16: () => import('@mcschema/java-1.16'), - 1.17: () => import('@mcschema/java-1.17'), - 1.18: () => import('@mcschema/java-1.18'), - '1.18.2': () => import('@mcschema/java-1.18.2'), - 1.19: () => import('@mcschema/java-1.19'), - '1.19.3': () => import('@mcschema/java-1.19.3'), - '1.19.4': () => import('@mcschema/java-1.19.4'), - '1.20': () => import('@mcschema/java-1.20'), - '1.20.2': () => import('@mcschema/java-1.20.2'), - '1.20.3': () => import('@mcschema/java-1.20.3'), - '1.20.5': () => import('@mcschema/java-1.20.5'), - 1.21: () => import('@mcschema/java-1.21'), - '1.21.2': () => import('@mcschema/java-1.21.2'), -} - -export let CachedDecorator: INode -export let CachedFeature: INode -export let CachedCollections: CollectionRegistry -export let CachedSchemas: SchemaRegistry - async function getVersion(id: VersionId): Promise { if (!Versions[id]) { Versions[id] = (async () => { try { - const mcschema = await versionGetter[id]() - const collections = mcschema.getCollections() - const blockStates: BlockStateRegistry = {} - await fetchData(id, collections, blockStates) - const schemas = mcschema.getSchemas(collections) - initPartners(schemas, collections, id) - Versions[id] = { collections, schemas, blockStates } + const registries = await fetchRegistries(id) + const blockStates= await fetchBlockStates(id) + Versions[id] = { registries, blockStates } return Versions[id] } catch (e) { throw new Error(`Cannot get version "${id}": ${message(e)}`) @@ -82,65 +32,26 @@ async function getVersion(id: VersionId): Promise { return Versions[id] } -export async function getModel(version: VersionId, id: string): Promise { - if (!Models[id] || Models[id].version !== version) { - const versionData = await getVersion(version) - - CachedDecorator = Reference(versionData.schemas, 'configured_decorator') - CachedFeature = ChoiceNode([ - { - type: 'string', - node: StringNode(versionData.collections, { validator: 'resource', params: { pool: '$worldgen/configured_feature' } }), - }, - { - type: 'object', - node: Reference(versionData.schemas, 'configured_feature'), - }, - ], { choiceContext: 'feature' }) - - const schemaName = config.generators.find(g => g.id === id)?.schema - if (!schemaName) { - throw new Error(`Cannot find model ${id}`) - } - try { - const schema = versionData.schemas.get(schemaName) - const model = new DataModel(schema, { wrapLists: true }) - if (Models[id]) { - model.reset(Models[id].model.data, false) - } else { - model.validate(true) - model.history = [JSON.stringify(model.data)] - } - Models[id] = { model, version } - } catch (e) { - const err = new Error(`Cannot get generator "${id}" for version "${version}": ${message(e)}`) - if (e instanceof Error) err.stack = e.stack - throw err - } - } - return Models[id].model -} - -export async function getCollections(version: VersionId): Promise { - const versionData = await getVersion(version) - CachedCollections = versionData.collections - return versionData.collections -} - -export async function getBlockStates(version: VersionId): Promise { +export async function getBlockStates(version: VersionId): Promise> { const versionData = await getVersion(version) return versionData.blockStates } -export async function getSchemas(version: VersionId): Promise { - const versionData = await getVersion(version) - CachedSchemas = versionData.schemas - return versionData.schemas -} - export function checkVersion(versionId: string, minVersionId: string | undefined, maxVersionId?: string) { const version = config.versions.findIndex(v => v.id === versionId) const minVersion = minVersionId ? config.versions.findIndex(v => v.id === minVersionId) : 0 const maxVersion = maxVersionId ? config.versions.findIndex(v => v.id === maxVersionId) : config.versions.length - 1 return minVersion <= version && version <= maxVersion } + +export interface FileModel { + get text(): string + get data(): any +} + +export function createMockFileModel(): FileModel { + return { + text: '{}', + data: {}, + } +}