@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
1,149 lines • 80.4 kB
JavaScript
"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)