Performance help

This commit is contained in:
ForestOfLight
2025-04-17 21:16:13 -07:00
Unverified
parent 72e9b65d38
commit 35b7575d2f
5 changed files with 159 additions and 69 deletions
@@ -1,5 +1,6 @@
import { structureCollection } from './StructureCollection'; import { structureCollection } from './StructureCollection';
import { MenuForm } from '../classes/MenuForm'; import { MenuForm } from '../classes/MenuForm';
import { forceShow } from '../utils';
import { InstanceEditOptions } from './enums/InstanceEditOptions'; import { InstanceEditOptions } from './enums/InstanceEditOptions';
import { InstanceEditFormBuilder } from './InstanceEditFormBuilder'; import { InstanceEditFormBuilder } from './InstanceEditFormBuilder';
import { FormCancelationReason } from '@minecraft/server-ui'; import { FormCancelationReason } from '@minecraft/server-ui';
@@ -13,19 +14,20 @@ export class InstanceEditForm {
InstanceEditOptions.SetLayer, InstanceEditOptions.SetLayer,
InstanceEditOptions.Move, InstanceEditOptions.Move,
InstanceEditOptions.Statistics, InstanceEditOptions.Statistics,
InstanceEditOptions.RenameInstance, InstanceEditOptions.Settings,
InstanceEditOptions.DisableInstance, InstanceEditOptions.Rename,
InstanceEditOptions.Disable,
], ],
isNotEnabledAndIsNotPlaced: [ isNotEnabledAndIsNotPlaced: [
InstanceEditOptions.PlaceInstance, InstanceEditOptions.Place,
InstanceEditOptions.RenameInstance InstanceEditOptions.Rename
], ],
isNotEnabledButIsPlaced: [ isNotEnabledButIsPlaced: [
InstanceEditOptions.EnableInstance, InstanceEditOptions.Enable,
InstanceEditOptions.RenameInstance InstanceEditOptions.Rename
], ],
common: [ common: [
InstanceEditOptions.DeleteInstance, InstanceEditOptions.Delete,
InstanceEditOptions.MainMenu InstanceEditOptions.MainMenu
] ]
} }
@@ -39,7 +41,7 @@ export class InstanceEditForm {
show() { show() {
const currentOptions = this.getActiveOptions(); const currentOptions = this.getActiveOptions();
InstanceEditFormBuilder.buildInstance(this.instance, currentOptions).show(this.player).then((response) => { forceShow(this.player, InstanceEditFormBuilder.buildInstance(this.instance, currentOptions)).then((response) => {
if (response.canceled) return; if (response.canceled) return;
this.handleOption(currentOptions[response.selection]); this.handleOption(currentOptions[response.selection]);
}); });
@@ -66,19 +68,19 @@ export class InstanceEditForm {
handleOption(option) { handleOption(option) {
switch (option) { switch (option) {
case InstanceEditOptions.EnableInstance: case InstanceEditOptions.Enable:
this.instance.enable(); this.instance.enable();
break; break;
case InstanceEditOptions.DisableInstance: case InstanceEditOptions.Disable:
this.instance.disable(); this.instance.disable();
break; break;
case InstanceEditOptions.PlaceInstance: case InstanceEditOptions.Place:
this.instance.place(this.player.dimension.id, this.player.location); this.instance.place(this.player.dimension.id, this.player.location);
break; break;
case InstanceEditOptions.RenameInstance: case InstanceEditOptions.Rename:
this.renameInstanceForm(); this.renameInstanceForm();
break; break;
case InstanceEditOptions.DeleteInstance: case InstanceEditOptions.Delete:
structureCollection.delete(this.instanceName); structureCollection.delete(this.instanceName);
break; break;
case InstanceEditOptions.NextLayer: case InstanceEditOptions.NextLayer:
@@ -101,6 +103,9 @@ export class InstanceEditForm {
case InstanceEditOptions.MainMenu: case InstanceEditOptions.MainMenu:
new MenuForm(this.player, { jumpToInstance: false }); new MenuForm(this.player, { jumpToInstance: false });
break; break;
case InstanceEditOptions.Settings:
this.settingsForm();
break;
default: default:
this.player.sendMessage(`§cUnknown option: ${option}`); this.player.sendMessage(`§cUnknown option: ${option}`);
break; break;
@@ -142,4 +147,14 @@ export class InstanceEditForm {
this.player.sendMessage(statsForm.stats); this.player.sendMessage(statsForm.stats);
}); });
} }
settingsForm() {
InstanceEditFormBuilder.buildSettings(this.instance).show(this.player).then((response) => {
if (response.canceled)
return;
const shouldRender = response.formValues[0];
const trackPlayerDistance = response.formValues[1];
this.instance.setVerifierOptions({ shouldRender, trackPlayerDistance });
});
}
} }
@@ -2,6 +2,7 @@ import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
import { MenuFormBuilder } from './MenuFormBuilder'; import { MenuFormBuilder } from './MenuFormBuilder';
import { StructureVerifier } from './StructureVerifier'; import { StructureVerifier } from './StructureVerifier';
import { StructureStatistics } from './StructureStatistics'; import { StructureStatistics } from './StructureStatistics';
import { TicksPerSecond } from '@minecraft/server';
export class InstanceEditFormBuilder { export class InstanceEditFormBuilder {
static buildInstance(instance, options) { static buildInstance(instance, options) {
@@ -36,11 +37,19 @@ export class InstanceEditFormBuilder {
static async buildStatistics(instance) { static async buildStatistics(instance) {
const buildStatisticsForm = new ActionFormData() const buildStatisticsForm = new ActionFormData()
.title(MenuFormBuilder.menuTitle) .title(MenuFormBuilder.menuTitle)
const structureVerifier = new StructureVerifier(instance); const structureVerifier = new StructureVerifier(instance, { shouldRender: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond });
const verification = await structureVerifier.verifyStructure(); const verification = await structureVerifier.verifyStructure();
const statistics = new StructureStatistics(instance, verification); const statistics = new StructureStatistics(instance, verification);
const statsMessage = statistics.getMessage(); const statsMessage = statistics.getMessage();
buildStatisticsForm.body(statsMessage); buildStatisticsForm.body(statsMessage);
return { form: buildStatisticsForm, stats: statsMessage }; return { form: buildStatisticsForm, stats: statsMessage };
} }
static buildSettings(instance) {
return new ModalFormData()
.title(MenuFormBuilder.menuTitle)
.toggle('Toggle block validation.', instance.verifier.shouldRender)
.slider('Use the slider to restrict how far from players block validation occurs. Use 0 for no restriction.', 0, 10, 1, instance.verifier.trackPlayerDistance)
.submitButton('§aApply');
}
} }
@@ -1,5 +1,5 @@
import { world } from "@minecraft/server"; import { world } from "@minecraft/server";
import { Outliner } from "./Outliner"; import { Vector } from "../lib/Vector";
import { StructureOutliner } from "./StructureOutliner"; import { StructureOutliner } from "./StructureOutliner";
import { StructureVerifier } from "./StructureVerifier"; import { StructureVerifier } from "./StructureVerifier";
@@ -13,7 +13,12 @@ export class StructureInstance {
worldLocation: { x: 0, y: 0, z: 0 }, worldLocation: { x: 0, y: 0, z: 0 },
rotation: 0, rotation: 0,
mirror: false, mirror: false,
currentLayer: 0 currentLayer: 0,
verifier: {
shouldRender: true,
trackPlayerDistance: 5,
intervalOrLifetime: 10
}
}; };
outliner = void 0; outliner = void 0;
verifier = void 0; verifier = void 0;
@@ -83,39 +88,44 @@ export class StructureInstance {
try { try {
dimension = world.getDimension(this.#options.dimensionId); dimension = world.getDimension(this.#options.dimensionId);
} catch (e) { } catch (e) {
console.warn(`[StrucTool] Dimension '${this.#options.dimensionId}' not found. Defaulting to 'minecraft:overworld'.`);
dimension = world.getDimension("minecraft:overworld"); dimension = world.getDimension("minecraft:overworld");
} }
return dimension; return dimension;
} }
getBlock(structureLocation) { getBlock(structureLocation) {
return this.#structure.getBlockPermutation(structureLocation); const blockPermutation = this.#structure.getBlockPermutation({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z });
if (!blockPermutation)
return void 0;
blockPermutation.location = structureLocation;
return blockPermutation;
} }
*getBlocks() { *getBlocks(locations = void 0) {
const max = this.#structure.size; if (locations === void 0) {
for (let y = 0; y < max.y; y++) { for (let y = 0; y < this.#structure.size.y; y++) {
yield * this.getLayerBlocks(y); yield * this.getLayerBlocks(y);
} }
} else {
for (const location of locations) {
yield this.getBlock(location);
}
}
} }
*getLayerBlocks(y) { *getLayerBlocks(y) {
const max = this.#structure.size; for (let x = 0; x < this.#structure.size.x; x++) {
for (let x = 0; x < max.x; x++) { for (let z = 0; z < this.#structure.size.z; z++) {
for (let z = 0; z < max.z; z++) { yield this.getBlock({ x, y, z });
const blockPermutation = this.#structure.getBlockPermutation({ x, y, z });
if (!blockPermutation)
yield void 0;
blockPermutation.location = { x, y, z };
yield blockPermutation;
} }
} }
} }
getBounds() { getBounds() {
return { return {
min: { x: 0, y: 0, z: 0 }, min: new Vector(0, 0, 0),
max: this.#structure.size max: new Vector(this.#structure.size.x, this.#structure.size.y, this.#structure.size.z)
}; };
} }
@@ -123,8 +133,8 @@ export class StructureInstance {
if (!this.#options.isEnabled) if (!this.#options.isEnabled)
throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`); throw new Error(`[StrucTool] Instance '${this.name}' is not placed.`);
return { return {
min: { x: 0, y: this.#options.currentLayer - 1, z: 0 }, min: new Vector(0, this.#options.currentLayer - 1, 0),
max: { x: this.#structure.size.x, y: this.#options.currentLayer, z: this.#structure.size.z } max: new Vector(this.#structure.size.x, this.#options.currentLayer, this.#structure.size.z)
}; };
} }
@@ -184,7 +194,7 @@ export class StructureInstance {
if (!this.outliner) if (!this.outliner)
this.outliner = new StructureOutliner(this); this.outliner = new StructureOutliner(this);
if (!this.verifier) if (!this.verifier)
this.verifier = new StructureVerifier(this, { shouldRender: true }); this.verifier = new StructureVerifier(this, { shouldRender: this.#options.verifier.shouldRender, trackPlayerDistance: this.#options.verifier.trackPlayerDistance });
this.outliner.refresh(); this.outliner.refresh();
this.verifier.refresh(); this.verifier.refresh();
} }
@@ -216,19 +226,19 @@ export class StructureInstance {
} }
toGlobalCoords(structureLocation) { toGlobalCoords(structureLocation) {
return { return new Vector(
x: this.#options.worldLocation.x + structureLocation.x, this.#options.worldLocation.x + structureLocation.x,
y: this.#options.worldLocation.y + structureLocation.y, this.#options.worldLocation.y + structureLocation.y,
z: this.#options.worldLocation.z + structureLocation.z this.#options.worldLocation.z + structureLocation.z
}; );
} }
toStructureCoords(worldLocation) { toStructureCoords(worldLocation) {
return { return new Vector(
x: worldLocation.x - this.#options.worldLocation.x, worldLocation.x - this.#options.worldLocation.x,
y: worldLocation.y - this.#options.worldLocation.y, worldLocation.y - this.#options.worldLocation.y,
z: worldLocation.z - this.#options.worldLocation.z worldLocation.z - this.#options.worldLocation.z
}; );
} }
isEnabled() { isEnabled() {
@@ -272,4 +282,13 @@ export class StructureInstance {
getDimension() { getDimension() {
return world.getDimension(this.#options.dimensionId); return world.getDimension(this.#options.dimensionId);
} }
setVerifierOptions({ shouldRender, trackPlayerDistance, intervalOrLifetime }) {
this.#options.verifier.shouldRender = shouldRender;
this.#options.verifier.trackPlayerDistance = trackPlayerDistance;
this.#options.verifier.intervalOrLifetime = intervalOrLifetime;
this.updateOptions();
this.verifier.setOptions(this.#options.verifier);
this.verifier.refresh();
}
} }
@@ -1,24 +1,32 @@
import { BlockVerifier } from "./BlockVerifier"; import { BlockVerifier } from "./BlockVerifier";
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevel } from "./enums/BlockVerificationLevel";
import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender"; import { BlockVerificationLevelRender } from "./BlockVerificationLevelRender";
import { system, TicksPerSecond } from "@minecraft/server"; import { system, TicksPerSecond, world } from "@minecraft/server";
const MIN_TRACK_PLAYER_DISTANCE = 0;
const MAX_TRACK_PLAYER_DISTANCE = 7;
const MIN_LIFETIME = 8;
export class StructureVerifier { export class StructureVerifier {
instance;
blockVerificationLevels;
shouldRender; shouldRender;
isComplete; trackPlayerDistance;
interval = 5*20; intervalOrLifetime;
isBlockPopulationComplete;
isVerificationComplete;
#runner; #runner;
constructor(instance, { shouldRender = false } = {}) { constructor(instance, { shouldRender = false, trackPlayerDistance = 1, intervalOrLifetime = 10 } = {}) {
this.shouldRender = shouldRender;
this.instance = instance; this.instance = instance;
this.interval = Math.max(instance.getActiveVolume() / 50, 20); this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
this.setOptions({ shouldRender, trackPlayerDistance });
} }
startContinuousVerification() { startContinuousVerification() {
this.#runner = system.runInterval(() => { this.#runner = system.runInterval(() => {
this.verifyStructure(); this.verifyStructure();
}, this.interval); }, this.intervalOrLifetime);
} }
stopContinuousVerification() { stopContinuousVerification() {
@@ -35,21 +43,28 @@ export class StructureVerifier {
this.startContinuousVerification(); this.startContinuousVerification();
} }
init(shouldRender) { init() {
this.locationsToVerify = new Set();
this.blocksToVerify = [];
this.blockVerificationLevels = { correctlyAir: 0 };
this.isBlockPopulationComplete = false;
this.isVerificationComplete = false;
}
setOptions({ shouldRender = false, trackPlayerDistance = 0 }) {
this.blockVerificationLevels = { correctlyAir: 0 }; this.blockVerificationLevels = { correctlyAir: 0 };
this.shouldRender = shouldRender; this.shouldRender = shouldRender;
this.isComplete = false; this.trackPlayerDistance = Math.min(trackPlayerDistance, MAX_TRACK_PLAYER_DISTANCE);
this.isVerificationComplete = false;
} }
async verifyStructure() { async verifyStructure() {
this.init(this.shouldRender); this.init();
return new Promise((resolve) => { return new Promise(async (resolve) => {
if (this.instance.isUsingLayers()) await this.populateBlocksToVerify();
system.runJob(this.verifyBlocks(this.instance.getLayerBlocks(this.instance.getLayer()-1))); system.runJob(this.verifyBlocks(this.blocksToVerify));
else
system.runJob(this.verifyBlocks(this.instance.getBlocks()));
const checker = system.runInterval(() => { const checker = system.runInterval(() => {
if (this.isComplete) { if (this.isVerificationComplete) {
system.clearRun(checker); system.clearRun(checker);
resolve(this.blockVerificationLevels); resolve(this.blockVerificationLevels);
} }
@@ -57,6 +72,38 @@ export class StructureVerifier {
}); });
} }
populateBlocksToVerify() {
return new Promise((resolve) => {
if (this.trackPlayerDistance == 0)
return this.instance.getBlocks();
this.locationsToVerify = new Set();
for (const player of this.instance.getDimension().getPlayers())
system.runJob(this.populateActiveLocationsNearPlayer(player));
this.blocksToVerify = this.instance.getBlocks(this.locationsToVerify);
const checker = system.runInterval(() => {
if (this.isBlockPopulationComplete) {
system.clearRun(checker);
resolve();
}
}, 1);
});
}
*populateActiveLocationsNearPlayer(player) {
for (let x = -this.trackPlayerDistance; x < this.trackPlayerDistance; x++) {
for (let y = -this.trackPlayerDistance; y < this.trackPlayerDistance; y++) {
for (let z = -this.trackPlayerDistance; z < this.trackPlayerDistance; z++) {
const structureLocation = this.instance.toStructureCoords({ x: player.location.x + x, y: player.location.y + y, z: player.location.z + z });
if (this.instance.isLocationActive(player.dimension.id, structureLocation, { useLayers: true })) {
this.locationsToVerify.add({ x: structureLocation.x, y: structureLocation.y, z: structureLocation.z });
yield void 0;
}
}
}
}
this.isBlockPopulationComplete = true;
}
*verifyBlocks(blocks) { *verifyBlocks(blocks) {
for (const block of blocks) { for (const block of blocks) {
const verificationLevel = this.verifyBlock(block.location); const verificationLevel = this.verifyBlock(block.location);
@@ -66,19 +113,18 @@ export class StructureVerifier {
this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel; this.blockVerificationLevels[JSON.stringify(block.location)] = verificationLevel;
if (this.shouldRender) { if (this.shouldRender) {
const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) }; const dimensionLocation = { dimension: this.instance.getDimension(), location: this.instance.toGlobalCoords(block.location) };
new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.interval/TicksPerSecond); new BlockVerificationLevelRender(dimensionLocation, verificationLevel, this.intervalOrLifetime/TicksPerSecond);
} }
} }
yield void 0; yield void 0;
} }
this.isComplete = true; this.isVerificationComplete = true;
} }
verifyBlock(location) { verifyBlock(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;
}
const blockVerifier = new BlockVerifier(worldBlock, this.instance); const blockVerifier = new BlockVerifier(worldBlock, this.instance);
return blockVerifier.verify(); return blockVerifier.verify();
} }
@@ -1,14 +1,15 @@
export const InstanceEditOptions = Object.freeze({ export const InstanceEditOptions = Object.freeze({
Unknown: 'Unknown', Unknown: 'Unknown',
MainMenu: '<<', MainMenu: '<<',
PlaceInstance: '§aPlace Instance', Place: '§aPlace Instance',
EnableInstance: '§aEnable Instance', Enable: '§aEnable Instance',
DisableInstance: '§cDisable Instance', Disable: '§cDisable Instance',
RenameInstance: 'Rename Instance', Rename: 'Rename Instance',
DeleteInstance: '§cDelete Instance', Delete: '§cDelete Instance',
NextLayer: 'Increase Layer', NextLayer: 'Increase Layer',
PreviousLayer: 'Decrease Layer', PreviousLayer: 'Decrease Layer',
SetLayer: 'Set Layer', SetLayer: 'Set Layer',
Move: 'Move Here', Move: 'Move Here',
Statistics: 'Statistics', Statistics: 'Statistics',
Settings: 'Settings'
}); });