diff --git a/scripts/classes/BlockVerificationLevel.js b/scripts/classes/BlockVerificationLevel.js index 7e04270..cdc5a32 100644 --- a/scripts/classes/BlockVerificationLevel.js +++ b/scripts/classes/BlockVerificationLevel.js @@ -3,5 +3,6 @@ export const BlockVerificationLevel = Object.freeze({ NoMatch: 1, TypeMatch: 2, TypeAndStateMatch: 3, - Missing: 4 + Missing: 4, + isAir: 5 }); \ No newline at end of file diff --git a/scripts/classes/BlockVerifier.js b/scripts/classes/BlockVerifier.js index 9557c38..5ac24e9 100644 --- a/scripts/classes/BlockVerifier.js +++ b/scripts/classes/BlockVerifier.js @@ -1,19 +1,20 @@ import { BlockVerificationLevel } from "./BlockVerificationLevel"; export class BlockVerifier { - constructor(block, structure) { + constructor(block, instance) { this.block = block; - this.blockLocationInStructure = structure.toStructureCoords(block.location); - this.structure = structure; + this.instance = instance; + this.blockLocationInStructure = instance.toStructureCoords(block.location); } verify() { - const worldPermutation = this.block.permutation; - const structPermutation = this.structure.getBlock(this.blockLocationInStructure); - return this.evaluatePermutations(worldPermutation, structPermutation); + const structPermutation = this.instance.getBlock(this.blockLocationInStructure); + return this.evaluatePermutations(this.block.permutation, structPermutation); } evaluatePermutations(worldPermutation, structPermutation) { + if (this.isCorrectlyAir(worldPermutation, structPermutation)) + return this.air(); if (this.isMissing(worldPermutation, structPermutation)) return this.missing(); if (this.isExactMatch(worldPermutation, structPermutation)) @@ -23,6 +24,10 @@ export class BlockVerifier { return this.matchingNone(); } + isCorrectlyAir(worldPermutation, structPermutation) { + return worldPermutation.type.id === "minecraft:air" && structPermutation.type.id === "minecraft:air"; + } + isMissing(worldPermutation, structPermutation) { return worldPermutation.type.id === "minecraft:air" && structPermutation.type.id !== "minecraft:air"; } @@ -35,22 +40,23 @@ export class BlockVerifier { return worldPermuation.matches(structurePermuation.type.id, structurePermuation.getAllStates()); } + air() { + return BlockVerificationLevel.isAir; + } + missing() { return BlockVerificationLevel.Missing; } matchingPermutations() { - console.warn(`Block ${this.block.typeId} matches structure block ${structPermutation.typeId}`); return BlockVerificationLevel.TypeAndStateMatch; } matchingTypes() { - console.warn(`Block ${this.block.typeId} matches structure block ${structPermutation.typeId}`); return BlockVerificationLevel.TypeMatch; } matchingNone() { - console.warn(`Block ${this.block.typeId} does not match structure block ${structPermutation.typeId}`); return BlockVerificationLevel.NoMatch; } } \ No newline at end of file diff --git a/scripts/classes/InstanceEditForm.js b/scripts/classes/InstanceEditForm.js index c4acdb7..928a281 100644 --- a/scripts/classes/InstanceEditForm.js +++ b/scripts/classes/InstanceEditForm.js @@ -11,6 +11,7 @@ export class InstanceEditForm { InstanceEditOptions.PreviousLayer, InstanceEditOptions.SetLayer, InstanceEditOptions.Move, + InstanceEditOptions.Statistics, InstanceEditOptions.RenameInstance, InstanceEditOptions.DisableInstance, ], @@ -93,6 +94,9 @@ export class InstanceEditForm { case InstanceEditOptions.Move: this.instance.move(this.player.dimension.id, this.player.location); break; + case InstanceEditOptions.Statistics: + this.statisticsForm(); + break; case InstanceEditOptions.MainMenu: new MenuForm(this.player, { jumpToInstance: false }); break; @@ -104,7 +108,8 @@ export class InstanceEditForm { renameInstanceForm() { InstanceEditFormBuilder.buildRenameInstance(this.instanceName).show(this.player).then((response) => { - if (response.canceled) return; + if (response.canceled) + return; const newName = response.formValues[0]; if (newName === '') { this.player.sendMessage('§cInstance name cannot be empty.'); @@ -122,9 +127,15 @@ export class InstanceEditForm { setLayerForm() { InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => { - if (response.canceled) return; + if (response.canceled) + return; const selectedLayer = response.formValues[0]; this.instance.setLayer(parseInt(selectedLayer)); }); } + + async statisticsForm() { + const form = await InstanceEditFormBuilder.buildStatistics(this.instance) + form.show(this.player); + } } \ No newline at end of file diff --git a/scripts/classes/InstanceEditFormBuilder.js b/scripts/classes/InstanceEditFormBuilder.js index 118a062..3564309 100644 --- a/scripts/classes/InstanceEditFormBuilder.js +++ b/scripts/classes/InstanceEditFormBuilder.js @@ -1,5 +1,7 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { MenuFormBuilder } from './MenuFormBuilder'; +import { StructureVerifier } from './StructureVerifier'; +import { BlockVerificationLevel } from './BlockVerificationLevel'; export class InstanceEditFormBuilder { static buildInstance(instance, options) { @@ -30,4 +32,24 @@ export class InstanceEditFormBuilder { .slider("Layer", 0, maxLayer, 1, currentLayer) .submitButton('Set Layer'); } + + static async buildStatistics(instance) { + const buildStatisticsForm = new ActionFormData() + .title(MenuFormBuilder.menuTitle) + let message = ''; + const structureVerifier = new StructureVerifier(instance); + const statistics = await structureVerifier.verifyStructure(); + message += `§fStatistics for §a${instance.name}§f:\n`; + message += `§7Blocks: §2${instance.getTotalVolume() - statistics.correctlyAir}\n`; + message += `§7Correct: §a${this.getFormattedStatistic(statistics, BlockVerificationLevel.TypeAndStateMatch)}\n`; + message += `§7Block State Incorrect: §e${this.getFormattedStatistic(statistics, BlockVerificationLevel.TypeMatch)}\n`; + message += `§7Incorrect: §c${this.getFormattedStatistic(statistics, BlockVerificationLevel.NoMatch)}\n`; + message += `§7Missing: §c${this.getFormattedStatistic(statistics, BlockVerificationLevel.Missing)}\n`; + buildStatisticsForm.body(message); + return buildStatisticsForm; + } + + static getFormattedStatistic(statistics, blockVerificationLevel) { + return `${statistics[blockVerificationLevel]} (${statistics.percentages[blockVerificationLevel].toFixed(2)}%%)`; + } } \ No newline at end of file diff --git a/scripts/classes/InstanceEditOptions.js b/scripts/classes/InstanceEditOptions.js index 7b4fa3b..d74468d 100644 --- a/scripts/classes/InstanceEditOptions.js +++ b/scripts/classes/InstanceEditOptions.js @@ -9,5 +9,6 @@ export const InstanceEditOptions = Object.freeze({ NextLayer: 'Increase Layer', PreviousLayer: 'Decrease Layer', SetLayer: 'Set Layer', - Move: 'Move Here' + Move: 'Move Here', + Statistics: 'Statistics', }); \ No newline at end of file diff --git a/scripts/classes/StructureInstance.js b/scripts/classes/StructureInstance.js index 45efa79..82e3fc7 100644 --- a/scripts/classes/StructureInstance.js +++ b/scripts/classes/StructureInstance.js @@ -92,12 +92,8 @@ export class StructureInstance { *getBlocks() { const max = this.#structure.size; - for (let x = 0; x < max.x; x++) { - for (let y = 0; y < max.y; y++) { - for (let z = 0; z < max.z; z++) { - yield this.#structure.getBlockPermutation({ x, y, z }); - } - } + for (let y = 0; y < max.y; y++) { + yield * this.getLayerBlocks(y); } } @@ -105,7 +101,9 @@ export class StructureInstance { const max = this.#structure.size; for (let x = 0; x < max.x; x++) { for (let z = 0; z < max.z; z++) { - yield this.#structure.getBlockPermutation({ x, y, z }); + const blockPermutation = this.#structure.getBlockPermutation({ x, y, z }); + blockPermutation.location = { x, y, z }; + yield blockPermutation; } } } diff --git a/scripts/classes/StructureVerifier.js b/scripts/classes/StructureVerifier.js index 38cb5c6..10ad29a 100644 --- a/scripts/classes/StructureVerifier.js +++ b/scripts/classes/StructureVerifier.js @@ -1,43 +1,55 @@ import { BlockVerifier } from "./BlockVerifier"; import { BlockVerificationLevel } from "./BlockVerificationLevel"; -import { system } from "@minecraft/server"; export class StructureVerifier { - constructor(stucture) { - this.structure = structure; + constructor(instance) { + this.instance = instance; this.blockVerificationLevels = {}; this.statistics = {}; this.initStatistics(); } - verifyStructure() { - system.runJob(this.verifyBlocks()); + async verifyStructure() { + await this.runVerifier(); this.parseStatistics(); + return this.statistics; } - *verifyBlocks() { - for (const block of this.structure.getBlocks()) { - this.blockVerificationLevels[block.location] = this.verifyBlock(block.location); - yield; + async runVerifier() { + return new Promise((resolve) => { + this.verifyBlocks(); + resolve(); + }); + } + + verifyBlocks() { + for (const block of this.instance.getBlocks()) { + const verificationLevel = this.verifyBlock(block.location); + if (verificationLevel !== BlockVerificationLevel.isAir) + this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; + else + this.statistics.correctlyAir++; } } verifyBlock(location) { - const worldBlock = this.structure.getDimension().getBlock(location); + const worldBlock = this.instance.getDimension().getBlock(this.instance.toGlobalCoords(location)); if (!worldBlock) throw new Error(`Block at ${JSON.stringify(location)} could not be accessed.`); - const blockVerifier = new BlockVerifier(worldBlock, this.structure); + const blockVerifier = new BlockVerifier(worldBlock, this.instance); return blockVerifier.verify(); } initStatistics() { - for (const verificationlevel of Object.keys(BlockVerificationLevel)) { + for (const verificationlevel of Object.values(BlockVerificationLevel)) { this.statistics[verificationlevel] = 0; } + this.statistics.percentages = {}; + this.statistics.correctlyAir = 0; } parseStatistics() { - for (const blockVerificationLevel of Object.keys(this.statistics)) { + for (const blockVerificationLevel of Object.values(BlockVerificationLevel)) { this.parseStatistic(blockVerificationLevel); } } @@ -48,6 +60,6 @@ export class StructureVerifier { this.statistics[verificationlevel]++; } } - this.statistics.percentages[blockVerificationLevel] = this.statistics[blockVerificationLevel] / this.structure.getTotalVolume() * 100; + this.statistics.percentages[blockVerificationLevel] = this.statistics[blockVerificationLevel] / (this.instance.getTotalVolume() - this.statistics.correctlyAir) * 100; } } \ No newline at end of file