UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

1,035 lines (1,033 loc) 78.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const Utilities_1 = require("../core/Utilities"); const IField_1 = require("../dataform/IField"); const Database_1 = require("../minecraft/Database"); const LegacyDocumentationDefinition_1 = require("../minecraft/docs/LegacyDocumentationDefinition"); const JsonSchemaDefinition_1 = require("../jsonschema/JsonSchemaDefinition"); const StorageUtilities_1 = require("../storage/StorageUtilities"); const ContentIndex_1 = require("../core/ContentIndex"); const DataFormUtilities_1 = require("../dataform/DataFormUtilities"); const EntityTypeDefinition_1 = require("../minecraft/EntityTypeDefinition"); const ICondition_1 = require("../dataform/ICondition"); const FieldUtilities_1 = require("../dataform/FieldUtilities"); const JsonFormExclusionList = ["is_a", "in_the", "_with_"]; const MAX_FORM_DEPTH = 100; class FormJsonDocumentationGenerator { constructor() { this.defsById = {}; this.defsByTitle = {}; this.defRefs = {}; this.defCategories = {}; } async updateFormSource(folder, isPreview) { this.defsById = {}; this.defsByTitle = {}; const metadataFolder = isPreview ? await Database_1.default.loadPreviewMetadataFolder() : await Database_1.default.loadReleaseMetadataFolder(); const schemaFolder = metadataFolder?.ensureFolder("json_schemas"); if (schemaFolder) { await this.loadSchemas(schemaFolder, "misc"); } const formJsonFolder = folder.ensureFolder("forms"); await formJsonFolder.ensureExists(); await this.exportJsonSchemaForms(formJsonFolder); const aiGoalsNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Server Entity Documentation/AI Goals/", isPreview); if (aiGoalsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, aiGoalsNode, "entity"); } const attributesNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Server Entity Documentation/Attributes/", isPreview); if (attributesNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, attributesNode, "entity"); } const propertiesNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Server Entity Documentation/Properties/", isPreview); if (propertiesNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, propertiesNode, "entity"); } const entityComponentsNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Server Entity Documentation/Components/", isPreview); if (entityComponentsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, entityComponentsNode, "entity"); } const triggersComponentsNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Server Entity Documentation/Triggers/", isPreview); if (triggersComponentsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, triggersComponentsNode, "entity"); } const filtersComponentsNode = await LegacyDocumentationDefinition_1.default.loadNode("entities", "/Filters/", isPreview); if (filtersComponentsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, filtersComponentsNode, "entityfilters"); } const entityEventsComponentsNode = await LegacyDocumentationDefinition_1.default.loadNode("entity-events", "/", isPreview); if (entityEventsComponentsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, entityEventsComponentsNode, "entityevents"); } const blocksComponentsNode = await LegacyDocumentationDefinition_1.default.loadNode("blocks", "/Blocks/Block Components/", isPreview); if (blocksComponentsNode) { await this.generateFormNodesFromLegacyDocNode(formJsonFolder, blocksComponentsNode, "block"); } const schemasNode = await LegacyDocumentationDefinition_1.default.loadNode("schemas", "/Schemas/", isPreview); if (schemasNode) { await this.generateFormNodesFromPseudoSchemaDocs(formJsonFolder, schemasNode, "visual"); } const fogsNode = await LegacyDocumentationDefinition_1.default.loadNode("fogs", "/Fog Definitions/Fog Schema/", isPreview); if (fogsNode) { await this.generateFormNodesFromPseudoSchemaDocs(formJsonFolder, fogsNode, "fogs"); } /* These come from JSON schema now. const biomesNode = await LegacyDocumentationDefinition.loadNode("biomes", "/Schema/", isPreview); if (biomesNode) { await this.generateFormNodesFromNode(formJsonFolder, biomesNode, "biomes"); } */ const featuresNode = await LegacyDocumentationDefinition_1.default.loadNode("features", "/Supported features/", isPreview); if (featuresNode) { const resultForms = await this.generateFormNodesFromPseudoSchemaDocs(formJsonFolder, featuresNode, "features"); if (resultForms) { this.generateSubformsFromFields(formJsonFolder, resultForms, "features"); } } } async generateSubformsFromFields(formJsonFolder, resultForms, prefix) { const outerForms = []; for (const form of resultForms) { if (form && form.fields) { for (const field of form.fields) { if (field.subForm) { const newForm = { id: field.id, title: field.title, fields: field.subForm.fields, }; outerForms.push(newForm); } } } } for (const form of outerForms) { if (form && form.id) { const name = this.getFormFileName(form.id, form.dataVersion); DataFormUtilities_1.default.mergeFields(form); DataFormUtilities_1.default.fixupFields(form); await this.annotateFormJson(form, name, prefix); await this.mergeToFile(formJsonFolder, name, form, prefix); } } return outerForms; } async generateFormNodesFromPseudoSchemaDocs(formJsonFolder, node, prefix) { if (!node.description && !node.examples) { return undefined; } const formStack = []; let formStackIndex = -1; const outerForms = []; const objectSkippedAt = []; let lastField = []; let ignoreNextObject = 0; let integrateNextProperty = false; let integrateNextNextProperty = false; let nodeSet = node.description; if (!nodeSet && node.examples) { nodeSet = []; for (const examp of node.examples) { nodeSet.push(...examp.text); } } for (const docLine of nodeSet) { let docLineMod = docLine; let commentStr = undefined; integrateNextNextProperty = false; let commentIndex = docLine.indexOf(" //"); if (commentIndex > 0) { commentStr = docLine.substring(commentIndex + 3); docLineMod = docLine.substring(0, commentIndex); } const docLineTrim = docLineMod.replace(/ /gi, "").trim(); const startQuote = docLineMod.indexOf('"'); let endQuote = docLineMod.lastIndexOf('"'); const endCompare = docLineMod.lastIndexOf(">"); let mainStr = undefined; let endStr = undefined; if (startQuote >= 0 && endQuote > startQuote) { if (endCompare === endQuote + 1) { endQuote = endCompare; } mainStr = docLineMod.substring(startQuote + 1, endQuote); endStr = docLineMod.substring(endQuote + 1); } if (docLineTrim.endsWith(":{") && !mainStr) { // this is the pattern from schemas.json const firstColon = docLineTrim.indexOf(":"); if (firstColon >= 0) { formStackIndex++; for (let i = formStackIndex; i < MAX_FORM_DEPTH; i++) { lastField[i] = undefined; objectSkippedAt[i] = 0; } const form = { id: docLineTrim.substring(0, firstColon), fields: [] }; outerForms.push(form); formStack[formStackIndex] = form; const secondColon = docLineTrim.indexOf(":", firstColon + 1); if (secondColon > firstColon) { const verStr = docLineTrim.substring(firstColon + 1, secondColon); if (Utilities_1.default.isVersionString(verStr)) { form.dataVersion = Utilities_1.default.normalizeVersionString(verStr); } } } } else if (docLineTrim === "{" && formStackIndex < 0) { // this is the pattern from fogs.json, features.json (only one form/object, typically) - handle the outer level form formStackIndex++; for (let i = formStackIndex; i < MAX_FORM_DEPTH; i++) { lastField[i] = undefined; objectSkippedAt[i] = 0; } const form = { id: (prefix ? prefix : "obj") + (outerForms.length > 0 ? outerForms.length + 1 : ""), fields: [], }; outerForms.push(form); formStack[formStackIndex] = form; } else if (docLineTrim === "{") { if (ignoreNextObject > 0) { ignoreNextObject--; objectSkippedAt[formStackIndex]++; } else { const lastFieldStack = lastField[formStackIndex]; formStackIndex++; for (let i = formStackIndex; i < MAX_FORM_DEPTH; i++) { lastField[i] = undefined; objectSkippedAt[i] = 0; } if (lastFieldStack && !lastFieldStack.subForm) { const form = { id: undefined, fields: [] }; formStack[formStackIndex] = form; lastFieldStack.subForm = form; } } } else if (docLineTrim === "}" && formStackIndex >= 0) { if (objectSkippedAt[formStackIndex] > 0) { objectSkippedAt[formStackIndex]--; } else { formStackIndex--; } } if (formStackIndex >= 0 && formStack[formStackIndex] && mainStr) { let fieldDefinition = undefined; if (docLineTrim.startsWith('int"')) { fieldDefinition = { dataType: IField_1.FieldDataType.int, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } else if (docLineTrim.startsWith('bool"')) { fieldDefinition = { dataType: IField_1.FieldDataType.boolean, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } else if (docLineTrim.startsWith('string"')) { fieldDefinition = { dataType: IField_1.FieldDataType.string, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } else if (docLineTrim.startsWith('molang"')) { fieldDefinition = { dataType: IField_1.FieldDataType.molang, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } else if (docLineTrim.startsWith('array"')) { fieldDefinition = { dataType: IField_1.FieldDataType.stringArray, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; const firstArrow = mainStr.indexOf("<"); const secondArrow = mainStr.indexOf(">"); const lastFieldStack = lastField[formStackIndex - 1]; if (firstArrow >= 0 && secondArrow > firstArrow) { if (lastFieldStack && (lastFieldStack.dataType === IField_1.FieldDataType.object || lastFieldStack.dataType === IField_1.FieldDataType.objectArray)) { lastFieldStack.dataType = IField_1.FieldDataType.keyedObjectCollection; lastFieldStack.keyDescription = mainStr; } } integrateNextNextProperty = true; ignoreNextObject++; } else if (docLineTrim.startsWith('enumerated_value"')) { const firstArrow = mainStr.indexOf("<"); const secondArrow = mainStr.indexOf(">"); if (firstArrow >= 0 && secondArrow > firstArrow) { const fieldId = mainStr.substring(0, firstArrow); const choiceStr = mainStr.substring(firstArrow + 1, secondArrow); const choices = choiceStr.split(","); const choiceSet = []; for (const choice of choices) { if (choice.length > 0) { choiceSet.push({ id: Utilities_1.default.removeQuotes(choice) }); } } fieldDefinition = { dataType: IField_1.FieldDataType.string, id: fieldId, choices: choiceSet, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } else { fieldDefinition = { dataType: IField_1.FieldDataType.string, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } } else if (docLineTrim.startsWith('object"')) { let fieldDataType = IField_1.FieldDataType.object; const firstArrow = mainStr.indexOf("<"); const secondArrow = mainStr.indexOf(">"); const lastFieldStack = lastField[formStackIndex - 1]; const curFieldStack = lastField[formStackIndex]; if (firstArrow >= 0 && secondArrow > firstArrow) { const subStr = mainStr.substring(firstArrow, secondArrow - firstArrow); if (subStr.indexOf("array") >= 0 && curFieldStack) { if (!curFieldStack.alternates) { curFieldStack.alternates = []; } const newField = { id: curFieldStack.id, dataType: IField_1.FieldDataType.objectArray, }; curFieldStack.alternates.push(newField); lastField[formStackIndex] = newField; } else { if (lastFieldStack && (lastFieldStack.dataType === IField_1.FieldDataType.object || lastFieldStack.dataType === IField_1.FieldDataType.objectArray)) { lastFieldStack.dataType = IField_1.FieldDataType.keyedObjectCollection; lastFieldStack.keyDescription = mainStr; } ignoreNextObject++; } } else { fieldDefinition = { dataType: fieldDataType, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } } else if (docLineTrim.startsWith('version"')) { fieldDefinition = { dataType: IField_1.FieldDataType.version, id: mainStr, title: Utilities_1.default.humanifyMinecraftName(mainStr), }; } if (fieldDefinition) { if (integrateNextProperty && fieldDefinition.id.indexOf("<") >= 0) { if (fieldDefinition.dataType === IField_1.FieldDataType.molang) { const lastFieldStack = lastField[formStackIndex]; if (lastFieldStack) { lastFieldStack.dataType = IField_1.FieldDataType.molangArray; } } integrateNextProperty = false; } else { integrateNextProperty = false; if (commentStr) { fieldDefinition.description = commentStr.trim(); } if (docLineTrim.indexOf(":opt") >= 0) { fieldDefinition.isRequired = false; } else { fieldDefinition.isRequired = true; } lastField[formStackIndex] = fieldDefinition; formStack[formStackIndex].fields.push(fieldDefinition); } } if (integrateNextNextProperty) { integrateNextProperty = true; } } } for (const form of outerForms) { if (form && form.id) { const name = this.getFormFileName(form.id, form.dataVersion); DataFormUtilities_1.default.mergeFields(form); DataFormUtilities_1.default.fixupFields(form); await this.annotateFormJson(form, name, prefix); await this.mergeToFile(formJsonFolder, name, form, prefix); } } return outerForms; } async generateFormJson(inputFolder, outputFolder) { await outputFolder.deleteAllFolderContents(); await this.generateFormJsonFromFolder(inputFolder, outputFolder); } async generateFormJsonFromFolder(inputFolder, outputFolder) { await outputFolder.ensureExists(); await inputFolder.load(); const fileList = { files: [], folders: [] }; for (const folderName in inputFolder.folders) { const folder = inputFolder.folders[folderName]; if (folder) { await this.generateFormJsonFromFolder(folder, outputFolder.ensureFolder(folderName)); fileList.folders.push(folderName); } } for (const fileName in inputFolder.files) { const file = inputFolder.files[fileName]; try { if (file) { await file.loadContent(); const jsonO = StorageUtilities_1.default.getJsonObject(file); if (jsonO) { const outputFile = outputFolder.ensureFile(fileName); fileList.files.push(fileName); await this.finalizeJsonForm(jsonO, outputFile); } } } catch (e) { console.log("Error processing " + fileName + ": " + e); } } const indexFile = outputFolder.ensureFile("index.json"); indexFile.setContent(JSON.stringify(fileList)); await indexFile.saveContent(); } async finalizeJsonForm(formObj, outputFile) { if (!formObj.generated_doNotEdit && !formObj.generatedFromSchema_doNotEdit && formObj.id) { const id = formObj.id.replace(/\:/gi, "_").replace(/\./gi, "_"); await outputFile.loadContent(); const originalNode = StorageUtilities_1.default.getJsonObject(outputFile); await this.annotateFormJson(formObj, id, outputFile.parentFolder.name, originalNode); } if (formObj.generatedFromSchema_doNotEdit) { this.mergeOntoForm(formObj, formObj.generatedFromSchema_doNotEdit); } if (formObj.generated_doNotEdit) { this.mergeOntoForm(formObj, formObj.generated_doNotEdit); } formObj.generated_doNotEdit = undefined; formObj.generatedFromSchema_doNotEdit = undefined; outputFile.setContent(JSON.stringify(formObj, undefined, 2)); await outputFile.saveContent(); } mergeOntoForm(formObj, genForm) { if (!formObj.description || formObj.description === "") { formObj.description = genForm.description; } if (!formObj.title || formObj.title === "") { formObj.title = genForm.title; } if (formObj.samples) { for (const samplePath in genForm.samples) { formObj.samples[samplePath] = genForm.samples[samplePath]; } } else { formObj.samples = genForm.samples; } if (!formObj.id) { formObj.id = genForm.id; } if (!formObj.note) { formObj.note = genForm.note; } if (!formObj.note2) { formObj.note2 = genForm.note2; } if (!formObj.note3) { formObj.note3 = genForm.note3; } if (!formObj.restrictions) { formObj.restrictions = genForm.restrictions; } if (!formObj.requires) { formObj.requires = genForm.requires; } if (!formObj.scalarField) { formObj.scalarField = genForm.scalarField; } if (!formObj.customField) { formObj.customField = genForm.customField; } if (!formObj.scalarFieldUpgradeName) { formObj.scalarFieldUpgradeName = genForm.scalarFieldUpgradeName; } if (!formObj.isDeprecated) { formObj.isDeprecated = genForm.isDeprecated; } if (!formObj.tags) { formObj.tags = genForm.tags; } if (!formObj.isInternal) { formObj.isInternal = genForm.isInternal; } if (!formObj.dataVersion) { formObj.dataVersion = genForm.dataVersion; } if (formObj.fields && formObj.fields.length === 0) { formObj.id = genForm.id; formObj.fields = genForm.fields; } else { const formFields = {}; if (!formObj.fields) { formObj.fields = genForm.fields; } else { for (const targetField of formObj.fields) { formFields[targetField.id] = targetField; } for (const field of genForm.fields) { const targetField = formFields[field.id]; if (!targetField) { formObj.fields.push(field); } else { targetField.samples = field.samples; if (!targetField.defaultValue) { targetField.defaultValue = field.defaultValue; } if (!targetField.alternates) { targetField.alternates = field.alternates; } if (!targetField.description) { targetField.description = field.description; } if (!targetField.title) { targetField.title = field.title; } if (!targetField.humanifyValues) { targetField.humanifyValues = field.humanifyValues; } if (!targetField.tags) { targetField.tags = field.tags; } if (!targetField.minLength) { targetField.minLength = field.minLength; } if (!targetField.maxLength) { targetField.maxLength = field.maxLength; } if (!targetField.minValue) { targetField.minValue = field.minValue; } if (!targetField.maxValue) { targetField.maxValue = field.maxValue; } if (!targetField.suggestedMinValue) { targetField.suggestedMinValue = field.suggestedMinValue; } if (!targetField.suggestedMaxValue) { targetField.suggestedMaxValue = field.suggestedMaxValue; } if (!targetField.isRequired) { targetField.isRequired = field.isRequired; } if (targetField.dataType === undefined) { targetField.dataType = field.dataType; } if (!targetField.choices) { targetField.choices = field.choices; } if (!targetField.validity) { targetField.validity = field.validity; } } } } } } async exportJsonSchemaForms(formJsonFolder) { for (const key in this.defsByTitle) { if (!this.defRefs[key]) { await this.processAndExportJsonSchemaNode(formJsonFolder, key); } } } async processAndExportJsonSchemaNode(formJsonFolder, title) { const formNode = await this.getJsonFormFromJsonSchemaKey(title); if (formNode && formNode.fields.length > 0) { if (title.indexOf("omponents") >= 0) { if (formNode.fields) { for (const field of formNode.fields) { if (field.subForm) { const name = this.getFormFileNameBase(field.id); const category = this.defCategories[title]; field.subForm.id = field.id; await this.annotateFormJson(field.subForm, name, category); await this.mergeToFile(formJsonFolder, name, field.subForm, category, true); } } } } else { let versionlessTitle = title.substring(title.lastIndexOf(".") + 1); versionlessTitle = this.getVersionlessString(versionlessTitle); const name = this.getFormFileNameBase(versionlessTitle); let matchesExclusion = false; for (const exclusion of JsonFormExclusionList) { if (name.indexOf(exclusion) >= 0) { matchesExclusion = true; break; } } if (!matchesExclusion) { const category = this.defCategories[title]; await this.annotateFormJson(formNode, name, category); await this.mergeToFile(formJsonFolder, name, formNode, category, true); } } } } getFormFileNameBase(key) { key = key.toLowerCase(); if (key.startsWith("struct_") || key.startsWith("struct ")) { key = key.substring(7); } if ((key.startsWith("enum_") || key.startsWith("enum ")) && key.indexOf("num_property") < 0) { key = key.substring(5); } key = key.replace("sharedtypes", ""); key = StorageUtilities_1.default.sanitizePathBasic(key); return key; } getVersionlessString(key) { let verStart = key.indexOf(" v1."); if (verStart >= 0) { const nextSpace = key.indexOf(" ", verStart + 4); if (nextSpace >= 0) { key = key.substring(0, verStart) + key.substring(nextSpace + 1); } else { key = key.substring(0, verStart); } } return key; } async getJsonFormFromJsonSchemaKey(keyName) { let rootNodeName = undefined; let rootNodeNameVersionless = undefined; let rootNode = undefined; const keyVersionless = this.getVersionlessString(keyName); // attempt to get the latest version of a component by sorting on the node name e.g., minecraft:item v1.21.60 should sort later than minecraft:item v1.21.40 // though we should replace this with a more sophisticated sorter for version :-/ for (const candidateKey in this.defsByTitle) { const candidateKeyVersionless = this.getVersionlessString(candidateKey); if (candidateKeyVersionless === keyVersionless) { if (!rootNodeName || (candidateKey.localeCompare(rootNodeName) > 0 && candidateKeyVersionless === rootNodeNameVersionless)) { rootNodeName = candidateKey; rootNodeNameVersionless = candidateKeyVersionless; rootNode = this.defsByTitle[candidateKey]; } } } for (const key in this.defsById) { if (key.indexOf(keyName) >= 0) { const keyVersionless = this.getVersionlessString(key); if (!rootNodeName || (key.localeCompare(rootNodeName) > 0 && keyVersionless === rootNodeNameVersionless)) { rootNodeName = key; rootNodeNameVersionless = keyVersionless; rootNode = this.defsByTitle[key]; } } } if (rootNode === undefined) { return; } if (rootNodeName !== keyName) { return; } return await this.getJsonFormFromJsonSchemaDefinition(rootNode, keyName); } async getJsonFormFromJsonSchemaDefinition(node, nodeName, fieldList) { const fields = []; if (node.properties) { for (const propName in node.properties) { const propNode = node.properties[propName]; if (propNode && typeof propNode !== "boolean") { const field = await this.getFieldFromJsonPropertyNode(propNode, propName, fieldList); if (field) { fields.push(field); } } } } if (!nodeName) { if (node.title) { nodeName = node.title; } else { nodeName = ""; } } const docForm = { title: Utilities_1.default.humanifyMinecraftName(nodeName), description: node.description ? node.description : Utilities_1.default.humanifyMinecraftName(nodeName), fields: fields, }; return docForm; } async loadSchemas(schemaFolder, categoryName) { await schemaFolder.load(); for (const fileName in schemaFolder.files) { const file = schemaFolder.files[fileName]; if (file && file.type === "json" && file.name !== "index.json") { const jsonSchema = await JsonSchemaDefinition_1.default.ensureOnFile(file); if (jsonSchema && jsonSchema.data) { this.processDef(jsonSchema.data, categoryName); } } } for (const folderName in schemaFolder.folders) { const folder = schemaFolder.folders[folderName]; if (folder) { if (!folder.name.startsWith("v1") && folder.name !== "common" && folder.name !== "components") { categoryName = folder.name; } if (categoryName.startsWith("1.")) { categoryName = "misc"; } if ((schemaFolder.parentFolder?.name === "client" || schemaFolder.parentFolder?.parentFolder?.name === "client") && categoryName.indexOf("client") < 0) { categoryName = "client_" + categoryName; } await this.loadSchemas(folder, categoryName); } } } processDef(schemaDef, category, depth = 0) { if (schemaDef["$id"]) { this.defsById[schemaDef["$id"]] = schemaDef; if (this.defRefs[schemaDef["$id"]] === undefined) { this.defRefs[schemaDef["$id"]] = depth ? 1 : 0; } if (schemaDef.title && category.indexOf("client") < 0) { if (schemaDef.title?.indexOf("lient ") >= 0 && category.indexOf("client") < 0) { category = "client_" + category; } } this.defCategories[schemaDef["$id"]] = category; } if (schemaDef.title) { this.defsByTitle[category + "." + schemaDef.title] = schemaDef; if (this.defRefs[category + "." + schemaDef.title] === undefined) { this.defRefs[category + "." + schemaDef.title] = depth ? 1 : 0; } this.defCategories[category + "." + schemaDef.title] = category; } for (const propName in schemaDef.properties) { const propNode = schemaDef.properties[propName]; if (propNode && typeof propNode !== "boolean") { if (propNode.$ref) { if (this.defRefs[propNode.$ref] === undefined) { this.defRefs[propNode.$ref] = 1; } else { this.defRefs[propNode.$ref]++; } } } } if (schemaDef.definitions) { for (const defName in schemaDef.definitions) { const def = schemaDef.definitions[defName]; if (def && typeof def !== "boolean") { this.defsById[defName.toString()] = def; this.processDef(def, category, depth + 1); } } } } async generateFormNodesFromLegacyDocNode(formJsonFolder, node, prefix) { for (const childNode of node.nodes) { if (childNode.name) { const name = this.getFormFileName(childNode.name); const formDocNode = this.getFormFromDocNode(childNode, childNode.name); const genFormDocNode = await this.getOriginalFormDefinition(formJsonFolder, name, prefix); await this.annotateFormJson(formDocNode, name, prefix, genFormDocNode); await this.mergeToFile(formJsonFolder, name, formDocNode, prefix); } } } async getOriginalFormDefinition(formJsonFolder, name, categoryName) { if (categoryName && categoryName.length > 0) { formJsonFolder = formJsonFolder.ensureFolder(categoryName); } name = name.toLowerCase(); name = name.replace(/ /gi, "_"); const file = formJsonFolder.ensureFile(name + ".form.json"); await file.loadContent(); return StorageUtilities_1.default.getJsonObject(file); } async mergeToFile(formJsonFolder, name, formDefNode, categoryName, isSchema) { if (categoryName && categoryName.length > 0) { formJsonFolder = formJsonFolder.ensureFolder(categoryName); } name = name.toLowerCase(); name = name.replace(/ /gi, "_"); const file = formJsonFolder.ensureFile(name + ".form.json"); await file.loadContent(); let jsonO = StorageUtilities_1.default.getJsonObject(file); if (isSchema) { if (jsonO) { jsonO.generatedFromSchema_doNotEdit = formDefNode; } else { jsonO = { id: formDefNode.id, fields: [], generatedFromSchema_doNotEdit: formDefNode, }; } } else { if (jsonO) { jsonO.generated_doNotEdit = formDefNode; } else { jsonO = { id: formDefNode.id, fields: [], generated_doNotEdit: formDefNode, }; } } file.setContent(JSON.stringify(jsonO, undefined, 2)); await file.saveContent(); } async annotateFormJson(formDefNode, name, prefix, originalNode) { let canonName = EntityTypeDefinition_1.default.getComponentFromBaseFileName(name); let isMinecraftComponent = false; if (name.startsWith("minecraft_")) { isMinecraftComponent = true; if (canonName.startsWith("behavior.")) { let hasPrioNode = false; for (const field of formDefNode.fields) { if (field.id === "priority") { hasPrioNode = true; } } if (!hasPrioNode) { formDefNode.fields.push({ id: "priority", title: "Priority", hideSamples: true, description: "As priority approaches 0, the priority is increased. The higher the priority, the sooner this behavior will be executed as a goal.", dataType: 0, }); } } } if (prefix === "entity" && isMinecraftComponent) { await this.addVanillaMatches(formDefNode, canonName, [ ContentIndex_1.AnnotationCategory.entityComponentDependent, ContentIndex_1.AnnotationCategory.entityComponentDependentInGroup, ]); await this.addSamplesMatches(formDefNode, canonName, [ ContentIndex_1.AnnotationCategory.entityComponentDependent, ContentIndex_1.AnnotationCategory.entityComponentDependentInGroup, ]); } else if (prefix === "item" && isMinecraftComponent) { await this.addVanillaMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.itemComponentDependent]); await this.addSamplesMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.itemComponentDependent]); } else if (prefix === "block" && isMinecraftComponent) { await this.addVanillaMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.blockComponentDependent]); await this.addSamplesMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.blockComponentDependent]); } else if (prefix === "entityfilters") { await this.addVanillaMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.entityFilter]); await this.addSamplesMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.entityFilter]); } else if (prefix === "features") { await this.addVanillaMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.entityFilter]); await this.addSamplesMatches(formDefNode, canonName, [ContentIndex_1.AnnotationCategory.entityFilter]); } if (formDefNode.samples) { this.distributeSampleValues(formDefNode.samples, formDefNode, [], originalNode); } } async distributeSampleValues(samplesNode, formDefNode, pathTokens, originalNode) { if (!formDefNode.fields) { return; } const fieldsById = {}; for (const field of formDefNode.fields) { fieldsById[field.id] = field; } const originalFieldsById = {}; if (originalNode && originalNode.fields) { for (const field of originalNode.fields) { originalFieldsById[field.id] = field; } } for (const exampleFilePath in samplesNode) { const sampleList = samplesNode[exampleFilePath]; if (sampleList) { for (const sample of sampleList) { if (sample.content && typeof sample.content === "object") { let obj = sample.content; if (obj) { for (const fieldName in obj) { const fieldVal = obj[fieldName]; let subForm = undefined; if (!fieldsById[fieldName] && (!originalNode || !originalNode.customField || originalFieldsById[fieldName])) { let dataType = IField_1.FieldDataType.string; if (fieldName.startsWith("on_")) { dataType = IField_1.FieldDataType.minecraftEventTrigger; } else { if (typeof fieldVal === "number") { dataType = IField_1.FieldDataType.number; } else if (Array.isArray(fieldVal)) { if (fieldVal.length > 0) { if (typeof fieldVal[0] === "object") { dataType = IField_1.FieldDataType.objectArray; subForm = DataFormUtilities_1.default.generateFormFromObject(fieldName, fieldVal[0], exampleFilePath); } else if (typeof fieldVal[0] === "string") { dataType = IField_1.FieldDataType.stringArray; } } } else if (typeof fieldVal === "object") { dataType = IField_1.FieldDataType.object; subForm = DataFormUtilities_1.default.generateFormFromObject(fieldName, fieldVal, exampleFilePath); } } const newField = { id: fieldName, title: Utilities_1.default.humanifyMinecraftName(fieldName), dataType: dataType, subForm: subForm, }; fieldsById[fieldName] = newField; formDefNode.fields.push(newField); } } } } } } } for (const field of formDefNode.fields) { const pathTokensToSearch = pathTokens.slice(); pathTokensToSearch.push(field.id); for (const exampleFilePath in samplesNode) { const sampleList = samplesNode[exampleFilePath]; if (sampleList) { for (const sample of sampleList) { if (sample.content && typeof sample.content === "object") { let obj = sample.content; for (let i = 0; i < pathTokensToSearch.length; i++) { if (obj && obj[pathTokensToSearch[i]] !== undefined) { obj = obj[pathTokensToSearch[i]]; } else { obj = undefined; } } if (obj) { let exampleInstanceCount = 0; if (!field.samples) { field.samples = {}; } if (field.subForm) { let exampleObj = obj; if (Array.isArray(exampleObj) && exampleObj.length > 0) { const subSamples = {}; let subSamplesAdded = 0; const path = exampleFilePath; for (const subObj of exampleObj) { if (typeof subObj === "object") { if (!subSamples[path]) { subSamples[path] = []; } subSamples[path].push({ path: sample.path + "|" + pathTokensToSearch.join(".") + "|" + subSamplesAdded.toString(), content: subObj, }); subSamplesAdded++; } } if (subSamplesAdded > 0) { await this.distributeSampleValues(subSamples, field.subForm, pathTokensToSearch); } } } const exampleSamp = JSON.stringify(obj); const path = exampleFilePath; for (const path in field.samples) { for (let i = 0; i < field.samples[path].length; i++) { if (JSON.stringify(field.samples[path][i].content) === exampleSamp) { exampleInstanceCount++; } } } if (exampleInstanceCount < 2) { if (!field.samples[path]) { field.samples[path] = []; } field.samples[path].push({ path: sample.path, content: obj }); } } } } } } } } async addVanillaMatches(formDefNode, name, annotations) { const vanillaMatches = await Database_1.default.getPreviewVanillaMatches(name, true, annotations); if (vanillaMatches && vanillaMatches.length > 0) { if (!formDefNode.samples) { formDefNode.samples = {}; } const vanillaPreview = await Database_1.default.getPreviewVanillaFolder(); if (!vanillaPreview) { return; } for (const match of vanillaMatches) { if (match.value.startsWith("/") && match.value.indexOf("metadata") < 0) { const file = await vanillaPreview.getFileFromRelativePath(match.value); if (file && (await file.exists())) { await file.loadContent(); const jsonO = StorageUtilities_1.default.getJsonObject(file); if (jsonO) { if (!formDefNode.samples["/vanilla" + match.value]) { formDefNode.samples["/vanilla" + match.value] = []; } this.appendNodesByName(formDefNode.samples["/vanilla" + match.value], "minecraft:" + name, jsonO, "/"); } } } }