UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

523 lines (522 loc) 19.5 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 }); const ste_events_1 = require("ste-events"); const Utilities_1 = __importDefault(require("../core/Utilities")); const Project_1 = require("../app/Project"); const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const IProjectItemData_1 = require("../app/IProjectItemData"); const IProjectData_1 = require("../app/IProjectData"); const ResourceManifestDefinition_1 = __importDefault(require("./ResourceManifestDefinition")); const MinecraftUtilities_1 = __importDefault(require("./MinecraftUtilities")); const Log_1 = __importDefault(require("../core/Log")); class BehaviorManifestDefinition { _file; _id; _isLoaded = false; _loadedWithComments = false; definition; _onLoaded = new ste_events_1.EventDispatcher(); get isLoaded() { return this._isLoaded; } get file() { return this._file; } set file(newFile) { this._file = newFile; } get onLoaded() { return this._onLoaded.asEvent(); } get productType() { if (!this.definition || !this.definition.metadata) { return undefined; } return this.definition.metadata.product_type; } set productType(value) { this.ensureMetadata(); if (!this.definition || !this.definition.metadata) { return; } this.definition.metadata.product_type = value; } get description() { if (!this.definition || !this.definition.header) { return undefined; } return this.definition.header.description; } set description(newDescription) { if (this.definition && this.definition.header && newDescription) { this.definition.header.description = newDescription; } } get name() { if (this.definition && this.definition.header) { return this.definition.header.name; } return undefined; } set name(newName) { if (this.definition && this.definition.header && newName) { this.definition.header.name = newName; } } get capabilities() { if (!this.definition || !this.definition.capabilities) { return undefined; } return this.definition.capabilities; } get id() { if (this.definition && this.definition.header) { return this.definition.header.uuid; } return this._id; } set id(newId) { if (this.definition && this.definition.header && newId) { this.definition.header.uuid = newId; } this._id = newId; } async setUuid(newId, project) { const oldId = this.id; this.id = newId; if (newId && oldId && project) { await BehaviorManifestDefinition.setNewBehaviorPackId(project, newId, oldId); } } static async setNewBehaviorPackId(project, newBehaviorPackId, oldBehaviorPackId) { const itemsCopy = project.getItemsCopy(); let setBehaviorPack = false; for (let i = 0; i < itemsCopy.length; i++) { const pi = itemsCopy[i]; if (pi.primaryFile) { if (pi.itemType === IProjectItemData_1.ProjectItemType.behaviorPackManifestJson && !setBehaviorPack) { const bpManifestJson = await BehaviorManifestDefinition.ensureOnFile(pi.primaryFile); if (bpManifestJson) { if (bpManifestJson.id && Utilities_1.default.uuidEqual(bpManifestJson.id, oldBehaviorPackId)) { bpManifestJson.id = newBehaviorPackId; setBehaviorPack = true; await bpManifestJson.save(); } else if (bpManifestJson.definition && bpManifestJson.definition.dependencies) { const deps = bpManifestJson.definition?.dependencies; if (deps && Array.isArray(deps)) { for (const dep of deps) { if (dep.uuid === oldBehaviorPackId) { dep.uuid = newBehaviorPackId; } } } await bpManifestJson.save(); } } } else if (pi.itemType === IProjectItemData_1.ProjectItemType.resourcePackManifestJson) { const rpManifestJson = await ResourceManifestDefinition_1.default.ensureOnFile(pi.primaryFile); if (rpManifestJson) { if (rpManifestJson.definition && rpManifestJson.definition.dependencies) { const deps = rpManifestJson.definition?.dependencies; if (deps && Array.isArray(deps)) { for (const dep of deps) { if (dep.uuid === oldBehaviorPackId) { dep.uuid = newBehaviorPackId; } } } await rpManifestJson.save(); } } } } } } hasAddonProperties() { return this.productType === "addon"; } async setAddonProperties() { this.productType = "addon"; await this.save(); } randomizeModuleUuids(newScriptModuleId, oldScriptModuleId) { if (!this.definition) { return; } for (let i = 0; i < this.definition.modules.length; i++) { const mod = this.definition.modules[i]; if (mod.uuid) { if (oldScriptModuleId && newScriptModuleId && (mod.uuid === oldScriptModuleId || mod.uuid === newScriptModuleId)) { mod.uuid = newScriptModuleId; } else { mod.uuid = Utilities_1.default.createUuid(); } } } } getNonScriptModuleDependencyCount() { if (!this.definition || !this.definition.dependencies || !Array.isArray(this.definition.dependencies)) { return 0; } let count = 0; for (let dependency of this.definition.dependencies) { if (dependency.uuid) { count++; } } return count; } getFirstNonScriptModuleDependency() { if (!this.definition || !this.definition.dependencies || !Array.isArray(this.definition.dependencies)) { return undefined; } for (let dependency of this.definition.dependencies) { if (dependency.uuid) { return dependency; } } return undefined; } static async ensureOnFile(file, loadHandler) { let bmj; if (file.manager === undefined) { bmj = new BehaviorManifestDefinition(); bmj.file = file; file.manager = bmj; } if (file.manager !== undefined && file.manager instanceof BehaviorManifestDefinition) { bmj = file.manager; if (!bmj.isLoaded) { if (loadHandler) { bmj.onLoaded.subscribe(loadHandler); } await bmj.load(); } } return bmj; } get version() { if (!this.definition || !this.definition.header || !this.definition.header.version) { return undefined; } return this.definition.header.version; } get minEngineVersion() { if (!this.definition || !this.definition.header || !this.definition.header.min_engine_version) { return undefined; } return this.definition.header.min_engine_version; } setMinEngineVersion(versionArray, project) { const header = this.ensureHeaderForProject(project); header.min_engine_version = versionArray; } setModuleVersion(moduleName, version) { if (!this.definition || !this.definition.dependencies) { return false; } let verActual = version; let changedVals = false; if (version.indexOf("-") < 1) { const verArr = version.split("."); if (verArr && verArr.length === 3) { verActual = []; for (let i = 0; i < verArr.length; i++) { try { verActual.push(parseInt(verArr[i])); } catch (e) { throw e; } if (Number.isNaN(verActual[i])) { throw new Error("Could not parse " + version); } } } } for (let i = 0; i < this.definition.dependencies.length; i++) { const dep = this.definition.dependencies[i]; if (dep.module_name === moduleName) { if (dep.version !== verActual) { dep.version = verActual; changedVals = true; } } } return changedVals; } persist() { if (this._file === undefined) { return false; } Log_1.default.assert(this.definition !== null, "BMDP"); if (!this.definition) { return false; } return this._file.setObjectContentIfSemanticallyDifferent(this.definition); } ensureDefinition(name, description) { if (!this.definition) { this.definition = { format_version: 2, header: { name: name, description: description, version: [0, 0, 1], min_engine_version: [1, 20, 10], uuid: Utilities_1.default.createUuid(), }, modules: [], dependencies: [], }; } } ensureHeaderForProject(project) { return this.ensureHeader(project.title, project.description); } ensureMetadata() { if (!this.definition) { return undefined; } if (!this.definition.metadata) { this.definition.metadata = {}; } return this.definition.metadata; } ensureGeneratedWith(toolName, versionString) { const metadata = this.ensureMetadata(); if (!metadata) { return undefined; } if (!metadata.generated_with) { metadata.generated_with = {}; } if (!metadata.generated_with[toolName]) { metadata.generated_with[toolName] = []; } if (!metadata.generated_with[toolName].includes(versionString)) { metadata.generated_with[toolName].push(versionString); } } ensureHeader(name, description) { this.ensureDefinition(name, description); if (!this.definition) { throw new Error(); } if (!this.definition.header) { this.definition.header = this.getDefaultHeader(name, description); } return this.definition.header; } getDefaultHeader(name, description) { return { name: name, description: description, version: [0, 0, 1], min_engine_version: [1, 20, 10], uuid: Utilities_1.default.createUuid(), }; } getScriptModule() { if (!this.definition) { return undefined; } for (let i = 0; i < this.definition.modules.length; i++) { const mod = this.definition.modules[i]; if (mod.type && mod.type.toLowerCase() === "script") { return mod; } } return undefined; } ensureScriptModule(name, description) { this.ensureDefinition(name, description); if (!this.definition) { throw new Error(); } for (let i = 0; i < this.definition.modules.length; i++) { const mod = this.definition.modules[i]; if (mod.type && mod.type.toLowerCase() === "script") { return mod; } } const mod = { type: "script", description: name, version: [0, 0, 1], uuid: Utilities_1.default.createUuid(), language: "javascript", }; this.definition.modules.push(mod); return mod; } async save() { if (this._file === undefined) { return; } if (this.persist()) { await this._file.saveContent(false); } } /** * Loads the definition from the file. * @param preserveComments If true, uses comment-preserving JSON parsing for edit/save cycles. * If false (default), uses efficient standard JSON parsing. * Can be called again with true to "upgrade" a read-only load to read/write. */ async load(preserveComments = false) { // If already loaded with comments, we have the "best" version - nothing more to do if (this._isLoaded && this._loadedWithComments) { return; } // If already loaded without comments and caller doesn't need comments, we're done if (this._isLoaded && !preserveComments) { return; } if (this._file === undefined) { return; } if (!this._file.isContentLoaded) { await this._file.loadContent(); } if (this._file.content === null || this._file.content instanceof Uint8Array) { this._isLoaded = true; this._loadedWithComments = preserveComments; this._onLoaded.dispatch(this, this); return; } // Use comment-preserving parser only when needed for editing this.definition = preserveComments ? StorageUtilities_1.default.getJsonObjectWithComments(this._file) : StorageUtilities_1.default.getJsonObject(this._file); this._isLoaded = true; this._loadedWithComments = preserveComments; this._onLoaded.dispatch(this, this); } static async getContent(project, contentOnlyUpdate) { let versionMajor = 0; let versionMinor = 0; let versionPatch = 1; if (project.versionMajor !== undefined) { versionMajor = project.versionMajor; } if (project.versionMinor !== undefined) { versionMinor = project.versionMinor; } if (project.versionPatch !== undefined) { versionPatch = project.versionPatch; } const modulesList = []; const dependenciesList = []; let scriptFile = undefined; let pi = project.getFirstItemByType(IProjectItemData_1.ProjectItemType.catalogIndexJs); if (pi !== undefined) { scriptFile = pi.primaryFile; } const behaviorPackRootFolder = await project.getDefaultBehaviorPackFolder(false, contentOnlyUpdate); if (scriptFile === undefined) { pi = project.getFirstItemByType(IProjectItemData_1.ProjectItemType.js); if (pi !== undefined) { scriptFile = pi.primaryFile; } if (scriptFile === undefined) { pi = project.getFirstItemByType(IProjectItemData_1.ProjectItemType.testJs); if (pi !== undefined) { scriptFile = pi.primaryFile; } } } const scriptState = await project.getScriptState(); if (scriptState.hasScript && scriptFile !== null && scriptFile !== undefined && behaviorPackRootFolder !== null) { let path = scriptFile.getFolderRelativePath(behaviorPackRootFolder); if (path !== undefined) { if (path.startsWith("/")) { path = path.substring(1, path.length); } modulesList.push({ description: project.title + " script", language: "javascript", type: "script", uuid: project.defaultScriptModuleUniqueId, version: [versionMajor, versionMinor, versionPatch], entry: path, }); for (let i = 0; i < Project_1.minecraftScriptModules.length; i++) { const mod = Project_1.minecraftScriptModules[i]; if (scriptState.hasModule[mod.id]) { dependenciesList.push({ uuid: mod.uuid, module_name: mod.module_name, version: mod.preferredVersion, }); } } } } const manifest = { format_version: 2, __comment__: Project_1.AUTOGENERATED_WHOLEFILE_GENERAL_SEPARATOR, header: { description: project.description, name: project.title, uuid: project.defaultBehaviorPackUniqueId, version: [versionMajor, versionMinor, versionPatch], min_engine_version: [1, 20, 10], }, modules: modulesList, dependencies: dependenciesList, }; if (project.focus === IProjectData_1.ProjectFocus.editorExtension) { manifest.capabilities = ["editorExtension"]; } return JSON.stringify(manifest, null, 2); } static validatePackReferenceVersion(version) { if (!version) { return { isValid: false, errorMessage: "Missing version", }; } // Use MinecraftUtilities to parse version (handles both string and array formats) const versionArray = MinecraftUtilities_1.default.getVersionArrayFrom(version); if (!versionArray || versionArray.length !== 3 || !versionArray.every((v) => typeof v === "number" && v >= 0)) { return { isValid: false, errorMessage: "Invalid version format. Expected valid version string or array of 3 non-negative numbers", }; } return { isValid: true, versionArray: versionArray, }; } static validatePackId(packId) { if (!packId || typeof packId !== "string") { return { isValid: false, errorMessage: "Missing or invalid pack_id", }; } if (!MinecraftUtilities_1.default.isValidUuid(packId)) { return { isValid: false, errorMessage: `Invalid UUID format for pack_id [${packId}]`, }; } return { isValid: true }; } } exports.default = BehaviorManifestDefinition;