UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

1,237 lines (1,236 loc) 63.2 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. Object.defineProperty(exports, "__esModule", { value: true }); const ZipStorage_1 = require("../storage/ZipStorage"); const ste_events_1 = require("ste-events"); const Log_1 = require("./../core/Log"); const WorldLevelDat_1 = require("./WorldLevelDat"); const Utilities_1 = require("../core/Utilities"); const StorageUtilities_1 = require("../storage/StorageUtilities"); const LevelDb_1 = require("./LevelDb"); const DataUtilities_1 = require("../core/DataUtilities"); const WorldChunk_1 = require("./WorldChunk"); const BlockCube_1 = require("./BlockCube"); const Block_1 = require("./Block"); const Entity_1 = require("./Entity"); const IStorage_1 = require("../storage/IStorage"); const MinecraftUtilities_1 = require("./MinecraftUtilities"); const NbtBinary_1 = require("./NbtBinary"); const NbtBinaryTag_1 = require("./NbtBinaryTag"); const AnchorSet_1 = require("./AnchorSet"); const ActorItem_1 = require("./ActorItem"); const Status_1 = require("../app/Status"); const BEHAVIOR_PACKS_RELPATH = "/world_behavior_packs.json"; const BEHAVIOR_PACK_HISTORY_RELPATH = "/world_behavior_pack_history.json"; const RESOURCE_PACKS_RELPATH = "/world_resource_packs.json"; const RESOURCE_PACK_HISTORY_RELPATH = "/world_resource_pack_history.json"; const LEVELDAT_RELPATH = "/level.dat"; const LEVELDATOLD_RELPATH = "/level.dat_old"; const LEVELNAMETXT_RELPATH = "/levelname.txt"; const MANIFEST_RELPATH = "/manifest.json"; const CHUNK_X_SIZE = 16; const CHUNK_Z_SIZE = 16; const CREATOR_TOOLS_EDITOR_BPUUID = "5d2f0b91-ca29-49da-a275-e6c6262ea3de"; class MCWorld { constructor() { this._anchors = new AnchorSet_1.default(); this._dynamicProperties = {}; this._isLoaded = false; this._isDataLoaded = false; this._onLoaded = new ste_events_1.EventDispatcher(); this._onDataLoaded = new ste_events_1.EventDispatcher(); this._hasDynamicProps = false; this._hasCustomProps = false; this._onPropertyChanged = new ste_events_1.EventDispatcher(); this.chunkCount = 0; this._chunkMinY = -64; this.actorsById = {}; this.regionsByDimension = {}; this.chunks = {}; } get project() { return this._project; } set project(newProject) { this._project = newProject; } get anchors() { return this._anchors; } get chunkMinY() { return this._chunkMinY; } set chunkMinY(newY) { this._chunkMinY = newY; } get effectiveRootFolder() { if (this._folder) { return this._folder; } if (this._file && this._file.fileContainerStorage) { return this._file.fileContainerStorage.rootFolder; } if (this._zipStorage !== undefined) { return this._zipStorage.rootFolder; } return undefined; } get manifest() { return this._manifest; } get hasDynamicProps() { return this._hasDynamicProps; } get hasCustomProps() { return this._hasCustomProps; } get minX() { return this._minX; } get maxX() { return this._maxX; } get minZ() { return this._minZ; } get maxZ() { return this._maxZ; } get generationSeed() { if (this._generationSeed === undefined && this._levelChunkMetaData && this._levelChunkMetaData.singleRoot) { const tag = this._levelChunkMetaData.singleRoot.find("GenerationSeed"); if (tag !== null) { this._generationSeed = tag.valueAsBigInt.toString(); } } return this._generationSeed; } async copyAsFolderTo(targetFolder) { if (this._folder) { await StorageUtilities_1.default.syncFolderTo(this._folder, targetFolder, true, true, true); } else if (this._file) { const storage = this.storage; if (storage) { await StorageUtilities_1.default.syncFolderTo(this.storage.rootFolder, targetFolder, true, true, true); } } } get storage() { if (this._file) { if (!this._file.fileContainerStorage) { this._file.fileContainerStorage = new ZipStorage_1.default(); this._file.fileContainerStorage.storagePath = this._file.extendedPath + "#"; } return this._file.fileContainerStorage; } if (this._zipStorage === undefined) { this._zipStorage = new ZipStorage_1.default(); } return this._zipStorage; } ensureZipStorage() { if (this._zipStorage === undefined) { this._zipStorage = new ZipStorage_1.default(); } } get onPropertyChanged() { return this._onPropertyChanged.asEvent(); } get storageErrorStatus() { if (!this.storage) { return IStorage_1.StorageErrorStatus.none; } return this.storage.errorStatus; } get storageErrorMessage() { return this.storage.errorMessage; } get storageFullPath() { if (this._file) { return this._file.fullPath; } if (this._folder) { return this._folder.fullPath; } return undefined; } get deferredTechnicalPreviewExperiment() { if (this.levelData !== undefined) { const val = this.levelData.deferredTechnicalPreviewExperiment; if (val === undefined) { return false; } return val; } return false; } set deferredTechnicalPreviewExperiment(newVal) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } if (this.levelData !== undefined) { this.levelData.deferredTechnicalPreviewExperiment = newVal; } } get betaApisExperiment() { if (this.levelData !== undefined) { const val = this.levelData.betaApisExperiment; if (val === undefined) { return false; } return val; } return false; } set betaApisExperiment(newVal) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } if (this.levelData !== undefined) { this.levelData.betaApisExperiment = newVal; } } get dataDrivenItemsExperiment() { if (this.levelData !== undefined) { const val = this.levelData.dataDrivenItemsExperiment; if (val === undefined) { return false; } return val; } return false; } set dataDrivenItemsExperiment(newVal) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } if (this.levelData !== undefined) { this.levelData.dataDrivenItemsExperiment = newVal; } } get name() { if (this._levelNameText !== undefined) { return this._levelNameText; } if (this.levelData !== undefined && this.levelData.levelName !== undefined) { return this.levelData.levelName; } if (this._file !== undefined) { return this._file.name; } return ""; } set name(newValue) { this._levelNameText = newValue; if (this.levelData !== undefined) { this.levelData.levelName = newValue; } } get file() { return this._file; } set file(newFile) { this._file = newFile; } get folder() { return this._folder; } set folder(newFolder) { this._folder = newFolder; } get isLoaded() { return this._isLoaded; } get spawnX() { if (this.levelData === undefined) { return undefined; } return this.levelData.spawnX; } set spawnX(newX) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } this.levelData.spawnX = newX; this._onPropertyChanged.dispatch(this, "spawnX"); } get spawnY() { if (this.levelData === undefined) { return undefined; } return this.levelData.spawnY; } set spawnY(newY) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } this.levelData.spawnY = newY; this._onPropertyChanged.dispatch(this, "spawnY"); } get spawnZ() { if (this.levelData === undefined) { return undefined; } return this.levelData.spawnZ; } set spawnZ(newZ) { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } this.levelData.spawnZ = newZ; this._onPropertyChanged.dispatch(this, "spawnZ"); } get onLoaded() { return this._onLoaded.asEvent(); } get onDataLoaded() { return this._onDataLoaded.asEvent(); } static async ensureMCWorldOnFolder(folder, project, handler) { if (folder.manager === undefined) { const world = new MCWorld(); world.project = project; world.folder = folder; folder.manager = world; } if (folder.manager !== undefined && folder.manager instanceof MCWorld) { const mcworld = folder.manager; if (!mcworld.isLoaded) { if (handler) { mcworld.onLoaded.subscribe(handler); } await mcworld.load(false); } else if (handler) { handler(mcworld, mcworld, { unsub: () => { }, stopPropagation: () => { } }); } return mcworld; } return undefined; } static async ensureOnItem(projectItem) { let mcworld = undefined; if (projectItem.folder) { mcworld = await MCWorld.ensureMCWorldOnFolder(projectItem.folder, projectItem.project); } else if (projectItem.file) { mcworld = await MCWorld.ensureOnFile(projectItem.file, projectItem.project); } if (!mcworld) { Log_1.default.debugAlert("Could not find respective world."); } return mcworld; } static async ensureOnFile(file, project, handler) { if (file.manager === undefined) { const world = new MCWorld(); world.project = project; world.file = file; file.manager = world; } if (file.manager !== undefined && file.manager instanceof MCWorld) { const mcworld = file.manager; if (!mcworld.isLoaded) { if (handler) { mcworld.onLoaded.subscribe(handler); } await mcworld.load(false); } else if (handler) { handler(mcworld, mcworld, { unsub: () => { }, stopPropagation: () => { } }); } return mcworld; } return undefined; } loadAnchorsFromDynamicProperties() { if (this._dynamicProperties && this._dynamicProperties[CREATOR_TOOLS_EDITOR_BPUUID]) { this._anchors.clearAll(); const anchorStr = this._dynamicProperties && this._dynamicProperties[CREATOR_TOOLS_EDITOR_BPUUID]["anchors"]; if (anchorStr && typeof anchorStr === "string") { this._anchors.fromString(anchorStr); this.saveAutoGenItems(); } } } _updateMeta() { this.regionsByDimension = {}; for (const dimNum in this.chunks) { const dim = this.chunks[dimNum]; let regions = []; for (const xNumStr in dim) { const xNum = parseInt(xNumStr); const xPlane = dim[xNum]; for (const zNumStr in xPlane) { const zNum = parseInt(zNumStr); let addedToRegion = false; for (const region of regions) { if (xNum >= region.minX && xNum <= region.maxX && zNum >= region.minZ && zNum <= region.maxZ) { region.minX = Math.min(region.minX, xNum - 1); region.minZ = Math.min(region.minZ, zNum - 1); region.maxX = Math.max(region.maxX, xNum + 1); region.maxZ = Math.max(region.maxZ, zNum + 1); addedToRegion = true; } } if (!addedToRegion) { regions.push({ minX: xNum - 1, minZ: zNum - 1, maxX: xNum + 1, maxZ: zNum + 1, }); } } } this.regionsByDimension[dimNum] = this._coalesceRegions(regions); } } _coalesceRegions(regions) { const newRegions = []; for (const region of regions) { let addedToRegion = false; for (const newRegion of newRegions) { if (region.minX >= newRegion.minX && region.minX <= newRegion.maxX && region.minZ >= newRegion.minZ && region.minZ <= newRegion.maxZ) { newRegion.minX = Math.min(newRegion.minX, region.minX - 1); newRegion.minZ = Math.min(newRegion.minZ, region.minZ - 1); newRegion.maxX = Math.max(newRegion.maxX, region.minX + 1); newRegion.maxZ = Math.max(newRegion.maxZ, region.minZ + 1); addedToRegion = true; break; } if (region.maxX >= newRegion.minX && region.maxX <= newRegion.maxX && region.minZ >= newRegion.minZ && region.minZ <= newRegion.maxZ) { newRegion.minX = Math.min(newRegion.minX, region.maxX - 1); newRegion.minZ = Math.min(newRegion.minZ, region.minZ - 1); newRegion.maxX = Math.max(newRegion.maxX, region.maxX + 1); newRegion.maxZ = Math.max(newRegion.maxZ, region.minZ + 1); addedToRegion = true; break; } if (region.minX >= newRegion.minX && region.minX <= newRegion.maxX && region.maxZ >= newRegion.minZ && region.maxZ <= newRegion.maxZ) { newRegion.minX = Math.min(newRegion.minX, region.minX - 1); newRegion.minZ = Math.min(newRegion.minZ, region.maxZ - 1); newRegion.maxX = Math.max(newRegion.maxX, region.minX + 1); newRegion.maxZ = Math.max(newRegion.maxZ, region.maxZ + 1); addedToRegion = true; break; } if (region.maxX >= newRegion.minX && region.maxX <= newRegion.maxX && region.maxZ >= newRegion.minZ && region.maxZ <= newRegion.maxZ) { newRegion.minX = Math.min(newRegion.minX, region.maxX - 1); newRegion.minZ = Math.min(newRegion.minZ, region.maxZ - 1); newRegion.maxX = Math.max(newRegion.maxX, region.maxX + 1); newRegion.maxZ = Math.max(newRegion.maxZ, region.maxZ + 1); addedToRegion = true; break; } } if (!addedToRegion) { newRegions.push(region); } } return newRegions; } _pushError(message, contextIn) { this.isInErrorState = true; if (this.errorMessages === undefined) { this.errorMessages = []; } Log_1.default.error(message + (contextIn ? " " + contextIn : "")); this.errorMessages.push({ message: message, context: contextIn, }); } async save() { if (this.storageErrorStatus === IStorage_1.StorageErrorStatus.unprocessable) { return; } await this.saveWorldManifest(); await this.saveLevelnameTxt(); await this.saveLevelDat(); await this.saveAutoGenItems(); await this.saveWorldBehaviorPacks(); await this.saveWorldBehaviorPackHistory(); await this.saveWorldResourcePacks(); await this.saveWorldResourcePackHistory(); } async saveWorldManifest() { if (this._manifest !== undefined && this.effectiveRootFolder !== undefined) { this._manifest.header.name = this.name; const manifestJsonFile = await this.effectiveRootFolder.ensureFileFromRelativePath(MANIFEST_RELPATH); if (manifestJsonFile !== undefined) { manifestJsonFile.setContent(JSON.stringify(this._manifest, null, 2)); await manifestJsonFile.saveContent(); } } } async saveLevelnameTxt() { const name = this.name; if (name !== undefined && this.effectiveRootFolder !== undefined) { const rootDataFile = await this.effectiveRootFolder.ensureFileFromRelativePath(LEVELNAMETXT_RELPATH); if (rootDataFile !== undefined) { rootDataFile.setContent(name); await rootDataFile.saveContent(); } } } async saveLevelDat() { if (this.levelData !== undefined && this.effectiveRootFolder !== undefined) { this.levelData.persist(); let rootDataFile = await this.effectiveRootFolder.ensureFileFromRelativePath(LEVELDAT_RELPATH); const bytes = this.levelData.getBytes(); if (rootDataFile !== undefined && bytes !== undefined) { rootDataFile.setContent(bytes); await rootDataFile.saveContent(); } rootDataFile = await this.effectiveRootFolder.ensureFileFromRelativePath(LEVELDATOLD_RELPATH); if (rootDataFile !== undefined && bytes !== undefined) { rootDataFile.setContent(bytes); await rootDataFile.saveContent(); } } } async getBytes() { if (this._file) { if (!this._file.fileContainerStorage) { this._file.fileContainerStorage = new ZipStorage_1.default(); this._file.fileContainerStorage.storagePath = this._file.extendedPath + "#"; } await this.save(); return await this._file.fileContainerStorage.generateUint8ArrayAsync(); } if (this._zipStorage === undefined) { return undefined; } await this.save(); return await this._zipStorage.generateUint8ArrayAsync(); } async syncFolderTo(folder) { await this.save(); const sourceFolder = this.effectiveRootFolder; if (!sourceFolder) { Log_1.default.unexpectedUndefined("SFT"); return; } await StorageUtilities_1.default.syncFolderTo(sourceFolder, folder, true, true, true); } async saveToFile() { if (this._zipStorage === undefined || this._file === undefined) { return; } const bytes = await this.getBytes(); if (bytes !== undefined) { this._file.setContent(bytes); } } ensurePackReferenceSet(packRefSet) { if (this.worldBehaviorPacks === undefined) { this.worldBehaviorPacks = []; } if (this.worldResourcePacks === undefined) { this.worldResourcePacks = []; } if (this.worldBehaviorPackHistory === undefined) { this.worldBehaviorPackHistory = { packs: [], }; } if (this.worldResourcePackHistory === undefined) { this.worldResourcePacks = []; } if (this.worldResourcePackHistory === undefined) { this.worldResourcePackHistory = { packs: [], }; } if (packRefSet.behaviorPackReferences) { for (let i = 0; i < packRefSet.behaviorPackReferences.length; i++) { this.ensurePackReferenceInCollection(packRefSet.behaviorPackReferences[i], this.worldBehaviorPacks); this.ensurePackReferenceInHistory(packRefSet.behaviorPackReferences[i], this.worldBehaviorPackHistory, packRefSet.name); } } if (packRefSet.resourcePackReferences) { for (let i = 0; i < packRefSet.resourcePackReferences.length; i++) { this.ensurePackReferenceInCollection(packRefSet.resourcePackReferences[i], this.worldResourcePacks); this.ensurePackReferenceInHistory(packRefSet.resourcePackReferences[i], this.worldResourcePackHistory, packRefSet.name); } } } ensurePackReferenceInCollection(packRef, packRefs) { Log_1.default.assert(packRef.version.length === 3, "Packref version not within bounds."); const compareUuid = Utilities_1.default.canonicalizeId(packRef.uuid); for (let i = 0; i < packRefs.length; i++) { if (Utilities_1.default.canonicalizeId(packRefs[i].pack_id) === compareUuid) { return; } } packRefs.push({ pack_id: packRef.uuid, version: packRef.version, priority: packRef.priority ? packRef.priority : 32767, }); } ensurePackReferenceInHistory(packRef, packHistory, name) { Log_1.default.assert(packRef.version.length === 3, "Packref version not within bounds."); if (packHistory.packs === undefined) { packHistory.packs = []; } const compareUuid = Utilities_1.default.canonicalizeId(packRef.uuid); for (let i = 0; i < packHistory.packs.length; i++) { if (Utilities_1.default.canonicalizeId(packHistory.packs[i].uuid) === compareUuid) { return; } } packHistory.packs.push({ can_be_redownloaded: false, name: name, uuid: packRef.uuid, version: packRef.version }); } _loadFromNbt() { } getProperty(id) { switch (id.toLowerCase()) { case "spawnx": return this.spawnX; case "spawny": return this.spawnY; case "spawnz": return this.spawnZ; case "gametype": return this.levelData?.gameType; case "difficulty": return this.levelData?.difficulty; case "generator": return this.levelData?.generator; } } getBaseValue() { throw new Error("Method not implemented."); } setBaseValue(value) { throw new Error("Method not implemented."); } setProperty(id, newVal) { switch (id.toLowerCase()) { case "spawnX": this.spawnX = newVal; break; case "spawnY": this.spawnY = newVal; break; case "spawnZ": this.spawnZ = newVal; break; } } async load(force) { if ((this._isLoaded && !force) || (this._file === undefined && this._folder === undefined)) { return; } if (this._file) { await this._file.loadContent(); if (this._file.content === undefined || !(this._file.content instanceof Uint8Array)) { return; } await this.loadFromBytes(this._file.content); } if (this._folder) { await this.loadFromFolder(this._folder); } } ensureResourcePacksFromString(packStr) { const refs = MinecraftUtilities_1.default.getIdsAndVersions(packStr); for (const ref of refs) { this.ensureResourcePack(ref.uuid, ref.version, ref.uuid); } } ensureBehaviorPacksFromString(packStr) { const refs = MinecraftUtilities_1.default.getIdsAndVersions(packStr); for (const ref of refs) { this.ensureBehaviorPack(ref.uuid, ref.version, ref.uuid); } } ensureBehaviorPack(packId, version, packName, packPriority) { if (this.worldBehaviorPacks === undefined) { this.worldBehaviorPacks = []; } if (this.worldBehaviorPackHistory === undefined) { this.worldBehaviorPackHistory = { packs: [], }; } let wasAdded = false; const bp = this.getBehaviorPack(packId); if (bp === undefined) { this.worldBehaviorPacks.push({ pack_id: packId, version: version, priority: packPriority, }); wasAdded = true; } const bph = this.getBehaviorPackHistory(packId); if (bph === undefined) { this.worldBehaviorPackHistory.packs.push({ uuid: packId, version: version, name: packName, can_be_redownloaded: false, }); wasAdded = true; } return wasAdded; } getBehaviorPack(packId) { if (this.worldBehaviorPacks === undefined) { return undefined; } packId = Utilities_1.default.canonicalizeId(packId); for (let i = 0; i < this.worldBehaviorPacks.length; i++) { const worldBP = this.worldBehaviorPacks[i]; if (Utilities_1.default.canonicalizeId(worldBP.pack_id) === packId) { return worldBP; } } return undefined; } getBehaviorPackHistory(packId) { if (this.worldBehaviorPackHistory === undefined) { return undefined; } packId = Utilities_1.default.canonicalizeId(packId); const packs = this.worldBehaviorPackHistory.packs; for (let i = 0; i < packs.length; i++) { const worldBPH = packs[i]; if (Utilities_1.default.canonicalizeId(worldBPH.uuid) === packId) { return worldBPH; } } return undefined; } static sortPackRegByPriority(a, b) { return (a.priority === undefined ? 32767 : a.priority) - (b.priority === undefined ? 32767 : b.priority); } static sortPackCollectionByPriority(packRefs) { MCWorld.freezePackRegistrationOrder(packRefs); return packRefs.sort(MCWorld.sortPackRegByPriority); } static freezePackRegistrationOrder(packRefs) { for (let i = 0; i < packRefs.length; i++) { if (packRefs[i].priority === undefined) { packRefs[i].priority = i * 100; } } } async saveWorldBehaviorPacks() { if (this.effectiveRootFolder === undefined) { return; } const rootFolder = this.effectiveRootFolder; if (this.worldBehaviorPacks === undefined || this.worldBehaviorPacks.length === 0) { await rootFolder.deleteFileFromRelativePath(BEHAVIOR_PACKS_RELPATH); return; } const packsFile = await rootFolder.ensureFileFromRelativePath(BEHAVIOR_PACKS_RELPATH); let packRefColl = MCWorld.freezeAndStripPriorities(this.worldBehaviorPacks); packsFile.setContent(JSON.stringify(packRefColl, null, 2)); packsFile.saveContent(); } static freezeAndStripPriorities(coll) { let returnColl = []; const collSort = MCWorld.sortPackCollectionByPriority(coll); for (let i = 0; i < collSort.length; i++) { returnColl.push({ pack_id: collSort[i].pack_id, version: collSort[i].version, }); } return returnColl; } async saveWorldBehaviorPackHistory() { if (this.effectiveRootFolder === undefined) { return; } const rootFolder = this.effectiveRootFolder; if (this.worldBehaviorPackHistory === undefined || this.worldBehaviorPackHistory.packs.length === 0) { await rootFolder.deleteFileFromRelativePath(BEHAVIOR_PACK_HISTORY_RELPATH); return; } const packsFile = await rootFolder.ensureFileFromRelativePath(BEHAVIOR_PACK_HISTORY_RELPATH); packsFile.setContent(JSON.stringify(this.worldBehaviorPackHistory, null, 2)); packsFile.saveContent(); } ensureResourcePack(packId, version, packName, packPriority) { if (this.worldResourcePacks === undefined) { this.worldResourcePacks = []; } if (this.worldResourcePackHistory === undefined) { this.worldResourcePackHistory = { packs: [], }; } let wasAdded = false; const rp = this.getResourcePack(packId); if (rp === undefined) { this.worldResourcePacks.push({ pack_id: packId, version: version, priority: packPriority, }); wasAdded = true; } const rph = this.getResourcePackHistory(packId); if (rph === undefined) { this.worldResourcePackHistory.packs.push({ uuid: packId, version: version, name: packName, can_be_redownloaded: false, }); wasAdded = true; } return wasAdded; } getResourcePack(packId) { if (this.worldResourcePacks === undefined) { return undefined; } packId = Utilities_1.default.canonicalizeId(packId); for (let i = 0; i < this.worldResourcePacks.length; i++) { const worldRP = this.worldResourcePacks[i]; if (Utilities_1.default.canonicalizeId(worldRP.pack_id) === packId) { return worldRP; } } return undefined; } getResourcePackHistory(packId) { if (this.worldResourcePackHistory === undefined) { return undefined; } packId = Utilities_1.default.canonicalizeId(packId); const packs = this.worldResourcePackHistory.packs; for (let i = 0; i < packs.length; i++) { const worldBPH = packs[i]; if (Utilities_1.default.canonicalizeId(worldBPH.uuid) === packId) { return worldBPH; } } return undefined; } async saveWorldResourcePacks() { if (this.effectiveRootFolder === undefined) { return; } const rootFolder = this.effectiveRootFolder; if (this.worldResourcePacks === undefined || this.worldResourcePacks.length === 0) { await rootFolder.deleteFileFromRelativePath(RESOURCE_PACKS_RELPATH); return; } const packsFile = await rootFolder.ensureFileFromRelativePath(RESOURCE_PACKS_RELPATH); let packRefColl = MCWorld.freezeAndStripPriorities(this.worldResourcePacks); packsFile.setContent(JSON.stringify(packRefColl, null, 2)); packsFile.saveContent(); } async saveWorldResourcePackHistory() { if (this.effectiveRootFolder === undefined) { return; } const rootFolder = this.effectiveRootFolder; if (this.worldResourcePackHistory === undefined || this.worldResourcePackHistory.packs.length === 0) { await rootFolder.deleteFileFromRelativePath(RESOURCE_PACK_HISTORY_RELPATH); return; } const packsFile = await rootFolder.ensureFileFromRelativePath(RESOURCE_PACK_HISTORY_RELPATH); packsFile.setContent(JSON.stringify(this.worldResourcePackHistory, null, 2)); packsFile.saveContent(); } async loadFromBytes(content) { let storage = undefined; if (this._file) { if (!this._file.fileContainerStorage) { this._file.fileContainerStorage = new ZipStorage_1.default(); this._file.fileContainerStorage.storagePath = this._file.extendedPath + "#"; } storage = this._file.fileContainerStorage; } else { this._zipStorage = new ZipStorage_1.default(); storage = this._zipStorage; } await storage.loadFromUint8Array(content, this._file?.name); const rootFolder = storage.rootFolder; await this.loadFromFolder(rootFolder); } async applyWorldSettings(worldSettings) { if (!this._isLoaded) { await this.load(false); } this.ensureLevelData(); if (this.levelData) { this.levelData.ensureDefaults(); if (worldSettings) { this.levelData.applyFromWorldSettings(worldSettings); } } } ensureLevelData() { if (this.levelData === undefined) { this.levelData = new WorldLevelDat_1.default(); } return this.levelData; } async loadFromFolder(rootFolder) { const rootDataFile = await rootFolder.getFileFromRelativePath(LEVELDAT_RELPATH); if (rootDataFile !== undefined) { await rootDataFile.loadContent(); if (rootDataFile.content !== undefined && rootDataFile.content instanceof Uint8Array) { this.levelData = new WorldLevelDat_1.default(); this.levelData.loadFromNbtBytes(rootDataFile.content); Utilities_1.default.appendErrors(this, this.levelData); this._loadFromNbt(); } } const levelNameTextFile = await rootFolder.getFileFromRelativePath(LEVELNAMETXT_RELPATH); if (levelNameTextFile !== undefined) { await levelNameTextFile.loadContent(); if (levelNameTextFile.content !== undefined && typeof levelNameTextFile.content === "string") { this.name = levelNameTextFile.content; } } const manifestJsonFile = await rootFolder.getFileFromRelativePath(MANIFEST_RELPATH); if (manifestJsonFile !== undefined) { await manifestJsonFile.loadContent(); if (manifestJsonFile.content !== undefined && typeof manifestJsonFile.content === "string") { this._manifest = JSON.parse(manifestJsonFile.content); } } let packsFile = await rootFolder.getFileFromRelativePath(BEHAVIOR_PACKS_RELPATH); if (packsFile !== undefined) { await packsFile.loadContent(); if (packsFile.content !== undefined && typeof packsFile.content === "string") { try { this.worldBehaviorPacks = JSON.parse(packsFile.content); } catch { this._pushError("Could not parse behavior pack file content"); this.worldBehaviorPacks = undefined; } } } packsFile = await rootFolder.getFileFromRelativePath(RESOURCE_PACKS_RELPATH); if (packsFile !== undefined) { await packsFile.loadContent(); if (packsFile.content !== undefined && typeof packsFile.content === "string") { try { this.worldResourcePacks = JSON.parse(packsFile.content); } catch { this._pushError("Could not parse resource pack file content." + packsFile.fullPath); this.worldResourcePacks = undefined; } } } let packHistoryFile = await rootFolder.getFileFromRelativePath(BEHAVIOR_PACK_HISTORY_RELPATH); if (packHistoryFile !== undefined) { await packHistoryFile.loadContent(); if (packHistoryFile.content !== undefined && typeof packHistoryFile.content === "string") { try { this.worldBehaviorPackHistory = JSON.parse(packHistoryFile.content); } catch { this._pushError("Could not parse behavior pack history file content"); this.worldBehaviorPackHistory = undefined; } } } packHistoryFile = await rootFolder.getFileFromRelativePath(RESOURCE_PACK_HISTORY_RELPATH); if (packHistoryFile !== undefined) { await packHistoryFile.loadContent(); if (packHistoryFile.content !== undefined && typeof packHistoryFile.content === "string") { try { this.worldResourcePackHistory = JSON.parse(packHistoryFile.content); } catch { this._pushError("Could not parse resource pack history file content: " + packHistoryFile.fullPath); this.worldResourcePackHistory = undefined; } } } const imageFile = await rootFolder.getFileFromRelativePath("/world_icon.jpeg"); if (imageFile !== undefined) { await imageFile.loadContent(); if (imageFile.content instanceof Uint8Array) { this.imageBase64 = Utilities_1.default.uint8ArrayToBase64(imageFile.content); } } this._isLoaded = true; this._onLoaded.dispatch(this, this); } async loadData(force = false) { if (!force && this._isDataLoaded) { return; } const loadOper = await this._project?.carto.notifyOperationStarted("Starting first-pass load of '" + this.name + "' world", Status_1.StatusTopic.worldLoad); const rootFolder = this.effectiveRootFolder; if (!rootFolder) { return; } await rootFolder.load(); const dbFolder = await rootFolder.getFolderFromRelativePath("/db"); const ldbFileArr = []; const logFileArr = []; const manifestFileArr = []; if (dbFolder) { await dbFolder.load(); for (const fileName in dbFolder.files) { const file = dbFolder.files[fileName]; if (file) { const extension = StorageUtilities_1.default.getTypeFromName(file.name); if (fileName.startsWith("MANIFEST")) { manifestFileArr.push(file); } else if (extension === "ldb") { // console.log("Adding map file " + file.name + "|" + ldbFileArr.length); ldbFileArr.push(file); } else if (extension === "log") { // console.log("Adding map file " + file.name); logFileArr.push(file); } } } } this.levelDb = new LevelDb_1.default(ldbFileArr, logFileArr, manifestFileArr, this.name); await this.levelDb.init(async (message) => { await this._project?.carto.notifyStatusUpdate(message, Status_1.StatusTopic.worldLoad); }); Utilities_1.default.appendErrors(this, this.levelDb); if (loadOper !== undefined) { await this._project?.carto.notifyOperationEnded(loadOper, "Completed first-pass load of '" + this.name + "' world", Status_1.StatusTopic.worldLoad); } await this.loadFromLevelDb(this.levelDb); } async loadFromLevelDb(levelDb) { this.levelDb = levelDb; await this.processWorldData(); this._updateMeta(); this._onDataLoaded.dispatch(this, this); this._isDataLoaded = true; } getTopBlockY(x, z, dim) { const chunkX = Math.floor(x / CHUNK_X_SIZE); const xDim = this.chunks[dim ? dim : 0][chunkX]; if (xDim === undefined) { return undefined; } const chunkZ = Math.floor(z / CHUNK_Z_SIZE); const zDim = xDim[chunkZ]; if (zDim === undefined) { return undefined; } return zDim.getTopBlockY(x - chunkX * CHUNK_X_SIZE, z - chunkZ * CHUNK_Z_SIZE); } getTopBlock(x, z, dim) { const chunkX = Math.floor(x / CHUNK_X_SIZE); const xDim = this.chunks[dim ? dim : 0][chunkX]; if (xDim === undefined) { return undefined; } const chunkZ = Math.floor(z / CHUNK_Z_SIZE); const zDim = xDim[chunkZ]; if (zDim === undefined) { return undefined; } return zDim.getTopBlock(x - chunkX * CHUNK_X_SIZE, z - chunkZ * CHUNK_Z_SIZE); } spawnEntity(entityTypeId, location) { const e = new Entity_1.default(); return e; } getBlock(blockLocation, dim) { const chunkX = Math.floor(blockLocation.x / CHUNK_X_SIZE); const xDim = this.chunks[dim ? dim : 0][chunkX]; if (xDim === undefined) { return new Block_1.default("air"); } const chunkZ = Math.floor(blockLocation.z / CHUNK_Z_SIZE); const chunk = xDim[chunkZ]; if (chunk === undefined) { return new Block_1.default("air"); } let offsetX = blockLocation.x % 16; let offsetZ = blockLocation.z % 16; if (offsetX < 0) { offsetX += 16; } if (offsetZ < 0) { offsetZ += 16; } const block = chunk.getBlock(offsetX, blockLocation.y, offsetZ); if (!block) { return new Block_1.default("air"); } return block; } async processWorldData() { if (!this.levelDb) { return; } this.chunks = []; this.chunkCount = 0; const processOper = await this._project?.carto.notifyOperationStarted("Starting second-pass load of '" + this.name + "' world", Status_1.StatusTopic.worldLoad); for (const keyname in this.levelDb.keys) { const keyValue = this.levelDb.keys[keyname]; if (keyname.startsWith("AutonomousEntities")) { } else if (keyname.startsWith("schedulerWT")) { } else if (keyname.startsWith("Overworld") && keyValue) { const overworldBytes = keyValue.value; if (overworldBytes) { const overworld = new NbtBinary_1.default(); overworld.context = this.name + " overworld"; overworld.fromBinary(overworldBytes, true, false, 0, true); this._overworldData = overworld; } } else if (keyname.startsWith("BiomeData") && keyValue) { const biomeDataBytes = keyValue.value; if (biomeDataBytes) { const biomeData = new NbtBinary_1.default(); biomeData.context = this.name + " biome data"; biomeData.fromBinary(biomeDataBytes, true, false, 0, true); this._biomeData = biomeData; } } else if (keyname.startsWith("CustomProperties")) { this._hasCustomProps = true; } else if (keyname.startsWith("DynamicProperties") && keyValue) { this._hasDynamicProps = true; const dynamicPropertyBytes = keyValue.value; if (dynamicPropertyBytes) { const dynamicProps = new NbtBinary_1.default(); dynamicProps.context = this.name + " dynamic props"; dynamicProps.fromBinary(dynamicPropertyBytes, true, false, 0, true); if (dynamicProps.singleRoot) { const children = dynamicProps.singleRoot.getTagChildren(); this._dynamicProperties = {}; for (const child of children) { if (child.name && Utilities_1.default.isValidUuid(child.name)) { this._dynamicProperties[child.name] = {}; const bpChildren = child.getTagChildren(); for (const propChild of bpChildren) { if (propChild.name && propChild.type === NbtBinaryTag_1.NbtTagType.string) { this._dynamicProperties[child.name][propChild.name] = propChild.valueAsString; if (child.name === CREATOR_TOOLS_EDITOR_BPUUID) { this.loadAnchorsFromDynamicProperties(); } } } } } } } } else if (keyname.startsWith("LevelChunkMetaDataDictionary") && keyValue) { const levelChunkMetaBytes = keyValue.value; if (levelChunkMetaBytes) { const levelChunkMeta = new NbtBinary_1.default(); levelChunkMeta.context = this.name + " level chunk metadata"; levelChunkMeta.fromBinary(levelChunkMetaBytes, true, false, 12, true); this._levelChunkMetaData = levelChunkMeta; } } else if (keyname.startsWith("structuretemplate_")) { } else if (keyname.startsWith("digp") && keyValue) { const keyBytes = keyValue.keyBytes; if (keyBytes) { const x = DataUtilities_1.default.getSignedInteger(keyBytes[4], keyBytes[5], keyBytes[6], keyBytes[7], true); const z = DataUtilities_1.default.getSignedInteger(keyBytes[8], keyBytes[9], keyBytes[10], keyBytes[11], true); Log_1.default.assert(keyBytes.length === 16 || keyBytes.length === 24 || keyBytes.length === 20 || keyBytes.length === 14 || keyBytes.length === 13 || keyBytes.length === 12, "Unexpected digp key size (" + keyBytes.length + ")"); let dim = 0; if (keyBytes.length >= 17) { dim = DataUtilities_1.default.getSignedInteger(keyBytes[8], keyBytes[9], keyBytes[10], keyBytes[11], true); Log_1.default.assert(dim >= 0 && dim <= 2, "Unexpected dimension index - digp (" + dim + ")"); } if (this.chunks[dim] === undefined) { this.chunks[dim] = []; } if (this.chunks[dim][x] === undefined) { this.chunks[dim][x] = []; } if (this.chunks[dim][x][z] === undefined) { const wc = new WorldChunk_1.default(this, x, z); this.chunkCount++; this.chunks[dim][x][z] = wc; } if (keyValue.value !== undefined) { const keyValueBytes = keyValue.value; if (keyValueBytes.length > 0 && keyValueBytes.length % 8 === 0) { let hexStr = ""; for (let bc = 0; bc < keyValueBytes.length; bc += 8) { hexStr += Utilities_1.default.convertToHexString([ keyValueBytes[bc + 0], keyValueBytes[bc + 1], keyValueBytes[bc + 2], keyValueBytes[bc + 3], keyValueBytes[bc + 4], keyValueBytes[bc + 5], keyValueBytes[bc + 6], keyValueBytes[bc + 7], ]); } this.chunks[dim][x][z].addActorDigest(hexStr); } else if (keyValueBytes.length !== 0) { // Log.error("Unexpected actor digest length", this.name); } } } } else if (keyname.startsWith("actorprefix") && keyValue) { const keyBytes = keyValue.keyBytes; if (keyBytes && keyBytes.length === 19 && keyValue.value) { const hexStr = Utilities_1.default.convertToHexString([ keyBytes[11], keyBytes[12], keyBytes[13], keyBytes[14], keyBytes[15], keyBytes[16], keyBytes[17], keyBytes[18], ]); const actorItem = new ActorItem_1.default(hexStr, keyValue.value); this.actorsById[hexStr] = actorItem; } else if (keyBytes && keyBytes.length === 27 && keyValue.value) { const hexStr = Utilities_1.default.convertToHexString([ keyBytes[11], keyBytes[12], keyBytes[13], keyBytes[14], keyBytes[15], keyBytes[16], keyBytes[17], keyBytes[18], keyBytes[19], keyBytes[20],