UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

959 lines 76.6 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ProjectMetaCategory = exports.NewItemTypeAddMode = exports.NewEntityTypeAddMode = void 0; const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const MinecraftUtilities_1 = __importDefault(require("../minecraft/MinecraftUtilities")); const Project_1 = require("./Project"); const IProjectItemData_1 = require("./IProjectItemData"); const Database_1 = __importDefault(require("../minecraft/Database")); const Log_1 = __importDefault(require("../core/Log")); const Utilities_1 = __importDefault(require("../core/Utilities")); const IProjectData_1 = require("./IProjectData"); const BehaviorManifestDefinition_1 = __importDefault(require("../minecraft/BehaviorManifestDefinition")); const NpmPackageDefinition_1 = __importDefault(require("../devproject/NpmPackageDefinition")); const ResourceManifestDefinition_1 = __importDefault(require("../minecraft/ResourceManifestDefinition")); const ProjectItemUtilities_1 = __importDefault(require("./ProjectItemUtilities")); const Pack_1 = require("../minecraft/Pack"); const BlockTypeDefinition_1 = __importDefault(require("../minecraft/BlockTypeDefinition")); const ProjectUpdateRunner_1 = __importDefault(require("../updates/ProjectUpdateRunner")); const ProjectStandard_1 = __importDefault(require("./ProjectStandard")); const ProjectAutogeneration_1 = __importDefault(require("./ProjectAutogeneration")); const TypeScriptDefinition_1 = __importDefault(require("../minecraft/TypeScriptDefinition")); const Constants_1 = require("../core/Constants"); const ProjectContent_1 = __importDefault(require("./ProjectContent")); const behaviorPackFolderHints = ["behavior_pack", "/bp/", "/bps/"]; const resourcePackFolderHints = ["resource_pack", "/rp/", "/rps/"]; var NewEntityTypeAddMode; (function (NewEntityTypeAddMode) { NewEntityTypeAddMode[NewEntityTypeAddMode["baseId"] = 0] = "baseId"; })(NewEntityTypeAddMode || (exports.NewEntityTypeAddMode = NewEntityTypeAddMode = {})); var NewItemTypeAddMode; (function (NewItemTypeAddMode) { NewItemTypeAddMode[NewItemTypeAddMode["baseId"] = 0] = "baseId"; })(NewItemTypeAddMode || (exports.NewItemTypeAddMode = NewItemTypeAddMode = {})); var ProjectMetaCategory; (function (ProjectMetaCategory) { ProjectMetaCategory[ProjectMetaCategory["mix"] = 0] = "mix"; ProjectMetaCategory[ProjectMetaCategory["worldTemplate"] = 1] = "worldTemplate"; ProjectMetaCategory[ProjectMetaCategory["texturePack"] = 2] = "texturePack"; ProjectMetaCategory[ProjectMetaCategory["addOn"] = 3] = "addOn"; ProjectMetaCategory[ProjectMetaCategory["skinPack"] = 4] = "skinPack"; ProjectMetaCategory[ProjectMetaCategory["persona"] = 5] = "persona"; })(ProjectMetaCategory || (exports.ProjectMetaCategory = ProjectMetaCategory = {})); class ProjectUtilities { static async ensureContentInDefaultScriptFile(project, signatureToken, content, placeAtBottom) { const defaultScriptFile = await project.getDefaultScriptsFile(); if (defaultScriptFile) { if (!defaultScriptFile.isContentLoaded) { await defaultScriptFile.loadContent(); } if (typeof defaultScriptFile.content === "string" && defaultScriptFile.content.length > 0) { if (defaultScriptFile.content.indexOf(signatureToken) < 0) { let newContent = defaultScriptFile.content; if (!placeAtBottom) { newContent = content + newContent; } else { newContent = newContent + content; } defaultScriptFile.setContent(newContent); await defaultScriptFile.saveContent(); } } } } static async getDefaultFolderForPack(project, packType) { if (packType === Pack_1.PackType.behavior) { return await project.getDefaultBehaviorPackFolder(); } else if (packType === Pack_1.PackType.resource) { return await project.getDefaultResourcePackFolder(); } else if (packType === Pack_1.PackType.skin) { return await project.getDefaultSkinPackFolder(); } else if (packType === Pack_1.PackType.design) { return await project.getDefaultSkinPackFolder(); } throw new Error(); } static getSimplifiedProjectName(projectName) { // strip out a trailing -xxxxxx (dash + 6 chars) that is used in Electron file-system based project names let lastDash = projectName.lastIndexOf("-"); if (lastDash >= 2 && lastDash === projectName.length - 7) { return projectName.substring(0, projectName.length - 7); } return projectName; } static getPackTypeName(packType) { switch (packType) { case Pack_1.PackType.behavior: return "Behavior"; case Pack_1.PackType.resource: return "Resource"; case Pack_1.PackType.skin: return "Skin"; default: return "Unknown"; } } static async ensureJsonItem(project, jsonContent, fileName) { if (!project.projectFolder) { return undefined; } const typeInfo = ProjectItemUtilities_1.default.inferTypeFromJsonContent(jsonContent, fileName); let packType = typeInfo.packType; if (packType === undefined) { packType = Pack_1.PackType.behavior; } const baseFolder = await ProjectItemUtilities_1.default.getDefaultFolderForType(project, typeInfo.itemType); if (!baseFolder) { return undefined; } let folderPath = baseFolder.getFolderRelativePath(project.projectFolder); if (folderPath === undefined) { return undefined; } fileName = StorageUtilities_1.default.getUniqueChildFolderName(fileName, baseFolder); const contentFile = baseFolder.ensureFile(fileName); contentFile.setContent(jsonContent); contentFile.saveContent(); const relPath = contentFile.getFolderRelativePath(project.projectFolder); if (relPath !== undefined) { return project.ensureItemByProjectPath(relPath, IProjectItemData_1.ProjectItemStorageType.singleFile, fileName, typeInfo.itemType, Project_1.FolderContext.unknown, undefined, IProjectItemData_1.ProjectItemCreationType.normal); } return undefined; } static getItemsFromSearch(project, searchResults) { if (searchResults === undefined) { return []; } const searchItems = []; for (const val of searchResults) { for (const item of project.items) { if (item.projectPath && val.value === item.projectPath) { searchItems.push(item); } } } return searchItems; } static isBehaviorPackRelated(folderName) { for (const hint of behaviorPackFolderHints) { if (folderName.toLowerCase().indexOf(hint) >= 0) { return true; } } return false; } static isResourcePackRelated(folderName) { for (const hint of resourcePackFolderHints) { if (folderName.toLowerCase().indexOf(hint) >= 0) { return true; } } return false; } static inferJsonProjectItemTypeFromExtension(path) { const extension = StorageUtilities_1.default.getTypeFromName(path).toLowerCase(); switch (extension) { case "json": return IProjectItemData_1.ProjectItemType.unknownJson; case "mcaddon": return IProjectItemData_1.ProjectItemType.MCAddon; case "mcpack": return IProjectItemData_1.ProjectItemType.MCPack; case "mcworld": return IProjectItemData_1.ProjectItemType.MCWorld; case "mctemplate": return IProjectItemData_1.ProjectItemType.MCTemplate; case "mcproject": return IProjectItemData_1.ProjectItemType.MCProject; case "tga": case "png": case "jpg": return IProjectItemData_1.ProjectItemType.texture; case "ogg": case "fsb": case "mp3": return IProjectItemData_1.ProjectItemType.audio; } return IProjectItemData_1.ProjectItemType.unknown; } static inferJsonProjectItemTypeFromPath(path) { const lowerPath = path.toLowerCase(); if (this.isBehaviorPackRelated(lowerPath)) { if (lowerPath.indexOf("/biomes/") >= 0) { return IProjectItemData_1.ProjectItemType.biomeBehavior; } if (lowerPath.indexOf("/entities/") >= 0) { return IProjectItemData_1.ProjectItemType.entityTypeBehavior; } if (lowerPath.indexOf("/items/") >= 0) { return IProjectItemData_1.ProjectItemType.itemTypeBehavior; } if (lowerPath.indexOf("/animation_controllers/") >= 0) { return IProjectItemData_1.ProjectItemType.animationControllerBehaviorJson; } if (lowerPath.indexOf("/animations/") >= 0) { return IProjectItemData_1.ProjectItemType.animationBehaviorJson; } } if (this.isResourcePackRelated(lowerPath)) { if (lowerPath.indexOf("/animation_controllers/") >= 0) { return IProjectItemData_1.ProjectItemType.animationControllerResourceJson; } if (lowerPath.indexOf("/animations/") >= 0) { return IProjectItemData_1.ProjectItemType.animationResourceJson; } if (lowerPath.indexOf("/attachables/") >= 0) { return IProjectItemData_1.ProjectItemType.attachableResourceJson; } if (lowerPath.indexOf("/biomes/") >= 0) { return IProjectItemData_1.ProjectItemType.biomeResource; } if (lowerPath.indexOf("/entity/") >= 0) { return IProjectItemData_1.ProjectItemType.entityTypeResource; } if (lowerPath.indexOf("/entity/") >= 0) { return IProjectItemData_1.ProjectItemType.entityTypeResource; } if (lowerPath.endsWith("/blocks.json")) { return IProjectItemData_1.ProjectItemType.blocksCatalogResourceJson; } if (lowerPath.endsWith("/sounds.json")) { return IProjectItemData_1.ProjectItemType.soundDefinitionCatalog; } if (lowerPath.endsWith("/biomes_client.json")) { return IProjectItemData_1.ProjectItemType.biomesClientCatalogResource; } } if (lowerPath.indexOf("/fogs/") >= 0) { return IProjectItemData_1.ProjectItemType.fogResourceJson; } // Check for texture_set.json BEFORE /models/ check - texture_set files can exist in /models/ folders // (e.g., /textures/models/armor/chain_1.texture_set.json) if (lowerPath.indexOf(".texture_set.json") >= 0) { return IProjectItemData_1.ProjectItemType.textureSetJson; } if (lowerPath.indexOf("/models/") >= 0) { return IProjectItemData_1.ProjectItemType.modelGeometryJson; } if (lowerPath.indexOf("/particles/") >= 0) { return IProjectItemData_1.ProjectItemType.particleJson; } if (lowerPath.indexOf("/render_controllers/") >= 0) { return IProjectItemData_1.ProjectItemType.renderControllerJson; } if (lowerPath.indexOf("/sounds/") >= 0) { if (lowerPath.indexOf("music_definitions.json") >= 0) { return IProjectItemData_1.ProjectItemType.musicDefinitionJson; } if (lowerPath.indexOf("sound_definitions.json") >= 0) { return IProjectItemData_1.ProjectItemType.soundDefinitionCatalog; } return this.inferJsonProjectItemTypeFromExtension(path); } if (lowerPath.indexOf("/textures/") >= 0) { if (lowerPath.indexOf("flipbook_textures.json") >= 0) { return IProjectItemData_1.ProjectItemType.flipbookTexturesJson; } if (lowerPath.indexOf("item_texture.json") >= 0) { return IProjectItemData_1.ProjectItemType.itemTextureJson; } if (lowerPath.indexOf("terrain_texture.json") >= 0) { return IProjectItemData_1.ProjectItemType.terrainTextureCatalogResourceJson; } if (lowerPath.indexOf("texture_list.json") >= 0) { return IProjectItemData_1.ProjectItemType.textureListJson; } if (lowerPath.indexOf(".texture_set.json") >= 0) { return IProjectItemData_1.ProjectItemType.textureSetJson; } if (lowerPath.indexOf("/ui/") >= 0 && this.inferJsonProjectItemTypeFromExtension(path) === IProjectItemData_1.ProjectItemType.texture) { return IProjectItemData_1.ProjectItemType.uiTexture; } return this.inferJsonProjectItemTypeFromExtension(path); } if (lowerPath.indexOf("/loot_tables/") >= 0) { return IProjectItemData_1.ProjectItemType.lootTableBehavior; } if (lowerPath.indexOf("/recipes/") >= 0) { return IProjectItemData_1.ProjectItemType.recipeBehavior; } if (lowerPath.indexOf("/spawn_rules/") >= 0) { return IProjectItemData_1.ProjectItemType.spawnRuleBehavior; } if (lowerPath.indexOf("/trading/") >= 0) { return IProjectItemData_1.ProjectItemType.tradingBehaviorJson; } return this.inferJsonProjectItemTypeFromExtension(path); } /** * Attempts to infer a project item type using the top-level keys present in a JSON object. * Falls back to unknown when no meaningful match is found. */ static inferJsonProjectItemTypeFromContent(json) { if (!json || typeof json !== "object" || Array.isArray(json)) { return IProjectItemData_1.ProjectItemType.unknownJson; } const metadataKeys = new Set(["format_version", "minecraft:format_version"]); const rootKeys = Object.keys(json).filter((key) => !metadataKeys.has(key.toLowerCase())); if (rootKeys.length === 0) { return IProjectItemData_1.ProjectItemType.unknownJson; } const normalizedKeys = rootKeys.map((key) => key.toLowerCase()); const directMappings = { "minecraft:block": IProjectItemData_1.ProjectItemType.blockTypeBehavior, "minecraft:item": IProjectItemData_1.ProjectItemType.itemTypeBehavior, "minecraft:entity": IProjectItemData_1.ProjectItemType.entityTypeBehavior, "minecraft:spawn_rules": IProjectItemData_1.ProjectItemType.spawnRuleBehavior, "minecraft:spawn_groups": IProjectItemData_1.ProjectItemType.spawnGroupJson, "minecraft:loot_table": IProjectItemData_1.ProjectItemType.lootTableBehavior, "minecraft:feature": IProjectItemData_1.ProjectItemType.featureBehavior, "minecraft:feature_rules": IProjectItemData_1.ProjectItemType.featureRuleBehavior, "minecraft:trading": IProjectItemData_1.ProjectItemType.tradingBehaviorJson, "minecraft:dialogue": IProjectItemData_1.ProjectItemType.dialogueBehaviorJson, "minecraft:volume": IProjectItemData_1.ProjectItemType.volumeBehaviorJson, "minecraft:behavior_tree": IProjectItemData_1.ProjectItemType.behaviorTreeJson, "minecraft:aim_assist_category": IProjectItemData_1.ProjectItemType.aimAssistCategoryJson, "minecraft:aim_assist_preset": IProjectItemData_1.ProjectItemType.aimAssistPresetJson, "minecraft:dimension": IProjectItemData_1.ProjectItemType.dimensionJson, "minecraft:structure": IProjectItemData_1.ProjectItemType.jigsawStructure, "minecraft:structure_set": IProjectItemData_1.ProjectItemType.jigsawStructureSet, "minecraft:template_pool": IProjectItemData_1.ProjectItemType.jigsawTemplatePool, "minecraft:processor_list": IProjectItemData_1.ProjectItemType.jigsawProcessorList, "minecraft:biome": IProjectItemData_1.ProjectItemType.biomeBehavior, "minecraft:client_entity": IProjectItemData_1.ProjectItemType.entityTypeResource, "minecraft:attachable": IProjectItemData_1.ProjectItemType.attachableResourceJson, "minecraft:fog_settings": IProjectItemData_1.ProjectItemType.fogResourceJson, "minecraft:geometry": IProjectItemData_1.ProjectItemType.modelGeometryJson, "minecraft:block_culling": IProjectItemData_1.ProjectItemType.blockCulling, particle_effect: IProjectItemData_1.ProjectItemType.particleJson, render_controllers: IProjectItemData_1.ProjectItemType.renderControllerJson, "minecraft:render_controller": IProjectItemData_1.ProjectItemType.renderControllerJson, "minecraft:texture_set": IProjectItemData_1.ProjectItemType.textureSetJson, "minecraft:atmospherics": IProjectItemData_1.ProjectItemType.atmosphericsJson, "minecraft:pbr": IProjectItemData_1.ProjectItemType.pbrJson, "minecraft:point_lights": IProjectItemData_1.ProjectItemType.pointLightsJson, "minecraft:water": IProjectItemData_1.ProjectItemType.waterJson, "minecraft:shadows": IProjectItemData_1.ProjectItemType.shadowsJson, "minecraft:uniforms": IProjectItemData_1.ProjectItemType.uniformsJson, uniforms: IProjectItemData_1.ProjectItemType.uniformsJson, "minecraft:renderer": IProjectItemData_1.ProjectItemType.rendererJson, renderer: IProjectItemData_1.ProjectItemType.rendererJson, lods: IProjectItemData_1.ProjectItemType.lodJson, "minecraft:lods": IProjectItemData_1.ProjectItemType.lodJson, loading_messages: IProjectItemData_1.ProjectItemType.loadingMessagesJson, splashes: IProjectItemData_1.ProjectItemType.splashesJson, "minecraft:font": IProjectItemData_1.ProjectItemType.fontMetadataJson, font_metadata: IProjectItemData_1.ProjectItemType.fontMetadataJson, emoticons: IProjectItemData_1.ProjectItemType.emoticonsJson, "minecraft:emoticons": IProjectItemData_1.ProjectItemType.emoticonsJson, contents: IProjectItemData_1.ProjectItemType.contentsJson, "minecraft:contents": IProjectItemData_1.ProjectItemType.contentsJson, }; for (const key of normalizedKeys) { const direct = directMappings[key]; if (direct !== undefined) { return direct; } } // Check for legacy geometry format (format_version 1.8.0) where keys are like "geometry.bat", "geometry.pig" for (const key of normalizedKeys) { if (key.startsWith("geometry.")) { return IProjectItemData_1.ProjectItemType.modelGeometryJson; } } return IProjectItemData_1.ProjectItemType.unknownJson; } static getItemFromAnnotatedValue(project, value) { for (const item of project.items) { if (item.projectPath === value.value) { return item; } } return undefined; } static async isVibrantVisualsCompatible(project) { for (const item of project.items) { if (!item.isContentLoaded) { await item.loadContent(); } if (item.primaryFile) { const manifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(item.primaryFile); if (manifestJson && manifestJson.capabilities && manifestJson.capabilities.includes("pbr")) { return true; } } } return false; } static isVibrantVisualsEnhanced(project) { for (const item of project.items) { if (ProjectItemUtilities_1.default.isVibrantVisualsRelated(item)) { return true; } } return false; } static async ensureDefaultItems(project) { if (project.focus === IProjectData_1.ProjectFocus.gameTests && project.projectFolder !== null) { const bpFolder = await project.ensureDefaultBehaviorPackFolder(); const defaultPath = bpFolder.getFolderRelativePath(project.projectFolder); let pi = project.ensureItemByTag("gametestjs", "Test", IProjectItemData_1.ProjectItemType.testJs, defaultPath + "scripts/test.js", IProjectItemData_1.ProjectItemStorageType.singleFile); let file = await pi.loadFileContent(); if (file !== null) { let content = ""; if (project.preferredScriptLanguage === IProjectData_1.ProjectScriptLanguage.typeScript) { content = ProjectContent_1.default.getDefaultTestTypeScript(this.name, StorageUtilities_1.default.getBaseFromName("test")); } else { content = ProjectContent_1.default.getDefaultTestJavaScript(this.name, StorageUtilities_1.default.getBaseFromName("test")); } file.setContent(content); } await project.ensureJsIndexFile(); pi = project.ensureItemByTag("manifestjson", "manifest", IProjectItemData_1.ProjectItemType.behaviorPackManifestJson, defaultPath + "manifest.json", IProjectItemData_1.ProjectItemStorageType.singleFile, IProjectItemData_1.ProjectItemCreationType.generated); pi = project.ensureItemByTag("gameteststructure", "Basic", IProjectItemData_1.ProjectItemType.structure, defaultPath + "structures/gametests/basic.mcstructure", IProjectItemData_1.ProjectItemStorageType.singleFile); file = await pi.loadFileContent(); if (file !== null) { const result = ProjectContent_1.default.generateDefaultStructure(); if (result !== undefined) { file.setContent(result); } } } } static hasEntities(project) { const projectItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.entityTypeBehavior); if (projectItems.length > 0) { return true; } const projectItemsResource = project.getItemsByType(IProjectItemData_1.ProjectItemType.entityTypeResource); if (projectItemsResource.length > 0) { return true; } return false; } static hasBlocks(project) { const projectItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.blockTypeBehavior); if (projectItems.length > 0) { return true; } return false; } static hasItems(project) { const projectItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.itemTypeBehavior); if (projectItems.length > 0) { return true; } return false; } static async hasDocumentationMetadata(folder, depth) { await folder.load(); for (const folderName in folder.folders) { if (folderName === "checkpoint_input" || folderName === "metadata" || folderName === "type_definitions" || folderName === "typedefs") { return true; } if (depth === undefined || depth < 5) { const isMetadata = await ProjectUtilities.hasDocumentationMetadata(folder, depth ? depth + 1 : 1); if (isMetadata) { return true; } } } return false; } static async prepareProjectForDocumentation(project) { await project.ensureProjectFolder(); if (!project.projectFolder) { return; } const hasMetadata = await ProjectUtilities.hasDocumentationMetadata(project.projectFolder); if (hasMetadata) { return; } const result = await Database_1.default.loadPreviewMetadataFolder(); if (result) { project.accessoryFolders = [result]; } } static async getBaseBehaviorPackPath(project) { const bpFolder = await project.ensureDefaultBehaviorPackFolder(); if (project.projectFolder === null) { return undefined; } const defaultPath = bpFolder.getFolderRelativePath(project.projectFolder); return defaultPath; } static async getBaseResourcePackPath(project) { const rpFolder = await project.ensureDefaultResourcePackFolder(); if (project.projectFolder === null) { return undefined; } const defaultPath = rpFolder.getFolderRelativePath(project.projectFolder); return defaultPath; } static async applyScriptEntryPoint(project, newScriptEntryPoint) { project.scriptEntryPoint = newScriptEntryPoint; if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultBehaviorPackUniqueId) { const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultBehaviorPackUniqueId)) { const sm = manifestJson.ensureScriptModule(project.title, project.description); sm.entry = project.scriptEntryPoint; manifestJson.save(); } } } } } } static async applyDescription(project, newDescription) { project.description = newDescription; if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultBehaviorPackUniqueId) { const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.packageJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const npmPackageJson = await NpmPackageDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (npmPackageJson && npmPackageJson.definition) { npmPackageJson.definition.description = newDescription; await npmPackageJson.save(); } } } else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultBehaviorPackUniqueId)) { const header = manifestJson.ensureHeader(project.title, project.description); header.description = newDescription; await manifestJson.save(); } } } else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultResourcePackUniqueId)) { const header = manifestJson.ensureHeader(project.title, project.description); header.description = newDescription; await manifestJson.save(); } } } } } } static async applyCreator(project, newCreator) { project.creator = newCreator; } static async applyShortName(project, newShortName) { project.shortName = newShortName; } static async ensureGeneratedWith(project, isToolWeb) { const appName = isToolWeb ? "mctoolsweb" : "mctoolscli"; if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultBehaviorPackUniqueId) { const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.primaryFile && projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { const bpManifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (bpManifestJson && bpManifestJson.definition) { bpManifestJson.ensureGeneratedWith(appName, Constants_1.constants.version); await bpManifestJson.save(); } } else if (projectItem.primaryFile && projectItem.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { const rpManifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (rpManifestJson && rpManifestJson.definition) { rpManifestJson.ensureGeneratedWith(appName, Constants_1.constants.version); await rpManifestJson.save(); } } } } } static async applyTitle(project, newTitle) { project.title = newTitle; if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultBehaviorPackUniqueId) { const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.primaryFile && projectItem.itemType === IProjectItemData_1.ProjectItemType.packageJson) { const npmPackageJson = await NpmPackageDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (npmPackageJson && npmPackageJson.definition) { npmPackageJson.definition.name = newTitle; await npmPackageJson.save(); } } else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultBehaviorPackUniqueId)) { const header = manifestJson.ensureHeader(project.title, project.description); header.name = newTitle; await manifestJson.save(); } } } else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultResourcePackUniqueId)) { const header = manifestJson.ensureHeader(project.title, project.description); header.name = newTitle; await manifestJson.save(); } } } } } } static async applyBehaviorPackUniqueId(project, newBehaviorPackId) { const oldBehaviorPackId = project.defaultBehaviorPackUniqueId; await project.setDefaultBehaviorPackUniqueIdAndUpdateDependencies(newBehaviorPackId); if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultBehaviorPackUniqueId) { let bpackCount = 0; const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { bpackCount++; } } for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && (bpackCount <= 1 || Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultBehaviorPackUniqueId) || Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, oldBehaviorPackId))) { const header = manifestJson.ensureHeader(project.title, project.description); header.uuid = newBehaviorPackId; manifestJson.save(); } } } } } } static async applyResourcePackUniqueId(project, newResourcePackId) { const oldResourcePackId = project.defaultResourcePackUniqueId; await project.setDefaultResourcePackUniqueIdAndUpdateDependencies(newResourcePackId); if (project.effectiveEditPreference === IProjectData_1.ProjectEditPreference.summarized && project.defaultResourcePackUniqueId) { let rpackCount = 0; const itemsCopy = project.getItemsCopy(); for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { rpackCount++; } } for (const projectItem of itemsCopy) { if (projectItem.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { await projectItem.loadFileContent(); if (projectItem.primaryFile) { const manifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(projectItem.primaryFile); if (manifestJson && manifestJson.definition && (rpackCount <= 1 || Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, project.defaultResourcePackUniqueId) || Utilities_1.default.uuidEqual(manifestJson.definition.header.uuid, oldResourcePackId))) { const header = manifestJson.ensureHeader(project.title, project.description); header.uuid = newResourcePackId; manifestJson.save(); } } } } } } static getSuggestedProjectShortName(creator, name) { return this.getSuggestedShortName(creator) + "_" + this.getSuggestedShortName(name); } static getSuggestedShortName(caption) { caption = caption.trim().replace(/-/g, ""); caption = caption.replace(/[_ :;=]/g, ""); let capitalStr = ""; for (let i = 0; i < caption.length; i++) { if (caption[i] >= "A" && caption[i] <= "Z") { capitalStr += caption[i].toLowerCase(); } } if (capitalStr.length > 1) { return capitalStr; } if (caption.length <= 8) { return caption.toLowerCase(); } return caption.substring(0, 8).toLowerCase(); } static getSuggestedProjectName(project) { return this.getSuggestedProjectNameFromElements(project.id, project.gitHubFolder, project.gitHubRepoName); } static getSuggestedProjectNameFromElements(id, gitHubFolder, gitHubRepoName) { // Special case for addonStarter: use "My Add-On" (task 027) if (id === "addonStarter") { return "My Add-On"; } let projName = "my"; if (id) { projName += id; } else if (gitHubFolder !== undefined) { projName += gitHubFolder; projName = projName.replace(" behavior_packs", ""); } else { projName += gitHubRepoName; } projName = projName.replace(/_/gi, ""); projName = projName.replace(/\//gi, ""); projName = projName.replace(/\\/gi, ""); projName = projName.replace(/ /gi, ""); return projName; } static async getBaseScriptsPath(project) { const scriptsFolder = await project.ensureDefaultScriptsFolder(); if (project.projectFolder === null) { return undefined; } return scriptsFolder.getFolderRelativePath(project.projectFolder); } static async renameDefaultFolders(project, newTokenName) { const bpFolder = await project.getDefaultBehaviorPackFolder(true); const rpFolder = await project.getDefaultResourcePackFolder(true); newTokenName = MinecraftUtilities_1.default.makeNameFolderSafe(newTokenName); if (bpFolder) { await this._renameOrCopyFolder(bpFolder, newTokenName, "behavior pack"); } if (rpFolder) { await this._renameOrCopyFolder(rpFolder, newTokenName, "resource pack"); } } /** * Attempts to rename a pack folder. If the rename fails (e.g., OneDrive file locks), * falls back to copying contents to a new folder and deleting the original. */ static async _renameOrCopyFolder(folder, newName, label) { try { // First attempt: direct rename. This is fast but can fail on systems with // file locks (OneDrive, antivirus, indexers) throwing EPERM/EACCES. let renamed = false; try { renamed = await folder.rename(newName); } catch (renameErr) { // Rename threw (e.g., EPERM). In-memory state is still clean because // NodeFolder.moveTo() performs the disk op before updating state. // Fall through to copy+delete below. Log_1.default.debug("Rename threw for " + label + " folder (" + renameErr.message + "); falling back to copy + delete."); } if (renamed) { return; // rename succeeded on disk and in-memory } // Disk rename failed or threw. Fall back to copy + delete. Log_1.default.debug("Disk rename failed for " + label + " folder; falling back to copy + delete."); const parentFolder = folder.parentFolder; if (!parentFolder) { Log_1.default.error("Cannot copy " + label + " folder: no parent folder."); return; } const targetFolder = parentFolder.ensureFolder(newName); await targetFolder.ensureExists(); await StorageUtilities_1.default.syncFolderTo(folder, targetFolder, true, true, false); try { await folder.deleteThisFolder(); } catch (deleteErr) { // If delete fails, we still have the new folder with the correct content. // The old template folder will remain but is not harmful — the project will use the new one. Log_1.default.debug("Could not delete old " + label + " template folder: " + deleteErr.toString()); } } catch (e) { // perhaps folder could not be renamed because a folder exists; continue in this case. Log_1.default.error("Error renaming " + label + " folder: " + e.toString()); } } static async processNewProject(project, title, description, suggestedShortName, isWeb) { await project.inferProjectItemsFromFiles(); if (suggestedShortName) { await ProjectUtilities.renameDefaultFolders(project, suggestedShortName); } await ProjectAutogeneration_1.default.updateProjectAutogeneration(project, true); await ProjectUtilities.randomizeAllUids(project); // Friendly-name fallback for the addon starter (and similar gallery templates). // The "myaddonStarter" string comes from `getSuggestedProjectNameFromElements` // when callers ignore the `addonStarter` special case (line 947) and feed the // raw "my" + galleryId derivation back into processNewProject. That string // also leaks into the description because the New Project Dialog defaults // description to the title. Substitute a friendlier title and a non-tautological // description so first-time creators don't see "myaddonStarter / myaddonStarter" // in their pack manifest. const friendlyTitle = ProjectUtilities.makeFriendlyProjectTitle(title); if (friendlyTitle !== title) { title = friendlyTitle; // If the description was just a copy of the auto-derived title, swap it out too. if (!description || description === title || ProjectUtilities.makeFriendlyProjectTitle(description) === friendlyTitle) { description = "An add-on starter project. Customize this in manifest.json or via the editor."; } } else if (description && description === title) { // Title was already friendly but description tautologically copies it. Provide a // gentle nudge so the manifest isn't "Foo / Foo". description = title + " — customize this description in manifest.json or via the editor."; } await ProjectUtilities.applyTitle(project, title); await ProjectUtilities.applyDescription(project, description); await ProjectUtilities.ensureGeneratedWith(project, isWeb); await ProjectStandard_1.default.ensureIsStandard(project); const pur = new ProjectUpdateRunner_1.default(project); await pur.updateProject(); await project.save(true); } /** * Detects the auto-derived "my" + galleryId pattern (e.g. "myaddonStarter") * produced by `getSuggestedProjectNameFromElements` when the addonStarter * special case is bypassed, and substitutes a human-readable title. * * Currently only normalises the addon starter family — extend with additional * checks if more gallery templates start leaking technical ids into manifests. */ static makeFriendlyProjectTitle(title) { if (!title) { return title; } const lower = title.trim().toLowerCase(); if (lower === "myaddonstarter") { return "My Add-on Starter"; } return title; } static async setNewModuleId(project, newModuleId, oldModuleId) { const itemsCopy = project.getItemsCopy(); let setResourcePack = false; for (let i = 0; i < itemsCopy.length; i++) { const pi = itemsCopy[i]; if (pi.primaryFile) { if (pi.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson && !setResourcePack) { const rpManifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(pi.primaryFile); if (rpManifestJson) { if (rpManifestJson.definition && rpManifestJson.definition.modules) { const mods = rpManifestJson.definition.modules; for (const mod of mods) { if (mod.uuid === oldModuleId) { mod.uuid = newModuleId; } } } } } else if (pi.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { const bpManifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(pi.primaryFile); if (bpManifestJson) { if (bpManifestJson.definition && bpManifestJson.definition.modules) { const mods = bpManifestJson.definition.modules; for (const mod of mods) { if (mod.uuid === oldModuleId) { mod.uuid = newModuleId; } } } } } } } } static async getDefaultBehaviorPackFolder(project, paths) { const bpFolder = await project.getDefaultBehaviorPackFolder(); if (!bpFolder) { return undefined; } const srFolder = await bpFolder.ensureFolderFromRelativePath(paths.join("/")); await srFolder.ensureExists(); return srFolder; } static async getIsAddon(project) { const itemsCopy = project.getItemsCopy(); let rpCount = 0; let bpCount = 0; let hasThingsThatAreNotInAddons = false; for (let i = 0; i < itemsCopy.length; i++) { const pi = itemsCopy[i]; if (pi.primaryFile) { if (pi.itemType === IProjectItemData_1.ProjectItemType.worldFolder || pi.itemType === IProjectItemData_1.ProjectItemType.worldTemplateManifestJson || pi.itemType === IProjectItemData_1.ProjectItemType.levelDbLdb || pi.itemType === IProjectItemData_1.ProjectItemType.levelDbLog || pi.itemType === IProjectItemData_1.ProjectItemType.levelDbManifest || pi.itemType === IProjectItemData_1.ProjectItemType.MCWorld || pi.itemType === IProjectItemData_1.ProjectItemType.MCProject || pi.itemType === IProjectItemData_1.ProjectItemType.MCTemplate || pi.itemType === IProjectItemData_1.ProjectItemType.levelDat) { hasThingsThatAreNotInAddons = true; } else if (pi.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { rpCount++; const rpManifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(pi.primaryFile); if (rpManifestJson) { if (!rpManifestJson.hasAddonProperties()) { return false; } } } else if (pi.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson) { bpCount++; const bpManifestJson = await BehaviorManifestDefinition_1.default.ensureOnFile(pi.primaryFile); if (bpManifestJson) { if (!bpManifestJson.hasAddonProperties()) { return false; } } } } } return !hasThingsThatAreNotInAddons && bpCount === 1 && rpCount === 1; } static async getMetaCategory(project) { const itemsCopy = project.getItemsCopy(); let rpCount = 0; let bpCount = 0; let spCount = 0; let personaCount = 0; let worldTemplateCount = 0; let hasPacksWithAddOnProperties = false; let hasPacksWithoutAddOnProperties = false; for (let i = 0;