Now is standalone from Canopy

This commit is contained in:
ForestOfLight
2025-04-20 02:06:55 -07:00
Unverified
parent d6c9acdaab
commit 1ca91a0993
28 changed files with 256 additions and 151 deletions
@@ -1,22 +1,15 @@
import { BuilderOptions } from "./BuilderOptions"; import { BuilderOptions } from "./BuilderOptions";
import { Builders } from "./Builders";
export class Builder { export class Builder {
constructor(playerId) { constructor(playerId) {
this.playerId = playerId; this.playerId = playerId;
this.options = new BuilderOptions(playerId);
Builders.add(this);
} }
getOptionIds() { isOptionEnabled(optionId) {
return Object.keys(this.options.options).sort((a, b) => a.localeCompare(b)); return BuilderOptions.isEnabled(optionId, this.playerId);
} }
getOption(id) { setOption(optionId, value) {
return this.options.get(id); return BuilderOptions.setValue(optionId, this.playerId, value);
}
setOption(id, value) {
return this.options.setValue(id, value);
} }
} }
@@ -1,24 +1,37 @@
import { BuilderFormBuilder } from "./BuilderFormBuilder"; import { BuilderFormBuilder } from "./BuilderFormBuilder";
import { Builders } from "./Builders"; import { BuilderOptions } from "./BuilderOptions";
import { forceShow } from '../../utils';
export class BuilderForm { export class BuilderForm {
constructor(player) { constructor(player) {
this.player = player; this.player = player;
this.show();
} }
show() { show() {
forceShow(this.player, BuilderFormBuilder.buildSettings(this.player)).then((response) => { forceShow(this.player, BuilderFormBuilder.buildBuilderOptions(this.player)).then((response) => {
if (response.canceled) return; if (response.canceled) return;
this.applySettings(response.formValues); this.applySettings(response.formValues);
}); });
} }
applySettings(formValues) { applySettings(formValues) {
const optionIds = Builders.get(this.player.id).getOptionIds(); const optionIds = BuilderOptions.getOptionIds();
for (let i = 0; i < optionIds.length; i++) { for (let i = 0; i < optionIds.length; i++) {
const option = optionIds[i]; const option = BuilderOptions.get(optionIds[i]);
if (option.setValue(formValues[i]) && formValues[i] === true) const changedToValue = option.setValue(this.player.id, formValues[i]);
this.player.sendMessage(`§a${option.displayName} is enabled!§7 ${option.howToUse}`); if (changedToValue === true)
this.player.sendMessage(`§a${option.displayName} is now enabled!§7 ${option.howToUse}`);
else if (changedToValue === false)
this.player.sendMessage(`§c${option.displayName} is now disabled.`)
} }
} }
valueChangedToEnabled(hasChanged, newValue) {
return hasChanged && newValue === true;
}
valueChangedToDisabled(hasChanged, newValue) {
return hasChanged && newValue === false;
}
} }
@@ -1,15 +1,16 @@
import { ModalFormData } from "@minecraft/server-ui"; import { ModalFormData } from "@minecraft/server-ui";
import { MenuFormBuilder } from "../MenuFormBuilder"; import { MenuFormBuilder } from "../MenuFormBuilder";
import { Builders } from "./Builders"; import { BuilderOptions } from "./BuilderOptions";
export class BuilderFormBuilder { export class BuilderFormBuilder {
static buildSettings(player) { static buildBuilderOptions(player) {
const form = new ModalFormData() const form = new ModalFormData()
.title(MenuFormBuilder.menuTitle); .title(MenuFormBuilder.menuTitle);
const builder = Builders.get(player.id); for (const optionId of BuilderOptions.getOptionIds()) {
for (const optionId of builder.getOptionIds()) { const option = BuilderOptions.get(optionId);
const option = builder.getOption(optionId); form.toggle(`${option.displayName} - ${option.description}`, option.isEnabled(player.id));
form.toggle(`${option.displayName} - ${option.description}`, option.getValue());
} }
form.submitButton('§2Apply');
return form;
} }
} }
@@ -1,18 +1,16 @@
import { BuilderOptions } from "./BuilderOptions"; import { BuilderOptions } from "./BuilderOptions";
import { Option } from "../Option"; import { world } from "@minecraft/server";
export class BuilderOption extends Option { export class BuilderOption {
playerId;
identifier; identifier;
displayName; displayName;
description; description;
value; howToUse;
#onEnable; #onEnable;
#onDisable; #onDisable;
#DP_NAMESPACE = "builderOptions"; #DP_NAMESPACE = "builderOptions";
constructor({ player, identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) { constructor({ identifier, displayName, description, howToUse, onEnableCallback = () => {}, onDisableCallback = () => {} }) {
this.playerId = player.id;
this.identifier = identifier; this.identifier = identifier;
this.displayName = displayName; this.displayName = displayName;
this.description = description; this.description = description;
@@ -22,32 +20,24 @@ export class BuilderOption extends Option {
BuilderOptions.add(this); BuilderOptions.add(this);
} }
save() { isEnabled(playerId) {
this.saveToDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`, this); return world.getDynamicProperty(`${this.#DP_NAMESPACE}:${playerId}:${this.identifier}`) === true;
} }
load() { setValue(playerId, value) {
this.loadFromDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`);
this.value = this.value ?? false;
}
clear() {
this.clearDP(this.#DP_NAMESPACE, `${this.playerId}:${this.identifier}`);
}
getValue() {
return this.value;
}
setValue(value) {
if (value) if (value)
this.#onEnable(); this.#onEnable(playerId);
else else
this.#onDisable(); this.#onDisable(playerId);
if (this.value !== value) { if (this.isEnabled(playerId) !== value) {
this.value = value; this.save(playerId, value);
return value; return value;
} }
this.save(playerId, value);
return void 0; return void 0;
} }
save(playerId, value) {
world.setDynamicProperty(`${this.#DP_NAMESPACE}:${playerId}:${this.identifier}`, value);
}
} }
@@ -1,34 +1,23 @@
import { world } from "@minecraft/server";
import { BuilderOption } from "./BuilderOption";
export class BuilderOptions { export class BuilderOptions {
playerId = void 0; static options = {};
options = {};
constructor(playerId) { static add(builderOption) {
this.playerId = playerId; this.options[builderOption.identifier] = builderOption;
this.loadOptions();
} }
loadOptions() { static get(optionId) {
const optionDPs = world.getDynamicPropertyIds().filter((id) => id.startsWith(`builderOptions:${this.playerId}:`)); return this.options[optionId];
for (const optionDP of optionDPs)
new BuilderOption(JSON.parse(world.getDynamicProperty(optionDP)));
} }
add(builderOption) { static getOptionIds() {
this.options[builderOption.identifer] = builderOption; return Object.keys(this.options).sort((a, b) => a - b);
} }
get(id) { static isEnabled(optionId, playerId) {
return this.options[id]; return this.options[optionId].isEnabled(playerId);
} }
getValue(id) { static setValue(optionId, playerId, value) {
return this.options[id].getValue(); return this.options[optionId].setValue(playerId, value);
}
setValue(id, value) {
return this.options[id].setValue(value);
} }
} }
@@ -4,22 +4,33 @@ import { Builder } from "./Builder";
export class Builders { export class Builders {
static builders = {}; static builders = {};
add(playerId) { static add(playerId) {
if (this.builders[playerId])
return;
this.builders[playerId] = new Builder(playerId); this.builders[playerId] = new Builder(playerId);
} }
remove(playerId) { static remove(playerId) {
delete this.builders[playerId]; delete this.builders[playerId];
} }
get(id) { static get(id) {
return this.builders[id]; return this.builders[id];
} }
onJoin(playerId) { static onJoin(playerId) {
this.add(playerId); this.add(playerId);
} }
static onLeave(playerId) {
this.remove(playerId);
}
} }
world.afterEvents.playerJoin.subscribe((event) => Builders.onJoin(event.playerId)); world.afterEvents.playerJoin.subscribe((event) => Builders.onJoin(event.playerId));
world.beforeEvents.playerLeave.subscribe((event) => Builders.onLeave(event.player)); world.beforeEvents.playerLeave.subscribe((event) => Builders.onLeave(event.player.id));
world.afterEvents.worldLoad.subscribe((event) => {
for (const player of world.getAllPlayers()) {
Builders.onJoin(player.id);
}
});
@@ -0,0 +1,6 @@
export class InvalidInstanceError extends Error {
constructor(message) {
super(message);
this.name = 'InvalidInstanceError';
}
}
@@ -1,7 +1,7 @@
import { structureCollection } from './StructureCollection'; import { structureCollection } from '../Structure/StructureCollection';
import { MenuForm } from '../MenuForm'; import { MenuForm } from '../MenuForm';
import { forceShow } from '../../utils'; import { forceShow } from '../../utils';
import { InstanceButtons } from '../enums/InstanceButtons'; import { InstanceButtons } from '../Enums/InstanceButtons';
import { InstanceFormBuilder } from './InstanceFormBuilder'; import { InstanceFormBuilder } from './InstanceFormBuilder';
import { FormCancelationReason } from '@minecraft/server-ui'; import { FormCancelationReason } from '@minecraft/server-ui';
@@ -11,12 +11,11 @@ export class InstanceForm {
isEnabled: [ isEnabled: [
InstanceButtons.NextLayer, InstanceButtons.NextLayer,
InstanceButtons.PreviousLayer, InstanceButtons.PreviousLayer,
InstanceButtons.SetLayer,
InstanceButtons.Move, InstanceButtons.Move,
InstanceButtons.Statistics, InstanceButtons.Statistics,
InstanceButtons.Settings, InstanceButtons.Settings,
InstanceButtons.Rename, InstanceButtons.Rename,
InstanceButtons.Disable, InstanceButtons.Disable
], ],
isNotEnabledAndIsNotPlaced: [ isNotEnabledAndIsNotPlaced: [
InstanceButtons.Place, InstanceButtons.Place,
@@ -149,7 +148,11 @@ export class InstanceForm {
if (response.canceled) if (response.canceled)
return; return;
this.instance.setVerifierEnabled(response.formValues[0]); this.instance.setVerifierEnabled(response.formValues[0]);
this.instance.setLayer(parseInt(response.formValues[1])); if (response.formValues[1])
this.instance.setVerifierDistance(5);
else
this.instance.setVerifierDistance(0);
this.instance.setLayer(parseInt(response.formValues[2]));
}); });
} }
} }
@@ -1,7 +1,7 @@
import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
import { MenuFormBuilder } from '../MenuFormBuilder'; import { MenuFormBuilder } from '../MenuFormBuilder';
import { StructureVerifier } from './StructureVerifier'; import { StructureVerifier } from '../Verifier/StructureVerifier';
import { StructureStatistics } from './StructureStatistics'; import { StructureStatistics } from '../Structure/StructureStatistics';
import { TicksPerSecond } from '@minecraft/server'; import { TicksPerSecond } from '@minecraft/server';
export class InstanceFormBuilder { export class InstanceFormBuilder {
@@ -29,7 +29,7 @@ export class InstanceFormBuilder {
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, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond }); const structureVerifier = new StructureVerifier(instance, { isEnabled: true, trackPlayerDistance: 0, intervalOrLifetime: 30 * TicksPerSecond, isStandalone: true });
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();
@@ -42,7 +42,8 @@ export class InstanceFormBuilder {
.title(MenuFormBuilder.menuTitle) .title(MenuFormBuilder.menuTitle)
.label('Use the slider to select the layer. Use 0 for all layers.') .label('Use the slider to select the layer. Use 0 for all layers.')
.toggle('Block Validation', instance.options.verifier.isEnabled) .toggle('Block Validation', instance.options.verifier.isEnabled)
.slider("Layer", 0, maxLayer, 1, currentLayer) .toggle('Distance-Based Block Validation', instance.verifier.getTrackPlayerDistance() !== 0)
.submitButton('§aApply'); .slider("Layer", 0, instance.getMaxLayer(), 1, instance.getLayer())
.submitButton('§2Apply');
} }
} }
@@ -22,6 +22,7 @@ export class InstanceOptions extends Option {
} }
constructor(instanceName, structureId) { constructor(instanceName, structureId) {
super();
this.instanceName = instanceName; this.instanceName = instanceName;
this.structureId = structureId; this.structureId = structureId;
this.load(); this.load();
@@ -1,8 +1,8 @@
import { Vector } from "../lib/Vector"; import { Vector } from "../../lib/Vector";
import { StructureOutliner } from "./StructureOutliner"; import { StructureOutliner } from "../Structure/StructureOutliner";
import { StructureVerifier } from "./StructureVerifier"; import { StructureVerifier } from "../Verifier/StructureVerifier";
import { Structure } from "../Structure/Structure";
import { InstanceOptions } from "./InstanceOptions"; import { InstanceOptions } from "./InstanceOptions";
import { Structure } from "./Structure";
import { TicksPerSecond } from "@minecraft/server"; import { TicksPerSecond } from "@minecraft/server";
export class StructureInstance { export class StructureInstance {
@@ -19,11 +19,11 @@ export class StructureInstance {
delete() { delete() {
this.disable(); this.disable();
this.options.clear();
delete this.options; delete this.options;
delete this.structure; delete this.structure;
delete this.outliner; delete this.outliner;
delete this.verifier; delete this.verifier;
this.options.clear();
} }
refreshBox() { refreshBox() {
+12 -4
View File
@@ -1,7 +1,8 @@
import { forceShow } from '../utils'; import { forceShow } from '../utils';
import { structureCollection } from './StructureCollection'; import { structureCollection } from './Structure/StructureCollection';
import { MenuFormBuilder } from './MenuFormBuilder'; import { MenuFormBuilder } from './MenuFormBuilder';
import { InstanceForm } from './InstanceForm'; import { InstanceForm } from './Instance/InstanceForm';
import { BuilderForm } from './Builder/BuilderForm';
export class MenuForm { export class MenuForm {
constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) { constructor(player, { jumpToInstance = false, instanceName = void 0 } = {}) {
@@ -13,7 +14,7 @@ export class MenuForm {
if (jumpToInstance) { if (jumpToInstance) {
if (!instanceName) if (!instanceName)
instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName(); instanceName = structureCollection.getStructure(this.player.dimension.id, this.player.location, { useActiveLayer: false })?.getName();
if (structureCollection.get(instanceName)) { if (instanceName) {
new InstanceForm(this.player, instanceName); new InstanceForm(this.player, instanceName);
return; return;
} }
@@ -29,8 +30,15 @@ export class MenuForm {
return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => { return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => {
if (response.canceled) if (response.canceled)
return; return;
const selectedInstanceName = structureCollection.getInstanceNames()[response.selection]; let selection = response.selection;
if (selection === 0) {
new BuilderForm(this.player);
return void 0;
} else {
selection--;
const selectedInstanceName = structureCollection.getInstanceNames()[selection];
return selectedInstanceName || this.createNewInstance(); return selectedInstanceName || this.createNewInstance();
}
}); });
} catch (e) { } catch (e) {
if (e.message === 'Menu timed out.') { if (e.message === 'Menu timed out.') {
@@ -1,13 +1,14 @@
import { ActionFormData, ModalFormData } from '@minecraft/server-ui'; import { ActionFormData, ModalFormData } from '@minecraft/server-ui';
import { structureCollection } from './StructureCollection'; import { structureCollection } from './Structure/StructureCollection';
export class MenuFormBuilder { export class MenuFormBuilder {
static menuTitle = '§l§2Construct §8Menu'; static menuTitle = '§l§2Construct';
static buildAllInstanceName() { static buildAllInstanceName() {
const allInstanceNameForm = new ActionFormData() const allInstanceNameForm = new ActionFormData()
.title(this.menuTitle) .title(this.menuTitle)
.body('Select an instance:'); .body('Select an instance:');
allInstanceNameForm.button('Builder Settings');
structureCollection.getInstanceNames().forEach(instanceName => { structureCollection.getInstanceNames().forEach(instanceName => {
allInstanceNameForm.button(`§2${instanceName}`); allInstanceNameForm.button(`§2${instanceName}`);
}); });
+1 -1
View File
@@ -1,4 +1,4 @@
import { structureCollection } from "./StructureCollection"; import { structureCollection } from "./Structure/StructureCollection";
import { world } from "@minecraft/server"; import { world } from "@minecraft/server";
export class Raycaster { export class Raycaster {
@@ -1,5 +1,5 @@
import { world } from "@minecraft/server"; import { world } from "@minecraft/server";
import { Vector } from "../lib/Vector"; import { Vector } from "../../lib/Vector";
export class Structure { export class Structure {
structureId; structureId;
@@ -1,5 +1,6 @@
import { InstanceOptions } from './InstanceOptions'; import { InvalidInstanceError } from '../Errors/InvalidInstanceError';
import { StructureInstance } from './StructureInstance'; import { InstanceOptions } from '../Instance/InstanceOptions';
import { StructureInstance } from '../Instance/StructureInstance';
import { world } from '@minecraft/server'; import { world } from '@minecraft/server';
class StructureCollection { class StructureCollection {
@@ -26,7 +27,7 @@ class StructureCollection {
add(instanceName, structureId) { add(instanceName, structureId) {
if (this.structures[instanceName]) if (this.structures[instanceName])
throw new Error(`Instance ${instanceName} already exists.`); throw new InvalidInstanceError(`Instance ${instanceName} already exists.`);
const structure = new StructureInstance(instanceName, structureId); const structure = new StructureInstance(instanceName, structureId);
this.structures[instanceName] = structure; this.structures[instanceName] = structure;
return structure; return structure;
@@ -34,9 +35,8 @@ class StructureCollection {
get(instanceName) { get(instanceName) {
const structure = this.structures[instanceName]; const structure = this.structures[instanceName];
if (!structure) { if (!structure)
throw new Error(`Instance ${instanceName} not found.`); throw new InvalidInstanceError(`Instance ${instanceName} not found.`);
}
return structure; return structure;
} }
@@ -1,4 +1,4 @@
import { Outliner } from './Outliner'; import { Outliner } from '../Outliner';
export class StructureOutliner { export class StructureOutliner {
constructor(instance) { constructor(instance) {
@@ -42,7 +42,7 @@ export class StructureOutliner {
} }
layeredDraw() { layeredDraw() {
const { min, max } = this.instance.getLayeredBounds(); const { min, max } = this.instance.getLayerBounds(this.instance.getLayer());
this.outliner.setVertices(this.dimension, this.instance.toGlobalCoords(min), this.instance.toGlobalCoords(max)); this.outliner.setVertices(this.dimension, this.instance.toGlobalCoords(min), this.instance.toGlobalCoords(max));
this.outliner.addStandaloneParticles(this.getCornerVertices()); this.outliner.addStandaloneParticles(this.getCornerVertices());
} }
@@ -1,4 +1,4 @@
import { BlockVerificationLevel } from './enums/BlockVerificationLevel.js'; import { BlockVerificationLevel } from '../Enums/BlockVerificationLevel.js';
export class StructureStatistics { export class StructureStatistics {
constructor(instance, verification) { constructor(instance, verification) {
@@ -1,6 +1,6 @@
import { MolangVariableMap } from "@minecraft/server"; import { MolangVariableMap } from "@minecraft/server";
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
import { Vector } from "../lib/Vector"; import { Vector } from "../../lib/Vector";
export class BlockVerificationLevelRender { export class BlockVerificationLevelRender {
opacity = 0.2; opacity = 0.2;
@@ -1,4 +1,4 @@
import { BlockVerificationLevel } from "./enums/BlockVerificationLevel"; import { BlockVerificationLevel } from "../Enums/BlockVerificationLevel";
export class BlockVerifier { export class BlockVerifier {
constructor(block, instance) { constructor(block, instance) {
@@ -1,8 +1,8 @@
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 "../Verifier/BlockVerificationLevelRender";
import { system, TicksPerSecond } from "@minecraft/server"; import { system, TicksPerSecond } from "@minecraft/server";
import { Vector } from "../lib/Vector"; import { Vector } from "../../lib/Vector";
const MIN_TRACK_PLAYER_DISTANCE = 0; const MIN_TRACK_PLAYER_DISTANCE = 0;
const MAX_TRACK_PLAYER_DISTANCE = 7; const MAX_TRACK_PLAYER_DISTANCE = 7;
@@ -21,11 +21,17 @@ export class StructureVerifier {
#verifyJob; #verifyJob;
#populateJob = {}; #populateJob = {};
constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10 } = {}) { constructor(instance, { isEnabled = false, trackPlayerDistance = 0, intervalOrLifetime = 10, isStandalone: isIndependent = false } = {}) {
this.instance = instance; this.instance = instance;
this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME); this.intervalOrLifetime = Math.max(intervalOrLifetime, MIN_LIFETIME);
if (isIndependent) {
this.isIndependent = isIndependent;
this.enabled = isEnabled;
this.trackPlayerDistance = trackPlayerDistance;
} else {
this.instance.options.setVerifierEnabled(isEnabled); this.instance.options.setVerifierEnabled(isEnabled);
this.instance.options.setVerifierDistance(trackPlayerDistance); this.instance.options.setVerifierDistance(trackPlayerDistance);
}
this.locationsToVerify = new Set(); this.locationsToVerify = new Set();
} }
@@ -50,11 +56,18 @@ export class StructureVerifier {
} }
isEnabled() { isEnabled() {
if (this.isIndependent)
return this.enabled;
return this.instance.options.verifier.isEnabled; return this.instance.options.verifier.isEnabled;
} }
getTrackPlayerDistance() { getTrackPlayerDistance() {
return Math.min(MAX_TRACK_PLAYER_DISTANCE, Math.max(MIN_TRACK_PLAYER_DISTANCE, this.instance.options.verifier.trackPlayerDistance)); let distance;
if (this.isIndependent)
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() { init() {
+7 -2
View File
@@ -2,6 +2,7 @@ import { Command } from '../lib/canopy/CanopyExtension';
import { extension } from '../config'; import { extension } from '../config';
import { world, system } from '@minecraft/server'; import { world, system } from '@minecraft/server';
import { MenuForm } from '../classes/MenuForm'; import { MenuForm } from '../classes/MenuForm';
import { structureCollection } from '../classes/Structure/StructureCollection'
const ACTION_ITEM = 'minecraft:paper'; const ACTION_ITEM = 'minecraft:paper';
@@ -21,7 +22,11 @@ world.beforeEvents.itemUse.subscribe((event) => {
function openMenu(sender, event = void 0) { function openMenu(sender, event = void 0) {
const options = { jumpToInstance: true } const options = { jumpToInstance: true }
if (event) if (event) {
options.instanceName = event.itemStack?.typeId; const instanceNames = structureCollection.getInstanceNames();
const instanceName = event.itemStack?.nameTag;
if (instanceNames.includes(instanceName))
options.instanceName = instanceName;
}
new MenuForm(sender, options); new MenuForm(sender, options);
} }
+70
View File
@@ -0,0 +1,70 @@
/**
* @license
* MIT License
*
* Copyright (c) 2024 ForestOfLight
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import { world } from '@minecraft/server';
class Rule {
#identifier;
#description;
#contingentRules;
#independentRules;
constructor({ identifier, description, contingentRules = [], independentRules = [] }) {
this.#identifier = identifier;
this.#description = description;
this.#contingentRules = contingentRules;
this.#independentRules = independentRules;
}
getID() {
return this.#identifier;
}
getDescription() {
return this.#description;
}
getContigentRules() {
return this.#contingentRules;
}
getIndependentRules() {
return this.#independentRules;
}
getValue() {
const value = world.getDynamicProperty(this.#identifier);
if (String(value) === 'true')
return true;
if (['false', 'undefined'].includes(String(value)))
return false;
throw new Error(`Rule ${this.#identifier} has an invalid value: ${value} (${typeof value})`);
}
setValue(value) {
world.setDynamicProperty(this.#identifier, value);
}
}
export default Rule;
+7 -4
View File
@@ -1,7 +1,10 @@
// Rules // Setup
import './rules/easyPlace'; import './classes/Builder/Builders';
import './rules/fastEasyPlace';
import './rules/materialGrabber'; // Options
import './options/easyPlace';
import './options/fastEasyPlace';
import './options/materialGrabber';
// Commands // Commands
import './commands/construct'; import './commands/construct';
+10 -10
View File
@@ -1,24 +1,24 @@
import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BuilderOption } from '../classes/Builder/BuilderOption';
import { BlockPermutation, EntityComponentTypes, GameMode, ItemStack, system, world } from '@minecraft/server'; import { BlockPermutation, EntityComponentTypes, GameMode, ItemStack, system, world } from '@minecraft/server';
import { structureCollection } from '../classes/StructureCollection'; import { structureCollection } from '../classes/Structure/StructureCollection';
import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions, import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, specialItemPlacementConversions,
blockIdToItemStackMap } from '../data'; blockIdToItemStackMap } from '../data';
import { fetchMatchingItemSlot } from '../utils'; import { fetchMatchingItemSlot } from '../utils';
const ACTION_SLOT = 35; const ACTION_SLOT = 27;
new BuilderOption({ const builderOption = new BuilderOption({
identifier: 'easyPlace', identifier: 'easyPlace',
displayName: 'Easy Place', displayName: 'Easy Place',
description: 'Always place the correct block.', description: 'Always place the correct structure block.',
howToUse: "Place blocks in a structure with a paper named 'Easy Place' in the bottom right slot of your inventory to always place the correct block.", howToUse: "Place blocks in a structure with a paper named 'Easy Place' in the inventory slot above your first hotbar slot to always place the correct block."
onEnableCallback: () => { world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock); }, });
onDisableCallback: () => { world.beforeEvents.playerPlaceBlock.unsubscribe(onPlayerPlaceBlock); }
}) world.beforeEvents.playerPlaceBlock.subscribe(onPlayerPlaceBlock);
function onPlayerPlaceBlock(event) { function onPlayerPlaceBlock(event) {
const { player, block, permutationBeingPlaced } = event; const { player, block } = event;
if (!player || !block || !hasActionItemInCorrectSlot(player)) return; if (!player || !block || !builderOption.isEnabled(player.id) || !hasActionItemInCorrectSlot(player)) return;
const structureBlock = structureCollection.fetchStructureBlock(block.dimension.id, block.location); const structureBlock = structureCollection.fetchStructureBlock(block.dimension.id, block.location);
if (!structureBlock) if (!structureBlock)
return; return;
@@ -4,19 +4,21 @@ import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlo
blockIdToItemStackMap } from '../data'; blockIdToItemStackMap } from '../data';
import { Raycaster } from '../classes/Raycaster'; import { Raycaster } from '../classes/Raycaster';
const PROCESS_INTERVAL = 2;
let runner = void 0; let runner = void 0;
new BuilderOption({ const builderOption = new BuilderOption({
identifier: 'fastEasyPlace', identifier: 'fastEasyPlace',
displayName: 'Fast Easy Place', displayName: 'Fast Easy Place',
description: 'Place structure blocks just by looking at them.', description: 'Place structure blocks just by looking at them.',
howToUse: "Look at structure blocks with a paper named 'Easy Place' in your hand to place them.", howToUse: "Look at structure blocks with a paper named 'Easy Place' in your hand to place them.",
onEnableCallback: () => { runner = system.runInterval(onTick, 2); }, });
onDisableCallback: () => { system.clearRun(runner); }
}) system.runInterval(onTick, PROCESS_INTERVAL);
function onTick() { function onTick() {
for (const player of world.getAllPlayers()) { for (const player of world.getAllPlayers()) {
if (!player) if (!player || !builderOption.isEnabled(player.id))
continue; continue;
processEasyPlace(player); processEasyPlace(player);
} }
@@ -1,20 +1,15 @@
import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BuilderOption } from '../classes/Builder/BuilderOption';
import { world } from '@minecraft/server'; import { world } from '@minecraft/server';
new BuilderOption({ const builderOption = new BuilderOption({
identifier: 'materialGrabber', identifier: 'materialGrabber',
displayName: 'Material Grabber', displayName: 'Material Grabber',
description: 'Pulls structure items from inventories.', description: 'Pulls structure items from inventories.',
howToUse: "Interact with inventories using a paper named 'Material Grabber' to pull structure items from them.", howToUse: "Interact with inventories using a paper named 'Material Grabber' to pull structure items from them.",
onEnableCallback: () => { });
world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract);
world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract); world.beforeEvents.playerInteractWithBlock.subscribe(onPlayerInteract);
}, world.beforeEvents.playerInteractWithEntity.subscribe(onPlayerInteract);
onDisableCallback: () => {
world.beforeEvents.playerInteractWithBlock.unsubscribe(onPlayerInteract);
world.beforeEvents.playerInteractWithEntity.unsubscribe(onPlayerInteract);
}
})
function onPlayerInteract(event) { function onPlayerInteract(event) {
// get inventory // get inventory