@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
1,305 lines (1,304 loc) • 75.9 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 });
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) {