UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

381 lines (380 loc) 14.3 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 Log_1 = __importDefault(require("../core/Log")); const ste_events_1 = require("ste-events"); const ManagedComponent_1 = require("./ManagedComponent"); const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const Database_1 = __importDefault(require("./Database")); const MinecraftUtilities_1 = __importDefault(require("./MinecraftUtilities")); const IProjectItemData_1 = require("../app/IProjectItemData"); const AttachableResourceDefinition_1 = __importDefault(require("./AttachableResourceDefinition")); const TypeScriptDefinition_1 = __importDefault(require("./TypeScriptDefinition")); const Utilities_1 = __importDefault(require("../core/Utilities")); const ProjectUtilities_1 = __importDefault(require("../app/ProjectUtilities")); class ItemTypeDefinition { _wrapper = null; _file; _id; _isLoaded = false; _loadedWithComments = false; _managed = {}; _data; _onLoaded = new ste_events_1.EventDispatcher(); _onComponentAdded = new ste_events_1.EventDispatcher(); _onComponentRemoved = new ste_events_1.EventDispatcher(); _onComponentChanged = new ste_events_1.EventDispatcher(); get data() { return this._data; } get onComponentAdded() { return this._onComponentAdded.asEvent(); } get onComponentRemoved() { return this._onComponentRemoved.asEvent(); } get onComponentChanged() { return this._onComponentChanged.asEvent(); } get isLoaded() { return this._isLoaded; } get behaviorPackFile() { return this._file; } get onLoaded() { return this._onLoaded.asEvent(); } set behaviorPackFile(newFile) { if (this._file) { this._file.onFileContentUpdated.unsubscribe(this._handleFileUpdated); } this._file = newFile; if (this._file) { this._file.onFileContentUpdated.subscribe(this._handleFileUpdated); } } _handleFileUpdated(file, fileB) { this._data = undefined; this._isLoaded = false; this._wrapper = null; this._managed = {}; } get id() { if (this._id === undefined) { return ""; } return this._id; } set id(newId) { this._id = newId; } get formatVersion() { if (this._wrapper) { return this._wrapper.format_version; } return undefined; } get shortId() { if (this._id !== undefined) { if (this._id.startsWith("minecraft:")) { return this._id.substring(10, this._id.length); } return this._id; } return undefined; } async getFormatVersionIsCurrent() { const fv = this.getFormatVersion(); if (fv === undefined || fv.length !== 3) { return false; } return await Database_1.default.isRecentVersionFromVersionArray(fv); } getFormatVersion() { if (!this._wrapper) { return undefined; } return MinecraftUtilities_1.default.getVersionArrayFrom(this._wrapper.format_version); } setFormatVersion(version) { if (!this._wrapper) { return; } this._wrapper.format_version = version; } constructor() { this._handleFileUpdated = this._handleFileUpdated.bind(this); } ensureComponent(id, defaultData) { const comp = this.getComponent(id); if (comp) { return comp; } return this.addComponent(id, defaultData); } getComponent(id) { if (!this._data || !this._data.components) { return undefined; } if (!Utilities_1.default.isUsableAsObjectKey(id)) { Log_1.default.unsupportedToken(id); throw new Error(); } if (!this._managed[id]) { const comp = this._data.components[id]; if (comp) { this._managed[id] = new ManagedComponent_1.ManagedComponent(this._data.components, id, comp); } } return this._managed[id]; } notifyComponentUpdated(id) { const component = this.getComponent(id); if (component === undefined) { Log_1.default.unexpectedUndefined("ITNCU"); } else { this._onComponentChanged.dispatch(this, component); } } getAllComponents() { return this.getComponents(); } getCustomComponentIds() { let customComponentIds = []; const customComponent = this.getComponent("minecraft:custom_components"); if (customComponent) { let compData = customComponent.getData(); if (compData && Array.isArray(compData)) { for (const str of compData) { if (typeof str === "string") { customComponentIds.push(str); } } } } for (const comp of this.getComponents()) { if (!comp.id.startsWith("minecraft:") && !comp.id.startsWith("tag:")) { customComponentIds.push(comp.id); } } return customComponentIds; } getComponents() { const componentSet = []; if (this._data !== undefined) { for (const componentName in this._data.components) { const component = this.getComponent(componentName); if (component !== undefined) { componentSet.push(component); } } } return componentSet; } async addChildItems(project, item, index) { let customComponentIds = this.getCustomComponentIds(); // Check TypeScript files for custom components (only if we have custom component IDs) if (customComponentIds && customComponentIds.length > 0) { const tsItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.ts); for (const candItem of tsItems) { if (!candItem.isContentLoaded) { await candItem.loadContent(); } if (candItem.primaryFile) { if (!candItem.primaryFile.isContentLoaded) { await candItem.primaryFile.loadContent(); } const tsd = await TypeScriptDefinition_1.default.ensureOnFile(candItem.primaryFile); if (tsd && tsd.data) { let doAddTs = false; for (const customCompId of customComponentIds) { if (tsd.data.indexOf(customCompId) >= 0) { doAddTs = true; break; } } if (doAddTs) { item.addChildItem(candItem); } } } } } // Check attachable resources if (index && this.id) { // Use pre-built index for O(1) lookup const matchingAttachables = index.getItemsById(index.attachablesById, this.id); for (const candItem of matchingAttachables) { item.addChildItem(candItem); } } else { const attachableItems = project.getItemsByType(IProjectItemData_1.ProjectItemType.attachableResourceJson); for (const candItem of attachableItems) { if (!candItem.isContentLoaded) { await candItem.loadContent(); } if (candItem.primaryFile) { const ard = await AttachableResourceDefinition_1.default.ensureOnFile(candItem.primaryFile); if (ard) { const id = ard.id; if (id === this.id) { item.addChildItem(candItem); } } } } } } setBehaviorPackFormatVersion(versionStr) { this._ensureBehaviorPackDataInitialized(); if (this._wrapper) { this._wrapper.format_version = versionStr; } } addComponent(id, componentOrData) { this._ensureBehaviorPackDataInitialized(); const bpData = this._data; const mc = componentOrData instanceof ManagedComponent_1.ManagedComponent ? componentOrData : new ManagedComponent_1.ManagedComponent(bpData.components, id, componentOrData); bpData.components[id] = mc.getData(); this._managed[id] = mc; this._onComponentAdded.dispatch(this, mc); return mc; } removeComponent(id) { if (this._data === undefined) { return; } const newBehaviorPacks = {}; const newManagedComponents = {}; for (const name in this._data.components) { if (name !== id) { const component = this._data.components[name]; newBehaviorPacks[name] = component; } } for (const name in this._managed) { if (name !== id) { newManagedComponents[name] = this._managed[name]; } } this._data.components = newBehaviorPacks; this._managed = newManagedComponents; } _ensureBehaviorPackDataInitialized() { if (this._data === undefined) { this._data = { description: { identifier: "unknown", }, components: {}, events: {}, }; } } async addCustomComponent(itemTypeItem, componentName) { let componentNameShort = componentName; const idx = componentName.indexOf(":"); if (idx >= 0) { componentNameShort = componentName.substring(idx + 1); } this.ensureComponent(componentName, {}); const fileNameSugg = Utilities_1.default.getHumanifiedObjectNameNoSpaces(componentNameShort); this.setFormatVersion("1.21.100"); await ProjectUtilities_1.default.ensureTypeScriptFileWith(itemTypeItem.project, componentName, "new-templates", "itemCustomComponent", fileNameSugg, { "example:newComponentId": componentName, ExampleNewComponent: fileNameSugg, initExampleNew: "init" + fileNameSugg, }); await ProjectUtilities_1.default.ensureContentInDefaultScriptFile(itemTypeItem.project, "import { init" + fileNameSugg, "import { init" + fileNameSugg + ' } from "./' + fileNameSugg + '"\n', false); await ProjectUtilities_1.default.ensureContentInDefaultScriptFile(itemTypeItem.project, "init" + fileNameSugg + "()", "init" + fileNameSugg + "();\n", true); this.persist(); } static async ensureOnFile(file, loadHandler, preserveComments) { let itt; if (file.manager === undefined) { itt = new ItemTypeDefinition(); itt.behaviorPackFile = file; file.manager = itt; } if (file.manager !== undefined && file.manager instanceof ItemTypeDefinition) { itt = file.manager; if (!itt.isLoaded || (preserveComments && !itt._loadedWithComments)) { if (loadHandler) { itt.onLoaded.subscribe(loadHandler); } await itt.load(preserveComments); } } return itt; } static isVisualComponent(value) { if (value === "minecraft:icon" || value === "minecraft:display_name" || value === "minecraft:glint" || value === "minecraft:hover_text_color") { return true; } return false; } persist() { if (this._file === undefined) { return false; } Log_1.default.assert(!this._isLoaded || this._wrapper !== null, "ITDP"); if (!this._wrapper) { return false; } return this._file.setObjectContentIfSemanticallyDifferent(this._wrapper); } /** * 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._onLoaded.dispatch(this, this); return; } // Use comment-preserving parser only when needed for editing this._wrapper = preserveComments ? StorageUtilities_1.default.getJsonObjectWithComments(this._file) : StorageUtilities_1.default.getJsonObject(this._file); if (this._wrapper) { const item = this._wrapper["minecraft:item"]; if (item && item.description) { this.id = item.description.identifier; } this._data = item; } this._isLoaded = true; this._loadedWithComments = preserveComments; this._onLoaded.dispatch(this, this); } } exports.default = ItemTypeDefinition;