@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
1,215 lines (1,214 loc) • 55.9 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 });
const IProjectItemData_1 = require("./IProjectItemData");
const Log_1 = __importDefault(require("./../core/Log"));
const IProjectItemData_2 = require("./IProjectItemData");
const ste_events_1 = require("ste-events");
const IFile_1 = require("../storage/IFile");
const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities"));
const EntityTypeDefinition_1 = __importDefault(require("../minecraft/EntityTypeDefinition"));
const IProjectData_1 = require("./IProjectData");
const MCWorld_1 = __importDefault(require("../minecraft/MCWorld"));
const ZipStorage_1 = __importDefault(require("../storage/ZipStorage"));
const Utilities_1 = __importDefault(require("../core/Utilities"));
const IStorage_1 = require("../storage/IStorage");
const ProjectItemUtilities_1 = __importDefault(require("./ProjectItemUtilities"));
const ProjectAutogeneration_1 = __importDefault(require("./ProjectAutogeneration"));
const ProjectItemRelations_1 = __importDefault(require("./ProjectItemRelations"));
const ProjectItemVariant_1 = __importDefault(require("./ProjectItemVariant"));
const IProjectItemVariant_1 = require("./IProjectItemVariant");
const Database_1 = __importDefault(require("../minecraft/Database"));
const ProjectVariant_1 = __importDefault(require("./ProjectVariant"));
const MinecraftUtilities_1 = __importDefault(require("../minecraft/MinecraftUtilities"));
class ProjectItem {
_data;
_project;
_onPropertyChanged = new ste_events_1.EventDispatcher();
_onFileRetrieved = new ste_events_1.EventDispatcher();
_onFolderRetrieved = new ste_events_1.EventDispatcher();
_onLoaded = new ste_events_1.EventDispatcher();
_defaultFile;
_defaultFolder;
_pendingLoadRequests = [];
_isLoading = false;
_isFileContentProcessed = false;
_imageUrlBase64Cache;
_pack;
_primaryFile;
_accessoryFolder;
parentItems;
childItems;
unfulfilledRelationships;
_variants;
constructor(parent, incomingData) {
this._project = parent;
this._defaultFile = null;
this._defaultFolder = null;
this._accessoryFolder = null;
this._isFileContentProcessed = false;
this._variants = {};
this._handleMCWorldLoaded = this._handleMCWorldLoaded.bind(this);
this.sortVariantsMostImportantFirst = this.sortVariantsMostImportantFirst.bind(this);
this.sortVariantsMostImportantLast = this.sortVariantsMostImportantLast.bind(this);
if (incomingData) {
this._data = incomingData;
// Migration: older projects may have persisted an empty-label default variant
// (label: "", variantType: general). The empty-label variant is purely a runtime
// placeholder for the item's default file and should not be persisted; leaving
// it in #data.variants makes _getVariantList() report a phantom entry that other
// code paths (e.g. variant.ensureFileStorage subpack lookup) can mishandle.
if (this._data.variants && this._data.variants[""] !== undefined) {
delete this._data.variants[""];
}
}
else {
this._data = {
variants: {},
itemType: IProjectItemData_2.ProjectItemType.unknown,
projectPath: null,
storageType: IProjectItemData_1.ProjectItemStorageType.singleFile,
tags: [],
name: "",
};
}
}
get parentItemCount() {
if (this.parentItems === undefined) {
return 0;
}
return this.parentItems.length;
}
get childItemCount() {
if (this.childItems === undefined) {
return 0;
}
return this.childItems.length;
}
get unfulfilledRelationshipsCount() {
if (this.unfulfilledRelationships === undefined) {
return 0;
}
return this.unfulfilledRelationships.length;
}
get isInWorld() {
return this._data.isInWorld;
}
set isInWorld(isInWorld) {
this._data.isInWorld = isInWorld;
}
get project() {
return this._project;
}
get onPropertyChanged() {
return this._onPropertyChanged.asEvent();
}
get onLoaded() {
return this._onLoaded.asEvent();
}
get onFileRetrieved() {
return this._onFileRetrieved.asEvent();
}
get onFolderRetrieved() {
return this._onFolderRetrieved.asEvent();
}
get gitHubReference() {
return this._data.gitHubReference;
}
get isInFileContainer() {
if (!this.projectPath) {
return false;
}
return this.projectPath.indexOf("#") >= 0;
}
get isFileContainerStorageItem() {
return (this.itemType === IProjectItemData_2.ProjectItemType.zip ||
this.itemType === IProjectItemData_2.ProjectItemType.MCWorld ||
this.itemType === IProjectItemData_2.ProjectItemType.MCProject ||
this.itemType === IProjectItemData_2.ProjectItemType.MCAddon ||
this.itemType === IProjectItemData_2.ProjectItemType.MCPack ||
this.itemType === IProjectItemData_2.ProjectItemType.MCTemplate);
}
hasCustomVariants() {
for (const key in this._data.variants) {
if (key !== "") {
return true;
}
}
return false;
}
hasVersionSliceCustomVariants() {
for (const key in this._data.variants) {
const varType = this._data.variants[key].variantType;
if (key !== "" &&
(varType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks ||
varType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice ||
varType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt)) {
return true;
}
}
return false;
}
_shouldEnsureDefaultVariant() {
return (this.defaultFile && (this.defaultFile.content || !this.hasCustomVariants())) || this.defaultFolder;
}
getVariant(variantName) {
if (variantName === "" && this._shouldEnsureDefaultVariant()) {
return this.ensureDefaultVariant();
}
return this._variants[variantName];
}
get isWorld() {
return (this.itemType === IProjectItemData_2.ProjectItemType.MCProject ||
this.itemType === IProjectItemData_2.ProjectItemType.MCWorld ||
this.itemType === IProjectItemData_2.ProjectItemType.MCTemplate ||
this.itemType === IProjectItemData_2.ProjectItemType.worldFolder);
}
static getGitHubSignature(info) {
let sig = info.owner + "|" + info.repoName + "|";
if (info.branch !== undefined) {
sig += info.branch;
}
if (info.folder !== undefined) {
sig += "|" + info.folder;
}
return sig;
}
get defaultVariant() {
if (this._shouldEnsureDefaultVariant()) {
return this.ensureDefaultVariant();
}
return this._variants[""];
}
async ensureAccessoryFolder() {
if (this._accessoryFolder) {
return this._accessoryFolder;
}
const rootAccessoryFolder = await this.project.ensureProjectItemAccessoryFolder();
const folderPath = ProjectItemUtilities_1.default.getAccessoryFolderPathFromFilePath(this);
this._accessoryFolder = await rootAccessoryFolder.ensureFolderFromRelativePath(folderPath);
return this._accessoryFolder;
}
ensureDefaultVariant() {
return this.ensureVariant("");
}
getVariantList() {
if (this.defaultFile || this.defaultFolder) {
this.ensureDefaultVariant();
}
return this._getVariantList();
}
_getVariantList() {
const vararr = [];
// The empty-label default variant is intentionally NOT persisted in
// _data.variants (see ensureVariant). If it has been instantiated as a
// runtime placeholder, include it first so callers iterating the variant
// list still see the item's default file/content.
if (this._variants[""] !== undefined) {
vararr.push(this._variants[""]);
}
for (const key in this._data.variants) {
if (key === "") {
continue;
}
const variant = this.ensureVariant(key);
vararr.push(variant);
}
return vararr;
}
getVariantListMostImportantLast() {
const vararr = [];
const varKeys = Object.keys(this._data.variants);
varKeys.sort(this.sortVariantsMostImportantLast);
for (const key of varKeys) {
const variant = this.ensureVariant(key);
vararr.push(variant);
}
return vararr;
}
getVariantListMostImportantFirst() {
const vararr = [];
const varKeys = Object.keys(this._data.variants);
varKeys.sort(this.sortVariantsMostImportantFirst);
for (const key of varKeys) {
const variant = this.ensureVariant(key);
vararr.push(variant);
}
return vararr;
}
hasNonDefaultVariant() {
const vararr = this._getVariantList();
if (vararr.length === 0) {
return false;
}
if (vararr.length > 1) {
return true;
}
if (vararr[0].label !== "") {
return true;
}
return false;
}
ensureVariant(label) {
label = ProjectVariant_1.default.canonicalizeVariantLabel(label);
if (!Utilities_1.default.isUsableAsObjectKey(label)) {
Log_1.default.unsupportedToken(label);
throw new Error();
}
if (!this._variants[label]) {
const pv = this.project.ensureVariant(label);
if (!this._data.variants) {
this._data.variants = {};
}
// The empty-label default variant is a runtime placeholder for the item's
// default file and is not persisted in #data.variants. Use a transient data
// object so the runtime ProjectItemVariant still works without leaking a
// phantom entry into the saved project (which historically caused
// _getVariantList() to report count=1 on items that have no real variants).
let variantData = this._data.variants[label];
if (variantData === undefined) {
variantData = { label: label, variantType: IProjectItemVariant_1.ProjectItemVariantType.general };
if (label !== "") {
this._data.variants[label] = variantData;
}
}
this._primaryFile = undefined;
this._variants[label] = new ProjectItemVariant_1.default(this, variantData, pv);
}
return this._variants[label];
}
/// NOTE: ProjectItem.ensureFileStorage or ensureStorage should be called before this method.
getFile(variantName) {
if (variantName === undefined || variantName === "") {
return this._defaultFile;
}
const variant = this.ensureVariant(variantName);
return variant.file;
}
/// NOTE: ProjectItem.ensureFolderStorage or ensureStorage should be called before this method.
getFolder(variantName) {
if (variantName === undefined || variantName === "") {
return this._defaultFolder;
}
const variant = this.ensureVariant(variantName);
return variant.folder;
}
/// NOTE: ProjectItem.ensure*Storage should be called before this method.
async getPack() {
if (this._pack) {
return this._pack;
}
let thisPath = undefined;
if (this._defaultFile) {
thisPath = this._defaultFile.storageRelativePath;
}
else if (this._defaultFolder) {
thisPath = this._defaultFolder.storageRelativePath;
}
if (thisPath === undefined) {
return undefined;
}
thisPath = StorageUtilities_1.default.canonicalizePath(thisPath);
await this.project.ensurePacks();
for (const pack of this.project.packs) {
if (thisPath.startsWith(StorageUtilities_1.default.canonicalizePath(pack.folder.storageRelativePath))) {
this._pack = pack;
return this._pack;
}
}
return undefined;
}
addUnfulfilledRelationship(path, itemType, isVanillaDependent) {
let pir = {
parentItem: this,
path: path,
itemType: itemType,
isVanillaDependent: isVanillaDependent === true,
};
if (this.unfulfilledRelationships === undefined) {
this.unfulfilledRelationships = [];
}
this.unfulfilledRelationships.push(pir);
}
async ensureDependencies() {
await ProjectItemRelations_1.default.calculateForItem(this);
}
addChildItem(childItem) {
if (this.childItems) {
for (const rel of this.childItems) {
if (rel.childItem === childItem && rel.parentItem === this) {
return;
}
}
}
if (childItem === this) {
return;
}
if (ProjectItemUtilities_1.default.wouldBeCircular(childItem)) {
return;
}
let hasChild = false;
let childHasParent = false;
if (this.childItems === undefined) {
this.childItems = [];
}
if (childItem.parentItems === undefined) {
childItem.parentItems = [];
}
for (const existingRelation of this.childItems) {
if (existingRelation.childItem === childItem && existingRelation.parentItem === this) {
hasChild = true;
}
}
for (const existingRelation of childItem.parentItems) {
if (existingRelation.childItem === childItem && existingRelation.parentItem === this) {
childHasParent = true;
}
}
const pir = {
parentItem: this,
childItem: childItem,
};
if (!hasChild) {
this.childItems.push(pir);
}
if (!childHasParent) {
childItem.parentItems.push(pir);
}
}
addParentItem(parentItem) {
if (ProjectItemUtilities_1.default.wouldBeCircular(parentItem)) {
return;
}
if (parentItem === this) {
return;
}
if (this.parentItems === undefined) {
this.parentItems = [];
}
if (parentItem.childItems === undefined) {
parentItem.childItems = [];
}
let hasParent = false;
let parentHasChild = false;
for (const existingRelation of this.parentItems) {
if (existingRelation.parentItem === parentItem && existingRelation.childItem === this) {
hasParent = true;
}
}
for (const existingRelation of parentItem.childItems) {
if (existingRelation.parentItem === parentItem && existingRelation.childItem === this) {
parentHasChild = true;
}
}
const pir = {
parentItem: parentItem,
childItem: this,
};
if (!hasParent) {
this.parentItems.push(pir);
}
if (!parentHasChild) {
parentItem.childItems.push(pir);
}
}
toString() {
return this.itemType + ": " + this.projectPath;
}
/// NOTE: ProjectItem.ensure*Storage should be called before this method.
async getPackRelativePath() {
const pack = await this.getPack();
if (!pack) {
return undefined;
}
if (this._defaultFile) {
return this._defaultFile.getFolderRelativePath(pack.folder);
}
else if (this._defaultFolder) {
return this._defaultFolder.getFolderRelativePath(pack.folder);
}
return undefined;
}
static gitHubReferencesEqual(refA, refB) {
if (refA === refB) {
return true;
}
if (refA === undefined && refB === undefined) {
return true;
}
if (refA !== undefined &&
refB !== undefined &&
refA.owner === refB.owner &&
refA.repoName === refB.repoName &&
refA.folder === refB.folder &&
refA.branch === refB.branch) {
return true;
}
return false;
}
set gitHubReference(value) {
if (ProjectItem.gitHubReferencesEqual(this._data.gitHubReference, value)) {
return;
}
this._data.gitHubReference = value;
this._project.notifyProjectItemChanged(this);
}
get title() {
return (StorageUtilities_1.default.getContaineredFileLeafPath(this.projectPath) +
" (" +
ProjectItemUtilities_1.default.getDescriptionForType(this._data.itemType).toLowerCase() +
")");
}
get typeTitle() {
return ProjectItemUtilities_1.default.getDescriptionForType(this._data.itemType);
}
getPackFolderName() {
if (this.projectPath === undefined || this.projectPath === null) {
return undefined;
}
let folderStoragePath = StorageUtilities_1.default.getFolderPath(this.projectPath);
if (folderStoragePath === undefined) {
return undefined;
}
let folderStoragePathLower = folderStoragePath.toLowerCase();
if (this.project.packs.length > 2) {
const endOfPacks = folderStoragePathLower.indexOf("_packs/");
if (endOfPacks > 0) {
const endOfPacksSegment = folderStoragePathLower.indexOf("/", endOfPacks + 7);
if (endOfPacksSegment > 0) {
return folderStoragePath.substring(endOfPacks + 7, endOfPacksSegment);
}
}
}
return undefined;
}
get folderPath() {
if (this.projectPath === undefined || this.projectPath === null) {
return undefined;
}
return StorageUtilities_1.default.getFolderPath(this.projectPath);
}
getFolderGroupingPath() {
if (this.projectPath === undefined || this.projectPath === null) {
return undefined;
}
let folderStoragePath = StorageUtilities_1.default.getFolderPath(this.projectPath);
if (folderStoragePath === undefined) {
return undefined;
}
folderStoragePath = MinecraftUtilities_1.default.clearCommonTerms(folderStoragePath);
let folderStoragePathLower = folderStoragePath.toLowerCase();
const folderTypeRoots = [...ProjectItemUtilities_1.default.getFolderRootsForType(this.itemType)];
folderTypeRoots.push("zip");
for (const folderTypeRoot of folderTypeRoots) {
if (this.project.hasMultiplePacksOfSameType) {
folderStoragePath = Utilities_1.default.replaceAllCaseInsensitive(folderStoragePath, "/" + folderTypeRoot + "/", "/");
}
else {
const start = folderStoragePathLower.indexOf("/" + folderTypeRoot + "/");
if (start >= 0) {
let packPrefix = this.getPackFolderName();
if (packPrefix === undefined) {
packPrefix = "";
}
else {
packPrefix += " ";
}
folderStoragePath = packPrefix + folderStoragePath.substring(start + 2 + folderTypeRoot.length);
folderStoragePathLower = folderStoragePath.toLowerCase();
}
}
}
folderStoragePath = folderStoragePath.replace(/#/gi, " ").trim();
return folderStoragePath;
}
getCommunitySchemaPath() {
return ProjectItemUtilities_1.default.getCommunitySchemaPathForType(this.itemType);
}
getOfficialSchemaPath() {
return ProjectItemUtilities_1.default.getOfficialSchemaPathForType(this.itemType);
}
getFormPath() {
// For geometry files, check if this is a legacy format (format_version 1.8.0 with "geometry.<name>" keys)
// vs modern format (format_version 1.21.0 with "minecraft:geometry")
if (this.itemType === IProjectItemData_2.ProjectItemType.modelGeometryJson && this.primaryFile) {
const content = StorageUtilities_1.default.getJsonObject(this.primaryFile);
if (content) {
// Check if any root key starts with "geometry." (legacy format)
const rootKeys = Object.keys(content);
const hasLegacyGeometry = rootKeys.some((key) => key.toLowerCase().startsWith("geometry."));
if (hasLegacyGeometry) {
return "visual/geometry.v1.8.0";
}
}
}
return ProjectItemUtilities_1.default.getFormPathForType(this.itemType);
}
get errorStatus() {
return this._data.errorStatus;
}
set errorStatus(newErrorStatus) {
this._data.errorStatus = newErrorStatus;
}
get source() {
return this._data.source;
}
set source(newSource) {
this._data.source = newSource;
}
get errorMessage() {
return this._data.errorMessage;
}
set errorMessage(newErrorMessage) {
this._data.errorMessage = newErrorMessage;
}
get projectPath() {
if (this._data.projectPath === undefined && this._data.storagePath) {
this._data.projectPath = this._data.storagePath;
return this._data.storagePath;
}
return this._data.projectPath;
}
set projectPath(newBasePath) {
this._data.projectPath = newBasePath;
}
get effectiveEditPreference() {
const ep = this.editPreference;
if (ep === IProjectItemData_1.ProjectItemEditPreference.projectDefault) {
return this._project.effectiveEditPreference;
}
else if (ep === IProjectItemData_1.ProjectItemEditPreference.forceRaw) {
return IProjectData_1.ProjectEditPreference.raw;
}
else {
return IProjectData_1.ProjectEditPreference.summarized;
}
}
get editPreference() {
if (this._data.editPreference === undefined) {
return IProjectItemData_1.ProjectItemEditPreference.projectDefault;
}
return this._data.editPreference;
}
set editPreference(newEditPreference) {
this._data.editPreference = newEditPreference;
}
get storageType() {
if (this._data.storageType === undefined) {
return IProjectItemData_1.ProjectItemStorageType.singleFile;
}
return this._data.storageType;
}
set storageType(newStorageType) {
this._data.storageType = newStorageType;
}
get creationType() {
return this._data.creationType;
}
set creationType(newCreationType) {
this._data.creationType = newCreationType;
}
get itemType() {
return this._data.itemType;
}
get defaultFile() {
return this._defaultFile;
}
/**
* Get the cached thumbnail data URL for this item.
* Returns undefined if no thumbnail has been generated.
*/
get cachedThumbnail() {
return this._data.cachedThumbnail;
}
/**
* Set the cached thumbnail for this item.
* Also updates the thumbnailGeneratedAt timestamp and clears the imageUrl cache.
*/
set cachedThumbnail(value) {
this._data.cachedThumbnail = value;
this._data.thumbnailGeneratedAt = value ? Date.now() : undefined;
// Clear the cached imageUrl so it picks up the new thumbnail
this._imageUrlBase64Cache = undefined;
}
/**
* Get the timestamp when the thumbnail was last generated.
*/
get thumbnailGeneratedAt() {
return this._data.thumbnailGeneratedAt;
}
/**
* Get the thumbnail link - path to another item whose thumbnail should be used.
*/
get thumbnailLink() {
return this._data.thumbnailLink;
}
/**
* Set the thumbnail link - path to another item whose thumbnail should be used.
*/
set thumbnailLink(value) {
this._data.thumbnailLink = value;
}
/**
* Check if this item type supports thumbnail generation.
*/
get supportsThumbnail() {
return this.itemType === IProjectItemData_2.ProjectItemType.modelGeometryJson;
}
/**
* Clear the cached thumbnail and imageUrl cache.
* Call this when the file content changes.
*/
clearThumbnailCache() {
this._data.cachedThumbnail = undefined;
this._data.thumbnailGeneratedAt = undefined;
this._imageUrlBase64Cache = undefined;
}
sortVariantsMostImportantLast(a, b) {
if (!this._variants) {
this._getVariantList();
}
if (!this._variants) {
return Utilities_1.default.staticCompare(a, b);
}
const va = this._variants[a];
const vb = this._variants[b];
if (!va || !vb || !va.label || !vb.label) {
return 0;
}
if ((va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice) ||
(va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt) ||
(va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks)) {
const versionIndexA = Database_1.default.getVersionIndexFromVersionStr(va.label);
const versionIndexB = Database_1.default.getVersionIndexFromVersionStr(vb.label);
return versionIndexA - versionIndexB;
}
return Utilities_1.default.staticCompare(va.label.toString(), vb.label.toString());
}
sortVariantsMostImportantFirst(a, b) {
if (!this._variants) {
this._getVariantList();
}
if (!this._variants) {
return Utilities_1.default.staticCompare(a, b);
}
const va = this._variants[a];
const vb = this._variants[b];
if (!va || !vb || !va.label || !vb.label) {
return 0;
}
if ((va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice) ||
(va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt) ||
(va.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks &&
vb.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks)) {
const versionIndexA = Database_1.default.getVersionIndexFromVersionStr(va.label);
const versionIndexB = Database_1.default.getVersionIndexFromVersionStr(vb.label);
return versionIndexB - versionIndexA;
}
return vb.label.localeCompare(va.label);
}
get primaryVariantLabel() {
const variantKeys = Object.keys(this._variants);
// if we have version slices, return the latest one that has a file
if (this.hasVersionSliceCustomVariants()) {
variantKeys.sort(this.sortVariantsMostImportantFirst);
for (const variantName of variantKeys) {
const variant = this._variants[variantName];
if (variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice ||
variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt ||
variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks) {
if (variant.file) {
return variant.label;
}
}
}
}
if (this._defaultFile) {
return undefined;
}
for (const variantName of variantKeys) {
if (variantName) {
const variant = this._variants[variantName];
if (variant.file) {
return variant.label;
}
}
}
return undefined;
}
get primaryFile() {
if (this._primaryFile) {
return this._primaryFile;
}
// if we have version slices, return the latest one that has a file
if (this.hasVersionSliceCustomVariants()) {
const variantKeys = Object.keys(this._variants);
variantKeys.sort(this.sortVariantsMostImportantFirst);
for (const variantName of variantKeys) {
const variant = this._variants[variantName];
if (variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSlice ||
variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAlt ||
variant.variantType === IProjectItemVariant_1.ProjectItemVariantType.versionSliceAltPacks) {
if (variant.file) {
this._primaryFile = variant.file;
return this._primaryFile;
}
}
}
}
if (this._defaultFile && (!this._defaultFile.isContentLoaded || this._defaultFile.content)) {
return this._defaultFile;
}
const variantKeys = Object.keys(this._variants);
variantKeys.sort(this.sortVariantsMostImportantFirst);
for (const variantName of variantKeys) {
if (variantName) {
const variant = this._variants[variantName];
if (variant.file) {
this._primaryFile = variant.file;
return this._primaryFile;
}
}
}
// as a last fallback, return an assumedly content-less default file
if (this._defaultFile) {
this._primaryFile = this._defaultFile;
return this._primaryFile;
}
return null;
}
get defaultFolder() {
return this._defaultFolder;
}
set itemType(newItemType) {
this._data.itemType = newItemType;
this._onPropertyChanged.dispatch(this, "itemType");
this._project.notifyProjectItemChanged(this);
}
get isContentLoaded() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder) {
if (!this.defaultFolder) {
return false;
}
if (!this.defaultFolder.isLoaded) {
return false;
}
if (this.itemType === IProjectItemData_2.ProjectItemType.worldFolder) {
if (!this.defaultFolder.manager || !(this.defaultFolder.manager instanceof MCWorld_1.default)) {
return false;
}
const world = this.defaultFolder.manager;
return world.isLoaded;
}
return true;
}
else if (this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile) {
const itemFile = this.primaryFile;
if (!itemFile) {
return false;
}
if (!itemFile.isContentLoaded) {
return false;
}
if (this.isWorld) {
if (!itemFile.manager || !(itemFile.manager instanceof MCWorld_1.default)) {
return false;
}
const world = itemFile.manager;
return world.isLoaded;
}
return this._isFileContentProcessed;
}
Log_1.default.unexpectedContentState();
return false;
}
get tags() {
return this._data.tags;
}
async setFile(file) {
if (file !== this._defaultFile) {
this._defaultFile = file;
this._isFileContentProcessed = false;
}
}
get needsSave() {
if (this._defaultFile === null &&
this.creationType === IProjectItemData_1.ProjectItemCreationType.generated &&
this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile) {
return true;
}
if (this._defaultFile === null) {
return false;
}
let val = this._defaultFile.needsSave;
if (!val) {
if (this.isFileContainerStorageItem &&
this._defaultFile.fileContainerStorage &&
this._defaultFile.fileContainerStorage instanceof ZipStorage_1.default) {
val = this._defaultFile.fileContainerStorage.isContentUpdated;
}
if (!val) {
const jsFile = this.getJavaScriptLibTwin();
if (jsFile !== undefined) {
val = jsFile.needsSave;
}
}
}
return val;
}
updateProjectPath() {
if (this._project && this._project.projectFolder) {
if (this._defaultFile) {
this.projectPath = this._defaultFile.getFolderRelativePath(this._project.projectFolder);
}
else if (this._defaultFolder) {
this.projectPath = this._defaultFolder.getFolderRelativePath(this._project.projectFolder);
}
}
}
async rename(newFileBaseName) {
if (!this.isContentLoaded) {
await this.loadContent();
}
await this._project.ensureProjectFolder();
if (this._project.projectFolder === undefined || this._project.projectFolder === null) {
return;
}
if (this._defaultFile !== null) {
await this._defaultFile.moveTo(this._defaultFile.parentFolder.storageRelativePath + newFileBaseName + "." + this._defaultFile.type);
this._data.name = newFileBaseName + "." + this._defaultFile.type;
this.projectPath = this._defaultFile.getFolderRelativePath(this._project.projectFolder);
this.storageType = IProjectItemData_1.ProjectItemStorageType.singleFile;
}
else {
this._data.name = newFileBaseName;
}
this._onPropertyChanged.dispatch(this, "name");
this._project.notifyProjectItemChanged(this);
}
async deleteItem() {
if (!this.isContentLoaded) {
await this.loadContent();
}
await ProjectItemRelations_1.default.deleteLinksFromParents(this);
if (this._defaultFile !== null) {
await this._defaultFile.deleteThisFile();
}
this._project.removeItem(this);
}
get imageUrl() {
if (this._imageUrlBase64Cache === null) {
return undefined;
}
if (this._imageUrlBase64Cache) {
return this._imageUrlBase64Cache;
}
// Check for cached thumbnail (generated by worker for geometry models, etc.)
if (this._data.cachedThumbnail) {
this._imageUrlBase64Cache = this._data.cachedThumbnail;
return this._imageUrlBase64Cache;
}
if (this.itemType === IProjectItemData_2.ProjectItemType.worldFolder) {
if (this.defaultFolder) {
if (this.defaultFolder.manager instanceof MCWorld_1.default) {
const world = this.defaultFolder.manager;
if (world.isLoaded) {
this._imageUrlBase64Cache = "data:image/jpg;base64, " + world.imageBase64;
return this._imageUrlBase64Cache;
}
}
}
}
else if (this.isWorld) {
const itemFile = this.primaryFile;
if (itemFile) {
if (itemFile.manager instanceof MCWorld_1.default) {
const world = itemFile.manager;
if (world.isLoaded) {
return "data:image/jpg;base64, " + world.imageBase64;
}
}
}
}
else if (this.projectPath && ProjectItemUtilities_1.default.isImageType(this.itemType)) {
if (this.projectPath && this.projectPath.endsWith(".tga")) {
// TGA files need async conversion, handled elsewhere
this._imageUrlBase64Cache = null;
}
else if (this._defaultFile && this._defaultFile.content && this._defaultFile.content instanceof Uint8Array) {
if (this._defaultFile.content.length > 10000) {
this._imageUrlBase64Cache = null;
return undefined;
}
// Determine the correct MIME type based on file extension
const ext = StorageUtilities_1.default.getTypeFromName(this.projectPath || "");
let mimeType = "image/png"; // Default to PNG
if (ext === "jpg" || ext === "jpeg") {
mimeType = "image/jpeg";
}
else if (ext === "gif") {
mimeType = "image/gif";
}
else if (ext === "webp") {
mimeType = "image/webp";
}
this._imageUrlBase64Cache =
"data:" + mimeType + ";base64," + Utilities_1.default.uint8ArrayToBase64(this._defaultFile.content);
return this._imageUrlBase64Cache;
}
}
return undefined;
}
get name() {
if (this.itemType === IProjectItemData_2.ProjectItemType.worldFolder) {
if (this.defaultFolder) {
if (this.defaultFolder.manager instanceof MCWorld_1.default) {
const world = this.defaultFolder.manager;
if (world.isLoaded) {
return world.name;
}
}
}
}
else if (this.itemType === IProjectItemData_2.ProjectItemType.MCWorld || this.itemType === IProjectItemData_2.ProjectItemType.MCTemplate) {
const itemFile = this.primaryFile;
if (itemFile) {
if (itemFile.manager instanceof MCWorld_1.default) {
const world = itemFile.manager;
if (world.isLoaded) {
return world.name;
}
}
}
}
// for certain types of project items, the name of the file is critical
if (this.projectPath &&
(this.itemType === IProjectItemData_2.ProjectItemType.js ||
this.itemType === IProjectItemData_2.ProjectItemType.ts ||
this.itemType === IProjectItemData_2.ProjectItemType.testJs ||
this.itemType === IProjectItemData_2.ProjectItemType.structure)) {
return StorageUtilities_1.default.getLeafName(this.projectPath);
}
if (this._data.name !== undefined) {
return this._data.name;
}
if (this.projectPath) {
return StorageUtilities_1.default.getLeafName(this.projectPath);
}
return "untitled";
}
async loadFolder() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder) {
if (this._defaultFolder === null &&
this.projectPath !== null &&
this.projectPath !== undefined &&
this.projectPath.startsWith("/") &&
this._project.projectFolder !== null &&
this._project.projectFolder !== undefined) {
const prefixPaths = this.projectPath.split("#");
if (prefixPaths.length > 1) {
let folderToLoadFrom = this._project.projectFolder;
for (let i = 0; i < prefixPaths.length - 1; i++) {
if (folderToLoadFrom) {
const zipFile = await folderToLoadFrom.ensureFileFromRelativePath(prefixPaths[i]);
if (!zipFile.isContentLoaded) {
await zipFile.loadContent();
}
if (zipFile.content && zipFile.content instanceof Uint8Array) {
if (!zipFile.fileContainerStorage) {
const zipStorage = new ZipStorage_1.default();
zipStorage.storagePath = zipFile.storageRelativePath + "#";
await zipStorage.loadFromUint8Array(zipFile.content, zipFile.name);
zipStorage.containerFile = zipFile;
zipFile.fileContainerStorage = zipStorage;
}
folderToLoadFrom = zipFile.fileContainerStorage.rootFolder;
}
else {
folderToLoadFrom = undefined;
}
}
}
if (folderToLoadFrom) {
this._defaultFolder = await folderToLoadFrom.ensureFolderFromRelativePath(prefixPaths[prefixPaths.length - 1]);
}
else {
// Log.debugAlert("Unable to parse a containerized file path of '" + this.storagePath + "'");
return null;
}
}
else {
this._defaultFolder = await this._project.projectFolder.ensureFolderFromRelativePath(this.projectPath);
}
await this._defaultFolder.load();
this._onFolderRetrieved.dispatch(this, this._defaultFolder);
if (this.itemType === IProjectItemData_2.ProjectItemType.worldFolder) {
const mcworld = await MCWorld_1.default.ensureMCWorldOnFolder(this._defaultFolder, this._project, this._handleMCWorldLoaded);
if (mcworld) {
this.errorMessage = mcworld.storageErrorMessage;
if (mcworld.storageErrorStatus === IStorage_1.StorageErrorStatus.unprocessable) {
this.errorStatus = IProjectItemData_1.ProjectItemErrorStatus.unprocessable;
}
else {
this.errorStatus = IProjectItemData_1.ProjectItemErrorStatus.none;
}
}
}
else {
this._fireLoadedEvent();
}
}
return this._defaultFolder;
}
return undefined;
}
_handleMCWorldLoaded(world, worldA) {
this._fireLoadedEvent();
}
_fireLoadedEvent() {
if (this._onLoaded && this.isContentLoaded) {
this._onLoaded.dispatch(this, this);
}
}
invalidateContentProcessedState() {
if (this._defaultFile) {
this._defaultFile.manager = undefined;
}
for (const varLabel in this._variants) {
const variant = this._variants[varLabel];
if (variant.file) {
variant.file.manager = undefined;
}
}
this._isFileContentProcessed = false;
}
async getManager() {
if (!this.isContentLoaded) {
await this.loadContent();
}
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile && this._defaultFile) {
return this._defaultFile.manager;
}
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder && this._defaultFolder) {
return this._defaultFolder.manager;
}
return undefined;
}
async ensureStorage() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder) {
await this.loadFolder();
}
else if (this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile) {
await this.ensureFileStorage();
}
}
get isStorageEnsured() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder) {
return !!this._defaultFolder;
}
return !!this.primaryFile;
}
async loadContentDirect() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.folder) {
await this.loadFolder();
}
else if (this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile) {
await this.loadFileContent();
}
}
async ensureFileStorage() {
if (this.storageType === IProjectItemData_1.ProjectItemStorageType.singleFile &&
this._defaultFile === null &&
this.projectPath !== null &&
this.projectPath !== undefined &&
this.projectPath.startsWith("/") &&
this._project.projectFolder !== null &&
this._project.projectFolder !== undefined) {
const prefixPaths = this.projectPath.split("#");
if (prefixPaths.length > 1) {
let folderToLoadFrom = this._project.projectFolder;
for (let i = 0; i < prefixPaths.length - 1; i++) {
if (folderToLoadFrom) {
const zipFile = await folderToLoadFrom.ensureFileFromRelativePath(prefixPaths[i]);
if (!zipFile.isContentLoaded) {
await zipFile.loadContent();
}
if (zipFile.content && zipFile.content instanceof Uint8Array) {
if (!zipFile.fileContainerStorage) {
const zipStorage = new ZipStorage_1.default();
zipStorage.storagePath = zipFile.storageRelativePath + "#";
await zipStorage.loadFromUint8Array(zipFile.content, zipFile.name);
zipStorage.containerFile = zipFile;
zipFile.fileContainerStorage = zipStorage;
}
folderToLoadFrom = zipFile.fileContainerStorage.rootFolder;
}
else {
folderToLoadFrom = undefined;
}
}
}
if (folderToLoadFrom) {
this._defaultFile = await folderToLoadFrom.ensureFileFromRelativePath(prefixPaths[prefixPaths.length - 1]);
this._isFileContentProcessed = false;
}
else {
Log_1.default.debugAlert("Unable to parse a containerized file path of '" + this.projectPath + "'");
return null;
}
}
else {
this._defaultFile = await this._project.projectFolder.ensureFileFromRelativePath(this.projectPath);
this._isFileContentProcessed = false;
}
}
return this._defaultFile;
}
async loadFileContent() {
if (this._isFileContentProcessed) {
return this._defaultFile;
}
if (!this._defaultFile) {
await this.ensureFileStorage();
}
const variants = this._getVariantList();
for (const variant of variants) {
await variant.ensureAndLoadFileStorage();
}
if (this._defaultFile) {
await this.loadFileStorage();
}
else if (variants.length > 0 && !this._isFileContentProcessed) {
// For variant-only items (no default file), mark as processed after
// all variant files have been loaded. Without this, isContentLoaded
// stays false forever because loadFileStorage() requires _defaultFile.
this._isFileContentProcessed = true;
this._fireLoadedEvent();
}
return this._defaultFile;
}
async loadFileStorage() {
if (!this._isFileContentProcessed && this._defaultFile) {
if (this._data.creationType === IProjectItemData_1.ProjectItemCreationType.generated) {
await ProjectAutogeneration_1.default.updateItemAutogeneration(this, true);
}
else {
if (!this._defaultFile.isContentLoaded) {
await this._defaultFile.loadContent();
}
}
await ProjectAutogeneration_1.default.updateItemAutogeneratedSideFiles(this);
this._isFileContentProcessed = true;
this._onFileRetrieved.dispatch(this, this._defaultFile);
if (this.itemType === IProjectItemData_2.ProjectItemType.MCWorld || this.itemType === IProjectItemData_2.ProjectItemType.MCTemplate) {
const mcworld = await MCWorld_1.default.ensureOnFile(this._defaultFile, this._project, this._handleMCWorldLoaded);
if (mcworld) {