From 79f4605943c7162953b764363dd526b35bede97f Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Fri, 18 Apr 2025 19:16:34 -0700 Subject: [PATCH] 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))); }