UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

1,149 lines 80.4 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const BlockType_1 = __importDefault(require("./BlockType")); const BlockBaseType_1 = __importDefault(require("./BlockBaseType")); const MinecraftUtilities_1 = __importDefault(require("./MinecraftUtilities")); const IBlockBaseTypeData_1 = require("./IBlockBaseTypeData"); const Log_1 = __importDefault(require("./../core/Log")); const HttpStorage_1 = __importDefault(require("../storage/HttpStorage")); const CreatorToolsHost_1 = __importDefault(require("../app/CreatorToolsHost")); const Utilities_1 = __importDefault(require("../core/Utilities")); const NpmModule_1 = __importDefault(require("../devproject/NpmModule")); const FieldUtilities_1 = __importDefault(require("../dataform/FieldUtilities")); const ContentIndex_1 = __importDefault(require("../core/ContentIndex")); const Project_1 = __importStar(require("../app/Project")); const IProjectItemData_1 = require("../app/IProjectItemData"); const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const ICreatorToolsData_1 = require("../app/ICreatorToolsData"); const ZipStorage_1 = __importDefault(require("../storage/ZipStorage")); const TerrainTextureCatalogDefinition_1 = __importDefault(require("./TerrainTextureCatalogDefinition")); const BlocksCatalogDefinition_1 = __importDefault(require("./BlocksCatalogDefinition")); const AppServiceProxy_1 = __importStar(require("../core/AppServiceProxy")); const ScriptModuleInfo_1 = require("../langcore/javascript/ScriptModuleInfo"); class Database { static isVanillaLoaded = false; static isScriptTypesLoaded = false; static isContentSourcesLoaded = false; static vanillaCatalog = null; static loadedFormCount = 0; static _creatorToolsIngameFile = null; static _creatorToolsIngameProject = null; static uxCatalog = {}; static _missingForms = new Set(); static formsFolders = {}; static modelTemplateCatalog = {}; static loadedModelTemplateCount = 0; static stable20TypeDefs = null; static libs = null; static stable10TypeDefs = null; static contentFolder = null; static snippetsFolder = null; static previewMetadataFolder = null; static releaseMetadataFolder = null; static previewVanillaFolder = null; static releaseVanillaFolder = null; static serveVanillaFolder = null; static samplesFolder = null; static releaseVanillaBehaviorPackFolder = null; static releaseVanillaResourcePackFolder = null; static local = null; static vanillaInfoData = null; static vanillaContentIndex = null; static blocksCatalog = undefined; static terrainTextureCatalog = undefined; static previewVanillaInfoData = null; static previewVanillaContentIndex = null; static releaseVanillaContentHashes = null; static _isLoadingReleaseVanillaHashes = false; static _pendingLoadReleaseVanillaHashRequests = []; static samplesInfoData = null; static samplesContentIndex = null; static addonsDocs = null; static biomesMetadata = null; static blocksMetadata = null; static entitiesMetadata = null; static itemsMetadata = null; static commandsMetadata = null; static latestVersion; static latestPreviewVersion; static _isLoadingSnippets = false; static _pendingLoadSnippetsRequests = []; static _isLoadingVanillaResourceDefinition = false; static _pendingLoadVanillaResourceDefinitionRequests = []; static _isLoadingVanillaInfoData = false; static _pendingLoadVanillaInfoDataRequests = []; static _isLoadingPreviewVanillaInfoData = false; static _pendingLoadPreviewVanillaInfoDataRequests = []; static _isLoadingSamples = false; static _pendingLoadSamplesRequests = []; static dataPath = "res/latest/"; static creatorToolsIngameBehaviorPackUUID = "37355e25-5c48-43be-ad24-107d6cf2ea74"; static creatorToolsIngameBehaviorPackVersion = [1, 0, 0]; static creatorToolsIngameResourcePackUUID = "a49a75c0-d3a1-47c5-adcd-70394b0fe749"; static creatorToolsIngameResourcePackVersion = [1, 0, 0]; static minecraftEduVersion = "1.21.90"; static minecraftEduPreviewVersion = "1.21.110"; static fallbackMinecraftVersion = "1.26.0"; // used if we fail to retrieve the latest version from the network static fallbackMinecraftPreviewVersion = "1.26.10.27"; // should be occasionally statically updated. static defaultContentSources = [ { id: "minecraftBedrockComMojang", localFolderPath: "<BDRK>", }, { id: "minecraftBedrockPreviewComMojang", localFolderPath: "<BDPV>", }, { id: "minecraftEducationComMojang", localFolderPath: "<EDUR>", }, { id: "minecraftEducationPreviewComMojang", localFolderPath: "<EDUP>", }, ]; static contentSources = []; /** * Gets all known Minecraft script module names. * Delegates to ScriptModuleInfoProvider which is the single source of truth. */ static get minecraftModuleNames() { return ScriptModuleInfo_1.ScriptModuleInfoProvider.getAllModuleNames(); } static maxMinecraftPatchVersions = { "1.19": "80", "1.20": "80", "1.21": "120", }; static moduleDescriptors = {}; static blockTypes = {}; static schemaContents = {}; static officialSchemaContents = {}; static blockBaseTypes = {}; static _blockTypesByLegacyId; static javaBlockTypeData = {}; static _defaultBlockBaseType; static get defaultBlockBaseType() { if (Database._defaultBlockBaseType === undefined) { Database._defaultBlockBaseType = Database.ensureBlockBaseType("block"); } return Database._defaultBlockBaseType; } static async ensureFormLoadedByPath(path) { const lastSlash = path.lastIndexOf("/"); if (lastSlash >= 0) { return await Database.ensureFormLoaded(path.substring(0, lastSlash), path.substring(lastSlash + 1)); } return undefined; } /** * Clears cached folder references for bulk content (vanilla, samples) to free memory. * This should be called after bulk operations like documentation generation are complete. */ static clearBulkContentCaches() { Database.previewVanillaFolder = null; Database.releaseVanillaFolder = null; Database.serveVanillaFolder = null; Database.samplesFolder = null; Database.previewVanillaContentIndex = null; Database.samplesContentIndex = null; Database.previewVanillaInfoData = null; Database.samplesInfoData = null; } /** * Reads a JSON file directly from the preview vanilla content without using the folder abstraction. * This avoids caching File/Folder objects in memory, which is important for batch processing. * @param relativePath Path relative to the vanilla preview root (e.g., "/behavior_pack/entities/pig.json") * @returns The parsed JSON object, or null if the file doesn't exist or can't be parsed */ static async readPreviewVanillaJsonFile(relativePath) { if (!Database.local) { return null; } // Normalize the path - remove leading slash if present if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } const fullPath = "res/latest/van/preview/" + relativePath; return await Database.local.readJsonFile(fullPath); } /** * Reads a JSON file directly from the samples content without using the folder abstraction. * This avoids caching File/Folder objects in memory, which is important for batch processing. * @param relativePath Path relative to the samples root (e.g., "/behavior_packs/example/entities/pig.json") * @returns The parsed JSON object, or null if the file doesn't exist or can't be parsed */ static async readSamplesJsonFile(relativePath) { if (!Database.local) { return null; } // Normalize the path - remove leading slash if present if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } const fullPath = "res/samples/" + relativePath; return await Database.local.readJsonFile(fullPath); } static getFormName(subFolder, name) { name = name.toLowerCase(); return (subFolder ? subFolder + "." : "") + name; } static isFormLoaded(subFolder, name) { const extendedName = Database.getFormName(subFolder, name); if (Database.uxCatalog[extendedName] !== undefined) { return true; } return false; } /** * Forms are loaded from a single canonical location: `data/forms/`. * * Generation: the `gulp copybedrockschemas` task lays down * `@minecraft/bedrock-schemas`'s upstream forms first, then OVERLAYS our * checked-in overrides from `app/public_supplemental/data/local_forms/` * directly on top of them (same-named files replace upstream copies). * * The result is a single merged tree at `public/data/forms/` (or * `toolbuild/vsc/data/forms/` for the VSC extension), so this loader does * not need a runtime fallback chain — there is only one place to look. * * To extend or replace an upstream form, drop a same-pathed file under * `app/public_supplemental/data/local_forms/<sub>/<name>.form.json` and * rebuild. To unsticky an override (e.g. because upstream caught up), just * delete that file and rebuild. */ static DEFAULT_FORMS_PATH = "data/forms/"; static ensureFormLoadedSync(subFolder, name) { name = name.toLowerCase(); const extendedName = Database.getFormName(subFolder, name); if (Database.uxCatalog[extendedName] !== undefined) { return Database.uxCatalog[extendedName]; } return Database._tryLoadFormSync(Database.DEFAULT_FORMS_PATH, subFolder, name); } static _tryLoadFormSync(basePath, subFolder, name) { let path = basePath; if (subFolder) { path += subFolder + "/"; } if (!Database.local) { return undefined; } const storage = Database.local.createStorage(path); if (!storage) { return undefined; } if (!storage.rootFolder.isLoaded) { if (storage.rootFolder.loadSync) { storage.rootFolder.loadSync(); } } const file = storage.rootFolder.files[name + ".form.json"]; if (!file) { return undefined; } if (!file.isContentLoaded) { if (file.loadContentSync) { file.loadContentSync(); } } const res = StorageUtilities_1.default.getJsonObject(file); if (res) { FieldUtilities_1.default.normalizeFormFieldDataTypes(res); } return res; } static async ensureFormLoaded(subFolder, name) { name = name.toLowerCase(); const extendedName = Database.getFormName(subFolder, name); if (Database.uxCatalog[extendedName] !== undefined) { return Database.uxCatalog[extendedName]; } // Skip re-fetching forms that were previously not found if (Database._missingForms.has(extendedName)) { return undefined; } const res = await Database._tryLoadForm(Database.DEFAULT_FORMS_PATH, subFolder, name); if (res) { Database.uxCatalog[extendedName] = res; Database.loadedFormCount++; return res; } Database._missingForms.add(extendedName); return undefined; } static async _tryLoadForm(basePath, subFolder, name) { let path = basePath; if (subFolder) { path += subFolder + "/"; } if (Database.local) { const storage = Database.local.createStorage(path); if (!storage) { return undefined; } if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } const file = storage.rootFolder.files[name + ".form.json"]; if (!file) { return undefined; } if (!file.isContentLoaded) { await file.loadContent(); } const res = StorageUtilities_1.default.getJsonObject(file); if (res) { FieldUtilities_1.default.normalizeFormFieldDataTypes(res); } return res; } // HTTP fallback: fetch directly. We swallow 404s silently because the local // override directory is expected to be sparse (most forms have no override). const httpPath = CreatorToolsHost_1.default.contentWebRoot + path + name + ".form.json"; try { const response = await axios_1.default.get(httpPath); const res = response.data; if (res) { FieldUtilities_1.default.normalizeFormFieldDataTypes(res); } return res; } catch { return undefined; } } static isModelTemplateLoaded(name) { const normalizedName = name.toLowerCase(); return Database.modelTemplateCatalog[normalizedName] !== undefined; } static getModelTemplate(name) { const normalizedName = name.toLowerCase(); return Database.modelTemplateCatalog[normalizedName]; } static async ensureModelTemplateLoaded(name) { const normalizedName = name.toLowerCase(); if (Database.modelTemplateCatalog[normalizedName] !== undefined) { return Database.modelTemplateCatalog[normalizedName]; } let path = "data/model_templates/"; if (Database.local) { const storage = Database.local.createStorage(path); if (storage) { if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } const file = storage.rootFolder.files[normalizedName + ".model.json"]; if (file) { if (!file.isContentLoaded) { await file.loadContent(); } const res = StorageUtilities_1.default.getJsonObject(file); if (res) { Database.modelTemplateCatalog[normalizedName] = res; Database.loadedModelTemplateCount++; return res; } } Log_1.default.fail("Could not load model template locally for '" + normalizedName + "'."); return undefined; } } else { path = CreatorToolsHost_1.default.contentWebRoot + path + normalizedName + ".model.json"; try { const response = await axios_1.default.get(path); Database.modelTemplateCatalog[normalizedName] = response.data; Database.loadedModelTemplateCount++; return response.data; } catch { Log_1.default.fail("Could not load model template for '" + path + "'."); } } return undefined; } static async getModelTemplateNames() { const templateNames = []; const path = "data/model_templates/"; if (Database.local) { const storage = Database.local.createStorage(path); if (storage) { if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } for (const fileName of Object.keys(storage.rootFolder.files)) { if (fileName.endsWith(".model.json")) { templateNames.push(fileName.replace(".model.json", "")); } } } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.contentWebRoot + path); if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } for (const fileName of Object.keys(storage.rootFolder.files)) { if (fileName.endsWith(".model.json")) { templateNames.push(fileName.replace(".model.json", "")); } } } return templateNames; } static async getFormsFolder(subFolder) { if (this.formsFolders[subFolder]) { return this.formsFolders[subFolder]; } const folderPath = "data/forms/" + subFolder + "/"; if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.contentWebRoot || !CreatorToolsHost_1.default.retrieveDataFromWebContentRoot)) { const storage = Database.local.createStorage(folderPath); if (storage) { if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } this.formsFolders[subFolder] = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.contentWebRoot + folderPath); if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } this.formsFolders[subFolder] = storage.rootFolder; } return this.formsFolders[subFolder]; } static getFormByPath(path) { const lastSlash = path.lastIndexOf("/"); if (lastSlash >= 0) { return Database.getForm(path.substring(0, lastSlash), path.substring(lastSlash + 1)); } return undefined; } static getForm(subFolder, name) { name = Database.getFormName(subFolder, name); return Database.uxCatalog[name]; } static getBlockType(name) { name = MinecraftUtilities_1.default.canonicalizeName(name); const blockType = Database.blockTypes[name]; return blockType; } static populateBlockTypesByLegacyId() { this._blockTypesByLegacyId = []; for (const blockTypeName in this.blockTypes) { const blockType = this.blockTypes[blockTypeName]; if (blockType.numericId !== undefined) { Log_1.default.assert(this._blockTypesByLegacyId[blockType.numericId] === undefined, "Multiple block types registered for the same ID:" + blockType.id); this._blockTypesByLegacyId[blockType.numericId] = blockType; } } } static async getModuleDescriptor(moduleId) { if (Database.moduleDescriptors[moduleId]) { return Database.moduleDescriptors[moduleId]; } const url = "https://registry.npmjs.org/" + moduleId; try { const response = await axios_1.default.get(url); Database.moduleDescriptors[moduleId] = new NpmModule_1.default(response.data); } catch (e) { if (e && (!e.message || !e.message.indexOf || e.message.indexOf("etwork ") < 0)) { Log_1.default.debugAlert("Could not load registry for '" + moduleId + "': " + e.toString()); } else { CreatorToolsHost_1.default.getCreatorTools()?.notifyStatusUpdate("Could not connect to network to retrieve latest script package details. Url: " + url); } } return Database.moduleDescriptors[moduleId]; } static async getNextMinecraftPreviewVersion() { const latestMinecraftPreviewVersion = await Database.getLatestMinecraftPreviewVersion(); if (!latestMinecraftPreviewVersion) { return undefined; } const ver = latestMinecraftPreviewVersion.split("."); if (!ver || !ver.length || ver.length < 3) { return undefined; } let triplet = undefined; try { triplet = [parseInt(ver[0]), parseInt(ver[1]), parseInt(ver[2]) + 10]; } catch (e) { return undefined; } return triplet.join("."); } static async getLatestMinecraftPreviewVersion() { const moduleDescriptor = await this.getModuleDescriptor("@minecraft/server"); if (!moduleDescriptor || !moduleDescriptor.latestPreviewVersion) { return "1.21.0"; } return moduleDescriptor.latestPreviewVersion; } static async getLatestMinecraftRetailVersion() { const moduleDescriptor = await this.getModuleDescriptor("@minecraft/server"); if (!moduleDescriptor || !moduleDescriptor.latestRetailVersion) { return "1.21.0"; } return moduleDescriptor.latestRetailVersion; } static async isRecentVersionFromVersionArray(version) { if (version === undefined || version.length !== 3) { return false; } const ver = await this.getLatestVersionInfo(ICreatorToolsData_1.MinecraftTrack.main); if (!ver) { return false; } const verArr = MinecraftUtilities_1.default.getVersionArrayFrom(ver); if (!verArr || verArr.length < 3) { return false; } const majorVersion = verArr[0]; const minorVersion = verArr[1]; if (version[0] < majorVersion) { return false; } if (majorVersion === version[0] && minorVersion - version[1] > 1) { return false; } return true; } static async getContentFolderFile(fileRelativePath) { await Database.loadContent(); if (Database.contentFolder === null || !CreatorToolsHost_1.default.getCreatorTools()) { Log_1.default.unexpectedContentState(); return undefined; } fileRelativePath = StorageUtilities_1.default.ensureStartsWithDelimiter(fileRelativePath); return await Database.contentFolder.ensureFileFromRelativePath(fileRelativePath); } static async getContentFolderContent(fileRelativePath) { const file = await Database.getContentFolderFile(fileRelativePath); if (!file) { return; } if (!file.isContentLoaded) { await file.loadContent(); } return file.content; } static async ensureCreatorToolsIngameFile() { if (Database._creatorToolsIngameFile) { return Database._creatorToolsIngameFile; } await Database.loadContent(); if (Database.contentFolder === null || !CreatorToolsHost_1.default.getCreatorTools()) { Log_1.default.unexpectedContentState(); return undefined; } const file = Database.contentFolder.ensureFile("creator_tools_ingame.mcaddon"); if (!file.isContentLoaded) { await file.loadContent(); } Database._creatorToolsIngameFile = file; return file; } static async ensureCreatorToolsIngameProject() { if (Database._creatorToolsIngameProject) { return Database._creatorToolsIngameProject; } const file = await Database.ensureCreatorToolsIngameFile(); const ct = CreatorToolsHost_1.default.getCreatorTools(); if (Database.contentFolder === null || !file || !ct) { Log_1.default.unexpectedContentState(); return undefined; } if (file.content instanceof Uint8Array) { Database._creatorToolsIngameProject = new Project_1.default(ct, "Creator Tools", null); const projectFolder = await Database._creatorToolsIngameProject.ensureProjectFolder(); const folder = projectFolder.ensureFolder("addons"); const contentFile = folder.ensureFile(file.name); contentFile.setContent(file.content); contentFile.saveContent(); const relPath = contentFile.getFolderRelativePath(Database._creatorToolsIngameProject.projectFolder); if (relPath !== undefined) { Database._creatorToolsIngameProject.ensureItemByProjectPath(relPath, IProjectItemData_1.ProjectItemStorageType.singleFile, file.name, IProjectItemData_1.ProjectItemType.MCAddon, Project_1.FolderContext.unknown, undefined, IProjectItemData_1.ProjectItemCreationType.normal); await Database._creatorToolsIngameProject.inferProjectItemsFromZipFile(relPath, contentFile, false); } return Database._creatorToolsIngameProject; } return undefined; } static async getLatestVersionInfo(track, force) { if (track === ICreatorToolsData_1.MinecraftTrack.edu) { return Database.minecraftEduVersion; } if (track === ICreatorToolsData_1.MinecraftTrack.eduPreview) { return Database.minecraftEduPreviewVersion; } // Test pinning hook: if MCT_TEST_PINNED_MC_VERSION (or _PREVIEW_VERSION) is set // in the environment, treat it as the canonical "current Minecraft version" for // this process. This insulates test baselines from upstream version drift — // every validator that pulls "latest" from here (FormatVersionManager, // MinEngineVersionManager, BaseGameVersionManager, etc.) will produce stable, // deterministic output regardless of when the bedrock-samples version.json // changes. Honors `force` so callers that explicitly want a refresh can bypass. if (!force && typeof process !== "undefined" && process.env) { if (track === ICreatorToolsData_1.MinecraftTrack.preview && process.env.MCT_TEST_PINNED_MC_PREVIEW_VERSION) { Database.latestPreviewVersion = process.env.MCT_TEST_PINNED_MC_PREVIEW_VERSION; return Database.latestPreviewVersion; } if (track === ICreatorToolsData_1.MinecraftTrack.main && process.env.MCT_TEST_PINNED_MC_VERSION) { Database.latestVersion = process.env.MCT_TEST_PINNED_MC_VERSION; return Database.latestVersion; } } if (track === ICreatorToolsData_1.MinecraftTrack.preview && Database.latestPreviewVersion && !force) { return Database.latestPreviewVersion; } if (track === ICreatorToolsData_1.MinecraftTrack.main && Database.latestVersion && !force) { return Database.latestVersion; } let minecraftInfoResponse = undefined; let versionUrl = "https://raw.githubusercontent.com/Mojang/bedrock-samples/main/version.json"; if (track === ICreatorToolsData_1.MinecraftTrack.preview) { versionUrl = "https://raw.githubusercontent.com/Mojang/bedrock-samples/preview/version.json"; } try { minecraftInfoResponse = await axios_1.default.get(versionUrl); } catch (e) { if (e && (!e.message || !e.message.indexOf || e.message.indexOf("etwork ") < 0)) { Log_1.default.debugAlert("Could not load version info from '" + versionUrl + "': " + e.toString()); } else { CreatorToolsHost_1.default.getCreatorTools()?.notifyStatusUpdate("Could not connect to network to retrieve latest Minecraft version details. Url: " + versionUrl); } if (track === ICreatorToolsData_1.MinecraftTrack.preview) { Database.latestPreviewVersion = Database.fallbackMinecraftPreviewVersion; return Database.fallbackMinecraftPreviewVersion; } if (track === ICreatorToolsData_1.MinecraftTrack.main) { Database.latestVersion = Database.fallbackMinecraftVersion; return Database.fallbackMinecraftVersion; } } let latestVersionIndex = 0; let latestVerStr = ""; try { if (minecraftInfoResponse === undefined || minecraftInfoResponse.data === undefined) { throw new Error("Unexpected empty response."); } const responseJson = JSON.parse(JSON.stringify(minecraftInfoResponse.data)); if (!responseJson) { throw new Error("Improperly formatted response (source: " + minecraftInfoResponse.data + ")"); } for (const verId in responseJson) { const ver = responseJson[verId].version; if (ver) { const isPreview = Database.getVersionIsPreview(ver); // version ends with .20 or higher const versionIndex = Database.getVersionIndexFromVersionStr(ver); if (versionIndex > 0 && versionIndex > latestVersionIndex && ((isPreview && track === ICreatorToolsData_1.MinecraftTrack.preview) || (!isPreview && track === ICreatorToolsData_1.MinecraftTrack.main))) { latestVersionIndex = versionIndex; // re-constitute the version number ourselves const verNums = ver.split("."); latestVerStr = verNums[0] + "." + verNums[1] + "." + verNums[2] + "." + Utilities_1.default.frontPadToLength(verNums[3], 2, "0"); } } } } catch (e) { Log_1.default.error("Could not process Minecraft version details." + e); throw new Error(e.toString()); } if (latestVerStr && latestVerStr.length > 0) { if (track === ICreatorToolsData_1.MinecraftTrack.preview) { Database.latestPreviewVersion = latestVerStr; } else { Database.latestVersion = latestVerStr; } } return latestVerStr; } static getVersionIsPreview(ver) { const verNums = ver.split("."); if (verNums.length !== 4) { return false; } for (let j = 0; j < verNums.length; j++) { if (!Utilities_1.default.isNumeric(verNums[j])) { return false; } } return parseInt(verNums[3]) > 19; } static getVersionIndexFromVersionStr(ver) { const verNums = ver.split("."); if (verNums.length !== 4 && verNums.length !== 3) { return -1; } for (let j = 0; j < verNums.length; j++) { if (!Utilities_1.default.isNumeric(verNums[j])) { return -1; } } let versionIndex = parseInt(verNums[0]) * 16777216 + parseInt(verNums[1]) * 65536 + parseInt(verNums[2]) * 256; if (verNums.length === 4) { versionIndex += parseInt(verNums[3]); } return versionIndex; } static getBlockTypeByLegacyId(id) { if (!this._blockTypesByLegacyId) { this._blockTypesByLegacyId = []; this.populateBlockTypesByLegacyId(); } const result = this._blockTypesByLegacyId[id]; Log_1.default.assert(result !== undefined); // Return undefined for unknown legacy IDs rather than asserting, // as worlds may contain blocks with IDs not in our catalog return result; } static getMatchingBlocks(searchTerm) { searchTerm = searchTerm.toLowerCase().trim(); searchTerm = searchTerm.replace(/_/gi, " "); const searchTerms = searchTerm.split(" "); let exactMatch = undefined; let matches = []; for (const name in this.blockTypes) { const bt = this.blockTypes[name]; const titleCanon = bt.title.toLowerCase(); if (titleCanon === searchTerm && exactMatch === undefined) { exactMatch = bt; } else { for (let i = 0; i < searchTerms.length; i++) { if (titleCanon.indexOf(searchTerms[i]) >= 0) { matches.push(bt); break; } } } } if (exactMatch !== undefined) { const newMatches = [exactMatch]; for (let i = 0; i < matches.length; i++) { newMatches.push(matches[i]); } matches = newMatches; } return matches; } static ensureBlockType(name) { name = MinecraftUtilities_1.default.canonicalizeName(name); let blockType = Database.blockTypes[name]; if (blockType == null && Database.vanillaCatalog != null) { blockType = new BlockType_1.default(name); Database.blockTypes[name] = blockType; } return blockType; } static ensureBlockBaseType(name) { name = MinecraftUtilities_1.default.canonicalizeName(name); let blockBaseType = Database.blockBaseTypes[name]; if (blockBaseType == null) { blockBaseType = new BlockBaseType_1.default(name); Database.blockBaseTypes[name] = blockBaseType; } return blockBaseType; } static async loadContentSources() { if (Database.isContentSourcesLoaded) { return Database.contentSources; } Database.contentSources = [...Database.defaultContentSources]; if (AppServiceProxy_1.default.hasAppService) { const contentSources = await AppServiceProxy_1.default.sendAsync(AppServiceProxy_1.AppServiceProxyCommands.getContentSources, ""); if (contentSources) { const contentSourcesO = JSON.parse(contentSources); if (contentSourcesO && Array.isArray(contentSourcesO)) { Database.contentSources.push(...contentSourcesO); } } } Database.isContentSourcesLoaded = true; return Database.contentSources; } static async loadContent() { if (Database.contentFolder !== null) { return; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.contentWebRoot)) { const storage = Database.local.createStorage("data/content/"); if (storage) { if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } Database.contentFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.contentWebRoot + "data/content/"); if (!storage.rootFolder.isLoaded) { await storage.rootFolder.load(); } Database.contentFolder = storage.rootFolder; } } static async initSnippetsFolder() { if (Database.snippetsFolder !== null) { return; } if (this._isLoadingSnippets) { const pendingLoad = this._pendingLoadSnippetsRequests; const prom = (resolve, reject) => { pendingLoad.push(resolve); }; await new Promise(prom); return; } else { this._isLoadingSnippets = true; let folder; if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.contentWebRoot || !CreatorToolsHost_1.default.retrieveDataFromWebContentRoot)) { const storage = Database.local.createStorage("data/snippets/"); if (storage) { folder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.contentWebRoot + "data/snippets/"); folder = storage.rootFolder; } if (folder === undefined) { return; } if (!folder.isLoaded) { await folder.load(); } for (const fileName in folder.files) { const file = folder.files[fileName]; if (file && !file.isContentLoaded) { await file.loadContent(); } } Database.snippetsFolder = folder; this._isLoadingSnippets = false; const pendingLoad = this._pendingLoadSnippetsRequests; this._pendingLoadSnippetsRequests = []; for (const prom of pendingLoad) { prom(undefined); } } } static itemMatchesSearch(item, searchString) { if (!searchString || searchString.length < 3) { return true; } const searchKey = searchString.toLowerCase(); if ((item.title && item.title.toLowerCase().indexOf(searchKey) >= 0) || (item.description && item.description.toLowerCase().indexOf(searchKey) >= 0)) { return true; } if (item.topics) { for (const topic of item.topics) { if (topic.toLowerCase().indexOf(searchKey) >= 0) { return true; } } } return false; } static async getSnippet(sampleSet, snippetId) { if (!Database.snippetsFolder) { await Database.initSnippetsFolder(); } if (!Database.snippetsFolder) { return undefined; } if (Database.snippetsFolder !== null && Database.snippetsFolder.files) { const file = Database.snippetsFolder.files[sampleSet + ".json"]; if (file) { const snipSet = StorageUtilities_1.default.getJsonObject(file); if (snipSet && snipSet[snippetId]) { return snipSet[snippetId]; } } } return undefined; } static async getAddonsDocs() { if (!Database.addonsDocs) { Database.addonsDocs = await Database.getMetadataObject("/doc_modules/addons.json"); } return Database.addonsDocs; } static async getBiomesMetadata() { if (!Database.biomesMetadata) { Database.biomesMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-biomes.json"); } return Database.biomesMetadata; } static async getBlocksMetadata() { if (!Database.blocksMetadata) { Database.blocksMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-blocks.json"); } return Database.blocksMetadata; } static async getEntitiesMetadata() { if (!Database.entitiesMetadata) { Database.entitiesMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-entities.json"); } return Database.entitiesMetadata; } static async getItemsMetadata() { if (!Database.itemsMetadata) { Database.itemsMetadata = await Database.getMetadataObject("/vanilladata_modules/mojang-items.json"); } return Database.itemsMetadata; } static async getCommandsMetadata() { if (!Database.commandsMetadata) { Database.commandsMetadata = await Database.getMetadataObject("/command_modules/mojang-commands.json"); } return Database.commandsMetadata; } static async getMetadataObject(metaPath) { await Database.loadPreviewMetadataFolder(); if (!Database.previewMetadataFolder) { return null; } const jsonFile = await Database.previewMetadataFolder.getFileFromRelativePath(metaPath); if (!jsonFile) { Log_1.default.debug("Could not find metadata file: " + metaPath); return null; } if (!jsonFile.isContentLoaded) { await jsonFile.loadContent(); } const jsonObj = StorageUtilities_1.default.getJsonObject(jsonFile); if (!jsonObj) { Log_1.default.debug("Could not parse metadata file: " + metaPath); return null; } return jsonObj; } static async getPreviewVanillaFile(filePath) { const vanillaFolder = await Database.getPreviewVanillaFolder(); if (!vanillaFolder) { return null; } const jsonFile = await vanillaFolder.getFileFromRelativePath(filePath); if (!jsonFile) { // File not found is expected for custom (non-vanilla) entities // Callers should handle null gracefully return null; } if (!jsonFile.isContentLoaded) { await jsonFile.loadContent(); } return jsonFile; } static async getPreviewVanillaObject(filePath) { const jsonFile = await Database.getPreviewVanillaFile(filePath); if (!jsonFile) { Log_1.default.unexpectedUndefined("GPVO" + filePath); return null; } const jsonObj = StorageUtilities_1.default.getJsonObject(jsonFile); if (!jsonObj) { Log_1.default.unexpectedUndefined("GPVA" + filePath); return null; } return jsonObj; } static async loadPreviewMetadataFolder() { if (!this.previewMetadataFolder) { if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot() || !CreatorToolsHost_1.default.retrieveDataFromWebContentRoot)) { const storage = Database.local.createStorage("res/latest/van/preview/metadata/"); if (storage) { this.previewMetadataFolder = storage.rootFolder; } } else { const metadataStorage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/preview/metadata/"); if (!metadataStorage.rootFolder.isLoaded) { await metadataStorage.rootFolder.load(); } this.previewMetadataFolder = metadataStorage.rootFolder; } } return this.previewMetadataFolder; } static async loadReleaseMetadataFolder() { if (!this.releaseMetadataFolder) { if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/release/metadata/"); if (storage) { this.releaseMetadataFolder = storage.rootFolder; } } else { const metadataStorage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/release/metadata/"); await metadataStorage.rootFolder.load(); this.releaseMetadataFolder = metadataStorage.rootFolder; } } return this.releaseMetadataFolder; } static async getReleaseVanillaFolder() { if (Database.releaseVanillaFolder !== null) { return Database.releaseVanillaFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/release/"); if (storage) { Database.releaseVanillaFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/release/"); Database.releaseVanillaFolder = storage.rootFolder; } if (Database.releaseVanillaFolder && !Database.releaseVanillaFolder.isLoaded) { await Database.releaseVanillaFolder.load(); } return Database.releaseVanillaFolder; } static async getPreviewVanillaFolder() { if (Database.previewVanillaFolder !== null) { return Database.previewVanillaFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/preview/"); if (storage) { Database.previewVanillaFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/preview/"); Database.previewVanillaFolder = storage.rootFolder; } if (Database.previewVanillaFolder && !Database.previewVanillaFolder.isLoaded) { await Database.previewVanillaFolder.load(); } return Database.previewVanillaFolder; } static async getServeVanillaFolder() { if (Database.serveVanillaFolder !== null) { return Database.serveVanillaFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/serve/"); if (storage) { Database.serveVanillaFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/serve/"); Database.serveVanillaFolder = storage.rootFolder; } if (Database.serveVanillaFolder && !Database.serveVanillaFolder.isLoaded) { await Database.serveVanillaFolder.load(); } return Database.serveVanillaFolder; } static async getSamplesFolder() { if (Database.samplesFolder !== null) { return Database.samplesFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/samples/microsoft/samples/"); if (storage) { Database.samplesFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/samples/microsoft/samples/"); Database.samplesFolder = storage.rootFolder; } if (Database.samplesFolder) { await Database.samplesFolder.load(); } return Database.samplesFolder; } static async getReleaseVanillaBehaviorPackFolder() { if (Database.releaseVanillaBehaviorPackFolder !== null) { return Database.releaseVanillaBehaviorPackFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/release/behavior_pack/"); if (storage) { Database.releaseVanillaBehaviorPackFolder = storage.rootFolder; } } else { const storage = HttpStorage_1.default.get(CreatorToolsHost_1.default.getVanillaContentRoot() + "res/latest/van/release/behavior_pack/"); Database.releaseVanillaBehaviorPackFolder = storage.rootFolder; } if (Database.releaseVanillaBehaviorPackFolder) { await Database.releaseVanillaBehaviorPackFolder.load(); } return Database.releaseVanillaBehaviorPackFolder; } static async getReleaseVanillaResourcePackFolder() { if (Database.releaseVanillaResourcePackFolder !== null) { return Database.releaseVanillaResourcePackFolder; } if (Database.local && (CreatorToolsHost_1.default.fullLocalStorage || !CreatorToolsHost_1.default.getVanillaContentRoot())) { const storage = Database.local.createStorage("res/latest/van/release/resource_pack/"); if (storage)