From 35b7575d2f73be41b9d7cf654f60f5881ecf28ff Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Thu, 17 Apr 2025 21:16:13 -0700 Subject: [PATCH 1/3] Performance help --- .../scripts/classes/InstanceEditForm.js | 41 ++++++--- .../classes/InstanceEditFormBuilder.js | 11 ++- .../scripts/classes/StructureInstance.js | 79 ++++++++++------- .../scripts/classes/StructureVerifier.js | 86 ++++++++++++++----- .../classes/enums/InstanceEditOptions.js | 11 +-- 5 files changed, 159 insertions(+), 69 deletions(-) diff --git a/StrucTool [BP]/scripts/classes/InstanceEditForm.js b/StrucTool [BP]/scripts/classes/InstanceEditForm.js index 0a87d14..e183aa4 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditForm.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditForm.js @@ -1,5 +1,6 @@ import { structureCollection } from './StructureCollection'; import { MenuForm } from '../classes/MenuForm'; +import { forceShow } from '../utils'; import { InstanceEditOptions } from './enums/InstanceEditOptions'; import { InstanceEditFormBuilder } from './InstanceEditFormBuilder'; import { FormCancelationReason } from '@minecraft/server-ui'; @@ -13,19 +14,20 @@ export class InstanceEditForm { InstanceEditOptions.SetLayer, InstanceEditOptions.Move, InstanceEditOptions.Statistics, - InstanceEditOptions.RenameInstance, - InstanceEditOptions.DisableInstance, + InstanceEditOptions.Settings, + InstanceEditOptions.Rename, + InstanceEditOptions.Disable, ], isNotEnabledAndIsNotPlaced: [ - InstanceEditOptions.PlaceInstance, - InstanceEditOptions.RenameInstance + InstanceEditOptions.Place, + InstanceEditOptions.Rename ], isNotEnabledButIsPlaced: [ - InstanceEditOptions.EnableInstance, - InstanceEditOptions.RenameInstance + InstanceEditOptions.Enable, + InstanceEditOptions.Rename ], common: [ - InstanceEditOptions.DeleteInstance, + InstanceEditOptions.Delete, InstanceEditOptions.MainMenu ] } @@ -39,7 +41,7 @@ export class InstanceEditForm { show() { const currentOptions = this.getActiveOptions(); - InstanceEditFormBuilder.buildInstance(this.instance, currentOptions).show(this.player).then((response) => { + forceShow(this.player, InstanceEditFormBuilder.buildInstance(this.instance, currentOptions)).then((response) => { if (response.canceled) return; this.handleOption(currentOptions[response.selection]); }); @@ -66,19 +68,19 @@ export class InstanceEditForm { handleOption(option) { switch (option) { - case InstanceEditOptions.EnableInstance: + case InstanceEditOptions.Enable: this.instance.enable(); break; - case InstanceEditOptions.DisableInstance: + case InstanceEditOptions.Disable: this.instance.disable(); break; - case InstanceEditOptions.PlaceInstance: + case InstanceEditOptions.Place: this.instance.place(this.player.dimension.id, this.player.location); break; - case InstanceEditOptions.RenameInstance: + case InstanceEditOptions.Rename: this.renameInstanceForm(); break; - case InstanceEditOptions.DeleteInstance: + case InstanceEditOptions.Delete: structureCollection.delete(this.instanceName); break; case InstanceEditOptions.NextLayer: @@ -101,6 +103,9 @@ export class InstanceEditForm { case InstanceEditOptions.MainMenu: new MenuForm(this.player, { jumpToInstance: false }); break; + case InstanceEditOptions.Settings: + this.settingsForm(); + break; default: this.player.sendMessage(`§cUnknown option: ${option}`); break; @@ -142,4 +147,14 @@ export class InstanceEditForm { this.player.sendMessage(statsForm.stats); }); } + + settingsForm() { + InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => { + if (response.canceled) + return; + const shouldRender = response.formValues[0]; + const trackPlayerDistance = response.formValues[1]; + this.instance.setVerifierOptions({ shouldRender, trackPlayerDistance }); + }); + } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js index c98d61a..3e316ab 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js @@ -2,6 +2,7 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { MenuFormBuilder } from './MenuFormBuilder'; import { StructureVerifier } from './StructureVerifier'; import { StructureStatistics } from './StructureStatistics'; +import { TicksPerSecond } from '@minecraft/server'; export class InstanceEditFormBuilder { static buildInstance(instance, options) { @@ -36,11 +37,19 @@ export class InstanceEditFormBuilder { static async buildStatistics(instance) { const buildStatisticsForm = new ActionFormData() .title(MenuFormBuilder.menuTitle) - const structureVerifier = new StructureVerifier(instance); + const structureVerifier = new StructureVerifier(instance, { shouldRender: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); const verification = await structureVerifier.verifyStructure(); const statistics = new StructureStatistics(instance, verification); const statsMessage = statistics.getMessage(); buildStatisticsForm.body(statsMessage); return { form: buildStatisticsForm, stats: statsMessage }; } + + static buildSettings(instance) { + return new ModalFormData() + .title(MenuFormBuilder.menuTitle) + .toggle('Toggle block validation.', instance.verifier.shouldRender) + .slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance) + .submitButton('§aApply'); + } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureInstance.js b/StrucTool [BP]/scripts/classes/StructureInstance.js index a507dae..0fd71a0 100644 --- a/StrucTool [BP]/scripts/classes/StructureInstance.js +++ b/StrucTool [BP]/scripts/classes/StructureInstance.js @@ -1,5 +1,5 @@ import { world } from "@minecraft/server"; -import { Outliner } from "./Outliner"; +import { Vector } from "../lib/Vector"; import { StructureOutliner } from "./StructureOutliner"; import { StructureVerifier } from "./StructureVerifier"; @@ -13,7 +13,12 @@ export class StructureInstance { worldLocation: { x: 0, y: 0, z: 0 }, rotation: 0, mirror: false, - currentLayer: 0 + currentLayer: 0, + verifier: { + shouldRender: true, + trackPlayerDistance: 5, + intervalOrLifetime: 10 + } }; outliner = void 0; verifier = void 0; @@ -83,39 +88,44 @@ export class StructureInstance { try { dimension = world.getDimension(this.#options.dimensionId); } catch (e) { + console.warn(`[StrucTool] Dimension '${this.#options.dimensionId}' not found. Defaulting to 'minecraft:overworld'.`); dimension = world.getDimension("minecraft:overworld"); } return dimension; } getBlock(structureLocation) { - return this.#structure.getBlockPermutation(structureLocation); + const blockPermutation = this.#structure.getBlockPermutation({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); + if (!blockPermutation) + return void 0; + blockPermutation.location = structureLocation; + return blockPermutation; } - *getBlocks() { - const max = this.#structure.size; - for (let y = 0; y < max.y; y++) { - yield * this.getLayerBlocks(y); + *getBlocks(locations = void 0) { + if (locations === void 0) { + for (let y = 0; y < this.#structure.size.y; y++) { + yield * this.getLayerBlocks(y); + } + } else { + for (const location of locations) { + yield this.getBlock(location); + } } } *getLayerBlocks(y) { - const max = this.#structure.size; - for (let x = 0; x < max.x; x++) { - for (let z = 0; z < max.z; z++) { - const blockPermutation = this.#structure.getBlockPermutation({ x, y, z }); - if (!blockPermutation) - yield void 0; - blockPermutation.location = { x, y, z }; - yield blockPermutation; + for (let x = 0; x < this.#structure.size.x; x++) { + for (let z = 0; z < this.#structure.size.z; z++) { + yield this.getBlock({ x, y, z }); } } } getBounds() { return { - min: { x: 0, y: 0, z: 0 }, - max: this.#structure.size + min: new Vector(0, 0, 0), + max: new Vector(this.#structure.size.x, this.#structure.size.y, this.#structure.size.z) }; } @@ -123,8 +133,8 @@ export class StructureInstance { if (!this.#options.isEnabled) throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`); return { - min: { x: 0, y: this.#options.currentLayer - 1, z: 0 }, - max: { x: this.#structure.size.x, y: this.#options.currentLayer, z: this.#structure.size.z } + min: new Vector(0, this.#options.currentLayer - 1, 0), + max: new Vector(this.#structure.size.x, this.#options.currentLayer, this.#structure.size.z) }; } @@ -184,7 +194,7 @@ export class StructureInstance { if (!this.outliner) this.outliner = new StructureOutliner(this); if (!this.verifier) - this.verifier = new StructureVerifier(this, { shouldRender: true }); + this.verifier = new StructureVerifier(this, { shouldRender: this.#options.verifier.shouldRender, trackPlayerDistance: this.#options.verifier.trackPlayerDistance }); this.outliner.refresh(); this.verifier.refresh(); } @@ -216,19 +226,19 @@ export class StructureInstance { } toGlobalCoords(structureLocation) { - return { - x: this.#options.worldLocation.x + structureLocation.x, - y: this.#options.worldLocation.y + structureLocation.y, - z: this.#options.worldLocation.z + structureLocation.z - }; + return new Vector( + this.#options.worldLocation.x + structureLocation.x, + this.#options.worldLocation.y + structureLocation.y, + this.#options.worldLocation.z + structureLocation.z + ); } toStructureCoords(worldLocation) { - return { - x: worldLocation.x - this.#options.worldLocation.x, - y: worldLocation.y - this.#options.worldLocation.y, - z: worldLocation.z - this.#options.worldLocation.z - }; + return new Vector( + worldLocation.x - this.#options.worldLocation.x, + worldLocation.y - this.#options.worldLocation.y, + worldLocation.z - this.#options.worldLocation.z + ); } isEnabled() { @@ -272,4 +282,13 @@ export class StructureInstance { getDimension() { return world.getDimension(this.#options.dimensionId); } + + setVerifierOptions({ shouldRender, trackPlayerDistance, intervalOrLifetime }) { + this.#options.verifier.shouldRender = shouldRender; + this.#options.verifier.trackPlayerDistance = trackPlayerDistance; + this.#options.verifier.intervalOrLifetime = intervalOrLifetime; + this.updateOptions(); + this.verifier.setOptions(this.#options.verifier); + this.verifier.refresh(); + } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureVerifier.js b/StrucTool [BP]/scripts/classes/StructureVerifier.js index 1d9d712..8f4c2bb 100644 --- a/StrucTool [BP]/scripts/classes/StructureVerifier.js +++ b/StrucTool [BP]/scripts/classes/StructureVerifier.js @@ -1,24 +1,32 @@ import { BlockVerifier } from "./BlockVerifier"; import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; -import { system, TicksPerSecond } from "@minecraft/server"; +import { system, TicksPerSecond, world } from "@minecraft/server"; + +const MIN_TRACK_PLAYER_DISTANCE = 0; +const MAX_TRACK_PLAYER_DISTANCE = 7; +const MIN_LIFETIME = 8; export class StructureVerifier { + instance; + blockVerificationLevels; shouldRender; - isComplete; - interval = 5*20; + trackPlayerDistance; + intervalOrLifetime; + isBlockPopulationComplete; + isVerificationComplete; #runner; - constructor(instance, { shouldRender = false } = {}) { - this.shouldRender = shouldRender; + constructor(instance, { shouldRender = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { this.instance = instance; - this.interval = Math.max(instance.getActiveVolume() / 50, 20); + this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); + this.setOptions({ shouldRender, trackPlayerDistance }); } startContinuousVerification() { this.#runner = system.runInterval(() => { this.verifyStructure(); - }, this.interval); + }, this.intervalOrLifetime); } stopContinuousVerification() { @@ -35,27 +43,66 @@ export class StructureVerifier { this.startContinuousVerification(); } - init(shouldRender) { + init() { + this.locationsToVerify = new Set(); + this.blocksToVerify = []; + this.blockVerificationLevels = { correctlyAir: 0 }; + this.isBlockPopulationComplete = false; + this.isVerificationComplete = false; + } + + setOptions({ shouldRender = false, trackPlayerDistance = 0 }) { this.blockVerificationLevels = { correctlyAir: 0 }; this.shouldRender = shouldRender; - this.isComplete = false; + this.trackPlayerDistance = Math.min(trackPlayerDistance, MAX_TRACK_PLAYER_DISTANCE); + this.isVerificationComplete = false; } async verifyStructure() { - this.init(this.shouldRender); - return new Promise((resolve) => { - if (this.instance.isUsingLayers()) - system.runJob(this.verifyBlocks(this.instance.getLayerBlocks(this.instance.getLayer()-1))); - else - system.runJob(this.verifyBlocks(this.instance.getBlocks())); + this.init(); + return new Promise(async (resolve) => { + await this.populateBlocksToVerify(); + system.runJob(this.verifyBlocks(this.blocksToVerify)); const checker = system.runInterval(() => { - if (this.isComplete) { + if (this.isVerificationComplete) { system.clearRun(checker); resolve(this.blockVerificationLevels); } }, 1); }); } + + populateBlocksToVerify() { + return new Promise((resolve) => { + if (this.trackPlayerDistance == 0) + return this.instance.getBlocks(); + this.locationsToVerify = new Set(); + for (const player of this.instance.getDimension().getPlayers()) + system.runJob(this.populateActiveLocationsNearPlayer(player)); + this.blocksToVerify = this.instance.getBlocks(this.locationsToVerify); + const checker = system.runInterval(() => { + if (this.isBlockPopulationComplete) { + system.clearRun(checker); + resolve(); + } + }, 1); + }); + } + + *populateActiveLocationsNearPlayer(player) { + for (let x = -this.trackPlayerDistance; x < this.trackPlayerDistance; x++) { + for (let y = -this.trackPlayerDistance; y < this.trackPlayerDistance; y++) { + for (let z = -this.trackPlayerDistance; z < this.trackPlayerDistance; z++) { + const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z }); + if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useLayers: true })) { + this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); + yield void 0; + } + } + } + } + this.isBlockPopulationComplete = true; + } *verifyBlocks(blocks) { for (const block of blocks) { @@ -66,19 +113,18 @@ export class StructureVerifier { this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; if (this.shouldRender) { const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) }; - new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.interval/TicksPerSecond); + new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond); } } yield void 0; } - this.isComplete = true; + this.isVerificationComplete = true; } verifyBlock(location) { const worldBlock = this.instance.getDimension().getBlock(this.instance.toGlobalCoords(location)); - if (!worldBlock) { + if (!worldBlock) return BlockVerificationLevel.Skipped; - } const blockVerifier = new BlockVerifier(worldBlock, this.instance); return blockVerifier.verify(); } diff --git a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js b/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js index d74468d..7071640 100644 --- a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js +++ b/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js @@ -1,14 +1,15 @@ export const InstanceEditOptions = Object.freeze({ Unknown: 'Unknown', MainMenu: '<<', - PlaceInstance: '§aPlace Instance', - EnableInstance: '§aEnable Instance', - DisableInstance: '§cDisable Instance', - RenameInstance: 'Rename Instance', - DeleteInstance: '§cDelete Instance', + Place: '§aPlace Instance', + Enable: '§aEnable Instance', + Disable: '§cDisable Instance', + Rename: 'Rename Instance', + Delete: '§cDelete Instance', NextLayer: 'Increase Layer', PreviousLayer: 'Decrease Layer', SetLayer: 'Set Layer', Move: 'Move Here', Statistics: 'Statistics', + Settings: 'Settings' }); \ No newline at end of file From 80aed5f3498382f22d677d6d81034ae3796adf93 Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Fri, 18 Apr 2025 01:22:50 -0700 Subject: [PATCH 2/3] Refactoring StructureInstance --- StrucTool [BP]/scripts/classes/BlockInfo.js | 2 +- .../classes/BlockVerificationLevelRender.js | 10 +- .../scripts/classes/InstanceEditForm.js | 68 ++-- .../classes/InstanceEditFormBuilder.js | 6 +- .../scripts/classes/InstanceOptions.js | 85 ++++ .../scripts/classes/MaterialCounter.js | 4 +- StrucTool [BP]/scripts/classes/MenuForm.js | 2 +- StrucTool [BP]/scripts/classes/Outliner.js | 10 +- StrucTool [BP]/scripts/classes/Raycaster.js | 8 +- StrucTool [BP]/scripts/classes/Structure.js | 56 +++ .../scripts/classes/StructureInstance.js | 366 +++++++----------- .../scripts/classes/StructureOutliner.js | 2 +- .../scripts/classes/StructureStatistics.js | 5 +- .../scripts/classes/StructureVerifier.js | 54 +-- ...eEditOptions.js => InstanceEditButtons.js} | 2 +- StrucTool [BP]/scripts/lib/Vector.js | 5 +- 16 files changed, 372 insertions(+), 313 deletions(-) create mode 100644 StrucTool [BP]/scripts/classes/InstanceOptions.js create mode 100644 StrucTool [BP]/scripts/classes/Structure.js rename StrucTool [BP]/scripts/classes/enums/{InstanceEditOptions.js => InstanceEditButtons.js} (88%) diff --git a/StrucTool [BP]/scripts/classes/BlockInfo.js b/StrucTool [BP]/scripts/classes/BlockInfo.js index 844c391..d2bbedc 100644 --- a/StrucTool [BP]/scripts/classes/BlockInfo.js +++ b/StrucTool [BP]/scripts/classes/BlockInfo.js @@ -14,7 +14,7 @@ class BlockInfo { } static showStructureBlockInfo(player) { - const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useLayers: false }); + const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: false }); if (!block && this.shownToLastTick.has(player.id)) { player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' }); this.shownToLastTick.delete(player.id); diff --git a/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js b/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js index 33ed279..34ebde3 100644 --- a/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js +++ b/StrucTool [BP]/scripts/classes/BlockVerificationLevelRender.js @@ -4,13 +4,13 @@ import { Vector } from "../lib/Vector"; export class BlockVerificationLevelRender { opacity = 0.2; - lifetime = 0; + lifetimeSeconds = 0; - constructor(dimensionLocation, verificationLevel, lifetime = 5) { + constructor(dimensionLocation, verificationLevel, lifetimeSeconds = 5) { this.dimension = dimensionLocation.dimension; - this.location = new Vector(dimensionLocation.location.x, dimensionLocation.location.y, dimensionLocation.location.z); + this.location = Vector.from(dimensionLocation.location); this.verificationLevel = verificationLevel; - this.lifetime = lifetime; + this.lifetimeSeconds = lifetimeSeconds; this.renderBlock(); } @@ -19,7 +19,7 @@ export class BlockVerificationLevelRender { const color = this.getRGBAMolang(); if (!color) return; - color.setFloat("lifetime", this.lifetime); + color.setFloat("lifetime", this.lifetimeSeconds); try { this.dimension.spawnParticle(particleLocation.particleType, particleLocation.location, color); } catch { diff --git a/StrucTool [BP]/scripts/classes/InstanceEditForm.js b/StrucTool [BP]/scripts/classes/InstanceEditForm.js index e183aa4..46db387 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditForm.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditForm.js @@ -1,7 +1,7 @@ import { structureCollection } from './StructureCollection'; import { MenuForm } from '../classes/MenuForm'; import { forceShow } from '../utils'; -import { InstanceEditOptions } from './enums/InstanceEditOptions'; +import { InstanceEditButtons } from './enums/InstanceEditButtons'; import { InstanceEditFormBuilder } from './InstanceEditFormBuilder'; import { FormCancelationReason } from '@minecraft/server-ui'; @@ -9,26 +9,26 @@ export class InstanceEditForm { instanceName; #buttons = { isEnabled: [ - InstanceEditOptions.NextLayer, - InstanceEditOptions.PreviousLayer, - InstanceEditOptions.SetLayer, - InstanceEditOptions.Move, - InstanceEditOptions.Statistics, - InstanceEditOptions.Settings, - InstanceEditOptions.Rename, - InstanceEditOptions.Disable, + InstanceEditButtons.NextLayer, + InstanceEditButtons.PreviousLayer, + InstanceEditButtons.SetLayer, + InstanceEditButtons.Move, + InstanceEditButtons.Statistics, + InstanceEditButtons.Settings, + InstanceEditButtons.Rename, + InstanceEditButtons.Disable, ], isNotEnabledAndIsNotPlaced: [ - InstanceEditOptions.Place, - InstanceEditOptions.Rename + InstanceEditButtons.Place, + InstanceEditButtons.Rename ], isNotEnabledButIsPlaced: [ - InstanceEditOptions.Enable, - InstanceEditOptions.Rename + InstanceEditButtons.Enable, + InstanceEditButtons.Rename ], common: [ - InstanceEditOptions.Delete, - InstanceEditOptions.MainMenu + InstanceEditButtons.Delete, + InstanceEditButtons.MainMenu ] } @@ -59,51 +59,51 @@ export class InstanceEditForm { if (!this.instance.hasLayers()) currentOptions = currentOptions.filter(option => - option !== InstanceEditOptions.SetLayer - && option !== InstanceEditOptions.NextLayer - && option !== InstanceEditOptions.PreviousLayer + option !== InstanceEditButtons.SetLayer + && option !== InstanceEditButtons.NextLayer + && option !== InstanceEditButtons.PreviousLayer ); return currentOptions; } handleOption(option) { switch (option) { - case InstanceEditOptions.Enable: + case InstanceEditButtons.Enable: this.instance.enable(); break; - case InstanceEditOptions.Disable: + case InstanceEditButtons.Disable: this.instance.disable(); break; - case InstanceEditOptions.Place: + case InstanceEditButtons.Place: this.instance.place(this.player.dimension.id, this.player.location); break; - case InstanceEditOptions.Rename: + case InstanceEditButtons.Rename: this.renameInstanceForm(); break; - case InstanceEditOptions.Delete: + case InstanceEditButtons.Delete: structureCollection.delete(this.instanceName); break; - case InstanceEditOptions.NextLayer: + case InstanceEditButtons.NextLayer: this.instance.increaseLayer(); new InstanceEditForm(this.player, this.instanceName); break; - case InstanceEditOptions.PreviousLayer: + case InstanceEditButtons.PreviousLayer: this.instance.decreaseLayer(); new InstanceEditForm(this.player, this.instanceName); break; - case InstanceEditOptions.SetLayer: + case InstanceEditButtons.SetLayer: this.setLayerForm(); break; - case InstanceEditOptions.Move: + case InstanceEditButtons.Move: this.instance.move(this.player.dimension.id, this.player.location); break; - case InstanceEditOptions.Statistics: + case InstanceEditButtons.Statistics: this.statisticsForm(); break; - case InstanceEditOptions.MainMenu: + case InstanceEditButtons.MainMenu: new MenuForm(this.player, { jumpToInstance: false }); break; - case InstanceEditOptions.Settings: + case InstanceEditButtons.Settings: this.settingsForm(); break; default: @@ -135,8 +135,7 @@ export class InstanceEditForm { InstanceEditFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => { if (response.canceled) return; - const selectedLayer = response.formValues[0]; - this.instance.setLayer(parseInt(selectedLayer)); + this.instance.setLayer(parseInt(response.formValues[0])); }); } @@ -152,9 +151,8 @@ export class InstanceEditForm { InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => { if (response.canceled) return; - const shouldRender = response.formValues[0]; - const trackPlayerDistance = response.formValues[1]; - this.instance.setVerifierOptions({ shouldRender, trackPlayerDistance }); + this.instance.setVerifierEnabled(response.formValues[0]); + this.instance.setVerifierDistance(response.formValues[1]); }); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js index 3e316ab..e1f0148 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js @@ -9,7 +9,7 @@ export class InstanceEditFormBuilder { const location = instance.getLocation(); const form = new ActionFormData() .title(MenuFormBuilder.menuTitle) - let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.getStructureId()}\n`; + let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.options.getStructureId()}\n`; if (instance.hasLocation()) body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`; form.body(body); @@ -37,7 +37,7 @@ export class InstanceEditFormBuilder { static async buildStatistics(instance) { const buildStatisticsForm = new ActionFormData() .title(MenuFormBuilder.menuTitle) - const structureVerifier = new StructureVerifier(instance, { shouldRender: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); + const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); const verification = await structureVerifier.verifyStructure(); const statistics = new StructureStatistics(instance, verification); const statsMessage = statistics.getMessage(); @@ -48,7 +48,7 @@ export class InstanceEditFormBuilder { static buildSettings(instance) { return new ModalFormData() .title(MenuFormBuilder.menuTitle) - .toggle('Toggle block validation.', instance.verifier.shouldRender) + .toggle('Toggle block validation.', instance.options.verifier.isEnabled) .slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance) .submitButton('§aApply'); } diff --git a/StrucTool [BP]/scripts/classes/InstanceOptions.js b/StrucTool [BP]/scripts/classes/InstanceOptions.js new file mode 100644 index 0000000..b3ce70b --- /dev/null +++ b/StrucTool [BP]/scripts/classes/InstanceOptions.js @@ -0,0 +1,85 @@ +import { Vector } from "../lib/Vector"; + +export class InstanceOptions { + instanceName = void 0; + structureId = void 0; + isEnabled = false; + dimensionId = void 0; + worldLocation = new Vector(); + currentLayer = 0; + verifier = { + isEnabled: true, + trackPlayerDistance: 5, + intervalOrLifetime: 10 + }; + + constructor(instanceName, structureId) { + this.instanceName = instanceName; + this.structureId = structureId; + this.load(); + } + + save() { + world.setDynamicProperty(`instanceOptions:${this.instanceName}`, JSON.stringify(this)); + } + + load() { + try { + const options = JSON.parse(world.getDynamicProperty(`instanceOptions:${this.instanceName}`)); + if (options) + Object.assign(this, options); + else + throw new Error("Options not found"); + } catch { + this.save(); + const options = JSON.parse(world.getDynamicProperty(`instanceOptions:${this.instanceName}`) || "{}"); + Object.assign(this, options); + } + this.worldLocation = Vector.from(this.worldLocation); + } + + clear() { + world.setDynamicProperty(`instanceOptions:${this.instanceName}`, void 0); + } + + getDimension() { + return world.getDimension(this.options.dimensionId); + } + + enable() { + this.isEnabled = true; + this.save(); + } + + disable() { + this.isEnabled = false; + this.save(); + } + + rename(newName) { + this.clear(); + this.instanceName = newName; + this.save(); + } + + move(dimensionId, worldLocation) { + this.dimensionId = dimensionId; + this.worldLocation = Vector.from(worldLocation).floor(); + this.save(); + } + + setLayer(layer) { + this.currentLayer = layer; + this.save(); + } + + setVerifierEnabled(enable) { + this.verifier.isEnabled = enable; + this.save(); + } + + setVerifierDistance(distance) { + this.verifier.trackPlayerDistance = distance; + this.save(); + } +} \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/MaterialCounter.js b/StrucTool [BP]/scripts/classes/MaterialCounter.js index 3c7974c..a880a13 100644 --- a/StrucTool [BP]/scripts/classes/MaterialCounter.js +++ b/StrucTool [BP]/scripts/classes/MaterialCounter.js @@ -4,7 +4,7 @@ class MaterialCounter { static getAll(name) { const structure = structureCollection.get(name); const materials = {}; - for (const block of structure.getBlocks()) { + for (const block of structure.getAllBlocks()) { const typeId = block?.getItemStack()?.typeId.replace('minecraft:', ''); if (!typeId) continue; if (!materials[typeId]) { @@ -32,7 +32,7 @@ class MaterialCounter { static getPrintable(name) { const structure = structureCollection.get(name); const materials = {}; - for (const block of structure.getBlocks()) { + for (const block of structure.getAllBlocks()) { const itemStack = block?.getItemStack(); const typeId = itemStack?.typeId.replace('minecraft:', ''); if (!typeId) continue; diff --git a/StrucTool [BP]/scripts/classes/MenuForm.js b/StrucTool [BP]/scripts/classes/MenuForm.js index 13cc1be..17e1ec5 100644 --- a/StrucTool [BP]/scripts/classes/MenuForm.js +++ b/StrucTool [BP]/scripts/classes/MenuForm.js @@ -12,7 +12,7 @@ export class MenuForm { async show(jumpToInstance = true) { let instanceName; if (jumpToInstance) { - instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useLayers: false })?.name; + instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.name; if (instanceName) { new InstanceEditForm(this.player, instanceName); return; diff --git a/StrucTool [BP]/scripts/classes/Outliner.js b/StrucTool [BP]/scripts/classes/Outliner.js index 817604a..6343e58 100644 --- a/StrucTool [BP]/scripts/classes/Outliner.js +++ b/StrucTool [BP]/scripts/classes/Outliner.js @@ -13,8 +13,8 @@ export class Outliner { constructor(dimension, min, max) { this.dimension = dimension; - this.min = new Vector(min.x, min.y, min.z); - this.max = new Vector(max.x, max.y, max.z); + this.min = Vector.from(min); + this.max = Vector.from(max); this.vertices = this.getVertices(min, max); } @@ -65,8 +65,8 @@ export class Outliner { setVertices(dimension, min, max) { this.dimension = dimension; - this.min = new Vector(min.x, min.y, min.z); - this.max = new Vector(max.x, max.y, max.z); + this.min = Vector.from(min); + this.max = Vector.from(max); this.vertices = this.getVertices(min, max); } @@ -103,7 +103,7 @@ export class Outliner { addStandaloneParticles(locations) { for (const location of locations) - this.vertices.push(new Vector(location.x, location.y, location.z)); + this.vertices.push(Vector.from(location)); } getNextParticleColor() { diff --git a/StrucTool [BP]/scripts/classes/Raycaster.js b/StrucTool [BP]/scripts/classes/Raycaster.js index 030b58c..0e36355 100644 --- a/StrucTool [BP]/scripts/classes/Raycaster.js +++ b/StrucTool [BP]/scripts/classes/Raycaster.js @@ -4,13 +4,13 @@ import { world } from "@minecraft/server"; export class Raycaster { static STEP_SIZE = 0.2; - static getStructureBlocks(dimension, startLocation, direction, { maxDistance = 7, getFirst = true, collideWithWorldBlocks = true, useLayers = true }) { + static getStructureBlocks(dimension, startLocation, direction, { maxDistance = 7, getFirst = true, collideWithWorldBlocks = true, useActiveLayer = true }) { // Can probably be optimized by the fact that we only need full blocks and aren't checking for partial blocks const blocks = []; let location = startLocation; let distance = 0; while (distance < maxDistance) { - const structure = structureCollection.getStructure(dimension.id, location, { useLayers }); + const structure = structureCollection.getStructure(dimension.id, location, { useActiveLayer }); if (structure) { const block = structure.getBlock(structure.toStructureCoords(location)); if (block?.type.id !== 'minecraft:air') { @@ -41,11 +41,11 @@ export class Raycaster { return blocks; } - static getTargetedStructureBlock(player, { isFirst = true, collideWithWorldBlocks = true, useLayers = true } = {}) { + static getTargetedStructureBlock(player, { isFirst = true, collideWithWorldBlocks = true, useActiveLayer = true } = {}) { const startLocation = player.getHeadLocation(); const direction = player.getViewDirection(); const maxDistance = 7; - const blocks = this.getStructureBlocks(player.dimension, startLocation, direction, { maxDistance, getFirst: isFirst, collideWithWorldBlocks, useLayers }); + const blocks = this.getStructureBlocks(player.dimension, startLocation, direction, { maxDistance, getFirst: isFirst, collideWithWorldBlocks, useActiveLayer }); if (blocks.length === 0) return void 0; return isFirst ? blocks[0] : blocks[blocks.length - 1]; diff --git a/StrucTool [BP]/scripts/classes/Structure.js b/StrucTool [BP]/scripts/classes/Structure.js new file mode 100644 index 0000000..e41d41e --- /dev/null +++ b/StrucTool [BP]/scripts/classes/Structure.js @@ -0,0 +1,56 @@ +import { world } from "@minecraft/server"; +import { Vector } from "../lib/Vector"; + +export class Structure { + structureId; + #structure; + + constructor(structureId) { + this.structureId = structureId; + this.#structure = world.structureManager.get(structureId); + if (!this.#structure) + throw new Error(`[StrucTool] Structure '${structureId}' not found.`); + this.#structure.saveToWorld(); + } + + getHeight() { + return this.#structure.size.y; + } + + getMin() { + return new Vector(0, 0, 0); + } + + getMax() { + return Vector.from(this.#structure.size); + } + + getBlock(structureLocation) { + const blockPermutation = this.#structure.getBlockPermutation(structureLocation); + if (!blockPermutation) + return void 0; + blockPermutation.location = structureLocation; + return blockPermutation; + } + + *getBlocks(locations) { + for (const location of locations) { + yield this.getBlock(location); + } + } + + *getLayerBlocks(y) { + for (let x = 0; x < this.#structure.size.x; x++) { + for (let z = 0; z < this.#structure.size.z; z++) { + yield this.getBlock({ x, y, z }); + } + } + } + + *getAllBlocks() { + for (let y = 0; y < this.#structure.size.y; y++) { + yield * this.getLayerBlocks(y); + } + } +} + \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureInstance.js b/StrucTool [BP]/scripts/classes/StructureInstance.js index 0fd71a0..9a5dbe0 100644 --- a/StrucTool [BP]/scripts/classes/StructureInstance.js +++ b/StrucTool [BP]/scripts/classes/StructureInstance.js @@ -1,191 +1,28 @@ -import { world } from "@minecraft/server"; import { Vector } from "../lib/Vector"; import { StructureOutliner } from "./StructureOutliner"; import { StructureVerifier } from "./StructureVerifier"; +import { InstanceOptions } from "./InstanceOptions"; +import { Structure } from "./Structure"; export class StructureInstance { - name; - #structure; - #options = { - structureId: void 0, - isEnabled: false, - dimensionId: void 0, - worldLocation: { x: 0, y: 0, z: 0 }, - rotation: 0, - mirror: false, - currentLayer: 0, - verifier: { - shouldRender: true, - trackPlayerDistance: 5, - intervalOrLifetime: 10 - } - }; - outliner = void 0; + options; + structure = void 0; verifier = void 0; + outliner = void 0; constructor(instanceName, structureId) { - this.name = instanceName; - this.#structure = world.structureManager.get(structureId); - if (!this.#structure) - throw new Error(`[StrucTool] Structure '${structureId}' not found.`); - this.#structure.saveToWorld(); - this.#options = this.loadOptions(); - this.#options.structureId = structureId; - this.updateOptions(); + this.structure = new Structure(structureId); + this.options = new InstanceOptions(instanceName, structureId); this.refreshBox(); } - loadOptions() { - try { - return JSON.parse(world.getDynamicProperty(`structOptions:${this.name}`)); - } catch (e) { - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - return this.#options; - } - delete() { this.disable(); - world.setDynamicProperty(`structOptions:${this.name}`, void 0); - this.#structure = void 0; - this.#options = void 0; + this.options.clear(); + delete this.options; + delete this.structure; delete this.outliner; - } - - static parseOptions(instanceName) { - const options = JSON.parse(world.getDynamicProperty(`structOptions:${instanceName}`)); - if (!options) - throw new Error(`[StrucTool] Instance '${instanceName}' not found.`); - return options; - } - - updateOptions() { - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - - getStructure() { - return this.#structure; - } - - getStructureId() { - return this.#options.structureId; - } - - getLocation() { - return { dimensionId: this.#options.dimensionId, location: this.#options.worldLocation }; - } - - getHeight() { - return this.#structure.size.y; - } - - getLayer() { - return this.#options.currentLayer || 0; - } - - getDimension() { - let dimension; - try { - dimension = world.getDimension(this.#options.dimensionId); - } catch (e) { - console.warn(`[StrucTool] Dimension '${this.#options.dimensionId}' not found. Defaulting to 'minecraft:overworld'.`); - dimension = world.getDimension("minecraft:overworld"); - } - return dimension; - } - - getBlock(structureLocation) { - const blockPermutation = this.#structure.getBlockPermutation({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); - if (!blockPermutation) - return void 0; - blockPermutation.location = structureLocation; - return blockPermutation; - } - - *getBlocks(locations = void 0) { - if (locations === void 0) { - for (let y = 0; y < this.#structure.size.y; y++) { - yield * this.getLayerBlocks(y); - } - } else { - for (const location of locations) { - yield this.getBlock(location); - } - } - } - - *getLayerBlocks(y) { - for (let x = 0; x < this.#structure.size.x; x++) { - for (let z = 0; z < this.#structure.size.z; z++) { - yield this.getBlock({ x, y, z }); - } - } - } - - getBounds() { - return { - min: new Vector(0, 0, 0), - max: new Vector(this.#structure.size.x, this.#structure.size.y, this.#structure.size.z) - }; - } - - getLayeredBounds() { - if (!this.#options.isEnabled) - throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`); - return { - min: new Vector(0, this.#options.currentLayer - 1, 0), - max: new Vector(this.#structure.size.x, this.#options.currentLayer, this.#structure.size.z) - }; - } - - getTotalVolume() { - return this.#structure.size.x * this.#structure.size.y * this.#structure.size.z; - } - - getActiveVolume() { - if (!this.#options.isEnabled) - return 0; - if (this.#options.currentLayer === 0) - return this.getTotalVolume(); - return this.#structure.size.x * this.#structure.size.z; - } - - rename(newName) { - world.setDynamicProperty(`structOptions:${this.name}`, void 0); - this.name = newName; - world.setDynamicProperty(`structOptions:${this.name}`, JSON.stringify(this.#options)); - } - - place(dimensionId, worldLocation) { - this.move(dimensionId, worldLocation); - this.enable(); - } - - enable() { - this.#options.isEnabled = true; - this.updateOptions(); - this.refreshBox(); - } - - disable() { - this.#options.isEnabled = false; - this.updateOptions(); - this.refreshBox(); - } - - move(dimensionId, location) { - this.#options.dimensionId = dimensionId; - this.#options.worldLocation = { x: Math.floor(location.x), y: Math.floor(location.y), z: Math.floor(location.z) }; - this.updateOptions(); - this.refreshBox(); - } - - setLayer(layer) { - if (layer < 0 || layer > this.#structure.size.y) - throw new Error(`[StrucTool] Layer ${layer} is out of bounds.`); - this.#options.currentLayer = layer; - this.updateOptions(); - this.refreshBox(); + delete this.verifier; } refreshBox() { @@ -194,101 +31,174 @@ export class StructureInstance { if (!this.outliner) this.outliner = new StructureOutliner(this); if (!this.verifier) - this.verifier = new StructureVerifier(this, { shouldRender: this.#options.verifier.shouldRender, trackPlayerDistance: this.#options.verifier.trackPlayerDistance }); + this.verifier = new StructureVerifier(this, { isEnabled: this.options.verifier.isEnabled, trackPlayerDistance: this.options.verifier.trackPlayerDistance }); this.outliner.refresh(); this.verifier.refresh(); } - isLocationInStructure(dimensionId, structureLocation) { - if (this.#options.dimensionId !== dimensionId) - return false - const { min, max } = this.getBounds(); - return structureLocation.x >= min.x && structureLocation.x < max.x - && structureLocation.y >= min.y && structureLocation.y < max.y - && structureLocation.z >= min.z && structureLocation.z < max.z; + getStructureId() { + return this.options.structureId; } - isLocationInLayer(dimensionId, structureLocation) { - if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId) - return false - const { min, max } = this.getLayeredBounds(true); - return structureLocation.x >= min.x && structureLocation.x < max.x - && structureLocation.y >= min.y && structureLocation.y < max.y - && structureLocation.z >= min.z && structureLocation.z < max.z; + getLocation() { + return { dimensionId: this.options.dimensionId, location: this.options.worldLocation }; } - isLocationActive(dimensionId, structureLocation, { useLayers = true } = {}) { - if (!this.#options.isEnabled || this.#options.dimensionId !== dimensionId) - return false - if (useLayers && this.#options.currentLayer !== 0) - return this.isLocationInLayer(dimensionId, structureLocation); - return this.isLocationInStructure(dimensionId, structureLocation); + getDimension() { + return this.options.getDimension(); } - toGlobalCoords(structureLocation) { - return new Vector( - this.#options.worldLocation.x + structureLocation.x, - this.#options.worldLocation.y + structureLocation.y, - this.#options.worldLocation.z + structureLocation.z - ); + getMaxLayer() { + return this.structure.getHeight(); } - toStructureCoords(worldLocation) { - return new Vector( - worldLocation.x - this.#options.worldLocation.x, - worldLocation.y - this.#options.worldLocation.y, - worldLocation.z - this.#options.worldLocation.z - ); + getLayer() { + return this.options.currentLayer; + } + + getBounds() { + return { + min: this.structure.getMin(), + max: this.structure.getMax() + } + } + + getActiveBounds() { + if (!this.options.isEnabled) + throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + if (this.hasLayerSelected()) + return this.getLayeredBounds(); + return this.getBounds(); + } + + getLayeredBounds() { + if (!this.options.isEnabled) + throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + const min = this.structure.getMin(); + const max = this.structure.getMax(); + return { + min: new Vector(min.x, this.options.currentLayer - 1, min.z), + max: new Vector(max.x, this.options.currentLayer, max.z) + }; + } + + getBlock(structureLocation) { + return this.structure.getBlock(structureLocation); + } + + getBlocks(structureLocations) { + return this.structure.getBlocks(structureLocations); + } + + getLayerBlocks(layer) { + return this.structure.getLayerBlocks(layer); + } + + getAllBlocks() { + return this.structure.getAllBlocks(); + } + + isLocationActive(dimensionId, structureLocation, { useActiveLayer = true } = {}) { + if (!this.options.isEnabled || this.options.dimensionId !== dimensionId) + return false; + let bounds; + if (useActiveLayer) + bounds = this.getActiveBounds(); + else + bounds = this.getBounds(); + return structureLocation.x >= bounds.min.x && structureLocation.x < bounds.max.x + && structureLocation.y >= bounds.min.y && structureLocation.y < bounds.max.y + && structureLocation.z >= bounds.min.z && structureLocation.z < bounds.max.z; } isEnabled() { - return this.#options.isEnabled; + return this.options.isEnabled; } hasLocation() { - return this.#options.dimensionId && this.#options.worldLocation.x !== 0 && this.#options.worldLocation.y !== 0 && this.#options.worldLocation.z !== 0; - } - - isUsingLayers() { - return this.hasLayers() && this.#options.currentLayer !== 0; + return this.options.dimensionId && this.options.worldLocation.x !== 0 && this.options.worldLocation.y !== 0 && this.options.worldLocation.z !== 0; } hasLayers() { - return this.#structure.size.y > 1; + return this.getMaxLayer() > 1; + } + + hasLayerSelected() { + return this.hasLayers() && this.options.currentLayer !== 0; + } + + hasWholeStructureSelected() { + return this.hasLocation() && this.options.currentLayer === 0; } isAtMaxLayer() { - return !this.hasLayers || this.#options.currentLayer >= this.#structure.size.y; + return !this.hasLayers || this.options.currentLayer >= this.getMaxLayer(); } isAtMinLayer() { - return !this.hasLayers || this.#options.currentLayer <= 0; + return !this.hasLayers || this.options.currentLayer <= 0; + } + + enable() { + this.options.enable(); + this.refreshBox(); + } + + disable() { + this.options.disable(); + this.refreshBox(); + } + + rename(newName) { + this.options.rename(newName); + } + + place(dimensionId, worldLocation) { + this.move(dimensionId, worldLocation); + this.enable(); + } + + move(dimensionId, worldLocation) { + this.options.move(dimensionId, worldLocation); + this.refreshBox(); + } + + setLayer(layer) { + if (layer < 0 || layer > this.getMaxLayer()) + throw new Error(`[StrucTool] Layer ${layer} is out of bounds.`); + this.options.setLayer(layer); + this.refreshBox(); + } + + setVerifierEnabled(enable) { + this.options.setVerifierEnabled(enable); + this.verifier.refresh(); + } + + setVerifierDistance(distance) { + this.options.setVerifierDistance(distance); + this.verifier.refresh(); } increaseLayer() { if (this.isAtMaxLayer()) this.setLayer(0); else - this.setLayer(this.#options.currentLayer + 1); + this.setLayer(this.options.currentLayer + 1); } decreaseLayer() { if (this.isAtMinLayer()) - this.setLayer(this.#structure.size.y); + this.setLayer(this.getMaxLayer()); else - this.setLayer(this.#options.currentLayer - 1); + this.setLayer(this.options.currentLayer - 1); } - getDimension() { - return world.getDimension(this.#options.dimensionId); + toGlobalCoords(structureLocation) { + return Vector.from(structureLocation).add(this.options.worldLocation); } - setVerifierOptions({ shouldRender, trackPlayerDistance, intervalOrLifetime }) { - this.#options.verifier.shouldRender = shouldRender; - this.#options.verifier.trackPlayerDistance = trackPlayerDistance; - this.#options.verifier.intervalOrLifetime = intervalOrLifetime; - this.updateOptions(); - this.verifier.setOptions(this.#options.verifier); - this.verifier.refresh(); + toStructureCoords(worldLocation) { + return Vector.from(worldLocation).subtract(this.options.worldLocation); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureOutliner.js b/StrucTool [BP]/scripts/classes/StructureOutliner.js index a11cbc6..65648fa 100644 --- a/StrucTool [BP]/scripts/classes/StructureOutliner.js +++ b/StrucTool [BP]/scripts/classes/StructureOutliner.js @@ -30,7 +30,7 @@ export class StructureOutliner { this.outliner.stopDraw(); if (!this.instance.isEnabled()) return; - if (this.instance.isUsingLayers()) + if (this.instance.hasLayerSelected()) this.layeredDraw(); else this.boxDraw(); diff --git a/StrucTool [BP]/scripts/classes/StructureStatistics.js b/StrucTool [BP]/scripts/classes/StructureStatistics.js index d1612d4..3962cfb 100644 --- a/StrucTool [BP]/scripts/classes/StructureStatistics.js +++ b/StrucTool [BP]/scripts/classes/StructureStatistics.js @@ -31,7 +31,8 @@ export class StructureStatistics { } getNonAirBlocks() { - return this.instance.getActiveVolume() - this.verification.correctlyAir; + const activeBounds = this.instance.getActiveBounds(); + return activeBounds.min.volume(activeBounds.max) - this.verification.correctlyAir; } getStat(blockVerificationLevel) { @@ -45,7 +46,7 @@ export class StructureStatistics { getMessage() { let message = ''; message += `§fStatistics for §a${this.instance.name}§f:`; - if (this.instance.isUsingLayers()) + if (this.instance.hasLayerSelected()) message += ` §7(layer ${this.instance.getLayer()})`; message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`; const skipped = this.getSkipped(); diff --git a/StrucTool [BP]/scripts/classes/StructureVerifier.js b/StrucTool [BP]/scripts/classes/StructureVerifier.js index 8f4c2bb..3c02386 100644 --- a/StrucTool [BP]/scripts/classes/StructureVerifier.js +++ b/StrucTool [BP]/scripts/classes/StructureVerifier.js @@ -1,7 +1,7 @@ import { BlockVerifier } from "./BlockVerifier"; import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; -import { system, TicksPerSecond, world } from "@minecraft/server"; +import { system, TicksPerSecond } from "@minecraft/server"; const MIN_TRACK_PLAYER_DISTANCE = 0; const MAX_TRACK_PLAYER_DISTANCE = 7; @@ -9,18 +9,20 @@ const MIN_LIFETIME = 8; export class StructureVerifier { instance; - blockVerificationLevels; - shouldRender; - trackPlayerDistance; intervalOrLifetime; + + locationsToVerify; + blocksToVerify; + blockVerificationLevels; isBlockPopulationComplete; isVerificationComplete; #runner; - constructor(instance, { shouldRender = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { + constructor(instance, { isEnabled = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { this.instance = instance; this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); - this.setOptions({ shouldRender, trackPlayerDistance }); + this.instance.options.setVerifierEnabled(isEnabled); + this.instance.options.setVerifierDistance(trackPlayerDistance); } startContinuousVerification() { @@ -43,6 +45,14 @@ export class StructureVerifier { this.startContinuousVerification(); } + isEnabled() { + return this.instance.options.verifier.isEnabled; + } + + getTrackPlayerDistance() { + return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.trackPlayerDistance)); + } + init() { this.locationsToVerify = new Set(); this.blocksToVerify = []; @@ -51,18 +61,13 @@ export class StructureVerifier { this.isVerificationComplete = false; } - setOptions({ shouldRender = false, trackPlayerDistance = 0 }) { - this.blockVerificationLevels = { correctlyAir: 0 }; - this.shouldRender = shouldRender; - this.trackPlayerDistance = Math.min(trackPlayerDistance, MAX_TRACK_PLAYER_DISTANCE); - this.isVerificationComplete = false; - } - - async verifyStructure() { + async verifyStructure(shouldRender = true) { + if (!this.isEnabled()) + return; this.init(); return new Promise(async (resolve) => { await this.populateBlocksToVerify(); - system.runJob(this.verifyBlocks(this.blocksToVerify)); + system.runJob(this.verifyBlocks(this.blocksToVerify, shouldRender)); const checker = system.runInterval(() => { if (this.isVerificationComplete) { system.clearRun(checker); @@ -74,8 +79,10 @@ export class StructureVerifier { populateBlocksToVerify() { return new Promise((resolve) => { - if (this.trackPlayerDistance == 0) - return this.instance.getBlocks(); + if (this.getTrackPlayerDistance == 0) { + this.blocksToVerify = this.instance.getAllBlocks(); + resolve(); + } this.locationsToVerify = new Set(); for (const player of this.instance.getDimension().getPlayers()) system.runJob(this.populateActiveLocationsNearPlayer(player)); @@ -90,11 +97,12 @@ export class StructureVerifier { } *populateActiveLocationsNearPlayer(player) { - for (let x = -this.trackPlayerDistance; x < this.trackPlayerDistance; x++) { - for (let y = -this.trackPlayerDistance; y < this.trackPlayerDistance; y++) { - for (let z = -this.trackPlayerDistance; z < this.trackPlayerDistance; z++) { + const distance = this.getTrackPlayerDistance(); + for (let x = -distance; x < distance; x++) { + for (let y = -distance; y < distance; y++) { + for (let z = -distance; z < distance; z++) { const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z }); - if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useLayers: true })) { + if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) { this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); yield void 0; } @@ -104,14 +112,14 @@ export class StructureVerifier { this.isBlockPopulationComplete = true; } - *verifyBlocks(blocks) { + *verifyBlocks(blocks, shouldRender) { for (const block of blocks) { const verificationLevel = this.verifyBlock(block.location); if (verificationLevel === BlockVerificationLevel.Air) { this.blockVerificationLevels.correctlyAir++; } else { this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; - if (this.shouldRender) { + if (shouldRender) { const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) }; new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond); } diff --git a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js b/StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js similarity index 88% rename from StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js rename to StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js index 7071640..1fafa5b 100644 --- a/StrucTool [BP]/scripts/classes/enums/InstanceEditOptions.js +++ b/StrucTool [BP]/scripts/classes/enums/InstanceEditButtons.js @@ -1,4 +1,4 @@ -export const InstanceEditOptions = Object.freeze({ +export const InstanceEditButtons = Object.freeze({ Unknown: 'Unknown', MainMenu: '<<', Place: '§aPlace Instance', diff --git a/StrucTool [BP]/scripts/lib/Vector.js b/StrucTool [BP]/scripts/lib/Vector.js index 85fe960..1245a38 100644 --- a/StrucTool [BP]/scripts/lib/Vector.js +++ b/StrucTool [BP]/scripts/lib/Vector.js @@ -1,6 +1,5 @@ /** - * Part of ItemStack Database by @gameza_src - * Unknown author + * Unknown author, with additions. */ const isVec3Symbol = Symbol("isVec3"); export function Vector(x = 0, y = 0, z = 0) { @@ -23,6 +22,7 @@ Vector.multiply = function multiply(vec, num) { } Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; } Vector.floor = function floor(vec) { return { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z), __proto__: Vector.prototype }; } +Vector.volume = function volume(a, b) { const [min, max] = Vector.sort(a, b); return (max.x - min.x) * (max.y - min.y) * (max.z - min.z); } Vector.projection = function projection(a, b) { return Vector.multiply(b, Vector.dot(a, b) / ((b.x * b.x + b.y * b.y + b.z * b.z) ** 2)); } Vector.rejection = function rejection(a, b) { return Vector.subtract(a, Vector.projection(a, b)); } Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); } @@ -56,6 +56,7 @@ Vector.prototype = { cross(vec) { return Vector.cross(this, vec); }, dot(vec) { return Vector.dot(this, vec); }, floor() { return Vector.floor(this); }, + volume(vec) { return Vector.volume(this, vec); }, add(vec) { return Vector.add(this, vec); }, subtract(vec) { return Vector.subtract(this, vec); }, multiply(num) { return Vector.multiply(this, num); }, From 79f4605943c7162953b764363dd526b35bede97f Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Fri, 18 Apr 2025 19:16:34 -0700 Subject: [PATCH 3/3] huge performance improvements --- StrucTool [BP]/scripts/classes/BlockInfo.js | 2 +- .../scripts/classes/InstanceEditForm.js | 1 - .../classes/InstanceEditFormBuilder.js | 3 +- .../scripts/classes/InstanceOptions.js | 8 ++- StrucTool [BP]/scripts/classes/MenuForm.js | 2 +- StrucTool [BP]/scripts/classes/Structure.js | 25 ++++++- .../scripts/classes/StructureCollection.js | 11 +-- .../scripts/classes/StructureInstance.js | 22 +++++- .../scripts/classes/StructureStatistics.js | 2 +- .../scripts/classes/StructureVerifier.js | 71 ++++++++++--------- StrucTool [BP]/scripts/lib/Vector.js | 5 +- 11 files changed, 104 insertions(+), 48 deletions(-) diff --git a/StrucTool [BP]/scripts/classes/BlockInfo.js b/StrucTool [BP]/scripts/classes/BlockInfo.js index d2bbedc..34f6b19 100644 --- a/StrucTool [BP]/scripts/classes/BlockInfo.js +++ b/StrucTool [BP]/scripts/classes/BlockInfo.js @@ -14,7 +14,7 @@ class BlockInfo { } static showStructureBlockInfo(player) { - const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: false }); + const block = Raycaster.getTargetedStructureBlock(player, { isFirst: true, collideWithWorldBlocks: true, useActiveLayer: true }); if (!block && this.shownToLastTick.has(player.id)) { player.onScreenDisplay.setActionBar({ text: 'Structure:\n§7None' }); this.shownToLastTick.delete(player.id); diff --git a/StrucTool [BP]/scripts/classes/InstanceEditForm.js b/StrucTool [BP]/scripts/classes/InstanceEditForm.js index 46db387..e68911e 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditForm.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditForm.js @@ -152,7 +152,6 @@ export class InstanceEditForm { if (response.canceled) return; this.instance.setVerifierEnabled(response.formValues[0]); - this.instance.setVerifierDistance(response.formValues[1]); }); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js index e1f0148..cd45b8b 100644 --- a/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js +++ b/StrucTool [BP]/scripts/classes/InstanceEditFormBuilder.js @@ -9,7 +9,7 @@ export class InstanceEditFormBuilder { const location = instance.getLocation(); const form = new ActionFormData() .title(MenuFormBuilder.menuTitle) - let body = `Instance: §a${instance.name}\n§fStructure: §2${instance.options.getStructureId()}\n`; + let body = `Instance: §a${instance.getName()}\n§fStructure: §2${instance.getStructureId()}\n`; if (instance.hasLocation()) body += `§7(${location.location.x} ${location.location.y} ${location.location.z} in ${location.dimensionId})\n`; form.body(body); @@ -49,7 +49,6 @@ export class InstanceEditFormBuilder { return new ModalFormData() .title(MenuFormBuilder.menuTitle) .toggle('Toggle block validation.', instance.options.verifier.isEnabled) - .slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance) .submitButton('§aApply'); } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/InstanceOptions.js b/StrucTool [BP]/scripts/classes/InstanceOptions.js index b3ce70b..afac847 100644 --- a/StrucTool [BP]/scripts/classes/InstanceOptions.js +++ b/StrucTool [BP]/scripts/classes/InstanceOptions.js @@ -1,4 +1,5 @@ import { Vector } from "../lib/Vector"; +import { world } from "@minecraft/server"; export class InstanceOptions { instanceName = void 0; @@ -13,6 +14,11 @@ export class InstanceOptions { intervalOrLifetime: 10 }; + static getInstanceStrucetureId(instanceName) { + const options = new InstanceOptions(instanceName, void 0); + return options.structureId; + } + constructor(instanceName, structureId) { this.instanceName = instanceName; this.structureId = structureId; @@ -43,7 +49,7 @@ export class InstanceOptions { } getDimension() { - return world.getDimension(this.options.dimensionId); + return world.getDimension(this.dimensionId); } enable() { diff --git a/StrucTool [BP]/scripts/classes/MenuForm.js b/StrucTool [BP]/scripts/classes/MenuForm.js index 17e1ec5..22c6681 100644 --- a/StrucTool [BP]/scripts/classes/MenuForm.js +++ b/StrucTool [BP]/scripts/classes/MenuForm.js @@ -12,7 +12,7 @@ export class MenuForm { async show(jumpToInstance = true) { let instanceName; if (jumpToInstance) { - instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.name; + instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName(); if (instanceName) { new InstanceEditForm(this.player, instanceName); return; diff --git a/StrucTool [BP]/scripts/classes/Structure.js b/StrucTool [BP]/scripts/classes/Structure.js index e41d41e..983d6bb 100644 --- a/StrucTool [BP]/scripts/classes/Structure.js +++ b/StrucTool [BP]/scripts/classes/Structure.js @@ -39,10 +39,10 @@ export class Structure { } } - *getLayerBlocks(y) { + *getLayerBlocks(layer) { for (let x = 0; x < this.#structure.size.x; x++) { for (let z = 0; z < this.#structure.size.z; z++) { - yield this.getBlock({ x, y, z }); + yield this.getBlock({ x, y: layer, z }); } } } @@ -52,5 +52,26 @@ export class Structure { yield * this.getLayerBlocks(y); } } + + getLayerLocations(layer) { + const locations = new Set(); + for (let x = 0; x < this.#structure.size.x; x++) { + for (let z = 0; z < this.#structure.size.z; z++) { + locations.add(new Vector(x, layer, z)); + } + } + return locations; + } + + getAllLocations() { + const locations = new Set(); + for (let y = 0; y < this.#structure.size.y; y++) { + const layerLocations = this.getLayerLocations(y); + for (const location of layerLocations) { + locations.add(location); + } + } + return locations; + } } \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureCollection.js b/StrucTool [BP]/scripts/classes/StructureCollection.js index 42628a4..299067c 100644 --- a/StrucTool [BP]/scripts/classes/StructureCollection.js +++ b/StrucTool [BP]/scripts/classes/StructureCollection.js @@ -1,3 +1,4 @@ +import { InstanceOptions } from './InstanceOptions'; import { StructureInstance } from './StructureInstance'; import { world } from '@minecraft/server'; @@ -8,12 +9,12 @@ class StructureCollection { this.structures = {}; } - loadExistingStructures() { - world.getDynamicPropertyIds().filter(id => id.startsWith('structOptions:')).forEach(id => { - const instanceName = id.replace('structOptions:', ''); + loadExistingInstances() { + world.getDynamicPropertyIds().filter(id => id.startsWith('instanceOptions:')).forEach(id => { + const instanceName = id.replace('instanceOptions:', ''); let structureId; try { - structureId = StructureInstance.parseOptions(instanceName).structureId; + structureId = InstanceOptions.getInstanceStrucetureId(instanceName); this.structures[instanceName] = new StructureInstance(instanceName, structureId); } catch (e) { world.sendMessage(`§c[StrucTool] Error loading structure instance '${instanceName}'. It will be removed.`); @@ -95,5 +96,5 @@ class StructureCollection { export const structureCollection = new StructureCollection(); world.afterEvents.worldLoad.subscribe(() => { - structureCollection.loadExistingStructures(); + structureCollection.loadExistingInstances(); }); \ No newline at end of file diff --git a/StrucTool [BP]/scripts/classes/StructureInstance.js b/StrucTool [BP]/scripts/classes/StructureInstance.js index 9a5dbe0..8cd540b 100644 --- a/StrucTool [BP]/scripts/classes/StructureInstance.js +++ b/StrucTool [BP]/scripts/classes/StructureInstance.js @@ -3,6 +3,7 @@ import { StructureOutliner } from "./StructureOutliner"; import { StructureVerifier } from "./StructureVerifier"; import { InstanceOptions } from "./InstanceOptions"; import { Structure } from "./Structure"; +import { TicksPerSecond } from "@minecraft/server"; export class StructureInstance { options; @@ -18,11 +19,11 @@ export class StructureInstance { delete() { this.disable(); - this.options.clear(); delete this.options; delete this.structure; delete this.outliner; delete this.verifier; + this.options.clear(); } refreshBox() { @@ -36,6 +37,10 @@ export class StructureInstance { this.verifier.refresh(); } + getName() { + return this.options.instanceName; + } + getStructureId() { return this.options.structureId; } @@ -111,6 +116,15 @@ export class StructureInstance { && structureLocation.z >= bounds.min.z && structureLocation.z < bounds.max.z; } + getAllActiveLocations() { + if (!this.options.isEnabled) + throw new Error(`[StrucTool] Instance '${this.options.instanceName}' is not placed.`); + if (this.hasLayerSelected()) + return this.structure.getLayerLocations(this.getLayer()-1); + else + return this.structure.getAllLocations(); + } + isEnabled() { return this.options.isEnabled; } @@ -177,6 +191,12 @@ export class StructureInstance { setVerifierDistance(distance) { this.options.setVerifierDistance(distance); + if (this.options.verifier.trackPlayerDistance === 0) { + const bounds = this.getBounds(); + this.options.verifier.intervalOrLifetime = Math.max(bounds.min.volume(bounds.max) / TicksPerSecond, 2*TicksPerSecond); + } else { + this.options.verifier.intervalOrLifetime = 10; + } this.verifier.refresh(); } diff --git a/StrucTool [BP]/scripts/classes/StructureStatistics.js b/StrucTool [BP]/scripts/classes/StructureStatistics.js index 3962cfb..0b09187 100644 --- a/StrucTool [BP]/scripts/classes/StructureStatistics.js +++ b/StrucTool [BP]/scripts/classes/StructureStatistics.js @@ -45,7 +45,7 @@ export class StructureStatistics { getMessage() { let message = ''; - message += `§fStatistics for §a${this.instance.name}§f:`; + message += `§fStatistics for §a${this.instance.getName()}§f:`; if (this.instance.hasLayerSelected()) message += ` §7(layer ${this.instance.getLayer()})`; message += `\n§7Blocks: §2${this.getNonAirBlocks()}\n`; diff --git a/StrucTool [BP]/scripts/classes/StructureVerifier.js b/StrucTool [BP]/scripts/classes/StructureVerifier.js index 3c02386..441b629 100644 --- a/StrucTool [BP]/scripts/classes/StructureVerifier.js +++ b/StrucTool [BP]/scripts/classes/StructureVerifier.js @@ -2,6 +2,7 @@ import { BlockVerifier } from "./BlockVerifier"; import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; import { system, TicksPerSecond } from "@minecraft/server"; +import { Vector } from "../lib/Vector"; const MIN_TRACK_PLAYER_DISTANCE = 0; const MAX_TRACK_PLAYER_DISTANCE = 7; @@ -12,17 +13,20 @@ export class StructureVerifier { intervalOrLifetime; locationsToVerify; - blocksToVerify; blockVerificationLevels; - isBlockPopulationComplete; + isLocationPopulationComplete; isVerificationComplete; - #runner; - constructor(instance, { isEnabled = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) { + #runner; + #verifyJob; + #populateJob = {}; + + constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) { this.instance = instance; this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); this.instance.options.setVerifierEnabled(isEnabled); this.instance.options.setVerifierDistance(trackPlayerDistance); + this.locationsToVerify = new Set(); } startContinuousVerification() { @@ -50,14 +54,13 @@ export class StructureVerifier { } getTrackPlayerDistance() { - return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.trackPlayerDistance)); + return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.verifier.trackPlayerDistance)); } init() { - this.locationsToVerify = new Set(); - this.blocksToVerify = []; + this.locationsToVerify.clear(); this.blockVerificationLevels = { correctlyAir: 0 }; - this.isBlockPopulationComplete = false; + this.isLocationPopulationComplete = false; this.isVerificationComplete = false; } @@ -66,8 +69,10 @@ export class StructureVerifier { return; this.init(); return new Promise(async (resolve) => { - await this.populateBlocksToVerify(); - system.runJob(this.verifyBlocks(this.blocksToVerify, shouldRender)); + await this.populateLocationsToVerify(); + if (this.#verifyJob) + system.clearJob(this.#verifyJob); + this.verifyJob = system.runJob(this.verifyBlocks(this.locationsToVerify, shouldRender)); const checker = system.runInterval(() => { if (this.isVerificationComplete) { system.clearRun(checker); @@ -77,22 +82,23 @@ export class StructureVerifier { }); } - populateBlocksToVerify() { + async populateLocationsToVerify() { return new Promise((resolve) => { - if (this.getTrackPlayerDistance == 0) { - this.blocksToVerify = this.instance.getAllBlocks(); + if (this.getTrackPlayerDistance() === 0) { + this.locationsToVerify = this.instance.getAllActiveLocations(); resolve(); + } else { + for (const job of Object.values(this.#populateJob)) + system.clearJob(job); + for (const player of this.instance.getDimension().getPlayers()) + this.#populateJob[player.id] = system.runJob(this.populateActiveLocationsNearPlayer(player)); + const checker = system.runInterval(() => { + if (this.isLocationPopulationComplete) { + system.clearRun(checker); + resolve(); + } + }, 1); } - this.locationsToVerify = new Set(); - for (const player of this.instance.getDimension().getPlayers()) - system.runJob(this.populateActiveLocationsNearPlayer(player)); - this.blocksToVerify = this.instance.getBlocks(this.locationsToVerify); - const checker = system.runInterval(() => { - if (this.isBlockPopulationComplete) { - system.clearRun(checker); - resolve(); - } - }, 1); }); } @@ -101,26 +107,27 @@ export class StructureVerifier { for (let x = -distance; x < distance; x++) { for (let y = -distance; y < distance; y++) { for (let z = -distance; z < distance; z++) { - const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z }); + const worldLocation = Vector.from(player.location).add(new Vector(x, y, z)).floor();; + const structureLocation = this.instance.toStructureCoords(worldLocation); if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useActiveLayer: true })) { - this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z }); - yield void 0; + this.locationsToVerify.add(structureLocation); } + yield void 0; } } } - this.isBlockPopulationComplete = true; + this.isLocationPopulationComplete = true; } - *verifyBlocks(blocks, shouldRender) { - for (const block of blocks) { - const verificationLevel = this.verifyBlock(block.location); + *verifyBlocks(locations, shouldRender) { + for (const location of locations) { + const verificationLevel = this.verifyBlock(location); if (verificationLevel === BlockVerificationLevel.Air) { this.blockVerificationLevels.correctlyAir++; } else { - this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; + this.blockVerificationLevels[JSON.stringify(location)] = verificationLevel; if (shouldRender) { - const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) }; + const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) }; new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond); } } diff --git a/StrucTool [BP]/scripts/lib/Vector.js b/StrucTool [BP]/scripts/lib/Vector.js index 1245a38..b0f4391 100644 --- a/StrucTool [BP]/scripts/lib/Vector.js +++ b/StrucTool [BP]/scripts/lib/Vector.js @@ -22,7 +22,10 @@ Vector.multiply = function multiply(vec, num) { } Vector.isVec3 = function isVec3(vec) { return vec[isVec3Symbol] === true; } Vector.floor = function floor(vec) { return { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z), __proto__: Vector.prototype }; } -Vector.volume = function volume(a, b) { const [min, max] = Vector.sort(a, b); return (max.x - min.x) * (max.y - min.y) * (max.z - min.z); } +Vector.volume = function volume(a, b) { + const [min, max] = Vector.sort(a, b); + return (max.x - min.x) * (max.y - min.y) * (max.z - min.z); +} Vector.projection = function projection(a, b) { return Vector.multiply(b, Vector.dot(a, b) / ((b.x * b.x + b.y * b.y + b.z * b.z) ** 2)); } Vector.rejection = function rejection(a, b) { return Vector.subtract(a, Vector.projection(a, b)); } Vector.reflect = function reflect(v, n) { return Vector.subtract(v, Vector.multiply(n, 2 * Vector.dot(v, n))); }