UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

1,305 lines (1,304 loc) 75.9 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 }); exports.DefaultCreatorName = exports.SidePaneMinWidth = exports.SidePaneMaxWidth = exports.CreatorToolsTargetSettings = exports.CreatorToolsMinecraftErrorStatus = exports.CreatorToolsMinecraftState = void 0; const ICreatorToolsData_1 = require("./ICreatorToolsData"); const Project_1 = __importDefault(require("./Project")); const ste_events_1 = require("ste-events"); const Status_1 = require("./Status"); const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const Utilities_1 = __importDefault(require("../core/Utilities")); const GitHubManager_1 = __importDefault(require("../github/GitHubManager")); const axios_1 = __importDefault(require("axios")); const Log_1 = __importDefault(require("../core/Log")); const AppServiceProxy_1 = __importStar(require("../core/AppServiceProxy")); const CommandRunner_1 = __importDefault(require("./CommandRunner")); const RemoteMinecraft_1 = __importDefault(require("./RemoteMinecraft")); const CreatorToolsHost_1 = __importStar(require("./CreatorToolsHost")); const ProcessHostedProxyMinecraft_1 = __importDefault(require("../clientapp/ProcessHostedProxyMinecraft")); const MinecraftGameProxyMinecraft_1 = __importDefault(require("../clientapp/MinecraftGameProxyMinecraft")); const WorldLevelDat_1 = require("../minecraft/WorldLevelDat"); const IWorldSettings_1 = require("../minecraft/IWorldSettings"); const Package_1 = __importDefault(require("./Package")); const CommandRegistry_1 = __importDefault(require("./CommandRegistry")); const ZipStorage_1 = __importDefault(require("../storage/ZipStorage")); const IProjectItemData_1 = require("./IProjectItemData"); const DeploymentStorageMinecraft_1 = __importDefault(require("./DeploymentStorageMinecraft")); const MinecraftUtilities_1 = __importDefault(require("../minecraft/MinecraftUtilities")); const IGalleryItem_1 = require("./IGalleryItem"); const ProjectUtilities_1 = __importDefault(require("./ProjectUtilities")); const ElectronStorage_1 = __importDefault(require("../electronclient/ElectronStorage")); const Storage_1 = __importDefault(require("../storage/Storage")); const Database_1 = __importDefault(require("../minecraft/Database")); const DeploymentTarget_1 = __importStar(require("./DeploymentTarget")); var CreatorToolsMinecraftState; (function (CreatorToolsMinecraftState) { CreatorToolsMinecraftState[CreatorToolsMinecraftState["none"] = 0] = "none"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["initializing"] = 1] = "initializing"; // for remote server, this is authenticating // for web sockets, this is opening up the web socket to receive connections from a client CreatorToolsMinecraftState[CreatorToolsMinecraftState["initialized"] = 2] = "initialized"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["preparing"] = 3] = "preparing"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["prepared"] = 4] = "prepared"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["starting"] = 5] = "starting"; // for remote server, this is creating a session on a socket // for web sockets, note you have to wait for external connections to come in. CreatorToolsMinecraftState[CreatorToolsMinecraftState["started"] = 6] = "started"; // for remote server, the session is ready and initial status has been established // for web sockets, an external client has connected in CreatorToolsMinecraftState[CreatorToolsMinecraftState["stopping"] = 7] = "stopping"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["stopped"] = 8] = "stopped"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["error"] = 9] = "error"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["newMinecraft"] = 10] = "newMinecraft"; CreatorToolsMinecraftState[CreatorToolsMinecraftState["disconnected"] = 11] = "disconnected"; })(CreatorToolsMinecraftState || (exports.CreatorToolsMinecraftState = CreatorToolsMinecraftState = {})); var CreatorToolsMinecraftErrorStatus; (function (CreatorToolsMinecraftErrorStatus) { CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["none"] = 0] = "none"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["actionInProgress"] = 1] = "actionInProgress"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["serverUnavailable"] = 2] = "serverUnavailable"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["serverError"] = 3] = "serverError"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["loginFailed"] = 4] = "loginFailed"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["generalError"] = 5] = "generalError"; CreatorToolsMinecraftErrorStatus[CreatorToolsMinecraftErrorStatus["configuration"] = 6] = "configuration"; })(CreatorToolsMinecraftErrorStatus || (exports.CreatorToolsMinecraftErrorStatus = CreatorToolsMinecraftErrorStatus = {})); exports.CreatorToolsTargetSettings = [ "Latest Minecraft Bedrock", "Latest Minecraft Bedrock preview", "Latest Minecraft Education", "Latest Minecraft Education preview", ]; exports.SidePaneMaxWidth = 880; exports.SidePaneMinWidth = 280; exports.DefaultCreatorName = "Minecraft Creator"; class CreatorTools { _isLoaded; _userGitHub; _anonGitHub; contentRoot = ""; processHostedMinecraft; deploymentStorageMinecraft; gameMinecraft; remoteMinecraft; activeMinecraft; prefsStorage; projectsStorage; deploymentStorage; worldStorage; packStorage; workingStorage; deploymentTargets = []; localFolderExists; localFileExists; ensureLocalFolder; createMinecraft; canCreateMinecraft; _pendingPackLoadRequests = []; _arePacksLoading = false; // Private instance variable for auth token - not persisted in #data // This is used for in-memory session tokens (e.g., from mct view command) _remoteServerAuthToken; // Runtime property for remote server EULA acceptance status // This is set from the server's auth response and indicates whether the // server has accepted the Minecraft EULA. Not persisted locally. _remoteServerEulaAccepted; local; #data; projects; status; activeOperations; packs; mcLogs = {}; _gallery; _galleryLoaded = false; isDeployingToMinecraft = false; hasAttemptedPersistentBrowserStorageSwitch = false; _onDeploymentStorageChanged = new ste_events_1.EventDispatcher(); _onMinecraftStateChanged = new ste_events_1.EventDispatcher(); _onMinecraftRefreshed = new ste_events_1.EventDispatcher(); _onPropertyChanged = new ste_events_1.EventDispatcher(); _onLoaded = new ste_events_1.EventDispatcher(); _onStatusAdded = new ste_events_1.EventDispatcher(); _onOperationCompleted = new ste_events_1.EventDispatcher(); _onStatusAddedAsync = []; _onGalleryLoaded = new ste_events_1.EventDispatcher(); get defaultDeploymentTargetType() { if (this.#data.defaultDeploymentTarget !== undefined) { return this.#data.defaultDeploymentTarget; } for (let i = 0; i < DeploymentTarget_1.MaxDeploymentTargets; i++) { if (this.deploymentStorage[i]) { return i; } } return DeploymentTarget_1.DeploymentTargetType.bedrock; } set defaultDeploymentTargetType(newType) { this.#data.defaultDeploymentTarget = newType; } get isLoaded() { return this._isLoaded; } get collapsedTypes() { if (!this.#data.collapsedTypes) { this.#data.collapsedTypes = []; } return this.#data.collapsedTypes; } get data() { return this.#data; } get defaultDeploymentStorage() { return this.deploymentStorage[this.defaultDeploymentTargetType]; } get defaultDeploymentTarget() { return this.getDeploymentTarget(this.defaultDeploymentTargetType); } set collapsedTypes(newCollapsedTypes) { this.#data.collapsedTypes = newCollapsedTypes; } get showMruPane() { if (this.#data.showMruPane === undefined) { return true; // Default to showing MRU pane } return this.#data.showMruPane; } set showMruPane(newValue) { this.#data.showMruPane = newValue; } get mruItemPaths() { if (!this.#data.mruItemPaths) { this.#data.mruItemPaths = []; } return this.#data.mruItemPaths; } set mruItemPaths(newPaths) { this.#data.mruItemPaths = newPaths; } get viewAsFiles() { if (this.#data.viewAsFiles === undefined) { return false; // Default to items view } return this.#data.viewAsFiles; } set viewAsFiles(newValue) { this.#data.viewAsFiles = newValue; } /** * Adds a project item path to the MRU list. * Moves existing entries to front and maintains max 10 items. */ addToMru(projectPath) { if (!projectPath) return; const paths = this.mruItemPaths; // Remove if already exists (to move to front) const existingIndex = paths.indexOf(projectPath); if (existingIndex !== -1) { paths.splice(existingIndex, 1); } // Add to front paths.unshift(projectPath); // Keep max 10 items if (paths.length > 10) { paths.pop(); } this.#data.mruItemPaths = paths; } /** * Removes a project item path from the MRU list. */ removeFromMru(projectPath) { if (!projectPath) return; const paths = this.mruItemPaths; const index = paths.indexOf(projectPath); if (index !== -1) { paths.splice(index, 1); this.#data.mruItemPaths = paths; } } get editPreference() { return this.#data.editPreference; } set editPreference(newValue) { this.#data.editPreference = newValue; } get disableFirstRun() { return this.#data.disableFirstRun === true; } set disableFirstRun(newValue) { this.#data.disableFirstRun = newValue; } get themePreference() { return this.#data.themePreference; } set themePreference(newValue) { this.#data.themePreference = newValue; } get worldSettings() { return this.#data.worldSettings; } get editorWorldSettings() { return this.#data.editorWorldSettings; } get activeMinecraftState() { if (this.activeMinecraft === undefined) { return CreatorToolsMinecraftState.none; } return this.activeMinecraft.state; } get onMinecraftStateChanged() { return this._onMinecraftStateChanged.asEvent(); } get onDeploymentStorageChanged() { return this._onDeploymentStorageChanged.asEvent(); } get formatBeforeSave() { if (this.#data.formatBeforeSave === undefined) { // Default OFF for pro-grade safety: many creators hand-format their JSON // (compact arrays for [1,0,0] versions, inline component groups, custom // indentation), and silently reformatting on save destroys diff signal // and intentional formatting. Opt-in is the safer default. return false; } return this.#data.formatBeforeSave; } set formatBeforeSave(newValue) { this.#data.formatBeforeSave = newValue; } get showLivePreview() { if (this.#data.showLivePreview === undefined) { return true; // Default to on } return this.#data.showLivePreview; } set showLivePreview(newValue) { this.#data.showLivePreview = newValue; } get conversionJarPath() { return this.#data.conversionJarPath; } set conversionJarPath(newValue) { this.#data.conversionJarPath = newValue; } get preferredTextSize() { if (this.#data.preferredTextSize === undefined) { return 16; } return this.#data.preferredTextSize; } set preferredTextSize(newValue) { this.#data.preferredTextSize = newValue; } get livePreviewWidth() { // Default to 260px, min 200px, max 600px if (this.#data.livePreviewWidth === undefined) { return 260; } if (this.#data.livePreviewWidth < 200) { return 200; } if (this.#data.livePreviewWidth > 600) { return 600; } return this.#data.livePreviewWidth; } set livePreviewWidth(newValue) { // Clamp to min 200px, max 600px if (newValue < 200) { newValue = 200; } if (newValue > 600) { newValue = 600; } this.#data.livePreviewWidth = newValue; } get itemSidePaneWidth() { if (this.#data.itemSidePaneWidth === undefined) { return 300; } if (this.#data.itemSidePaneWidth < exports.SidePaneMinWidth) { return exports.SidePaneMinWidth; } if (this.#data.itemSidePaneWidth > exports.SidePaneMaxWidth) { return exports.SidePaneMaxWidth; } return this.#data.itemSidePaneWidth; } set itemSidePaneWidth(newValue) { this.#data.itemSidePaneWidth = newValue; } get toolPaneWidth() { if (this.#data.toolPaneWidth === undefined) { return 380; } if (this.#data.toolPaneWidth < exports.SidePaneMinWidth) { return exports.SidePaneMinWidth; } if (this.#data.toolPaneWidth > exports.SidePaneMaxWidth) { return exports.SidePaneMaxWidth; } return this.#data.toolPaneWidth; } set toolPaneWidth(newValue) { this.#data.toolPaneWidth = newValue; } get preferredSuite() { return this.#data.preferredSuite; } set preferredSuite(newValue) { this.#data.preferredSuite = newValue; } get track() { return this.#data.track; } set track(newTrack) { if (newTrack !== this.#data.track) { this.#data.track = newTrack; this._onPropertyChanged.dispatch(this, "track"); } } get effectiveTrack() { if (this.#data.track !== undefined) { return this.#data.track; } return ICreatorToolsData_1.MinecraftTrack.main; } get useEditor() { return this.#data.useEditor; } set useEditor(newUseEditor) { if (newUseEditor !== this.#data.useEditor) { this.#data.useEditor = newUseEditor; } } get windowX() { if (this.#data.windowX === undefined) { return 0; } return this.#data.windowX; } set windowX(newVal) { this.#data.windowX = newVal; } get windowY() { if (this.#data.windowY === undefined) { return 0; } return this.#data.windowY; } set windowY(newVal) { this.#data.windowY = newVal; } get windowWidth() { if (this.#data.windowWidth === undefined) { return 1200; } return this.#data.windowWidth; } set windowWidth(newVal) { this.#data.windowWidth = newVal; } get windowHeight() { if (this.#data.windowHeight === undefined) { return 900; } return this.#data.windowHeight; } set windowHeight(newVal) { this.#data.windowHeight = newVal; } get windowSlot() { if (this.#data.windowSlot === undefined) { return 0; } return this.#data.windowSlot; } set windowSlot(newVal) { this.#data.windowSlot = newVal; } get creator() { return this.#data.creator; } set creator(newCreator) { if (this.#data.creator !== newCreator) { this.#data.creator = newCreator; this._onPropertyChanged.dispatch(this, "creator"); } } get windowState() { if (this.#data.windowState === undefined) { return ICreatorToolsData_1.WindowState.regular; } return this.#data.windowState; } set windowState(newVal) { this.#data.windowState = newVal; } get lastActiveMinecraftFlavor() { return this.#data.lastActiveMinecraftFlavor; } set lastActiveMinecraftFlavor(lastActiveMinecraftFlavor) { if (lastActiveMinecraftFlavor !== this.#data.lastActiveMinecraftFlavor) { this.#data.lastActiveMinecraftFlavor = lastActiveMinecraftFlavor; } } get remoteServerUrl() { if (this.#data.remoteServerUrl === undefined && CreatorToolsHost_1.default.baseUrl) { return Utilities_1.default.getBaseUrl(CreatorToolsHost_1.default.baseUrl); } return this.#data.remoteServerUrl; } get fullRemoteServerUrl() { if (!this.remoteServerUrl) { return undefined; } let url = this.remoteServerUrl.toLowerCase(); if (!url.startsWith("http") && url.indexOf("//") < 0) { if (url.indexOf("localhost") >= 0) { url = "http://" + url; } else { url = "https://" + url; } } url = Utilities_1.default.ensureEndsWithSlash(url); return url; } set remoteServerUrl(newPath) { if (newPath !== this.#data.remoteServerUrl) { this.#data.remoteServerUrl = newPath; } } get iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula() { return this.#data.iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula; } set iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula(newPort) { this.#data.iAgreeToTheMinecraftEndUserLicenseAgreementAndPrivacyStatementAtMinecraftDotNetSlashEula = newPort; } get dedicatedServerSlotCount() { return this.#data.dedicatedServerSlotCount; } set dedicatedServerSlotCount(newPort) { this.#data.dedicatedServerSlotCount = newPort; } get dedicatedServerMode() { if (this.#data.dedicatedServerMode === undefined) { return ICreatorToolsData_1.DedicatedServerMode.auto; } return this.#data.dedicatedServerMode; } set dedicatedServerMode(newMode) { this.#data.dedicatedServerMode = newMode; } get dedicatedServerPath() { return this.#data.dedicatedServerPath; } set minecraftGameMode(newMode) { this.#data.webSocketMode = newMode; } get minecraftGameMode() { return this.#data.webSocketMode; } set dedicatedServerPath(newPath) { this.#data.dedicatedServerPath = newPath; } get remoteServerPort() { if (this.#data.remoteServerPort === undefined) { return 0; } return this.#data.remoteServerPort; } set remoteServerPort(newPort) { this.#data.remoteServerPort = newPort; } get remoteServerAccessLevel() { return this.#data.remoteServerAccessLevel; } set remoteServerAccessLevel(newAccessLevel) { this.#data.remoteServerAccessLevel = newAccessLevel; } get remoteServerPasscode() { return this.#data.remoteServerPasscode; } set remoteServerPasscode(newPath) { this.#data.remoteServerPasscode = newPath; } get remoteServerAuthToken() { return this._remoteServerAuthToken; } set remoteServerAuthToken(newToken) { this._remoteServerAuthToken = newToken; } get remoteServerEulaAccepted() { return this._remoteServerEulaAccepted; } set remoteServerEulaAccepted(newValue) { this._remoteServerEulaAccepted = newValue; } get editorViewMode() { if (this.#data.editorViewMode === undefined) { return ICreatorToolsData_1.CreatorToolsEditorViewMode.itemsOnLeft; } return this.#data.editorViewMode; } set editorViewMode(newViewMode) { this.#data.editorViewMode = newViewMode; } get gallery() { return this._gallery; } get galleryLoaded() { return this._galleryLoaded; } get userGitHub() { if (this._userGitHub === undefined) { this._userGitHub = new GitHubManager_1.default(); } return this._userGitHub; } get anonGitHub() { if (this._anonGitHub === undefined) { this._anonGitHub = new GitHubManager_1.default(); } return this._anonGitHub; } get onLoaded() { return this._onLoaded.asEvent(); } get onGalleryLoaded() { return this._onGalleryLoaded.asEvent(); } get onPropertyChanged() { return this._onPropertyChanged.asEvent(); } get onStatusAdded() { return this._onStatusAdded.asEvent(); } get onOperationCompleted() { return this._onOperationCompleted.asEvent(); } get successfullyConnectedWebSocketToMinecraft() { if (this.#data.successfullyConnectedWebSocketToMinecraft === undefined) { return false; } return this.#data.successfullyConnectedWebSocketToMinecraft; } set successfullyConnectedWebSocketToMinecraft(newValue) { this.#data.successfullyConnectedWebSocketToMinecraft = newValue; } get successfullyStartedMinecraftServer() { if (this.#data.successfullyStartedMinecraftServer === undefined) { return false; } return this.#data.successfullyStartedMinecraftServer; } set successfullyStartedMinecraftServer(newValue) { this.#data.successfullyStartedMinecraftServer = newValue; } get successfullyConnectedToRemoteMinecraft() { if (this.#data.successfullyConnectedToRemoteMinecraft === undefined) { return false; } return this.#data.successfullyConnectedToRemoteMinecraft; } set successfullyConnectedToRemoteMinecraft(newValue) { this.#data.successfullyConnectedToRemoteMinecraft = newValue; } get defaultMinecraftFlavor() { if (this.#data.lastActiveMinecraftFlavor === undefined) { if (CreatorToolsHost_1.default.isAppServiceWeb) { return ICreatorToolsData_1.MinecraftFlavor.processHostedProxy; } else { return ICreatorToolsData_1.MinecraftFlavor.remote; } } return this.#data.lastActiveMinecraftFlavor; } subscribeStatusAddedAsync(fn) { this._onStatusAddedAsync.push(fn); } unsubscribeStatusAddedAsync(fn) { let newStatusAddedArr = []; for (let i = 0; i < this._onStatusAddedAsync.length; i++) { if (this._onStatusAddedAsync[i] !== fn) { newStatusAddedArr.push(this._onStatusAddedAsync[i]); } } this._onStatusAddedAsync = newStatusAddedArr; } setMinecraftFlavor(newValue) { this.ensureMinecraft(newValue); } get autoStartMinecraft() { if (this.#data.autoStartMinecraft === undefined) { return true; } return this.#data.autoStartMinecraft; } set autoStartMinecraft(newValue) { this.#data.autoStartMinecraft = newValue; } get file() { return this.prefsStorage.rootFolder.ensureFile("mctools.json"); } get prefsProjectsFolder() { return this.prefsStorage.rootFolder.ensureFolder("projects"); } constructor(settingsStorage, projectsStorage, deploymentsStorage, worldStorage, packStorage, workingStorage, contentRoot) { this.prefsStorage = settingsStorage; this.projectsStorage = projectsStorage; this.deploymentStorage = deploymentsStorage; this.packStorage = packStorage; this.worldStorage = worldStorage; this.workingStorage = workingStorage; if (contentRoot) { this.contentRoot = contentRoot; } this._handleMessageFromAppService = this._handleMessageFromAppService.bind(this); this._bubbleMinecraftStateChanged = this._bubbleMinecraftStateChanged.bind(this); this._bubbleMinecraftRefreshed = this._bubbleMinecraftRefreshed.bind(this); AppServiceProxy_1.default.onMessage.subscribe(this._handleMessageFromAppService); this.#data = { successfullyConnectedWebSocketToMinecraft: false, successfullyStartedMinecraftServer: false, successfullyConnectedToRemoteMinecraft: false, autoStartMinecraft: true, showScreenOnConnect: true, customTools: [], }; // in the case of a Minecraft Http Server self-hosted web page, assume all we want to do is connect back to our server. // But NOT in the Electron app — there we default to hosting BDS locally. if (CreatorToolsHost_1.default.baseUrl && !CreatorToolsHost_1.default.isAppServiceWeb) { this.setMinecraftFlavor(ICreatorToolsData_1.MinecraftFlavor.remote); this.successfullyConnectedWebSocketToMinecraft = true; } this._isLoaded = false; this.projects = []; this.status = []; this.activeOperations = []; } initializeWorldSettings() { if (this.#data.worldSettings === undefined) { this.#data.worldSettings = { gameType: WorldLevelDat_1.GameType.creative, generator: WorldLevelDat_1.Generator.infinite, randomSeed: "2000", backupType: IWorldSettings_1.BackupType.every5Minutes, useCustomSettings: false, isEditor: false, deployCreatorToolsInfrastructure: true, enableDebugger: true, enableDebuggerStreaming: true, packageReferences: [], }; this.ensureDefaultWorldName(); } } getDeploymentTarget(target) { let dt = this.deploymentTargets[target]; if (!dt && this.deploymentStorage[target]) { dt = new DeploymentTarget_1.default(this.deploymentStorage[target], target); this.deploymentTargets[target] = dt; } return dt; } ensureDefaultWorldName() { if (this.worldSettings && this.worldSettings.name === undefined) { this.worldSettings.name = "world " + Utilities_1.default.getDateStr(new Date()); } } initializeEditorWorldSettings() { if (this.#data.editorWorldSettings === undefined) { this.#data.editorWorldSettings = { gameType: WorldLevelDat_1.GameType.creative, generator: WorldLevelDat_1.Generator.infinite, backupType: IWorldSettings_1.BackupType.every5Minutes, useCustomSettings: false, isEditor: true, packageReferences: [], }; this.ensureDefaultEditorWorldName(); } } ensureDefaultEditorWorldName() { if (this.editorWorldSettings && this.editorWorldSettings.name === undefined) { this.editorWorldSettings.name = "editor project " + Utilities_1.default.getDateStr(new Date()); } } getCustomTool(index) { if (this.#data.customTools === undefined) { this.#data.customTools = []; } while (this.#data.customTools.length <= index) { this.#data.customTools.push({ name: "", type: 0, text: undefined, lastRunResult: undefined, }); } return this.#data.customTools[index]; } get defaultFunction() { if (this.#data === undefined || this.#data.defaultFunction === undefined) { return ""; } return this.#data.defaultFunction; } set defaultFunction(newFunction) { if (this.#data === undefined) { return; } this.#data.defaultFunction = Utilities_1.default.makeSafeForJson(newFunction); } async runCommand(command, project) { return await CommandRegistry_1.default.main.runCommand({ creatorTools: this, project: project, minecraft: this.activeMinecraft, host: CreatorToolsHost_1.default.hostManager, }, command); } async runMinecraftCommand(command) { if (this.activeMinecraft === undefined) { throw new Error("No minecraft active."); } const result = await this.activeMinecraft.runCommand(command); return result; } async loadPacks() { if (!this.packStorage) { throw new Error("Could not find pack storage"); } if (this._arePacksLoading) { const pendingLoad = this._pendingPackLoadRequests; const prom = (resolve, reject) => { pendingLoad.push(resolve); }; await new Promise(prom); } else { this._arePacksLoading = true; this.packs = []; const creatorToolsIngame = await Database_1.default.ensureCreatorToolsIngameFile(); if (creatorToolsIngame) { await this.ensurePackForFile(creatorToolsIngame); } Log_1.default.message("Loading packs from '" + this.packStorage.rootFolder.fullPath + "'"); await this.loadPacksFromFolder(this.packStorage.rootFolder); this._arePacksLoading = false; const pendingLoad = this._pendingPackLoadRequests; this._pendingPackLoadRequests = []; for (const prom of pendingLoad) { prom(undefined); } } } ensureAllTypesCollapsedExcept(itemType) { const newCollapsedTypes = []; for (let i = 0; i < IProjectItemData_1.MaxItemTypes; i++) { if (i !== itemType) { newCollapsedTypes.push(i); } } this.collapsedTypes = newCollapsedTypes; } ensureTypeIsCollapsed(itemType) { for (const itemTypeCollapsed of this.collapsedTypes) { if (itemTypeCollapsed === itemType) { return; } } this.collapsedTypes.push(itemType); } ensureTypeIsNotCollapsed(itemType) { const newCollapsedTypes = []; for (const itemTypeCollapsed of this.collapsedTypes) { if (itemTypeCollapsed !== itemType) { newCollapsedTypes.push(itemTypeCollapsed); } } this.#data.collapsedTypes = newCollapsedTypes; } async loadPacksFromFolder(folder) { await folder.load(); for (let fileName in folder.files) { const file = folder.files[fileName]; if (file && StorageUtilities_1.default.isContainerFile(file.storageRelativePath)) { await this.ensurePackForFile(file); } } } getPackByName(packName, isWorldFocused) { if (!this.packs) { Log_1.default.unexpectedUndefined("GPN"); return; } for (let i = 0; i < this.packs.length; i++) { if (this.packs[i].matches(packName, isWorldFocused)) { return this.packs[i]; } } return undefined; } getPackageByNameAndHash(packName, hash) { if (!this.packs) { Log_1.default.unexpectedUndefined("GPNH"); return; } for (let i = 0; i < this.packs.length; i++) { if (this.packs[i].matches(packName, false) && (hash === undefined || hash === this.packs[i].data?.sourceHash)) { return this.packs[i]; } } return this.getPackByName(packName); } async ensurePackForFile(file) { const pack = this._ensurePack(file.storageRelativePath); await pack.ensureData(this, file); return pack; } _ensurePack(storagePath) { if (this.packs === undefined) { this.packs = []; } for (let i = 0; i < this.packs.length; i++) { if (this.packs[i].storagePath === storagePath) { return this.packs[i]; } } const pack = new Package_1.default(StorageUtilities_1.default.getLeafName(storagePath), storagePath); this.packs.push(pack); return pack; } _handleMessageFromAppService(command, data) { switch (command) { case "externalKeyPress": if (data.startsWith("command")) { const commandIndex = parseInt(data.substring(7, data.length)); CommandRunner_1.default.runCustomTool(this, commandIndex); } break; case "localFileUpdate": ElectronStorage_1.default.processLocalFileUpdate(data); break; case "localFileAdded": ElectronStorage_1.default.processLocalFileAdded(data); break; case "localFileRemoved": ElectronStorage_1.default.processLocalFileRemoved(data); break; case "statusMessage": const firstPipe = data.indexOf("|"); const content = data.substring(firstPipe + 1, data.length); try { const contentO = JSON.parse(content); this.notifyExternalStatus(contentO); } catch (e) { } break; case "mctSavedInAppService": this.load(true); break; case "logFileUpdated": try { if (data) { const firstPipe = data.indexOf("|"); if (firstPipe > 0) { const fileName = data.substring(0, firstPipe); const content = data.substring(firstPipe + 1, data.length); this._handleLogFileUpdated(fileName, content); } } } catch (e) { Log_1.default.fail("Error parsing inbound log: " + e); } break; default: if (this.deploymentStorageMinecraft) { this.deploymentStorageMinecraft.processExternalMessage(command, data); } if (this.processHostedMinecraft) { this.processHostedMinecraft.processExternalMessage(command, data); } if (this.gameMinecraft) { this.gameMinecraft.processExternalMessage(command, data); } if (this.remoteMinecraft) { this.remoteMinecraft.processExternalMessage(command, data); } break; } } _handleLogFileUpdated(fileName, contents) { if (contents === null || contents === undefined || fileName === null || fileName === undefined) { return; } let arr = this.mcLogs[fileName]; if (arr === undefined) { arr = []; if (Utilities_1.default.isUsableAsObjectKey(fileName)) { this.mcLogs[fileName] = arr; } } const logItems = contents.split("\r"); for (let i = logItems.length - 1; i >= 0; i--) { const logItem = logItems[i]; if (logItem !== undefined && logItem.length > 3) { if (!arr.includes(logItem)) { arr.push(logItem); this.notifyStatusUpdate(logItem); } } } } async notifyStatusUpdate(message, topic) { const messageCanon = message.trim().toLowerCase(); if (messageCanon.length > 1) { const status = { time: new Date(), message: message, topic: topic, type: Status_1.StatusType.message, }; this.status.push(status); await this.callStatusAddedListeners(status); this.ensureStatusArrayIsTrimmed(); } } ensureStatusArrayIsTrimmed() { if (this.status.length > 10000) { const newStatusArr = []; for (let i = this.status.length - 9000; i < this.status.length; i++) { newStatusArr.push(this.status[i]); } this.status = newStatusArr; } } async callStatusAddedListeners(status) { this._onStatusAdded.dispatch(this, status); if (this._onStatusAddedAsync.length > 0) { let promises = []; for (let i = 0; i < this._onStatusAddedAsync.length; i++) { promises.push(this._onStatusAddedAsync[i](this, status)); } await Promise.all(promises); } } notifyExternalStatus(status) { // When status arrives via IPC (JSON serialization), time becomes a string. // Ensure it's a Date object for downstream consumers that call .getTime(). if (status.time && !(status.time instanceof Date)) { status.time = new Date(status.time); } this.status.push(status); if (status.type === Status_1.StatusType.operationStarted) { this.activeOperations.push(status); } else if ((status.type === Status_1.StatusType.operationEndedComplete || status.type === Status_1.StatusType.operationEndedErrors) && status.operationId !== null && status.operationId !== undefined) { this.removeOperation(status.operationId); } this.callStatusAddedListeners(status); return status.operationId; } async notifyOperationUpdate(updateOperationId, message, topic) { this.ensureStatusArrayIsTrimmed(); this.updateOperation(updateOperationId, message); const status = { message: message, operationId: updateOperationId, type: Status_1.StatusType.operationUpdate, time: new Date(), topic: topic, }; this.status.push(status); await this.callStatusAddedListeners(status); } async notifyOperationStarted(message, topic) { const status = { time: new Date(), message: message, type: Status_1.StatusType.operationStarted, topic: topic, }; status.operationId = status.time.getTime(); this.status.push(status); this.activeOperations.push(status); this.ensureStatusArrayIsTrimmed(); await this.callStatusAddedListeners(status); return status.operationId; } async notifyOperationEnded(endedOperationId, message, topic, endedWithErrors) { this.ensureStatusArrayIsTrimmed(); this.removeOperation(endedOperationId); this._onOperationCompleted.dispatch(this, endedOperationId); if (!message) { return; } const status = { message: message, operationId: endedOperationId, type: endedWithErrors ? Status_1.StatusType.operationEndedErrors : Status_1.StatusType.operationEndedComplete, time: new Date(), topic: topic, }; this.status.push(status); await this.callStatusAddedListeners(status); } updateOperation(id, newMessage) { for (let i = 0; i < this.activeOperations.length; i++) { const oper = this.activeOperations[i]; if (oper.operationId === id) { oper.message = newMessage; } } } removeOperation(id) { // remove operation from list of active operations. const newActiveOperations = []; for (let i = 0; i < this.activeOperations.length; i++) { const oper = this.activeOperations[i]; if (oper.operationId !== id) { newActiveOperations.push(oper); } } this.activeOperations = newActiveOperations; } async save() { const configFile = this.file; configFile.setObjectContentIfSemanticallyDifferent(this.#data); await configFile.saveContent(); if (AppServiceProxy_1.default.hasAppService) { AppServiceProxy_1.default.sendAsync(AppServiceProxy_1.AppServiceProxyCommands.reloadMct, ""); } } async loadGallery() { let result = null; // @ts-ignore if (typeof window !== "undefined") { const url = this.contentRoot + "data/gallery.json"; try { result = await axios_1.default.get(url); if (result) { this._gallery = result.data; } } catch (e) { Log_1.default.fail("Could not load gallery: " + e + " from '" + url + "'"); } } else if (this.local) { try { result = await this.local.readJsonFile("data/gallery.json"); } catch (e) { Log_1.default.fail("Could not load local file: " + e + " from 'data/gallery.json'"); } if (result !== null) { this._gallery = result; } } if (this._gallery) { for (const item of this._gallery.items) { if (item.sampleSet) { item.gitHubOwner = "microsoft"; item.gitHubRepoName = "minecraft-scripting-samples"; if (item.type === IGalleryItem_1.GalleryItemType.editorCodeSample) { item.gitHubFolder = "/editor-script-box"; if (!item.tags) { item.tags = ["editor"]; } } else { item.gitHubFolder = "/script-box"; } } if (item.fileList) { const newFileList = []; for (const fileItem of item.fileList) { newFileList.push(MinecraftUtilities_1.default.replaceMinecraftPathTokens(fileItem)); } item.fileList = newFileList; } } } this._galleryLoaded = true; this._onGalleryLoaded.dispatch(this, this._gallery); return this._gallery; } async getGalleryProjectByGitHub(repoName, owner, branch, folder, withFiles) { if (!this._galleryLoaded) { await this.loadGallery(); } if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) { return undefined; } repoName = repoName.toLowerCase(); owner = owner.toLowerCase(); for (const galProj of this._gallery.items) { if (galProj.gitHubRepoName && galProj.gitHubOwner && galProj.gitHubRepoName.toLowerCase() === repoName && galProj.gitHubOwner.toLowerCase() === owner && branch === galProj.gitHubBranch && folder === galProj.gitHubFolder && (!withFiles || galProj.fileList)) { return galProj; } } return undefined; } async getGalleryProjectById(galleryProjectId) { if (!this._galleryLoaded) { await this.loadGallery(); } if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) { return undefined; } galleryProjectId = galleryProjectId.toLowerCase(); galleryProjectId = galleryProjectId.replace(/-/gi, ""); for (const galProj of this._gallery.items) { if (galProj.id.toLowerCase() === galleryProjectId) { return galProj; } } return undefined; } async getGalleryProjectByCaption(galleryProjectTitle) { if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) { return undefined; } for (const galProj of this._gallery.items) { if (galProj.title === galleryProjectTitle) { return galProj; } } return undefined; } getGalleryProjectByType(itemType) { if (this._galleryLoaded === false || this._gallery === undefined || this._gallery.items === undefined) { return undefined; } const galleryItems = []; for (const galProj of this._gallery.items) { if (galProj.type === itemType) { galleryItems.push(galProj); } } return galleryItems; } async getNewProjectName(seedName) { if (!this._isLoaded) { await this.load(); } let newProjectName = StorageUtilities_1.default.convertFolderPlaceholders(seedName); let counter = 0; while (this.prefsProjectsFolder.fileExists(newProjectName + ".json") || this.projectsStorage.rootFolder.folderExists(newProjectName)) { counter++; newProjectName = StorageUtilities_1.default.convertFolderPlaceholders(seedName) + " " + counter; } return newProjectName; } async createNewProject(newProjectName, newProjectPath, newProjectFolder, newProjectFolderTitle, focus, includeDefaultItems, projectLanguage) { if (!this._isLoaded) { await this.load(); } const targetProjectName = await this.getNewProjectName(newProjectName); let projectPrefs; if (newProjectFolder) { projectPrefs = await newProjectFolder.ensureFileFromRelativePath("/.mct/prefs.mctp.json"); } else { projectPrefs = await this.prefsProjectsFolder.createFile(targetProjectName + ".json"); } const newProject = new Project_1.default(this, targetProjectName, projectPrefs); if (newProjectPath && !newProjectFolder) { newProject.localFolderPath = newProjectPath; } if (projectLanguage) { newProject.preferredScriptLanguage = projectLanguage; } if (newProjectFolder) { newProject.setProjectFolder(newProjectFolder); newProject.projectFolderTitle = newProjectFolderTitle; } else { await newProject.ensureProjectFolder(); } newProject.focus = focus; if (includeDefaultItems) { await ProjectUtilities_1.default.ensureDefaultItems(newProject); } // Default-item generation writes files to storage, which fires file-update events // that get recorded in changedFilesSinceLastSaved. For a brand-new project, none // of these represent user edits — they are the initial on-disk state. Reset the // change tracker so dirty-change indicators only appear after actual user edits. newProject.changedFilesSinceLastSaved = {}; this.projects.push(newProject); return newProject; } async createProjectFromContent(jsonStringOrBase64ZipContent) { let jsonO = undefined; let filteredContents = Utilities_1.default.fixJsonContent(jsonStringOrBase64ZipContent); try { jsonO = JSON.parse(filteredContents); } catch (e) { jsonO = undefined; } if (jsonO) { const tempStorage = new Storage_1.default(); const tempFile = tempStorage.rootFolder.ensureFile("import.json"); tempFile.setContent(jsonStringOrBase64ZipContent); return await this.createProjectFromFolder(tempStorage.rootFolder); } let zs = new ZipStorage_1.default(); await zs.loadFromBase64(jsonStringOrBase64ZipContent); if (zs.errorStatus !== undefined) { return zs.errorMessage; } if (zs.rootFolder) {