Start working at 1.2.0
/ build (push) Waiting to run

This commit is contained in:
2026-08-26 09:54:39 +08:00
Unverified
parent 36f2dd0685
commit 136d5ffa3b
159 changed files with 60793 additions and 617 deletions
@@ -0,0 +1,19 @@
export class BlockBudget {
#remaining = 0;
credit(blocks) {
this.#remaining += blocks;
}
spend(blocks) {
this.#remaining -= blocks;
}
isExhausted() {
return this.#remaining <= 0;
}
clear() {
this.#remaining = 0;
}
}
@@ -0,0 +1,25 @@
export class CellFlags {
static #sideMask = 0b111111;
static #opaqueShift = 6;
static NONE = 0;
static pack(markerMask, opaqueMask) {
return (markerMask & CellFlags.#sideMask) | ((opaqueMask & CellFlags.#sideMask) << CellFlags.#opaqueShift);
}
static markerMask(packed) {
return packed & CellFlags.#sideMask;
}
static opaqueMask(packed) {
return (packed >> CellFlags.#opaqueShift) & CellFlags.#sideMask;
}
static hasMarker(packed, side) {
return ((packed >> side) & 1) === 1;
}
static hasOpaque(packed, side) {
return ((packed >> (side + CellFlags.#opaqueShift)) & 1) === 1;
}
}
@@ -0,0 +1,34 @@
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { blockModelResolver } from "../Render/model/BlockModelResolver";
import { CellFlags } from "./CellFlags";
export class CellShape {
#instance;
#showsBlockPreview;
constructor(instance, showsBlockPreview) {
this.#instance = instance;
this.#showsBlockPreview = showsBlockPreview;
}
flagsFor(location, verificationLevel) {
switch (verificationLevel) {
case BlockVerificationLevel.NoMatch:
case BlockVerificationLevel.TypeMatch:
return blockModelResolver.overlaySideMasks();
case BlockVerificationLevel.Match:
return this.#structureBlockFlags(location);
case BlockVerificationLevel.Missing:
return this.#showsBlockPreview ? this.#structureBlockFlags(location) : CellFlags.NONE;
default:
return CellFlags.NONE;
}
}
#structureBlockFlags(location) {
const structureBlock = this.#instance.getBlock(location);
if (structureBlock === void 0)
return CellFlags.NONE;
return blockModelResolver.sideMasksOf(structureBlock);
}
}
@@ -0,0 +1,46 @@
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { CellShape } from "./CellShape";
export class CellVerifier {
#instance;
#shape;
constructor(instance, showsBlockPreview) {
this.#instance = instance;
this.#shape = new CellShape(instance, showsBlockPreview);
}
verify(location) {
const verificationLevel = this.#levelAt(location);
return {
verificationLevel,
flags: this.#shape.flagsFor(location, verificationLevel)
};
}
#levelAt(location) {
const globalLocation = this.#instance.toGlobalCoords(location);
const worldBlock = this.#instance.getDimension()?.getBlock(globalLocation);
if (!worldBlock)
return BlockVerificationLevel.Skipped;
return this.#compare(worldBlock, this.#instance.getBlock(location));
}
#compare(worldBlock, structureBlock) {
if (!structureBlock)
return BlockVerificationLevel.Air;
if (structureBlock.typeId === "minecraft:air")
return worldBlock.isAir ? BlockVerificationLevel.Air : BlockVerificationLevel.NoMatch;
if (worldBlock.isAir)
return BlockVerificationLevel.Missing;
if (worldBlock.typeId !== structureBlock.typeId)
return BlockVerificationLevel.NoMatch;
if (worldBlock.isWaterlogged !== structureBlock.isWaterlogged)
return BlockVerificationLevel.TypeMatch;
if (!structureBlock.hasStates)
return BlockVerificationLevel.Match;
if (worldBlock.permutation.matches(structureBlock.typeId, structureBlock.states))
return BlockVerificationLevel.Match;
return BlockVerificationLevel.TypeMatch;
}
}
@@ -0,0 +1,22 @@
import { Vector } from "../../lib/Vector";
import { Side } from "../Enums/Side";
import { instanceCollection } from "../Instance/InstanceCollection";
export class ChangedCellPatcher {
#patchedOffsets = Object.freeze([Vector.zero, ...Side.OFFSETS]);
patchAround(dimensionId, worldLocation) {
for (const offset of this.#patchedOffsets)
this.#patchCell(dimensionId, Vector.add(worldLocation, offset));
}
#patchCell(dimensionId, worldLocation) {
for (const instance of instanceCollection.getInstancesAt(dimensionId, worldLocation)) {
if (!instance.verifier || !instance.previewRenderer)
continue;
const location = instance.toStructureCoords(worldLocation);
instance.verifier.patchCell(location);
instance.previewRenderer.renderBlockAt(location);
}
}
}
@@ -0,0 +1,22 @@
export class ChunkGrid {
#chunkSize = 16;
#chunkMask = this.#chunkSize - 1;
#chunkShift = 4;
#chunkKeyStride = 4194304;
#origin;
constructor(origin) {
this.#origin = origin;
}
keyAt(x, z) {
const chunkX = (x + this.#origin.x) >> this.#chunkShift;
const chunkZ = (z + this.#origin.z) >> this.#chunkShift;
return chunkX * this.#chunkKeyStride + chunkZ;
}
endOfSpanX(x) {
return x + this.#chunkSize - ((x + this.#origin.x) & this.#chunkMask);
}
}
@@ -0,0 +1,32 @@
import { VerificationGrid } from "./VerificationGrid";
export class GridBuffers {
#completed;
#filling;
completed() {
return this.#completed;
}
filling() {
return this.#filling;
}
beginPass(bounds) {
this.#filling = this.#recycled(this.#filling, bounds);
return this.#filling;
}
commit() {
const finished = this.#filling;
this.#filling = this.#completed;
this.#completed = finished;
}
#recycled(grid, bounds) {
if (!grid?.matchesBounds(bounds))
return new VerificationGrid(bounds);
grid.clear();
return grid;
}
}
@@ -0,0 +1,19 @@
// Armed means the drawn picture does not yet describe the current bounds, so
// the next pass should run flat out. Reading and clearing are separate because
// each holder clears at a different point: the verifier only when a pass
// commits, the render cursor only when a lap ends.
export class PriorityPass {
#isArmed = false;
arm() {
this.#isArmed = true;
}
isArmed() {
return this.#isArmed;
}
disarm() {
this.#isArmed = false;
}
}
@@ -0,0 +1,32 @@
import { TicksPerSecond } from "@minecraft/server";
export class RefreshRate {
static MAX_BLOCKS_PER_TICK = 30;
static MIN_SECONDS = 1;
static MAX_SECONDS = 60;
static SECONDS_STEP = 1;
static DEFAULT_SECONDS = 15;
static clampSeconds(seconds) {
return Math.min(RefreshRate.MAX_SECONDS, Math.max(RefreshRate.MIN_SECONDS, seconds));
}
static priorityBlocksPerTick() {
return RefreshRate.MAX_BLOCKS_PER_TICK;
}
static blocksPerTick(volume, refreshSeconds) {
if (volume <= 0)
return 0;
const seconds = Math.max(refreshSeconds, RefreshRate.MIN_SECONDS);
return Math.min(volume / (seconds * TicksPerSecond), RefreshRate.MAX_BLOCKS_PER_TICK);
}
static cycleSeconds(volume, refreshSeconds) {
const rate = RefreshRate.blocksPerTick(volume, refreshSeconds);
if (rate <= 0)
return 0;
return volume / (rate * TicksPerSecond);
}
}
@@ -0,0 +1,24 @@
export class SkippedChunkTracker {
#unloadedChunks = new Set();
#outOfBoundsChunks = new Set();
startLayer() {
this.#outOfBoundsChunks.clear();
}
isSkipped(chunkKey) {
return this.#unloadedChunks.has(chunkKey) || this.#outOfBoundsChunks.has(chunkKey);
}
trackError(error, chunkKey) {
if (error?.name === 'LocationInUnloadedChunkError') {
this.#unloadedChunks.add(chunkKey);
return true;
}
if (error?.name === 'LocationOutOfWorldBoundariesError') {
this.#outOfBoundsChunks.add(chunkKey);
return true;
}
return false;
}
}
@@ -1,144 +1,147 @@
import { BlockVerifier } from "./BlockVerifier";
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { BlockVerificationLevelRender } from "../Render/BlockVerificationLevelRender";
import { system, TicksPerSecond } from "@minecraft/server";
import { system } from "@minecraft/server";
import { Vector } from "../../lib/Vector";
const MIN_TRACK_PLAYER_DISTANCE = 0;
const MAX_TRACK_PLAYER_DISTANCE = 7;
const MIN_LIFETIME = 8;
import { CellVerifier } from "./CellVerifier";
import { GridBuffers } from "./GridBuffers";
import { PriorityPass } from "./PriorityPass";
import { RefreshRate } from "./RefreshRate";
import { DebugBoxSweepObserver, SilentSweepObserver } from "./SweepObserver";
import { VerificationRun } from "./VerificationRun";
import { VerificationSweep } from "./VerificationSweep";
import { InstanceVerifierSettings, StandaloneVerifierSettings } from "./VerifierSettings";
export class StructureVerifier {
instance;
particleLifetime;
#instance;
#settings;
#buffers = new GridBuffers();
#priorityPass = new PriorityPass();
#run;
#loop;
#isPassPending = false;
locationsToVerify;
blockVerificationLevels;
isLocationPopulationComplete;
isVerificationComplete;
shouldStartNextVerification;
lastCompleteVerificationLevels;
#runner;
#verifyJob;
#populateJob = {};
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, particleLifetime = 10, isStandalone = false } = {}) {
this.instance = instance;
this.particleLifetime = Math.max(particleLifetime, MIN_LIFETIME);
if (isStandalone) {
this.isStandalone = isStandalone;
this.enabled = isEnabled;
this.trackPlayerDistance = trackPlayerDistance;
} else {
this.instance.options.setVerifierEnabled(isEnabled);
this.instance.options.setVerifierDistance(trackPlayerDistance);
}
this.locationsToVerify = new Set();
static forInstance(instance) {
return new StructureVerifier(instance, new InstanceVerifierSettings(instance));
}
startContinuousVerification() {
this.shouldStartNextVerification = true;
this.#runner = system.runInterval(() => {
if (this.shouldStartNextVerification)
this.verifyStructure();
});
static standalone(instance, options) {
return new StructureVerifier(instance, new StandaloneVerifierSettings(options));
}
stopContinuousVerification() {
if (!this.#runner)
return;
system.clearRun(this.#runner);
this.#runner = void 0;
}
refresh() {
this.stopContinuousVerification();
if (!this.instance.isEnabled())
return;
this.startContinuousVerification();
constructor(instance, settings) {
this.#instance = instance;
this.#settings = settings;
}
isEnabled() {
if (this.isStandalone)
return this.enabled;
return this.instance.options.verifier.isEnabled;
return this.#settings.isEnabled();
}
getTrackPlayerDistance() {
let distance;
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));
getCompletedGrid() {
return this.#buffers.completed();
}
refresh(isPriority = false) {
this.#stopLoop();
this.#cancelRun();
if (isPriority)
this.#priorityPass.arm();
if (this.#instance.isEnabled())
this.#startLoop();
}
async verifyStructure(shouldRender = false) {
if (!this.isEnabled())
return void 0;
const bounds = this.#instance.getActiveBounds();
const volume = Vector.volume(bounds.min, bounds.max);
if (volume <= 0)
return this.#buffers.completed();
this.#cancelRun();
const run = this.#beginRun(bounds, volume, shouldRender);
this.#finishRun(run, await run.start());
return this.#buffers.completed();
}
patchCell(location) {
const completed = this.#buffers.completed();
if (!completed)
return;
this.initVerification();
return new Promise(async (resolve) => {
if (this.#verifyJob)
system.clearJob(this.#verifyJob);
this.#verifyJob = system.runJob(this.verifyBlocks(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);
const cell = this.#tryVerifyCell(location);
if (!cell)
return;
completed.setCell(location, cell.verificationLevel, cell.flags);
const filling = this.#buffers.filling()
filling?.setCell(location, cell.verificationLevel, cell.flags);
}
#tryVerifyCell(location) {
try {
return this.#newCellVerifier().verify(location);
} catch {
return void 0;
}
}
#startLoop() {
this.#isPassPending = true;
this.#loop = system.runInterval(() => {
if (this.#isPassPending)
this.verifyStructure();
});
}
initVerification() {
this.shouldStartNextVerification = false;
this.locationsToVerify.clear();
this.blockVerificationLevels = { correctlyAir: 0 };
this.isLocationPopulationComplete = false;
this.isVerificationComplete = false;
}
*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.isVerificationComplete = true;
#stopLoop() {
if (!this.#loop)
return;
system.clearRun(this.#loop);
this.#loop = void 0;
}
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);
}
}
#beginRun(bounds, volume, shouldRender) {
this.#isPassPending = false;
this.#run = new VerificationRun(
this.#newSweep(bounds, shouldRender),
this.#blocksPerTick(volume)
);
return this.#run;
}
getVerificationLevel(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();
#blocksPerTick(volume) {
if (this.#priorityPass.isArmed())
return RefreshRate.priorityBlocksPerTick();
return this.#settings.blocksPerTick(volume);
}
getLastVerificationLevels() {
if (!this.lastCompleteVerificationLevels)
return {};
return this.lastCompleteVerificationLevels;
#finishRun(run, didComplete) {
if (this.#run === run)
this.#run = void 0;
if (!didComplete)
return;
this.#priorityPass.disarm();
this.#buffers.commit();
this.#isPassPending = true;
}
}
#cancelRun() {
this.#run?.cancel();
}
#newSweep(bounds, shouldRender) {
return new VerificationSweep({
bounds,
grid: this.#buffers.beginPass(bounds),
origin: this.#instance.toGlobalCoords({ x: 0, y: 0, z: 0 }),
cellVerifier: this.#newCellVerifier(),
observer: this.#newObserver(shouldRender)
});
}
#newCellVerifier() {
return new CellVerifier(this.#instance, this.#settings.showsBlockPreview());
}
#newObserver(shouldRender) {
if (!shouldRender)
return new SilentSweepObserver();
return new DebugBoxSweepObserver(this.#instance, this.#settings.particleLifetimeSeconds());
}
}
@@ -0,0 +1,27 @@
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { drawExpiringDebugBox } from "../Render/preview/ExpiringDebugBox";
export class SilentSweepObserver {
onCellVerified() {}
}
export class DebugBoxSweepObserver {
#instance;
#lifetimeSeconds;
constructor(instance, lifetimeSeconds) {
this.#instance = instance;
this.#lifetimeSeconds = lifetimeSeconds;
}
onCellVerified(location, verificationLevel) {
if (verificationLevel === BlockVerificationLevel.Air)
return;
drawExpiringDebugBox(
this.#instance.getDimension(),
this.#instance.toGlobalCoords(location),
verificationLevel,
this.#lifetimeSeconds
);
}
}
@@ -0,0 +1,102 @@
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel.js";
import { Side } from "../Enums/Side.js";
import { CellFlags } from "./CellFlags.js";
const LEVEL_COUNT = Object.keys(BlockVerificationLevel).length;
export class VerificationGrid {
#min;
#sizeX;
#sizeY;
#sizeZ;
#levels;
#flags;
constructor(bounds) {
this.#min = { x: bounds.min.x, y: bounds.min.y, z: bounds.min.z };
this.#sizeX = Math.max(bounds.max.x - bounds.min.x, 0);
this.#sizeY = Math.max(bounds.max.y - bounds.min.y, 0);
this.#sizeZ = Math.max(bounds.max.z - bounds.min.z, 0);
this.#levels = new Uint8Array(this.#sizeX * this.#sizeY * this.#sizeZ);
this.#flags = new Uint16Array(this.#levels.length);
}
matchesBounds(bounds) {
return this.#min.x === bounds.min.x && this.#min.y === bounds.min.y && this.#min.z === bounds.min.z
&& this.#sizeX === bounds.max.x - bounds.min.x
&& this.#sizeY === bounds.max.y - bounds.min.y
&& this.#sizeZ === bounds.max.z - bounds.min.z;
}
clear() {
this.#levels.fill(BlockVerificationLevel.Unknown);
this.#flags.fill(CellFlags.NONE);
}
setCell(location, verificationLevel, flags) {
const index = this.indexOf(location);
if (index === -1)
return;
this.#levels[index] = verificationLevel;
this.#flags[index] = flags;
}
setLevel(location, verificationLevel) {
const index = this.indexOf(location);
if (index === -1)
return;
this.#levels[index] = verificationLevel;
}
get(location) {
const index = this.indexOf(location);
if (index === -1)
return BlockVerificationLevel.Unknown;
return this.#levels[index];
}
occlusionMaskAt(location) {
let markerMask = CellFlags.NONE;
let opaqueMask = CellFlags.NONE;
for (let side = 0; side < Side.COUNT; side++) {
const neighborFlags = this.#neighborFlags(location, side);
const facingSide = Side.opposite(side);
if (CellFlags.hasMarker(neighborFlags, facingSide))
markerMask |= 1 << side;
if (CellFlags.hasOpaque(neighborFlags, facingSide))
opaqueMask |= 1 << side;
}
return CellFlags.pack(markerMask, opaqueMask);
}
#neighborFlags(location, side) {
const offset = Side.OFFSETS[side];
const index = this.#indexOfCoords(
location.x + offset.x, location.y + offset.y, location.z + offset.z
);
if (index === -1)
return CellFlags.NONE;
return this.#flags[index];
}
countByLevel() {
const counts = new Uint32Array(LEVEL_COUNT);
for (let index = 0; index < this.#levels.length; index++)
counts[this.#levels[index]]++;
return counts;
}
indexOf(location) {
return this.#indexOfCoords(location.x, location.y, location.z);
}
#indexOfCoords(x, y, z) {
const localX = x - this.#min.x;
const localY = y - this.#min.y;
const localZ = z - this.#min.z;
if (localX < 0 || localY < 0 || localZ < 0
|| localX >= this.#sizeX || localY >= this.#sizeY || localZ >= this.#sizeZ)
return -1;
return (localY * this.#sizeZ + localZ) * this.#sizeX + localX;
}
}
@@ -0,0 +1,67 @@
import { system } from "@minecraft/server";
import { BlockBudget } from "./BlockBudget";
export class VerificationRun {
#sweep;
#blocksPerTick;
#budget = new BlockBudget();
#steps;
#runner;
#settle;
#completion;
constructor(sweep, blocksPerTick) {
this.#sweep = sweep;
this.#blocksPerTick = blocksPerTick;
this.#completion = new Promise((resolve) => { this.#settle = resolve; });
}
start() {
this.#steps = this.#sweep.run();
this.#runner = system.runInterval(() => this.#advance());
return this.#completion;
}
cancel() {
this.#settleWith(false);
}
#advance() {
this.#budget.credit(this.#blocksPerTick);
try {
while (!this.#budget.isExhausted()) {
if (!this.#advanceOneStep())
return;
}
} catch (error) {
this.#settleWith(false);
throw error;
}
}
#advanceOneStep() {
const step = this.#steps.next();
if (step.done) {
this.#settleWith(true);
return false;
}
this.#budget.spend(step.value);
return true;
}
#settleWith(didComplete) {
if (!this.#settle)
return;
const settle = this.#settle;
this.#settle = void 0;
this.#stop();
settle(didComplete);
}
#stop() {
if (this.#runner !== void 0)
system.clearRun(this.#runner);
this.#runner = void 0;
this.#steps = void 0;
}
}
@@ -0,0 +1,71 @@
import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { Vector } from "../../lib/Vector";
import { ChunkGrid } from "./ChunkGrid";
import { SkippedChunkTracker } from "./SkippedChunkTracker";
export class VerificationSweep {
#bounds;
#grid;
#cellVerifier;
#observer;
#chunks;
#skippedChunks = new SkippedChunkTracker();
#location = new Vector();
constructor({ bounds, grid, cellVerifier, observer, origin }) {
this.#bounds = bounds;
this.#grid = grid;
this.#cellVerifier = cellVerifier;
this.#observer = observer;
this.#chunks = new ChunkGrid(origin);
}
*run() {
for (let y = this.#bounds.min.y; y < this.#bounds.max.y; y++) {
this.#skippedChunks.startLayer();
for (let z = this.#bounds.min.z; z < this.#bounds.max.z; z++)
yield* this.#sweepRow(y, z);
}
}
*#sweepRow(y, z) {
for (let x = this.#bounds.min.x; x < this.#bounds.max.x;) {
const endX = this.#sweepChunkSpan(x, y, z);
yield endX - x;
x = endX;
}
}
#sweepChunkSpan(startX, y, z) {
const chunkKey = this.#chunks.keyAt(startX, z);
const endX = Math.min(this.#chunks.endOfSpanX(startX), this.#bounds.max.x);
if (this.#skippedChunks.isSkipped(chunkKey))
this.#markSkipped(startX, endX, y, z);
else
this.#verifySpan(chunkKey, startX, endX, y, z);
return endX;
}
#verifySpan(chunkKey, startX, endX, y, z) {
let x = startX;
try {
for (; x < endX; x++)
this.#verifyCell(this.#location.set(x, y, z));
} catch (error) {
if (!this.#skippedChunks.trackError(error, chunkKey))
throw error;
this.#markSkipped(x, endX, y, z);
}
}
#verifyCell(location) {
const { verificationLevel, flags } = this.#cellVerifier.verify(location);
this.#grid.setCell(location, verificationLevel, flags);
this.#observer.onCellVerified(location, verificationLevel);
}
#markSkipped(startX, endX, y, z) {
for (let x = startX; x < endX; x++)
this.#grid.setLevel(this.#location.set(x, y, z), BlockVerificationLevel.Skipped);
}
}
@@ -0,0 +1,68 @@
import { TicksPerSecond } from "@minecraft/server";
import { renderProfileOf } from "../Enums/RenderMode";
import { RefreshRate } from "./RefreshRate";
const DEFAULT_PARTICLE_LIFETIME_TICKS = 10;
const DEFAULT_BLOCKS_PER_TICK = 10;
class ParticleLifetime {
static MIN_TICKS = 8;
static toSeconds(ticks) {
return Math.max(ticks, ParticleLifetime.MIN_TICKS) / TicksPerSecond;
}
}
export class InstanceVerifierSettings {
#instance;
constructor(instance) {
this.#instance = instance;
}
isEnabled() {
return this.#verifierOptions().isEnabled;
}
blocksPerTick(volume) {
return RefreshRate.blocksPerTick(volume, this.#verifierOptions().refreshSeconds);
}
showsBlockPreview() {
return renderProfileOf(this.#instance.options.renderMode).blockPreview;
}
particleLifetimeSeconds() {
return ParticleLifetime.toSeconds(this.#verifierOptions().particleLifetime);
}
#verifierOptions() {
return this.#instance.options.verifier;
}
}
export class StandaloneVerifierSettings {
#blocksPerTick;
#particleLifetimeTicks;
constructor({ blocksPerTick = DEFAULT_BLOCKS_PER_TICK, particleLifetime = DEFAULT_PARTICLE_LIFETIME_TICKS } = {}) {
this.#blocksPerTick = blocksPerTick;
this.#particleLifetimeTicks = particleLifetime;
}
isEnabled() {
return true;
}
blocksPerTick() {
return this.#blocksPerTick;
}
showsBlockPreview() {
return true;
}
particleLifetimeSeconds() {
return ParticleLifetime.toSeconds(this.#particleLifetimeTicks);
}
}