Merge branch 'StructureVerifier'
This commit is contained in:
@@ -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++) {
|
||||||
populateActive() {
|
const location = new Vector(x, y, z);
|
||||||
for (const block of this.instance.getActiveBlocks())
|
const block = this.instance.getBlock(location);
|
||||||
this.countBlock(block)
|
if (!block) continue;
|
||||||
|
this.countBlock(block);
|
||||||
|
yield void 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
countBlock(block) {
|
countBlock(block) {
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
|
|
||||||
@@ -42,14 +43,26 @@ export class VerificationRenderer {
|
|||||||
prepareRenderQueue() {
|
prepareRenderQueue() {
|
||||||
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,64 +101,34 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
*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;
|
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));
|
const worldBlock = this.instance.getDimension()?.getBlock(this.instance.toGlobalCoords(location));
|
||||||
if (!worldBlock)
|
if (!worldBlock)
|
||||||
return BlockVerificationLevel.Skipped;
|
return BlockVerificationLevel.Skipped;
|
||||||
|
|||||||
Reference in New Issue
Block a user