@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
959 lines • 76.6 kB
JavaScript
"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;