Major runtime improvements

This commit is contained in:
ForestOfLight
2025-06-07 02:05:55 -07:00
Unverified
parent 0b1fefbeb1
commit 989e23289c
9 changed files with 88 additions and 83 deletions
@@ -0,0 +1,6 @@
export class InvalidStructureError extends Error {
constructor(message) {
super(message);
this.name = 'InvalidStructureError';
}
}
@@ -6,6 +6,8 @@ import { EntityComponentTypes, TicksPerSecond } from '@minecraft/server';
import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel'; import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel';
export class InstanceFormBuilder { export class InstanceFormBuilder {
static structureVerifier;
static buildInstance(instance, options) { static buildInstance(instance, options) {
const location = instance.getLocation(); const location = instance.getLocation();
const form = new ActionFormData() const form = new ActionFormData()
@@ -32,10 +34,11 @@ export class InstanceFormBuilder {
.title(MenuFormBuilder.menuTitle) .title(MenuFormBuilder.menuTitle)
if (this.structureVerifier) if (this.structureVerifier)
throw new Error('StructureVerifier is already running.'); throw new Error('StructureVerifier is already running.');
const structureVerifier = new StructureVerifier(instance, { isEnabled: true, particleLifetime: 1*TicksPerSecond, isStandalone: true }); this.structureVerifier = new StructureVerifier(instance, { isEnabled: true, particleLifetime: 1*TicksPerSecond, isStandalone: true });
const verification = await structureVerifier.verifyStructure(true); const verification = await this.structureVerifier.verifyStructure(true);
const statistics = new StructureStatistics(instance, verification); const statistics = new StructureStatistics(instance, verification);
const statsMessage = statistics.getMessage(); const statsMessage = statistics.getMessage();
this.structureVerifier = void 0;
buildStatisticsForm.body(statsMessage); buildStatisticsForm.body(statsMessage);
return { form: buildStatisticsForm, stats: statsMessage }; return { form: buildStatisticsForm, stats: statsMessage };
} }
@@ -16,7 +16,7 @@ export class InstanceOptions extends Option {
particleLifetime: 10 particleLifetime: 10
}; };
static getInstanceStrucetureId(instanceName) { static getInstanceStructureId(instanceName) {
const options = new InstanceOptions(instanceName, void 0); const options = new InstanceOptions(instanceName, void 0);
return options.structureId; return options.structureId;
} }
@@ -1,4 +1,5 @@
import { ItemStack } from "@minecraft/server"; import { ItemStack, system } from "@minecraft/server";
import { Vector } from "../../lib/Vector";
class StructureMaterials { class StructureMaterials {
instance; instance;
@@ -16,10 +17,10 @@ class StructureMaterials {
populateInstance() { populateInstance() {
try { try {
if (this.instance.hasLocation()) if (this.instance.hasLocation() && this.instance.isEnabled())
this.populateActive(); system.runJob(this.populateActive());
else else
this.populateAll(); system.runJob(this.populateAll());
} catch (e) { } catch (e) {
if (e.name === 'InstanceNotPlacedError') if (e.name === 'InstanceNotPlacedError')
this.clear(); this.clear();
@@ -47,19 +48,28 @@ class StructureMaterials {
delete this.materials[itemType]; delete this.materials[itemType];
} }
populateAll() { *populateAll() {
for (let layer = 0; layer < this.instance.getMaxLayer(); layer++) for (let layer = 0; layer < this.instance.getMaxLayer(); layer++) {
this.populateLayer(layer) for (const block of this.instance.getLayerBlocks(layer)) {
this.countBlock(block)
yield void 0;
}
}
} }
populateLayer(layer) { *populateActive() {
for (const block of this.instance.getLayerBlocks(layer)) const bounds = this.instance.getActiveBounds();
this.countBlock(block) 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;
}
}
} }
populateActive() {
for (const block of this.instance.getActiveBlocks())
this.countBlock(block)
} }
countBlock(block) { countBlock(block) {
+5 -4
View File
@@ -66,6 +66,11 @@ export class MenuForm {
this.player.sendMessage(`§cInstance '${instanceName}' already exists. Try again with a new name.`); this.player.sendMessage(`§cInstance '${instanceName}' already exists. Try again with a new name.`);
return void 0; 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; return instanceName;
}); });
@@ -91,10 +96,6 @@ export class MenuForm {
const structureId = response.formValues[0]; const structureId = response.formValues[0];
if (structureId === '') if (structureId === '')
return void 0; 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; return structureId;
}); });
} }
@@ -8,6 +8,7 @@ export class VerificationRenderer {
instance; instance;
lastRenderedChunk; lastRenderedChunk;
bounds; bounds;
shortestDimension;
#runner; #runner;
#renderQueue = []; #renderQueue = [];
@@ -43,13 +44,25 @@ export class VerificationRenderer {
this.#renderQueue = []; this.#renderQueue = [];
const bounds = this.instance.getActiveBounds(); 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 x = bounds.min.x; x < bounds.max.x; x++) {
for (let z = bounds.min.z; z < bounds.max.z; z++) { for (let z = bounds.min.z; z < bounds.max.z; z++) {
this.#renderQueue.push({ x, y, z }); this.#renderQueue.push({ x, y, z });
} }
} }
} }
this.lastRenderedChunk = 0;
} }
renderNextChunk() { renderNextChunk() {
@@ -61,11 +74,12 @@ export class VerificationRenderer {
renderNextChunkForLargeStructure() { renderNextChunkForLargeStructure() {
const bounds = this.instance.getActiveBounds(); 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 lifetime = (maxChunk * RENDER_LIFETIME_FACTOR_TICKS) / TicksPerSecond;
const verificationLevels = this.instance.verifier.getLastVerificationLevels(); const verificationLevels = this.instance.verifier.getLastVerificationLevels();
const dimension = this.instance.getDimension(); 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) { for (const location of chunk) {
const verificationLevel = verificationLevels[JSON.stringify(location)]; const verificationLevel = verificationLevels[JSON.stringify(location)];
if (!verificationLevel) if (!verificationLevel)
@@ -97,6 +111,7 @@ export class VerificationRenderer {
shouldUseLargeStructureRendering() { shouldUseLargeStructureRendering() {
const bounds = this.instance.getActiveBounds(); 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;
} }
} }
@@ -1,5 +1,6 @@
import { world } from "@minecraft/server"; import { world } from "@minecraft/server";
import { Vector } from "../../lib/Vector"; import { Vector } from "../../lib/Vector";
import { InvalidStructureError } from "../Errors/InvalidStructureError";
export class Structure { export class Structure {
structureId; structureId;
@@ -9,7 +10,7 @@ export class Structure {
this.structureId = structureId; this.structureId = structureId;
this.#structure = world.structureManager.get(structureId); this.#structure = world.structureManager.get(structureId);
if (!this.#structure) if (!this.#structure)
throw new Error(`[Construct] Structure '${structureId}' not found.`); throw new InvalidStructureError(`[Construct] Structure '${structureId}' not found on world.`);
this.#structure.saveToWorld(); this.#structure.saveToWorld();
} }
@@ -15,7 +15,7 @@ class StructureCollection {
const instanceName = id.replace('instanceOptions:', ''); const instanceName = id.replace('instanceOptions:', '');
let structureId; let structureId;
try { try {
structureId = InstanceOptions.getInstanceStrucetureId(instanceName); structureId = InstanceOptions.getInstanceStructureId(instanceName);
this.structures[instanceName] = new StructureInstance(instanceName, structureId); this.structures[instanceName] = new StructureInstance(instanceName, structureId);
} catch (e) { } catch (e) {
world.sendMessage(`§c[Construct] Error loading structure instance '${instanceName}'. It will be removed.`); world.sendMessage(`§c[Construct] Error loading structure instance '${instanceName}'. It will be removed.`);
@@ -79,10 +79,9 @@ export class StructureVerifier {
return; return;
this.initVerification(); this.initVerification();
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
await this.populateLocationsToVerify();
if (this.#verifyJob) if (this.#verifyJob)
system.clearJob(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(() => { const checker = system.runInterval(() => {
if (this.isVerificationComplete) { if (this.isVerificationComplete) {
system.clearRun(checker); system.clearRun(checker);
@@ -102,49 +101,22 @@ export class StructureVerifier {
this.isVerificationComplete = false; this.isVerificationComplete = false;
} }
async populateLocationsToVerify() { *verifyBlocks(shouldRender) {
return new Promise((resolve) => { const bounds = this.instance.getActiveBounds();
if (this.getTrackPlayerDistance() === 0) { for (let y = bounds.min.y; y < bounds.max.y; y++) {
this.locationsToVerify = this.instance.getAllActiveLocations(); for (let z = bounds.min.z; z < bounds.max.z; z++) {
resolve(); for (let x = bounds.min.x; x < bounds.max.x; x++) {
} else { const location = new Vector(x, y, z);
for (const job of Object.values(this.#populateJob)) this.verifyBlock(location, shouldRender);
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);
}
yield void 0; yield void 0;
} }
} }
} }
this.isLocationPopulationComplete = true; this.isVerificationComplete = true;
} }
*verifyBlocks(locations, shouldRender) { verifyBlock(location, shouldRender) {
for (const location of locations) { const verificationLevel = this.getVerificationLevel(location);
const verificationLevel = this.verifyBlock(location);
if (verificationLevel === BlockVerificationLevel.Air) { if (verificationLevel === BlockVerificationLevel.Air) {
this.blockVerificationLevels.correctlyAir++; this.blockVerificationLevels.correctlyAir++;
} else { } else {
@@ -154,12 +126,9 @@ export class StructureVerifier {
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.particleLifetime/TicksPerSecond); new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.particleLifetime/TicksPerSecond);
} }
} }
yield void 0;
}
this.isVerificationComplete = true;
} }
verifyBlock(location) { getVerificationLevel(location) {
const worldBlock = this.instance.getDimension()?.getBlock(this.instance.toGlobalCoords(location)); const worldBlock = this.instance.getDimension()?.getBlock(this.instance.toGlobalCoords(location));
if (!worldBlock) if (!worldBlock)
return BlockVerificationLevel.Skipped; return BlockVerificationLevel.Skipped;