From 35b7575d2f73be41b9d7cf654f60f5881ecf28ff Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Thu, 17 Apr 2025 21:16:13 -0700 Subject: [PATCH] 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