UNPKG

bedrock-development

Version:

APIs for creating and editing files related to Minecraft Bedrock development.

264 lines (263 loc) 11.7 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { archiveDirectory, copySourceDirectory, copySourceFile, getFiles, setFiles } from "../file_manager.js"; import * as JSONC from 'comment-json'; import { chalk } from "../utils.js"; import * as fs from 'fs'; import path from "path"; import { v4 } from 'uuid'; import { execSync } from "child_process"; import { NbtFile } from "deepslate"; const APPDATA = (process.env.LOCALAPPDATA || (process.platform == 'darwin' ? process.env.HOME + '/Library/Preferences' : process.env.HOME + "/.local/share")).replace(/\\/g, '/'); export const MOJANG = `${APPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang`; const DOWNLOAD = `${process.env.USERPROFILE}/Downloads`.replace(/\\/g, '/'); export function cache(target, propertyName, descriptor) { if (descriptor.get) { const get = descriptor.get; target[`${propertyName}_cached`] = undefined; descriptor.get = function () { const obj = this; if (obj[`${propertyName}_cached`]) { return obj[`${propertyName}_cached`]; } else { const value = get.apply(obj); obj[`${propertyName}_cached`] = value; return value; } }; } } export class MinecraftWorld { get BehaviorPacks() { return this.getPacks('behavior'); } get ResourcePacks() { return this.getPacks('resource'); } get Name() { return String(fs.readFileSync(this.filePath + '/levelname.txt')); } get LevelDat() { const buffer = fs.readFileSync(this.filePath + '/level.dat'); const uint8Array = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); return NbtFile.read(uint8Array, { bedrockHeader: true, littleEndian: true }); } constructor(filePath) { this.filePath = filePath; } static create(worldName, options) { var _a; return __awaiter(this, void 0, void 0, function* () { const world = new MinecraftWorld(`${DOWNLOAD}/${worldName}`); if (options.behavior_pack_manifest_path) { world.addPack(MinecraftWorld.getPackFromManifest(options.behavior_pack_manifest_path)); } if (options.resource_pack_manifest_path) { world.addPack(MinecraftWorld.getPackFromManifest(options.resource_pack_manifest_path)); } copySourceFile('world/level.dat', `${world.filePath}/level.dat`); setFiles([{ filePath: `${world.filePath}/levelname.txt`, fileContents: worldName }]); const levelDat = world.LevelDat.toJson(); levelDat.root.LevelName = { type: 8, value: worldName }; levelDat.root.RandomSeed = { type: 4, value: [Math.floor(Math.random() * 1000), Math.floor(Math.random() * 1000)] }; levelDat.root.GameType = { type: 3, value: (_a = options.gamemode) !== null && _a !== void 0 ? _a : 0 }; if (options.flatworld) { levelDat.root.Generator = { type: 3, value: 2 }; } if (options.testworld) { levelDat.root.commandsEnabled = { type: 1, value: 1 }; levelDat.root.dodaylightcycle = { type: 1, value: 0 }; levelDat.root.domobloot = { type: 1, value: 0 }; levelDat.root.domobspawning = { type: 1, value: 0 }; levelDat.root.mobgriefing = { type: 1, value: 0 }; levelDat.root.keepinventory = { type: 1, value: 1 }; levelDat.root.doweathercycle = { type: 1, value: 0 }; } world.setLevelDat(NbtFile.fromJson(levelDat).write()); yield new Promise(resolve => { archiveDirectory(world.filePath, `${world.filePath}.mcworld`, () => { console.log(`${chalk.green(`Opening World`)}`); execSync(`"${world.filePath}.mcworld"`); fs.rmSync(`${world.filePath}.mcworld`); resolve(); }); }); return world; }); } static getAllWorlds() { try { let path = `${MOJANG}/minecraftWorlds`; return fs.readdirSync(path).map(dir => new MinecraftWorld(`${path}/${dir}`)); } catch (error) { console.log(error); return []; } } exportWorld(include_packs, type) { const outputPath = `${DOWNLOAD}/${this.Name}`; this.addManifest(); console.log(`${chalk.green(`Exporting ${this.filePath}`)}`); copySourceDirectory(this.filePath, outputPath); if (include_packs) { try { fs.mkdirSync(`${outputPath}/behavior_packs`); fs.mkdirSync(`${outputPath}/resource_packs`); } catch (error) { } for (const bp of this.BehaviorPacks) { console.log(`${chalk.green(`Exporting ${bp.name}`)}`); copySourceDirectory(bp.directory, `${outputPath}/behavior_packs/${bp.name}`); } for (const rp of this.ResourcePacks) { console.log(`${chalk.green(`Exporting ${rp.name}`)}`); copySourceDirectory(rp.directory, `${outputPath}/resource_packs/${rp.name}`); } } console.log(`${chalk.green(`Packaging World`)}`); return new Promise(resolve => { archiveDirectory(outputPath, `${outputPath}.mc${type}`, () => { console.log(`${chalk.green(`Cleaning Up`)}`); fs.rmSync(outputPath, { recursive: true, force: true }); console.log(`${chalk.green(`Packaged ${this.filePath}.mc${type} To ${DOWNLOAD}`)}`); execSync(`start %windir%\\explorer.exe "${DOWNLOAD.replace(/\//g, '\\')}"`); resolve(); }); }); } getPacks(type) { if (fs.existsSync(`${this.filePath}/world_${type}_packs.json`)) { let bp = JSONC.parse(String(fs.readFileSync(`${this.filePath}/world_${type}_packs.json`))); return bp.map(pack => { const directory = findPackPathWithID(pack.pack_id, type); return { type: type, name: path.basename(directory), uuid: pack.pack_id, directory, }; }); } return []; } static getPackFromManifest(filepath) { const manifest = JSON.parse(String(fs.readFileSync(filepath))); const type = filepath.includes('resource') ? 'resource' : 'behavior'; const uuid = manifest.header.uuid; const directory = path.dirname(filepath); const name = path.basename(directory); const pack = { directory, name, type, uuid, }; return type === 'resource' ? pack : pack; } addPack(pack) { let files = getFiles(this.filePath + `/world_${pack.type}_packs.json`); if (!files.length) { files.push({ filePath: this.filePath + `/world_${pack.type}_packs.json`, fileContents: '[]' }); } files = files.filter(file => { if (file.fileContents.includes(pack.uuid)) { console.warn(`${chalk.yellow(`${this.Name} already contains pack ${pack.name}`)}`); return false; } ; const json = JSON.parse(file.fileContents); json.push({ pack_id: pack.uuid, version: [1, 0, 0], }); file.fileContents = JSON.stringify(json, null, '\t'); file.handleExisting = 'overwrite'; return true; }); setFiles(files); } addManifest() { return __awaiter(this, void 0, void 0, function* () { const dat = this.LevelDat.toJson(); const version_array = dat.root.lastOpenedWithVersion.value.items.slice(0, 3); console.log(version_array); setFiles([{ filePath: `${this.filePath}/manifest.json`, handleExisting: 'overwrite_silent', fileContents: JSON.stringify({ format_version: 2, header: { base_game_version: version_array, description: "", lock_template_options: true, name: this.Name, platform_locked: false, uuid: v4(), version: [1, 0, 0] }, modules: [ { description: "", type: "world_template", uuid: v4(), version: [1, 0, 0] } ] }, null, '/t') }]); }); } setLevelDat(v) { let stream = fs.createWriteStream(this.filePath + '/level.dat'); stream.write(v); stream.end(); } } __decorate([ cache ], MinecraftWorld.prototype, "BehaviorPacks", null); __decorate([ cache ], MinecraftWorld.prototype, "ResourcePacks", null); __decorate([ cache ], MinecraftWorld.prototype, "Name", null); function findPackPathWithID(id, type) { let path = `${APPDATA}/Packages/Microsoft.MinecraftUWP_8wekyb3d8bbwe/LocalState/games/com.mojang`; // Check dev packs first for (const folder of fs.readdirSync(`${path}/development_${type}_packs`)) { let subpath = `${path}/development_${type}_packs/${folder}`; if (fs.existsSync(`${subpath}/manifest.json`)) { let manifest = JSONC.parse(String(fs.readFileSync(`${subpath}/manifest.json`))); if (manifest.header.uuid === id) { return subpath; } } } // Check other packs next for (const folder of fs.readdirSync(`${path}/${type}_packs`)) { let subpath = `${path}/${folder}`; if (fs.existsSync(`${subpath}/manifest.json`)) { let manifest = JSONC.parse(String(fs.readFileSync(`${subpath}/manifest.json`))); if (manifest.header.uuid === id) { return subpath; } } } return ''; }