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';
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 };
}
@@ -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;
}
@@ -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) {
+5 -4
View File
@@ -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;
});
}
@@ -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;
}
}
@@ -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();
}
@@ -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.`);
@@ -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);
@@ -102,64 +101,34 @@ export class StructureVerifier {
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;