From 989e23289c9aa9f19faa6bbb9e9d4c914e58bbd9 Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Sat, 7 Jun 2025 02:05:55 -0700 Subject: [PATCH] Major runtime improvements --- .../classes/Errors/InvalidStructureError.js | 6 ++ .../classes/Instance/InstanceFormBuilder.js | 7 +- .../classes/Instance/InstanceOptions.js | 2 +- .../classes/Materials/StructureMaterials.js | 40 ++++++---- Construct[BP]/scripts/classes/MenuForm.js | 9 ++- .../classes/Render/VerificationRenderer.js | 25 ++++-- .../scripts/classes/Structure/Structure.js | 3 +- .../classes/Structure/StructureCollection.js | 2 +- .../classes/Verifier/StructureVerifier.js | 77 ++++++------------- 9 files changed, 88 insertions(+), 83 deletions(-) create mode 100644 Construct[BP]/scripts/classes/Errors/InvalidStructureError.js diff --git a/Construct[BP]/scripts/classes/Errors/InvalidStructureError.js b/Construct[BP]/scripts/classes/Errors/InvalidStructureError.js new file mode 100644 index 0000000..97e88b8 --- /dev/null +++ b/Construct[BP]/scripts/classes/Errors/InvalidStructureError.js @@ -0,0 +1,6 @@ +export class InvalidStructureError extends Error { + constructor(message) { + super(message); + this.name = 'InvalidStructureError'; + } +} \ No newline at end of file diff --git a/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js b/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js index 8f5b953..386056e 100644 --- a/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js +++ b/Construct[BP]/scripts/classes/Instance/InstanceFormBuilder.js @@ -6,6 +6,8 @@ import { EntityComponentTypes, TicksPerSecond } from '@minecraft/server'; import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel'; export class InstanceFormBuilder { + static structureVerifier; + static buildInstance(instance, options) { const location = instance.getLocation(); const form = new ActionFormData() @@ -32,10 +34,11 @@ export class InstanceFormBuilder { .title(MenuFormBuilder.menuTitle) if (this.structureVerifier) throw new Error('StructureVerifier is already running.'); - const structureVerifier = new StructureVerifier(instance, { isEnabled: true, particleLifetime: 1*TicksPerSecond, isStandalone: true }); - const verification = await structureVerifier.verifyStructure(true); + this.structureVerifier = new StructureVerifier(instance, { isEnabled: true, particleLifetime: 1*TicksPerSecond, isStandalone: true }); + const verification = await this.structureVerifier.verifyStructure(true); const statistics = new StructureStatistics(instance, verification); const statsMessage = statistics.getMessage(); + this.structureVerifier = void 0; buildStatisticsForm.body(statsMessage); return { form: buildStatisticsForm, stats: statsMessage }; } diff --git a/Construct[BP]/scripts/classes/Instance/InstanceOptions.js b/Construct[BP]/scripts/classes/Instance/InstanceOptions.js index 6596863..a14550f 100644 --- a/Construct[BP]/scripts/classes/Instance/InstanceOptions.js +++ b/Construct[BP]/scripts/classes/Instance/InstanceOptions.js @@ -16,7 +16,7 @@ export class InstanceOptions extends Option { particleLifetime: 10 }; - static getInstanceStrucetureId(instanceName) { + static getInstanceStructureId(instanceName) { const options = new InstanceOptions(instanceName, void 0); return options.structureId; } diff --git a/Construct[BP]/scripts/classes/Materials/StructureMaterials.js b/Construct[BP]/scripts/classes/Materials/StructureMaterials.js index 81d3582..a4cc818 100644 --- a/Construct[BP]/scripts/classes/Materials/StructureMaterials.js +++ b/Construct[BP]/scripts/classes/Materials/StructureMaterials.js @@ -1,4 +1,5 @@ -import { ItemStack } from "@minecraft/server"; +import { ItemStack, system } from "@minecraft/server"; +import { Vector } from "../../lib/Vector"; class StructureMaterials { instance; @@ -16,10 +17,10 @@ class StructureMaterials { populateInstance() { try { - if (this.instance.hasLocation()) - this.populateActive(); + if (this.instance.hasLocation() && this.instance.isEnabled()) + system.runJob(this.populateActive()); else - this.populateAll(); + system.runJob(this.populateAll()); } catch (e) { if (e.name === 'InstanceNotPlacedError') this.clear(); @@ -47,19 +48,28 @@ class StructureMaterials { delete this.materials[itemType]; } - populateAll() { - for (let layer = 0; layer < this.instance.getMaxLayer(); layer++) - this.populateLayer(layer) + *populateAll() { + for (let layer = 0; layer < this.instance.getMaxLayer(); layer++) { + for (const block of this.instance.getLayerBlocks(layer)) { + this.countBlock(block) + yield void 0; + } + } } - populateLayer(layer) { - for (const block of this.instance.getLayerBlocks(layer)) - this.countBlock(block) - } - - populateActive() { - for (const block of this.instance.getActiveBlocks()) - this.countBlock(block) + *populateActive() { + const bounds = this.instance.getActiveBounds(); + for (let y = bounds.min.y; y < bounds.max.y; y++) { + for (let z = bounds.min.z; z < bounds.max.z; z++) { + for (let x = bounds.min.x; x < bounds.max.x; x++) { + const location = new Vector(x, y, z); + const block = this.instance.getBlock(location); + if (!block) continue; + this.countBlock(block); + yield void 0; + } + } + } } countBlock(block) { diff --git a/Construct[BP]/scripts/classes/MenuForm.js b/Construct[BP]/scripts/classes/MenuForm.js index 554c71c..c5c0ea0 100644 --- a/Construct[BP]/scripts/classes/MenuForm.js +++ b/Construct[BP]/scripts/classes/MenuForm.js @@ -66,6 +66,11 @@ export class MenuForm { this.player.sendMessage(`§cInstance '${instanceName}' already exists. Try again with a new name.`); return void 0; } + if (e.name === 'InvalidStructureError') { + this.player.sendMessage(`§cStructure ID '${structureId}' not found. If you're looking for a structure that you put in the structures folder, please restart your world and try again.`); + return void 0; + } + throw e; } return instanceName; }); @@ -91,10 +96,6 @@ export class MenuForm { const structureId = response.formValues[0]; if (structureId === '') return void 0; - if (!structureCollection.getWorldStructureIds().some(id => id.replace('mystructure:', '') === structureId)) { - this.player.sendMessage(`§cStructure ID '${structureId}' not found. If you're looking for a structure that you put in the structures folder, please restart your world and try again.`); - return void 0; - } return structureId; }); } diff --git a/Construct[BP]/scripts/classes/Render/VerificationRenderer.js b/Construct[BP]/scripts/classes/Render/VerificationRenderer.js index 23efc95..85d122e 100644 --- a/Construct[BP]/scripts/classes/Render/VerificationRenderer.js +++ b/Construct[BP]/scripts/classes/Render/VerificationRenderer.js @@ -8,6 +8,7 @@ export class VerificationRenderer { instance; lastRenderedChunk; bounds; + shortestDimension; #runner; #renderQueue = []; @@ -42,14 +43,26 @@ export class VerificationRenderer { prepareRenderQueue() { this.#renderQueue = []; const bounds = this.instance.getActiveBounds(); - for (let y = bounds.min.y; y < bounds.max.y; y++) { + for (let y = bounds.min.y; y < bounds.max.y; y++) { + this.prepareRenderQueueLayer(bounds, y); + } + this.lastRenderedChunk = 0; + } + + prepareRenderQueueLayer(bounds, y) { + if (bounds.max.x < bounds.max.z) { + for (let z = bounds.min.z; z < bounds.max.z; z++) { + for (let x = bounds.min.x; x < bounds.max.x; x++) { + this.#renderQueue.push({ x, y, z }); + } + } + } else { for (let x = bounds.min.x; x < bounds.max.x; x++) { for (let z = bounds.min.z; z < bounds.max.z; z++) { this.#renderQueue.push({ x, y, z }); } } } - this.lastRenderedChunk = 0; } renderNextChunk() { @@ -61,11 +74,12 @@ export class VerificationRenderer { renderNextChunkForLargeStructure() { const bounds = this.instance.getActiveBounds(); - const maxChunk = (bounds.min.volume(bounds.max) / bounds.max.x) / (bounds.max.y - bounds.min.y); + const shortestSideLength = Math.min(bounds.max.x, bounds.max.z); + const maxChunk = (bounds.min.volume(bounds.max) / shortestSideLength) / (bounds.max.y - bounds.min.y); const lifetime = (maxChunk * RENDER_LIFETIME_FACTOR_TICKS) / TicksPerSecond; const verificationLevels = this.instance.verifier.getLastVerificationLevels(); const dimension = this.instance.getDimension(); - const chunk = this.#renderQueue.splice(0, bounds.max.x); + const chunk = this.#renderQueue.splice(0, shortestSideLength); for (const location of chunk) { const verificationLevel = verificationLevels[JSON.stringify(location)]; if (!verificationLevel) @@ -97,6 +111,7 @@ export class VerificationRenderer { shouldUseLargeStructureRendering() { const bounds = this.instance.getActiveBounds(); - return this.instance.hasLayerSelected() || bounds.min.volume(bounds.max) > 300; + const maxVolume = 343; + return this.instance.hasLayerSelected() || bounds.min.volume(bounds.max) > maxVolume; } } \ No newline at end of file diff --git a/Construct[BP]/scripts/classes/Structure/Structure.js b/Construct[BP]/scripts/classes/Structure/Structure.js index 822f53e..933fca0 100644 --- a/Construct[BP]/scripts/classes/Structure/Structure.js +++ b/Construct[BP]/scripts/classes/Structure/Structure.js @@ -1,5 +1,6 @@ import { world } from "@minecraft/server"; import { Vector } from "../../lib/Vector"; +import { InvalidStructureError } from "../Errors/InvalidStructureError"; export class Structure { structureId; @@ -9,7 +10,7 @@ export class Structure { this.structureId = structureId; this.#structure = world.structureManager.get(structureId); if (!this.#structure) - throw new Error(`[Construct] Structure '${structureId}' not found.`); + throw new InvalidStructureError(`[Construct] Structure '${structureId}' not found on world.`); this.#structure.saveToWorld(); } diff --git a/Construct[BP]/scripts/classes/Structure/StructureCollection.js b/Construct[BP]/scripts/classes/Structure/StructureCollection.js index 91d1c4e..2ae6407 100644 --- a/Construct[BP]/scripts/classes/Structure/StructureCollection.js +++ b/Construct[BP]/scripts/classes/Structure/StructureCollection.js @@ -15,7 +15,7 @@ class StructureCollection { const instanceName = id.replace('instanceOptions:', ''); let structureId; try { - structureId = InstanceOptions.getInstanceStrucetureId(instanceName); + structureId = InstanceOptions.getInstanceStructureId(instanceName); this.structures[instanceName] = new StructureInstance(instanceName, structureId); } catch (e) { world.sendMessage(`§c[Construct] Error loading structure instance '${instanceName}'. It will be removed.`); diff --git a/Construct[BP]/scripts/classes/Verifier/StructureVerifier.js b/Construct[BP]/scripts/classes/Verifier/StructureVerifier.js index d4686fc..157ed6d 100644 --- a/Construct[BP]/scripts/classes/Verifier/StructureVerifier.js +++ b/Construct[BP]/scripts/classes/Verifier/StructureVerifier.js @@ -79,10 +79,9 @@ export class StructureVerifier { return; this.initVerification(); return new Promise(async (resolve) => { - await this.populateLocationsToVerify(); if (this.#verifyJob) system.clearJob(this.#verifyJob); - this.#verifyJob = system.runJob(this.verifyBlocks(this.locationsToVerify, shouldRender)); + this.#verifyJob = system.runJob(this.verifyBlocks(shouldRender)); const checker = system.runInterval(() => { if (this.isVerificationComplete) { system.clearRun(checker); @@ -101,65 +100,35 @@ export class StructureVerifier { this.isLocationPopulationComplete = false; this.isVerificationComplete = false; } - - async populateLocationsToVerify() { - return new Promise((resolve) => { - 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()) { - if (!player) - continue; - this.#populateJob[player.id] = system.runJob(this.populateActiveLocationsNearPlayer(player)); - } - const checker = system.runInterval(() => { - if (this.isLocationPopulationComplete) { - system.clearRun(checker); - resolve(); - } - }, 1); - } - }); - } - - *populateActiveLocationsNearPlayer(player) { - 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 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(structureLocation); - } + + *verifyBlocks(shouldRender) { + const bounds = this.instance.getActiveBounds(); + for (let y = bounds.min.y; y < bounds.max.y; y++) { + for (let z = bounds.min.z; z < bounds.max.z; z++) { + for (let x = bounds.min.x; x < bounds.max.x; x++) { + const location = new Vector(x, y, z); + this.verifyBlock(location, shouldRender); yield void 0; } } } - this.isLocationPopulationComplete = true; - } - - *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(location)] = verificationLevel; - if (shouldRender) { - const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) }; - new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.particleLifetime/TicksPerSecond); - } - } - yield void 0; - } this.isVerificationComplete = true; } - verifyBlock(location) { + verifyBlock(location, shouldRender) { + const verificationLevel = this.getVerificationLevel(location); + if (verificationLevel === BlockVerificationLevel.Air) { + this.blockVerificationLevels.correctlyAir++; + } else { + this.blockVerificationLevels[JSON.stringify(location)] = verificationLevel; + if (shouldRender) { + const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) }; + new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.particleLifetime/TicksPerSecond); + } + } + } + + getVerificationLevel(location) { const worldBlock = this.instance.getDimension()?.getBlock(this.instance.toGlobalCoords(location)); if (!worldBlock) return BlockVerificationLevel.Skipped;