@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
1,237 lines (1,236 loc) • 63.2 kB
JavaScript
"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],