@minecraft/creator-tools
Version:
Minecraft Creator Tools command line and libraries.
332 lines (330 loc) • 24.2 kB
JavaScript
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorldDataInfoGeneratorTest = void 0;
const ProjectInfoItem_1 = require("./ProjectInfoItem");
const IProjectItemData_1 = require("../app/IProjectItemData");
const MCWorld_1 = require("../minecraft/MCWorld");
const Log_1 = require("../core/Log");
const IInfoItemData_1 = require("./IInfoItemData");
const CommandBlockActor_1 = require("../minecraft/blockActors/CommandBlockActor");
const Status_1 = require("../app/Status");
const CommandStructure_1 = require("../app/CommandStructure");
const CommandRegistry_1 = require("../app/CommandRegistry");
const Dialogue_1 = require("../minecraft/Dialogue");
const ContentIndex_1 = require("../core/ContentIndex");
const NbtBinaryTag_1 = require("../minecraft/NbtBinaryTag");
const AnimationControllerBehaviorDefinition_1 = require("../minecraft/AnimationControllerBehaviorDefinition");
const AnimationBehaviorDefinition_1 = require("../minecraft/AnimationBehaviorDefinition");
const ProjectInfoUtilities_1 = require("./ProjectInfoUtilities");
var WorldDataInfoGeneratorTest;
(function (WorldDataInfoGeneratorTest) {
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["blocks"] = 1] = "blocks";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["blockData"] = 2] = "blockData";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["command"] = 3] = "command";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["executeSubCommand"] = 4] = "executeSubCommand";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["levelDat"] = 5] = "levelDat";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["levelDatExperiments"] = 6] = "levelDatExperiments";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["subchunklessChunks"] = 7] = "subchunklessChunks";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["chunks"] = 8] = "chunks";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["unexpectedCommandInMCFunction"] = 101] = "unexpectedCommandInMCFunction";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["minX"] = 103] = "minX";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["minZ"] = 104] = "minZ";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["maxX"] = 105] = "maxX";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["maxZ"] = 106] = "maxZ";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["unexpectedCommandInCommandBlock"] = 102] = "unexpectedCommandInCommandBlock";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["containsWorldImpactingCommand"] = 112] = "containsWorldImpactingCommand";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["commandIsFromOlderMinecraftVersion"] = 212] = "commandIsFromOlderMinecraftVersion";
WorldDataInfoGeneratorTest[WorldDataInfoGeneratorTest["errorProcessingWorld"] = 400] = "errorProcessingWorld";
})(WorldDataInfoGeneratorTest = exports.WorldDataInfoGeneratorTest || (exports.WorldDataInfoGeneratorTest = {}));
class WorldDataInfoGenerator {
constructor() {
this.id = "WORLDDATA";
this.title = "World Data Validation";
this.modernCommandVersion = 33; // corresponds to 1.20.0 versions of Minecraft.
this.performAddOnValidations = false;
this.performPlatformVersionValidations = false;
}
getTopicData(topicId) {
return {
title: ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, topicId),
};
}
summarize(info, infoSet) {
info.chunkCount = infoSet.getSummedNumberValue("WORLDDATA", WorldDataInfoGeneratorTest.unexpectedCommandInMCFunction);
info.subchunkLessChunkCount = infoSet.getSummedNumberValue("WORLDDATA", WorldDataInfoGeneratorTest.subchunklessChunks);
info.worldLoadErrors = infoSet.getCount("WORLDDATA", WorldDataInfoGeneratorTest.errorProcessingWorld);
}
processListOfCommands(commandList, items, projectItem, commandsPi, subCommandsPi, checkForSlash) {
for (let i = 0; i < commandList.length; i++) {
if (commandList[i].trim().length > 2 && (!checkForSlash || commandList[i].startsWith("/"))) {
const command = CommandStructure_1.default.parse(commandList[i]);
if (CommandRegistry_1.default.isMinecraftBuiltInCommand(command.name)) {
if (this.performAddOnValidations && CommandRegistry_1.default.isAddOnBlockedCommand(command.name)) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.warning, this.id, WorldDataInfoGeneratorTest.containsWorldImpactingCommand, "Contains command '" +
command.name +
"' which is impacts the state of the entire world, and generally shouldn't be used in an add-on", projectItem, command.name, undefined, commandList[i]));
}
commandsPi.incrementFeature(command.name);
if (command.name === "execute") {
let foundRun = false;
for (const arg of command.commandArguments) {
if (arg === "run") {
foundRun = true;
}
else if (foundRun && CommandRegistry_1.default.isMinecraftBuiltInCommand(arg)) {
subCommandsPi.incrementFeature(arg);
break;
}
}
}
}
else if (!this.performPlatformVersionValidations && !this.performAddOnValidations) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, 401, "Unexpected command '" + command.name + "'", projectItem, command.name, undefined, commandList[i]));
}
}
}
}
async generate(projectItem, contentIndex) {
const items = [];
if (projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCWorld &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCTemplate &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.worldFolder &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.dialogueBehaviorJson &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.animationControllerBehaviorJson &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.animationBehaviorJson &&
projectItem.itemType !== IProjectItemData_1.ProjectItemType.MCFunction) {
return items;
}
const blocksPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.blocks, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.blocks), projectItem);
items.push(blocksPi);
const blockActorsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.blockData, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.blockData), projectItem);
items.push(blockActorsPi);
const commandsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.command, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.command), projectItem);
items.push(commandsPi);
const subCommandsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.executeSubCommand, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.executeSubCommand), projectItem);
items.push(subCommandsPi);
const nbtPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.levelDat, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.levelDat), projectItem);
items.push(nbtPi);
const nbtExperimentsPi = new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.featureAggregate, this.id, WorldDataInfoGeneratorTest.levelDatExperiments, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.levelDatExperiments), projectItem);
items.push(nbtExperimentsPi);
if (projectItem.itemType === IProjectItemData_1.ProjectItemType.dialogueBehaviorJson) {
await projectItem.ensureFileStorage();
if (projectItem.file) {
const diaManifest = await Dialogue_1.default.ensureOnFile(projectItem.file);
if (diaManifest && diaManifest.definition && diaManifest.definition["minecraft:npc_dialogue"]) {
let scenes = diaManifest.definition["minecraft:npc_dialogue"].scenes;
for (const scene of scenes) {
if (scene.on_open_commands) {
this.processListOfCommands(scene.on_open_commands, items, projectItem, commandsPi, subCommandsPi, true);
}
if (scene.on_close_commands) {
this.processListOfCommands(scene.on_close_commands, items, projectItem, commandsPi, subCommandsPi, true);
}
}
let buttons = diaManifest.getAllButtons();
for (const button of buttons) {
if (button.commands) {
this.processListOfCommands(button.commands, items, projectItem, commandsPi, subCommandsPi, true);
}
}
}
}
}
else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.animationControllerBehaviorJson) {
await projectItem.ensureFileStorage();
if (projectItem.file) {
const acManifest = await AnimationControllerBehaviorDefinition_1.default.ensureOnFile(projectItem.file);
if (acManifest && acManifest.data && acManifest.data.animation_controllers) {
let states = acManifest.getAllStates();
for (const state of states) {
if (state.state.on_entry) {
this.processListOfCommands(state.state.on_entry, items, projectItem, commandsPi, subCommandsPi, true);
}
if (state.state.on_exit) {
this.processListOfCommands(state.state.on_exit, items, projectItem, commandsPi, subCommandsPi, true);
}
}
}
}
}
else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.animationBehaviorJson) {
await projectItem.ensureFileStorage();
if (projectItem.file) {
const animManifest = await AnimationBehaviorDefinition_1.default.ensureOnFile(projectItem.file);
if (animManifest && animManifest.data && animManifest.data.animations) {
let timelines = animManifest.getAllTimeline();
for (const timeline of timelines) {
if (timeline.timeline) {
this.processListOfCommands(timeline.timeline, items, projectItem, commandsPi, subCommandsPi, true);
}
}
}
}
}
else if (projectItem.itemType === IProjectItemData_1.ProjectItemType.MCFunction) {
let content = await projectItem.getStringContent();
if (content !== undefined) {
let contentLines = content.split("\n");
this.processListOfCommands(contentLines, items, projectItem, commandsPi, subCommandsPi, false);
}
}
if (projectItem.itemType === IProjectItemData_1.ProjectItemType.MCWorld ||
projectItem.itemType === IProjectItemData_1.ProjectItemType.MCTemplate ||
projectItem.itemType === IProjectItemData_1.ProjectItemType.worldFolder) {
let mcworld = await MCWorld_1.default.ensureOnItem(projectItem);
if (!mcworld) {
Log_1.default.debugAlert("Could not find respective world.");
return items;
}
await mcworld.load(false);
await mcworld.loadData(false);
if (mcworld.isInErrorState &&
mcworld.errorMessages &&
!this.performAddOnValidations &&
!this.performPlatformVersionValidations) {
for (const err of mcworld.errorMessages) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, WorldDataInfoGeneratorTest.errorProcessingWorld, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.errorProcessingWorld), projectItem, err.message + (err.context ? " - " + err.context : ""), mcworld.name));
}
}
if (projectItem.projectPath &&
contentIndex &&
mcworld.levelData &&
mcworld.levelData.nbt &&
mcworld.levelData.nbt.singleRoot) {
const children = mcworld.levelData.nbt.singleRoot.getTagChildren();
for (const child of children) {
if (child.name === "experiments") {
for (const experimentChild of child.getTagChildren()) {
if (experimentChild.type === NbtBinaryTag_1.NbtTagType.int ||
experimentChild.type === NbtBinaryTag_1.NbtTagType.byte ||
experimentChild.type === NbtBinaryTag_1.NbtTagType.string) {
nbtExperimentsPi.incrementFeature(experimentChild.name, experimentChild.valueAsString);
contentIndex.insert(experimentChild.name + "==" + experimentChild.valueAsString, projectItem.projectPath, ContentIndex_1.AnnotationCategory.experiment);
}
}
}
else if (child.type === NbtBinaryTag_1.NbtTagType.int ||
child.type === NbtBinaryTag_1.NbtTagType.byte ||
child.type === NbtBinaryTag_1.NbtTagType.string) {
if (child.name !== "LevelName" &&
child.name !== "FlatWorldLayers" &&
child.name !== "lightningTime" &&
child.name !== "EducationOid" &&
child.name !== "EducationProductId" &&
child.name !== "rainTime" &&
child.name !== "worldTemplateUUID" &&
!child.name.startsWith("LimitedWorld") &&
!child.name.startsWith("SpawnX") &&
!child.name.startsWith("SpawnY") &&
!child.name.startsWith("SpawnZ")) {
if (child.name.indexOf("ersion") >= 0 && !child.valueAsString.startsWith("1.")) {
nbtPi.incrementFeature(child.name, "(unknown version)");
}
else {
nbtPi.incrementFeature(child.name, child.valueAsString);
}
contentIndex.insert(child.name + "==" + child.valueAsString, projectItem.projectPath, ContentIndex_1.AnnotationCategory.worldProperty);
}
}
}
}
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.chunks, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.chunks), projectItem, mcworld.chunkCount, mcworld.name));
let blockCount = 0;
let chunkCount = 0;
let subchunkLessChunkCount = 0;
for (const dimIndex in mcworld.chunks) {
let dim = mcworld.chunks[dimIndex];
for (const chunkSliverIndex in dim) {
const chunkSliver = dim[chunkSliverIndex];
if (chunkSliver) {
for (const chunkId in chunkSliver) {
const chunk = chunkSliver[chunkId];
if (chunk) {
chunkCount++;
if (chunk.subChunks.length <= 0) {
subchunkLessChunkCount++;
}
if (chunkCount % 1000 === 0) {
await projectItem.project.carto.notifyStatusUpdate("World data validation: scanned " +
chunkCount / 1000 +
"K of " +
Math.floor(mcworld.chunkCount / 1000) +
"K chunks in " +
mcworld.name, Status_1.StatusTopic.validation);
}
const blockActors = chunk.blockActors;
for (let i = 0; i < blockActors.length; i++) {
const blockActor = blockActors[i];
if (blockActor.id) {
blockActorsPi.incrementFeature(blockActor.id);
}
if (blockActor instanceof CommandBlockActor_1.default) {
let cba = blockActor;
if (cba.version) {
blockActorsPi.spectrumIntFeature("Command Version", cba.version);
}
if (cba.version && cba.version < this.modernCommandVersion) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.recommendation, this.id, WorldDataInfoGeneratorTest.commandIsFromOlderMinecraftVersion, "Command '" + cba.command + "' is from an older Minecraft version (" + cba.version + ") ", projectItem, "(Command at location " + cba.x + ", " + cba.y + ", " + cba.z + ")", undefined, cba.command));
}
if (cba.command && cba.command.trim().length > 2) {
let command = CommandStructure_1.default.parse(cba.command);
if (CommandRegistry_1.default.isMinecraftBuiltInCommand(command.name)) {
if (this.performAddOnValidations && CommandRegistry_1.default.isAddOnBlockedCommand(command.name)) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.warning, this.id, WorldDataInfoGeneratorTest.containsWorldImpactingCommand, "Contains command '" +
command.name +
"' which is impacts the state of the entire world, and generally shouldn't be used in an add-on", projectItem, command.name, undefined, cba.command));
}
commandsPi.incrementFeature(command.name);
if (command.name === "execute") {
let foundRun = false;
for (const arg of command.commandArguments) {
if (arg === "run") {
foundRun = true;
}
else if (foundRun && CommandRegistry_1.default.isMinecraftBuiltInCommand(arg)) {
subCommandsPi.incrementFeature(arg);
break;
}
}
}
}
else if (!this.performAddOnValidations && !this.performPlatformVersionValidations) {
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.error, this.id, WorldDataInfoGeneratorTest.unexpectedCommandInCommandBlock, "Unexpected command '" + command.name + "'", projectItem, command.name, undefined, cba.command));
}
}
}
}
const blockList = chunk.getBlockList();
for (let i = 0; i < blockList.length; i++) {
const block = blockList[i];
if (block) {
blockCount++;
if (block.typeName) {
let type = block.typeName;
if (type.indexOf(":") >= 0 && type.indexOf("minecraft:") < 0) {
type = "(custom)";
}
blocksPi.incrementFeature(type);
}
}
}
chunk.clearCachedData();
}
}
}
}
}
blocksPi.data = blockCount;
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.minX, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.minX), projectItem, mcworld.minX, mcworld.name));
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.minZ, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.minZ), projectItem, mcworld.minZ, mcworld.name));
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.maxX, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.maxX), projectItem, mcworld.maxX, mcworld.name));
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.maxZ, ProjectInfoUtilities_1.default.getTitleFromEnum(WorldDataInfoGeneratorTest, WorldDataInfoGeneratorTest.maxZ), projectItem, mcworld.maxZ, mcworld.name));
items.push(new ProjectInfoItem_1.default(IInfoItemData_1.InfoItemType.info, this.id, WorldDataInfoGeneratorTest.subchunklessChunks, "Subchunkless Chunks", projectItem, subchunkLessChunkCount, mcworld.name));
}
return items;
}
}
exports.default = WorldDataInfoGenerator;
//# sourceMappingURL=../maps/info/WorldDataInfoGenerator.js.map