Merge branch 'StructureVerifier'
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
export const BlockVerificationLevel = Object.freeze({
|
||||
Unknown: 0,
|
||||
NoMatch: 1,
|
||||
TypeMatch: 2,
|
||||
TypeAndStateMatch: 3,
|
||||
Missing: 4,
|
||||
isAir: 5
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import { BlockVerificationLevel } from "./BlockVerificationLevel";
|
||||
|
||||
export class BlockVerifier {
|
||||
constructor(block, instance) {
|
||||
this.block = block;
|
||||
this.instance = instance;
|
||||
this.blockLocationInStructure = instance.toStructureCoords(block.location);
|
||||
}
|
||||
|
||||
verify() {
|
||||
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))
|
||||
return this.matchingPermutations();
|
||||
if (this.isTypeMatch(worldPermutation, structPermutation))
|
||||
return this.matchingTypes();
|
||||
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";
|
||||
}
|
||||
|
||||
isTypeMatch(worldPermuation, structurePermuation) {
|
||||
return worldPermuation.type.id === structurePermuation.type.id;
|
||||
}
|
||||
|
||||
isExactMatch(worldPermuation, structurePermuation) {
|
||||
return worldPermuation.matches(structurePermuation.type.id, structurePermuation.getAllStates());
|
||||
}
|
||||
|
||||
air() {
|
||||
return BlockVerificationLevel.isAir;
|
||||
}
|
||||
|
||||
missing() {
|
||||
return BlockVerificationLevel.Missing;
|
||||
}
|
||||
|
||||
matchingPermutations() {
|
||||
return BlockVerificationLevel.TypeAndStateMatch;
|
||||
}
|
||||
|
||||
matchingTypes() {
|
||||
return BlockVerificationLevel.TypeMatch;
|
||||
}
|
||||
|
||||
matchingNone() {
|
||||
return BlockVerificationLevel.NoMatch;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)}%%)`;
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
});
|
||||
@@ -45,8 +45,8 @@ export class MenuFormBuilder {
|
||||
static buildHowTo() {
|
||||
let body = "§aHow to Add Structures:\n"
|
||||
body += "§7- Save a structure using a §fstructure block§7 or the §f/structure§7 command.\n"
|
||||
body += "§f§lOR§r\n"
|
||||
body += "§7- Add a .mcstructure file to this pack's structures folder. When selecting your structure, select the 'Other' option and then use the filename (without '.mcstructure') as the Structure ID. After its first use, it will be added to the list of structures.";
|
||||
body += "§7§lOR§r\n"
|
||||
body += "§7- Add a §f.mcstructure§7 file to this pack's §fstructures folder§7. When selecting your structure, select the §fOther§7 option and then use the filename (without '.mcstructure') as the §fStructure ID§7. After its first use, it will be added to the list of structures.";
|
||||
body += "\n\n§cHow to Remove Structures:\n"
|
||||
body += "§7- Use the §f/structure delete§7 command to remove a structure from the world.\n"
|
||||
return new ActionFormData()
|
||||
|
||||
@@ -86,14 +86,14 @@ export class StructureInstance {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
getBlock(structureLocation) {
|
||||
return this.#structure.getBlockPermutation(structureLocation);
|
||||
}
|
||||
|
||||
*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 });
|
||||
}
|
||||
}
|
||||
yield * this.getLayerBlocks(y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,15 +101,13 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getBlock(structureLocation) {
|
||||
return this.#structure.getBlockPermutation(structureLocation);
|
||||
}
|
||||
|
||||
getBounds() {
|
||||
return {
|
||||
min: { x: 0, y: 0, z: 0 },
|
||||
@@ -126,6 +124,10 @@ export class StructureInstance {
|
||||
};
|
||||
}
|
||||
|
||||
getTotalVolume() {
|
||||
return this.#structure.size.x * this.#structure.size.y * this.#structure.size.z;
|
||||
}
|
||||
|
||||
rename(newName) {
|
||||
world.setDynamicProperty(`structOptions:${this.name}`, void 0);
|
||||
this.name = newName;
|
||||
@@ -247,4 +249,8 @@ export class StructureInstance {
|
||||
else
|
||||
this.setLayer(this.#options.currentLayer - 1);
|
||||
}
|
||||
|
||||
getDimension() {
|
||||
return world.getDimension(this.#options.dimensionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import { BlockVerifier } from "./BlockVerifier";
|
||||
import { BlockVerificationLevel } from "./BlockVerificationLevel";
|
||||
|
||||
export class StructureVerifier {
|
||||
constructor(instance) {
|
||||
this.instance = instance;
|
||||
this.blockVerificationLevels = {};
|
||||
this.statistics = {};
|
||||
this.initStatistics();
|
||||
}
|
||||
|
||||
async verifyStructure() {
|
||||
await this.runVerifier();
|
||||
this.parseStatistics();
|
||||
return this.statistics;
|
||||
}
|
||||
|
||||
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.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.instance);
|
||||
return blockVerifier.verify();
|
||||
}
|
||||
|
||||
initStatistics() {
|
||||
for (const verificationlevel of Object.values(BlockVerificationLevel)) {
|
||||
this.statistics[verificationlevel] = 0;
|
||||
}
|
||||
this.statistics.percentages = {};
|
||||
this.statistics.correctlyAir = 0;
|
||||
}
|
||||
|
||||
parseStatistics() {
|
||||
for (const blockVerificationLevel of Object.values(BlockVerificationLevel)) {
|
||||
this.parseStatistic(blockVerificationLevel);
|
||||
}
|
||||
}
|
||||
|
||||
parseStatistic(blockVerificationLevel) {
|
||||
for (const verificationlevel of Object.values(this.blockVerificationLevels)) {
|
||||
if (blockVerificationLevel === verificationlevel) {
|
||||
this.statistics[verificationlevel]++;
|
||||
}
|
||||
}
|
||||
this.statistics.percentages[blockVerificationLevel] = this.statistics[blockVerificationLevel] / (this.instance.getTotalVolume() - this.statistics.correctlyAir) * 100;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user