decoupled verification rendering from the verifier with much better animations

This commit is contained in:
ForestOfLight
2025-06-06 01:31:49 -07:00
Unverified
parent 709eccef8c
commit f8bbc659fb
11 changed files with 171 additions and 51 deletions
@@ -1,86 +0,0 @@
import { MolangVariableMap } from "@minecraft/server";
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { Vector } from "../../lib/Vector";
export class BlockVerificationLevelRender {
opacity = 0.2;
lifetimeSeconds = 0;
constructor(dimensionLocation, verificationLevel, lifetimeSeconds = 5) {
this.dimension = dimensionLocation.dimension;
this.location = Vector.from(dimensionLocation.location);
this.verificationLevel = verificationLevel;
this.lifetimeSeconds = lifetimeSeconds;
this.renderBlock();
}
renderBlock() {
const sizeScalar = this.verificationLevelToSizeScalar();
for (const particle of this.getBlockParticles(sizeScalar)) {
const color = this.getRGBAMolang();
if (!color)
return;
color.setFloat("lifetime", this.lifetimeSeconds);
color.setFloat("width", 0.5*sizeScalar);
color.setFloat("height", 0.5*sizeScalar);
try {
this.dimension.spawnParticle(particle.particleType, particle.location, color);
} catch {
/* pass */
}
}
}
getBlockParticles(sizeScalar = 1) {
const bottomFace = new Vector(0.5, 0, 0.5);
const topFace = new Vector(0.5, 1, 0.5);
const leftFace = new Vector(1, 0.5, 0.5);
const rightFace = new Vector(0, 0.5, 0.5);
const frontFace = new Vector(0.5, 0.5, 1);
const backFace = new Vector(0.5, 0.5, 0);
const center = new Vector(0.5, 0.5, 0.5);
return [
{ particleType: "construct:blockoverlay_xz", location: this.location.add(center).add(topFace.subtract(center).multiply(sizeScalar)) },
{ particleType: "construct:blockoverlay_xz", location: this.location.add(center).add(bottomFace.subtract(center).multiply(sizeScalar)) },
{ particleType: "construct:blockoverlay_yz", location: this.location.add(center).add(leftFace.subtract(center).multiply(sizeScalar)) },
{ particleType: "construct:blockoverlay_yz", location: this.location.add(center).add(rightFace.subtract(center).multiply(sizeScalar)) },
{ particleType: "construct:blockoverlay_xy", location: this.location.add(center).add(frontFace.subtract(center).multiply(sizeScalar)) },
{ particleType: "construct:blockoverlay_xy", location: this.location.add(center).add(backFace.subtract(center).multiply(sizeScalar)) }
];
}
getRGBAMolang() {
const rgb = this.verificationLevelToRGB();
if (!rgb) return;
rgb.alpha = this.opacity;
const molang = new MolangVariableMap();
molang.setColorRGBA("face_color", rgb);
return molang;
}
verificationLevelToRGB() {
switch (this.verificationLevel) {
case BlockVerificationLevel.NoMatch:
return { red: 1, green: 0, blue: 0};
case BlockVerificationLevel.TypeMatch:
return { red: 1, green: 1, blue: 0};
case BlockVerificationLevel.Missing:
return { red: 0, green: 0, blue: 1};
default:
return void 0;
}
}
verificationLevelToSizeScalar() {
switch (this.verificationLevel) {
case BlockVerificationLevel.NoMatch:
return 1.01;
case BlockVerificationLevel.TypeMatch:
return 1.01;
case BlockVerificationLevel.Missing:
return 0.90;
default:
return 1.00;
}
}
}
@@ -1,6 +1,6 @@
import { BlockVerifier } from "./BlockVerifier";
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { BlockVerificationLevelRender } from "../Verifier/BlockVerificationLevelRender";
import { BlockVerificationLevelRender } from "../Render/BlockVerificationLevelRender";
import { system, TicksPerSecond } from "@minecraft/server";
import { Vector } from "../../lib/Vector";
@@ -10,22 +10,24 @@ const MIN_LIFETIME = 8;
export class StructureVerifier {
instance;
intervalOrLifetime;
particleLifetime;
locationsToVerify;
blockVerificationLevels;
isLocationPopulationComplete;
isVerificationComplete;
shouldStartNextVerification;
lastCompleteVerificationLevels;
#runner;
#verifyJob;
#populateJob = {};
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10, isStandalone: isIndependent = false } = {}) {
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, particleLifetime = 10, isStandalone = false } = {}) {
this.instance = instance;
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
if (isIndependent) {
this.isIndependent = isIndependent;
this.particleLifetime = Math.max(particleLifetime, MIN_LIFETIME);
if (isStandalone) {
this.isStandalone = isStandalone;
this.enabled = isEnabled;
this.trackPlayerDistance = trackPlayerDistance;
} else {
@@ -36,9 +38,11 @@ export class StructureVerifier {
}
startContinuousVerification() {
this.shouldStartNextVerification = true;
this.#runner = system.runInterval(() => {
this.verifyStructure();
}, this.intervalOrLifetime);
if (this.shouldStartNextVerification)
this.verifyStructure();
});
}
stopContinuousVerification() {
@@ -56,45 +60,48 @@ export class StructureVerifier {
}
isEnabled() {
if (this.isIndependent)
if (this.isStandalone)
return this.enabled;
return this.instance.options.verifier.isEnabled;
}
getTrackPlayerDistance() {
let distance;
if (this.isIndependent)
if (this.isStandalone)
distance = this.trackPlayerDistance;
else
distance = this.instance.options.verifier.trackPlayerDistance
return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, distance));
}
init() {
this.locationsToVerify.clear();
this.blockVerificationLevels = { correctlyAir: 0 };
this.isLocationPopulationComplete = false;
this.isVerificationComplete = false;
}
async verifyStructure(shouldRender = true) {
async verifyStructure(shouldRender = false) {
if (!this.isEnabled())
return;
this.init();
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(this.locationsToVerify, shouldRender));
const checker = system.runInterval(() => {
if (this.isVerificationComplete) {
system.clearRun(checker);
this.lastCompleteVerificationLevels = JSON.parse(JSON.stringify(this.blockVerificationLevels));
this.shouldStartNextVerification = true;
resolve(this.blockVerificationLevels);
}
}, 1);
});
}
initVerification() {
this.shouldStartNextVerification = false;
this.locationsToVerify.clear();
this.blockVerificationLevels = { correctlyAir: 0 };
this.isLocationPopulationComplete = false;
this.isVerificationComplete = false;
}
async populateLocationsToVerify() {
return new Promise((resolve) => {
if (this.getTrackPlayerDistance() === 0) {
@@ -144,7 +151,7 @@ export class StructureVerifier {
this.blockVerificationLevels[JSON.stringify(location)] = verificationLevel;
if (shouldRender) {
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(location) };
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond);
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.particleLifetime/TicksPerSecond);
}
}
yield void 0;
@@ -153,10 +160,16 @@ export class StructureVerifier {
}
verifyBlock(location) {
const worldBlock = this.instance.getDimension().getBlock(this.instance.toGlobalCoords(location));
const worldBlock = this.instance.getDimension()?.getBlock(this.instance.toGlobalCoords(location));
if (!worldBlock)
return BlockVerificationLevel.Skipped;
const blockVerifier = new BlockVerifier(worldBlock, this.instance);
return blockVerifier.verify();
}
getLastVerificationLevels() {
if (!this.lastCompleteVerificationLevels)
return {};
return this.lastCompleteVerificationLevels;
}
}