diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 6121569..fea918a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -39,6 +39,31 @@ "echo": true, "close": true } + }, + { + "label": "regolith: watch preview", + "type": "shell", + "command": "regolith watch preview", + "isBackground": true, + "problemMatcher": { + "pattern": [ + { + "regexp": ".", + "file": 1, + "location": 2, + "message": 3 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": ".", + "endsPattern": "." + } + }, + "presentation": { + "echo": true, + "close": true + } } ] } \ No newline at end of file diff --git a/README.md b/README.md index 6c65927..8939ace 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ [![GitHub Downloads](https://img.shields.io/github/downloads/ForestOfLight/Construct/total?label=Github%20downloads&logo=github)](https://github.com/ForestOfLight/Construct/releases) [![Curseforge Downloads](https://cf.way2muchnoise.eu/full_1283139_downloads.svg)](https://www.curseforge.com/minecraft-bedrock/addons/construct) -[![Minecraft - Version](https://img.shields.io/badge/Minecraft-v1.21.130_(Bedrock)-brightgreen)](https://feedback.minecraft.net/hc/en-us/sections/360001186971-Release-Changelogs) +[![Minecraft - Version](https://img.shields.io/badge/Minecraft-v26.0_(Bedrock)-brightgreen)](https://feedback.minecraft.net/hc/en-us/sections/360001186971-Release-Changelogs) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c091aa1237d546c4b448a25fbc847f9a)](https://app.codacy.com/gh/ForestOfLight/Construct/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Discord](https://badgen.net/discord/members/9KGche8fxm?icon=discord&label=Discord&list=what)](https://discord.gg/9KGche8fxm) @@ -27,17 +28,13 @@ Give yourself the tools to ease the survival building process with Construct, an -> [!IMPORTANT] -> This is a standalone addon, but it is also a **Canopy Extension**, which means **Canopy** can be installed in your world for the best experience! -> [Download **Canopy** here!](https://github.com/ForestOfLight/Canopy) - ## Usage Construct's latest release can be downloaded from its [Releases Page](https://github.com/ForestOfLight/Construct/releases) right here on GitHub. Install it just like any other addon, though make sure the Beta APIs experiment is enabled! ### Construct Menu -Use `/construct:item` to get the Construct item, which opens the Construct menu. This item is also available in the Creative inventory under the "Items" tab. Using the Construct item will open a menu where you can manage your structures instances. You can create new instances of structures, edit existing ones, and delete them when you no longer need them. There are plenty options available to customize your building style! +Use `/construct:construct` to get the Construct item, which opens the Construct menu. This item is also available in the Creative inventory under the "Items" tab. Using the Construct item will open a menu where you can manage your structures instances. You can create new instances of structures, edit existing ones, and delete them when you no longer need them. There are plenty options available to customize your building style! *Pro tip: You can name the Construct item after a structure instance to automagically select it when you open the menu!* @@ -52,12 +49,9 @@ Construct uses Minecraft's vanilla structure system so that you can easily creat ### Commands -**Usage: `/construct:item`** +**Usage: `/construct:construct`** Gives you the Construct item. Use it to open the Construct menu. -**Usage: `./construct`** -Shows the Construct menu. Only available while **Canopy** is installed. - ## Join the Community Need help, want to discuss technical Minecraft, or follow future updates? [**Join our Discord!**](https://discord.gg/9KGche8fxm) diff --git a/config.json b/config.json index 69a4ea8..efbb358 100644 --- a/config.json +++ b/config.json @@ -20,6 +20,16 @@ "bpName": "project.name+'[BP]'" }, "filters": [] + }, + "preview": { + "export": { + "build": "preview", + "readOnly": false, + "target": "development", + "rpName": "project.name+'[RP]'", + "bpName": "project.name+'[BP]'" + }, + "filters": [] } } } diff --git a/packs/BP/manifest.json b/packs/BP/manifest.json index d594a20..6b466a6 100644 --- a/packs/BP/manifest.json +++ b/packs/BP/manifest.json @@ -1,11 +1,11 @@ { "format_version": 2, "header": { - "name": "Construct [BP] v1.0.6", + "name": "Construct [BP] v1.0.7", "description": "Survival building addon by §aForestOfLight§r.", "uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", - "min_engine_version": [1, 21, 130], - "version": [1, 0, 6] + "min_engine_version": [1, 26, 0], + "version": [1, 0, 7] }, "modules": [ { @@ -26,7 +26,7 @@ "dependencies": [ { "module_name": "@minecraft/server", - "version": "2.5.0-beta" + "version": "2.6.0-beta" }, { "module_name": "@minecraft/server-ui", @@ -34,7 +34,7 @@ }, { "uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4", // Construct RP - "version": [1, 0, 6] + "version": [1, 0, 7] } ], "metadata": { diff --git a/packs/BP/scripts/classes/Builder/Builders.js b/packs/BP/scripts/classes/Builder/Builders.js index 336471c..4681854 100644 --- a/packs/BP/scripts/classes/Builder/Builders.js +++ b/packs/BP/scripts/classes/Builder/Builders.js @@ -33,8 +33,10 @@ world.beforeEvents.playerLeave.subscribe((event) => { return; Builders.onLeave(event.player.id); }); -world.afterEvents.worldLoad.subscribe((event) => { +world.afterEvents.worldLoad.subscribe(() => { for (const player of world.getAllPlayers()) { + if (!player) + continue; Builders.onJoin(player.id); } }); \ No newline at end of file diff --git a/packs/BP/scripts/classes/Instance/FlexibleInstanceMove.js b/packs/BP/scripts/classes/Instance/FlexibleInstanceMove.js index ed3820a..34cf36b 100644 --- a/packs/BP/scripts/classes/Instance/FlexibleInstanceMove.js +++ b/packs/BP/scripts/classes/Instance/FlexibleInstanceMove.js @@ -52,6 +52,8 @@ export class FlexibleInstanceMove { } onFlexibleMovementTick() { + if (!this.player?.isValid) + this.finish(); const playerMovement = new PlayerMovement(this.player); const instanceVelocity = this.calculateInstanceMovement(playerMovement); this.move(instanceVelocity); @@ -105,8 +107,9 @@ export class FlexibleInstanceMove { this.instance.enable(); this.instance.flexMovingPlayerId = void 0; this.allowPlayerMovement(true); - const builder = Builders.get(this.player.id); - builder.flexibleInstanceMovement = void 0; + const builder = Builders.get(this.player?.id); + if (builder) + builder.flexibleInstanceMovement = void 0; world.beforeEvents.itemUse.unsubscribe(this.onPlayerUseItemBound); world.beforeEvents.playerLeave.unsubscribe(this.onPlayerLeaveBound); this.sendFeedback({ translate: 'construct.instance.flexibleMove.finish', with: [ diff --git a/packs/BP/scripts/classes/Instance/StructureInstance.js b/packs/BP/scripts/classes/Instance/StructureInstance.js index 925f73e..36bd6c0 100644 --- a/packs/BP/scripts/classes/Instance/StructureInstance.js +++ b/packs/BP/scripts/classes/Instance/StructureInstance.js @@ -252,7 +252,7 @@ export class StructureInstance { disableInstanceWhenNoPlayersOnline() { // This prevents a memory leak when no players are online. - world.beforeEvents.playerLeave.subscribe(event => { + world.beforeEvents.playerLeave.subscribe(() => { system.run(() => { if (world.getAllPlayers().length === 0 && this.options.isEnabled) { console.info(`[Construct] No players are online. Disabling instance: '${this.options.instanceName}'`); @@ -261,7 +261,7 @@ export class StructureInstance { } }); }); - world.afterEvents.playerJoin.subscribe(event => { + world.afterEvents.playerJoin.subscribe(() => { if (this.shouldEnableOnJoin) { console.info(`[Construct] A player rejoined. Re-enabling instance: '${this.options.instanceName}'`); this.enable(); diff --git a/packs/BP/scripts/classes/MenuForm.js b/packs/BP/scripts/classes/MenuForm.js index 320ea2d..dcff275 100644 --- a/packs/BP/scripts/classes/MenuForm.js +++ b/packs/BP/scripts/classes/MenuForm.js @@ -29,11 +29,14 @@ export class MenuForm { try { return forceShow(this.player, MenuFormBuilder.buildAllInstanceName()).then((response) => { if (response.canceled) - return; + return void 0; let selection = response.selection; if (selection === 0) { new BuilderForm(this.player); return void 0; + } else if (selection == structureCollection.getInstanceNames().length + 2) { + MenuFormBuilder.buildHowTo().show(this.player); + return void 0; } else { selection--; const selectedInstanceName = structureCollection.getInstanceNames()[selection]; @@ -43,7 +46,7 @@ export class MenuForm { } catch (e) { if (e.message === 'Menu timed out.') { this.player.sendMessage({ translate: 'construct.menu.open.timeout' }); - return; + return void 0; } throw e; } @@ -52,13 +55,13 @@ export class MenuForm { async createNewInstance() { return MenuFormBuilder.buildNewInstance().show(this.player).then(async (response) => { if (response.canceled) - return; + return void 0; const instanceName = response.formValues[0]; if (instanceName === '') return void 0; const structureId = await this.getStructureId(); if (!structureId) - return; + return void 0; try { structureCollection.add(instanceName, structureId); } catch (e) { @@ -79,11 +82,7 @@ export class MenuForm { async getStructureId() { return MenuFormBuilder.buildAllStructures().show(this.player).then((response) => { if (response.canceled) - return; - if (response.selection === structureCollection.getWorldStructureIds().length + 1) { - MenuFormBuilder.buildHowTo().show(this.player); - return; - } + return void 0; const selectedStructureId = structureCollection.getWorldStructureIds()[response.selection]; return selectedStructureId || this.getOtherStructureId(); }); @@ -92,7 +91,7 @@ export class MenuForm { getOtherStructureId() { return MenuFormBuilder.buildOtherStructure().show(this.player).then((response) => { if (response.canceled) - return; + return void 0; const structureId = response.formValues[0]; if (structureId === '') return void 0; diff --git a/packs/BP/scripts/classes/MenuFormBuilder.js b/packs/BP/scripts/classes/MenuFormBuilder.js index a9a96ff..fdaa4cd 100644 --- a/packs/BP/scripts/classes/MenuFormBuilder.js +++ b/packs/BP/scripts/classes/MenuFormBuilder.js @@ -13,6 +13,7 @@ export class MenuFormBuilder { allInstanceNameForm.button(`${structureCollection.get(instanceName).isEnabled() ? '§2' : '§c'}${instanceName}`); }); allInstanceNameForm.button({ translate: 'construct.mainmenu.newinstance' }); + allInstanceNameForm.button({ translate: 'construct.mainmenu.howto' }); return allInstanceNameForm; } @@ -45,12 +46,12 @@ export class MenuFormBuilder { static buildHowTo() { const message = { rawtext: [ - { translate: 'construct.mainmenu.selectstructure.howto.add.header' }, { text: '\n' }, - { translate: 'construct.mainmenu.selectstructure.howto.add.structureblock' }, { text: '\n' }, - { translate: 'construct.mainmenu.selectstructure.howto.add.or' }, { text: '\n' }, - { translate: 'construct.mainmenu.selectstructure.howto.add.mcstructure' }, { text: '\n' }, - { text: '\n' }, { translate: 'construct.mainmenu.selectstructure.howto.remove.header' }, { text: '\n' }, - { translate: 'construct.mainmenu.selectstructure.howto.remove.body' }, { text: '\n' }, + { translate: 'construct.mainmenu.howto.add.header' }, { text: '\n' }, + { translate: 'construct.mainmenu.howto.add.structureblock' }, { text: '\n' }, + { translate: 'construct.mainmenu.howto.add.or' }, { text: '\n' }, + { translate: 'construct.mainmenu.howto.add.mcstructure' }, { text: '\n' }, + { text: '\n' }, { translate: 'construct.mainmenu.howto.remove.header' }, { text: '\n' }, + { translate: 'construct.mainmenu.howto.remove.body' }, { text: '\n' }, ] }; return new ActionFormData() .title(this.menuTitle) diff --git a/packs/BP/scripts/classes/Outliner.js b/packs/BP/scripts/classes/Outliner.js index 34b40c1..240adcf 100644 --- a/packs/BP/scripts/classes/Outliner.js +++ b/packs/BP/scripts/classes/Outliner.js @@ -1,4 +1,4 @@ -import { MolangVariableMap, system, TicksPerSecond, world } from "@minecraft/server"; +import { MolangVariableMap, system, TicksPerSecond } from "@minecraft/server"; import { Vector } from "../lib/Vector"; export class Outliner { diff --git a/packs/BP/scripts/classes/Raycaster.js b/packs/BP/scripts/classes/Raycaster.js index 3cc5ead..ffcf08f 100644 --- a/packs/BP/scripts/classes/Raycaster.js +++ b/packs/BP/scripts/classes/Raycaster.js @@ -1,5 +1,4 @@ import { structureCollection } from "./Structure/StructureCollection"; -import { world } from "@minecraft/server"; export class Raycaster { static STEP_SIZE = 0.2; diff --git a/packs/BP/scripts/commands/construct.js b/packs/BP/scripts/commands/construct.js index ca7a863..05b3ab8 100644 --- a/packs/BP/scripts/commands/construct.js +++ b/packs/BP/scripts/commands/construct.js @@ -1,5 +1,3 @@ -import { Command } from '../lib/canopy/CanopyExtension'; -import { extension } from '../config'; import { world, system, EntityComponentTypes, ItemStack, CommandPermissionLevel, CustomCommandStatus, Player } from '@minecraft/server'; import { MenuForm } from '../classes/MenuForm'; import { structureCollection } from '../classes/Structure/StructureCollection' @@ -7,18 +5,10 @@ import { Builders } from '../classes/Builder/Builders'; export const MENU_ITEM = 'construct:menu'; -const menuCmd = new Command({ - name: 'construct', - description: { translate: 'construct.commands.construct' }, - usage: 'construct', - callback: (sender) => openMenu(sender) -}); -extension.addCommand(menuCmd); - system.beforeEvents.startup.subscribe((event) => { const command = { - name: 'construct:item', - description: 'construct.commands.item', + name: 'construct:construct', + description: 'construct.commands.construct', permissionLevel: CommandPermissionLevel.Any, cheatsRequired: false }; @@ -28,13 +18,13 @@ system.beforeEvents.startup.subscribe((event) => { function givePlayerConstructItem(origin) { const player = origin.sourceEntity; if (player instanceof Player === false) - return { status: CustomCommandStatus.Failure, message: 'construct.commands.item.denyorigin' }; + return { status: CustomCommandStatus.Failure, message: 'construct.commands.construct.denyorigin' }; system.run(() => { const givenItemStack = player.getComponent(EntityComponentTypes.Inventory)?.container?.addItem(new ItemStack(MENU_ITEM)); if (givenItemStack) - player.sendMessage({ translate: 'construct.commands.item.fail' }); + player.sendMessage({ translate: 'construct.commands.construct.fail' }); else - player.sendMessage({ translate: 'construct.commands.item.success' }); + player.sendMessage({ translate: 'construct.commands.construct.success' }); }); return { status: CustomCommandStatus.Success }; } diff --git a/packs/BP/scripts/config.js b/packs/BP/scripts/config.js deleted file mode 100644 index 70a9f73..0000000 --- a/packs/BP/scripts/config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { CanopyExtension } from './lib/canopy/CanopyExtension'; - -export const extension = new CanopyExtension({ - author: 'ForestOfLight', - name: 'Construct', - description: 'Survival building addon by §aForestOfLight§r.', - version: '1.0.6' -}); \ No newline at end of file diff --git a/packs/BP/scripts/lib/canopy/CanopyExtension.js b/packs/BP/scripts/lib/canopy/CanopyExtension.js deleted file mode 100644 index 8cf1acc..0000000 --- a/packs/BP/scripts/lib/canopy/CanopyExtension.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @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'; -import IPC from '../ipc/ipc'; -import Command from './Command'; -import Rule from './Rule'; -import { CommandCallbackRequest, CommandPrefixRequest, Ready, RegisterCommand, RegisterExtension, RegisterRule, RuleValueRequest, RuleValueSet, CommandPrefixResponse, RuleValueResponse } from './extension.ipc'; - -class CanopyExtension { - name; - version; - author; - description; - #commands = {}; - #rules = {}; - #isRegistrationReady = false; - - constructor({ name = 'Unnamed', version = '1.0.0', author = 'Unknown', description = { text: '' } }) { - this.id = this.#makeID(name); - this.name = name; - this.version = version; - this.author = author; - this.description = description; - - this.#registerExtension(); - this.#setupCommandPrefix(); - this.#handleCommandCallbacks(); - this.#handleRuleValueRequests(); - this.#handleRuleValueSetters(); - } - - addCommand(command) { - if (!(command instanceof Command)) - throw new Error('Command must be an instance of Command.'); - this.#commands[command.getName()] = command; - if (this.#isRegistrationReady) - this.#registerCommand(command); - } - - addRule(rule) { - if (!(rule instanceof Rule)) - throw new Error('Rule must be an instance of Rule.'); - this.#rules[rule.getID()] = rule; - if (this.#isRegistrationReady) - this.#registerRule(rule); - } - - getRuleValue(ruleID) { - return this.#rules[ruleID].getValue(); - } - - #makeID(name) { - if (typeof name !== 'string') - throw new Error(`[${name}] Could not register extension. Extension name must be a string.`); - const id = name.toLowerCase().replace(/[^a-z0-9 ]/g, '').replace(/ /g, '_'); - if (id.length === 0) - throw new Error(`[${name}] Could not register extension. Extension name must contain at least one alphanumeric character.`); - return id; - } - - #registerExtension() { - IPC.once('canopyExtension:ready', Ready, () => { - IPC.send('canopyExtension:registerExtension', RegisterExtension, { - name: this.name, - version: this.version, - author: this.author, - description: this.description - }); - }); - IPC.once(`canopyExtension:${this.id}:ready`, Ready, () => { - this.#isRegistrationReady = true; - for (const rule of Object.values(this.#rules)) - this.#registerRule(rule); - for (const command of Object.values(this.#commands)) - this.#registerCommand(command); - }); - } - - #registerCommand(command) { - IPC.send(`canopyExtension:${this.id}:registerCommand`, RegisterCommand, { - name: command.getName(), - description: command.getDescription(), - usage: command.getUsage(), - callback: false, - args: command.getArgs(), - contingentRules: command.getContingentRules(), - adminOnly: command.isAdminOnly(), - helpEntries: command.getHelpEntries(), - helpHidden: command.isHelpHidden(), - extensionName: this.name - }); - } - - #handleCommandCallbacks() { - IPC.on(`canopyExtension:${this.id}:commandCallbackRequest`, CommandCallbackRequest, (cmdData) => { - if (cmdData.senderName === undefined) - return; - const sender = world.getPlayers({ name: cmdData.senderName })[0]; - if (!sender) - throw new Error(`Sender ${cmdData.senderName} of ${cmdData.commandName} not found.`); - const parsedArgs = JSON.parse(cmdData.args); - this.#commands[cmdData.commandName].runCallback(sender, parsedArgs); - }); - } - - #registerRule(rule) { - IPC.send(`canopyExtension:${this.id}:registerRule`, RegisterRule, { - identifier: rule.getID(), - description: rule.getDescription(), - contingentRules: rule.getContigentRules(), - independentRules: rule.getIndependentRules(), - extensionName: this.name - }); - if (rule.getValue() === true) - rule.onEnable(); - } - - #handleRuleValueRequests() { - IPC.handle(`canopyExtension:${this.id}:ruleValueRequest`, RuleValueRequest, RuleValueResponse, (data) => { - const rule = this.#rules[data.ruleID]; - if (!rule) - throw new Error(`Rule ${data.ruleID} not found.`); - const value = rule.getValue(); - return { value }; - }); - } - - #handleRuleValueSetters() { - IPC.on(`canopyExtension:${this.id}:ruleValueSet`, RuleValueSet, (data) => { - const rule = this.#rules[data.ruleID]; - if (!rule) - throw new Error(`Rule ${data.ruleID} not found.`); - rule.setValue(data.value); - }); - } - - #setupCommandPrefix() { - const prefix = IPC.invoke(`canopyExtension:commandPrefixRequest`, CommandPrefixRequest, void 0, CommandPrefixResponse).then(result => { - Command.setPrefix(result.prefix); - }); - } -} - -export { CanopyExtension, Command, Rule }; \ No newline at end of file diff --git a/packs/BP/scripts/lib/canopy/Command.js b/packs/BP/scripts/lib/canopy/Command.js deleted file mode 100644 index 9d0651d..0000000 --- a/packs/BP/scripts/lib/canopy/Command.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @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. - */ - -class Command { - #name; - #description; - #usage; - #callback; - #args; - #contingentRules; - #adminOnly; - #helpEntries; - #helpHidden; - static #prefix = ''; - - constructor({ name, description = '', usage, callback, args = [], contingentRules = [], adminOnly = false, helpEntries = [], helpHidden = false }) { - this.#name = name; - this.#description = description; - this.#usage = usage; - this.#callback = callback; - this.#args = args; - this.#contingentRules = contingentRules; - this.#adminOnly = adminOnly; - this.#helpEntries = helpEntries; - this.#helpHidden = helpHidden; - } - - getName() { - return this.#name; - } - - getDescription() { - return this.#description; - } - - getUsage() { - return this.#usage; - } - - getCallback() { - return this.#callback; - } - - getArgs() { - return this.#args; - } - - getContingentRules() { - return this.#contingentRules; - } - - isAdminOnly() { - return this.#adminOnly; - } - - getHelpEntries() { - return this.#helpEntries; - } - - isHelpHidden() { - return this.#helpHidden; - } - - runCallback(sender, args) { - this.#callback(sender, args); - } - - sendUsage(sender) { - sender.sendMessage(`§cUsage: ${Command.#prefix}${this.#usage}`); - } - - static setPrefix(prefix) { - Command.#prefix = prefix; - } - - static getPrefix() { - return Command.#prefix; - } -} - -export default Command; \ No newline at end of file diff --git a/packs/BP/scripts/lib/canopy/Rule.js b/packs/BP/scripts/lib/canopy/Rule.js deleted file mode 100644 index 3ba7b4f..0000000 --- a/packs/BP/scripts/lib/canopy/Rule.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @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; \ No newline at end of file diff --git a/packs/BP/scripts/lib/canopy/extension.ipc.js b/packs/BP/scripts/lib/canopy/extension.ipc.js deleted file mode 100644 index 5e00ab3..0000000 --- a/packs/BP/scripts/lib/canopy/extension.ipc.js +++ /dev/null @@ -1,70 +0,0 @@ -/* eslint-disable new-cap */ -import { PROTO } from '../ipc/ipc' - -const description = PROTO.Object({ - text: PROTO.Optional(PROTO.String), - translate: PROTO.Optional(PROTO.String), - with: PROTO.Optional(PROTO.Array(PROTO.String)) -}); - -export const Ready = PROTO.Void; - -export const RegisterExtension = PROTO.Object({ - name: PROTO.String, - version: PROTO.String, - author: PROTO.String, - description: description, - isEndstone: PROTO.Boolean -}); - -export const RegisterCommand = PROTO.Object({ - name: PROTO.String, - description: description, - usage: PROTO.String, - callback: PROTO.Optional(PROTO.Undefined), - args: PROTO.Optional(PROTO.Array(PROTO.Object({ - type: PROTO.String, - name: PROTO.String - }))), - contingentRules: PROTO.Optional(PROTO.Array(PROTO.String)), - adminOnly: PROTO.Optional(PROTO.Boolean), - helpEntries: PROTO.Optional(PROTO.Array(PROTO.Object({ - usage: PROTO.String, - description: description - }))), - helpHidden: PROTO.Optional(PROTO.Boolean), - extensionName: PROTO.Optional(PROTO.String) -}); - -export const RegisterRule = PROTO.Object({ - identifier: PROTO.String, - description: description, - contingentRules: PROTO.Optional(PROTO.Array(PROTO.String)), - independentRules: PROTO.Optional(PROTO.Array(PROTO.String)), - extensionName: PROTO.Optional(PROTO.String) -}); - -export const RuleValueRequest = PROTO.Object({ - ruleID: PROTO.String -}); - -export const RuleValueResponse = PROTO.Object({ - value: PROTO.Boolean -}); - -export const RuleValueSet = PROTO.Object({ - ruleID: PROTO.String, - value: PROTO.Boolean -}); - -export const CommandCallbackRequest = PROTO.Object({ - commandName: PROTO.String, - senderName: PROTO.Optional(PROTO.String), - args: PROTO.String -}); - -export const CommandPrefixRequest = PROTO.Void; - -export const CommandPrefixResponse = PROTO.Object({ - prefix: PROTO.String -}); \ No newline at end of file diff --git a/packs/BP/scripts/lib/ipc/ipc.d.ts b/packs/BP/scripts/lib/ipc/ipc.d.ts deleted file mode 100644 index dc6ef67..0000000 --- a/packs/BP/scripts/lib/ipc/ipc.d.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** - * @license - * MIT License - * - * Copyright (c) 2025 OmniacDev - * - * 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. - */ -export declare namespace PROTO { - interface Serializable { - serialize(value: T, stream: ByteQueue): Generator; - deserialize(stream: ByteQueue): Generator; - } - class ByteQueue { - private _buffer; - private _data_view; - private _length; - private _offset; - get end(): number; - get front(): number; - get data_view(): DataView; - constructor(size?: number); - write(...values: number[]): void; - read(amount?: number): number[]; - ensure_capacity(size: number): void; - static from_uint8array(array: Uint8Array): ByteQueue; - to_uint8array(): Uint8Array; - } - namespace MIPS { - function serialize(byte_queue: PROTO.ByteQueue): Generator; - function deserialize(str: string): Generator; - } - const Void: PROTO.Serializable; - const Null: PROTO.Serializable; - const Undefined: PROTO.Serializable; - const Int8: PROTO.Serializable; - const Int16: PROTO.Serializable; - const Int32: PROTO.Serializable; - const UInt8: PROTO.Serializable; - const UInt16: PROTO.Serializable; - const UInt32: PROTO.Serializable; - const UVarInt32: PROTO.Serializable; - const Float32: PROTO.Serializable; - const Float64: PROTO.Serializable; - const String: PROTO.Serializable; - const Boolean: PROTO.Serializable; - const UInt8Array: PROTO.Serializable; - const Date: PROTO.Serializable; - function Object(obj: { - [K in keyof T]: PROTO.Serializable; - }): PROTO.Serializable; - function Array(value: PROTO.Serializable): PROTO.Serializable; - function Tuple(...values: { - [K in keyof T]: PROTO.Serializable; - }): PROTO.Serializable; - function Optional(value: PROTO.Serializable): PROTO.Serializable; - function Map(key: PROTO.Serializable, value: PROTO.Serializable): PROTO.Serializable>; - function Set(value: PROTO.Serializable): PROTO.Serializable>; - type Endpoint = string; - type Header = { - guid: string; - encoding: string; - index: number; - final: boolean; - }; - const Endpoint: PROTO.Serializable; - const Header: PROTO.Serializable
; -} -export declare namespace NET { - function serialize(byte_queue: PROTO.ByteQueue, max_size?: number): Generator; - function deserialize(strings: string[]): Generator; - function emit, T>(endpoint: string, serializer: S & PROTO.Serializable, value: T): Generator; - function listen>(endpoint: string, serializer: S & PROTO.Serializable, callback: (value: T) => Generator): () => void; -} -export declare namespace IPC { - /** Sends a message with `args` to `channel` */ - function send, T>(channel: string, serializer: S & PROTO.Serializable, value: T): void; - /** Sends an `invoke` message through IPC, and expects a result asynchronously. */ - function invoke, T, RS extends PROTO.Serializable, R>(channel: string, serializer: TS & PROTO.Serializable, value: T, deserializer: RS & PROTO.Serializable): Promise; - /** Listens to `channel`. When a new message arrives, `listener` will be called with `listener(args)`. */ - function on, T>(channel: string, deserializer: S & PROTO.Serializable, listener: (value: T) => void): () => void; - /** Listens to `channel` once. When a new message arrives, `listener` will be called with `listener(args)`, and then removed. */ - function once, T>(channel: string, deserializer: S & PROTO.Serializable, listener: (value: T) => void): () => void; - /** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */ - function handle, T, RS extends PROTO.Serializable, R>(channel: string, deserializer: TS & PROTO.Serializable, serializer: RS & PROTO.Serializable, listener: (value: T) => R): () => void; -} -export default IPC; diff --git a/packs/BP/scripts/lib/ipc/ipc.js b/packs/BP/scripts/lib/ipc/ipc.js deleted file mode 100644 index 8b24afe..0000000 --- a/packs/BP/scripts/lib/ipc/ipc.js +++ /dev/null @@ -1,593 +0,0 @@ -/** - * @license - * MIT License - * - * Copyright (c) 2025 OmniacDev - * - * 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 { ScriptEventSource, system, world } from '@minecraft/server'; -export var PROTO; -(function (PROTO) { - class ByteQueue { - get end() { - return this._length + this._offset; - } - get front() { - return this._offset; - } - get data_view() { - return this._data_view; - } - constructor(size = 256) { - this._buffer = new Uint8Array(size); - this._data_view = new DataView(this._buffer.buffer); - this._length = 0; - this._offset = 0; - } - write(...values) { - this.ensure_capacity(values.length); - this._buffer.set(values, this.end); - this._length += values.length; - } - read(amount = 1) { - if (this._length > 0) { - const max_amount = amount > this._length ? this._length : amount; - const values = this._buffer.subarray(this._offset, this._offset + max_amount); - this._length -= max_amount; - this._offset += max_amount; - return globalThis.Array.from(values); - } - return []; - } - ensure_capacity(size) { - if (this.end + size > this._buffer.length) { - const larger_buffer = new Uint8Array((this.end + size) * 2); - larger_buffer.set(this._buffer.subarray(this._offset, this.end), 0); - this._buffer = larger_buffer; - this._offset = 0; - this._data_view = new DataView(this._buffer.buffer); - } - } - static from_uint8array(array) { - const byte_queue = new ByteQueue(); - byte_queue._buffer = array; - byte_queue._length = array.length; - byte_queue._offset = 0; - byte_queue._data_view = new DataView(array.buffer); - return byte_queue; - } - to_uint8array() { - return this._buffer.subarray(this._offset, this.end); - } - } - PROTO.ByteQueue = ByteQueue; - let MIPS; - (function (MIPS) { - function* serialize(byte_queue) { - const uint8array = byte_queue.to_uint8array(); - let str = '(0x'; - for (let i = 0; i < uint8array.length; i++) { - const hex = uint8array[i].toString(16).padStart(2, '0').toUpperCase(); - str += hex; - yield; - } - str += ')'; - return str; - } - MIPS.serialize = serialize; - function* deserialize(str) { - if (str.startsWith('(0x') && str.endsWith(')')) { - const result = []; - const hex_str = str.slice(3, str.length - 1); - for (let i = 0; i < hex_str.length; i++) { - const hex = hex_str[i] + hex_str[++i]; - result.push(parseInt(hex, 16)); - yield; - } - return ByteQueue.from_uint8array(new Uint8Array(result)); - } - return new ByteQueue(); - } - MIPS.deserialize = deserialize; - })(MIPS = PROTO.MIPS || (PROTO.MIPS = {})); - PROTO.Void = { - *serialize() { }, - *deserialize() { } - }; - PROTO.Null = { - *serialize() { }, - *deserialize() { - return null; - } - }; - PROTO.Undefined = { - *serialize() { }, - *deserialize() { - return undefined; - } - }; - PROTO.Int8 = { - *serialize(value, stream) { - const length = 1; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setInt8(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getInt8(stream.front); - stream.read(1); - return value; - } - }; - PROTO.Int16 = { - *serialize(value, stream) { - const length = 2; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setInt16(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getInt16(stream.front); - stream.read(2); - return value; - } - }; - PROTO.Int32 = { - *serialize(value, stream) { - const length = 4; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setInt32(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getInt32(stream.front); - stream.read(4); - return value; - } - }; - PROTO.UInt8 = { - *serialize(value, stream) { - const length = 1; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setUint8(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getUint8(stream.front); - stream.read(1); - return value; - } - }; - PROTO.UInt16 = { - *serialize(value, stream) { - const length = 2; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setUint16(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getUint16(stream.front); - stream.read(2); - return value; - } - }; - PROTO.UInt32 = { - *serialize(value, stream) { - const length = 4; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setUint32(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getUint32(stream.front); - stream.read(4); - return value; - } - }; - PROTO.UVarInt32 = { - *serialize(value, stream) { - while (value >= 0x80) { - stream.write((value & 0x7f) | 0x80); - value >>= 7; - yield; - } - stream.write(value); - }, - *deserialize(stream) { - let value = 0; - let size = 0; - let byte; - do { - byte = stream.read()[0]; - value |= (byte & 0x7f) << (size * 7); - size += 1; - yield; - } while ((byte & 0x80) !== 0 && size < 10); - return value; - } - }; - PROTO.Float32 = { - *serialize(value, stream) { - const length = 4; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setFloat32(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getFloat32(stream.front); - stream.read(4); - return value; - } - }; - PROTO.Float64 = { - *serialize(value, stream) { - const length = 8; - stream.write(...globalThis.Array(length).fill(0)); - stream.data_view.setFloat64(stream.end - length, value); - }, - *deserialize(stream) { - const value = stream.data_view.getFloat64(stream.front); - stream.read(8); - return value; - } - }; - PROTO.String = { - *serialize(value, stream) { - yield* PROTO.UVarInt32.serialize(value.length, stream); - for (let i = 0; i < value.length; i++) { - const code = value.charCodeAt(i); - yield* PROTO.UVarInt32.serialize(code, stream); - } - }, - *deserialize(stream) { - const length = yield* PROTO.UVarInt32.deserialize(stream); - let value = ''; - for (let i = 0; i < length; i++) { - const code = yield* PROTO.UVarInt32.deserialize(stream); - value += globalThis.String.fromCharCode(code); - } - return value; - } - }; - PROTO.Boolean = { - *serialize(value, stream) { - stream.write(value ? 1 : 0); - }, - *deserialize(stream) { - const value = stream.read()[0]; - return value === 1; - } - }; - PROTO.UInt8Array = { - *serialize(value, stream) { - yield* PROTO.UVarInt32.serialize(value.length, stream); - stream.write(...value); - }, - *deserialize(stream) { - const length = yield* PROTO.UVarInt32.deserialize(stream); - return new Uint8Array(stream.read(length)); - } - }; - PROTO.Date = { - *serialize(value, stream) { - yield* PROTO.Float64.serialize(value.getTime(), stream); - }, - *deserialize(stream) { - return new globalThis.Date(yield* PROTO.Float64.deserialize(stream)); - } - }; - function Object(obj) { - return { - *serialize(value, stream) { - for (const key in obj) { - yield* obj[key].serialize(value[key], stream); - } - }, - *deserialize(stream) { - const result = {}; - for (const key in obj) { - result[key] = yield* obj[key].deserialize(stream); - } - return result; - } - }; - } - PROTO.Object = Object; - function Array(value) { - return { - *serialize(array, stream) { - yield* PROTO.UVarInt32.serialize(array.length, stream); - for (const item of array) { - yield* value.serialize(item, stream); - } - }, - *deserialize(stream) { - const result = []; - const length = yield* PROTO.UVarInt32.deserialize(stream); - for (let i = 0; i < length; i++) { - result[i] = yield* value.deserialize(stream); - } - return result; - } - }; - } - PROTO.Array = Array; - function Tuple(...values) { - return { - *serialize(tuple, stream) { - for (let i = 0; i < values.length; i++) { - yield* values[i].serialize(tuple[i], stream); - } - }, - *deserialize(stream) { - const result = []; - for (let i = 0; i < values.length; i++) { - result[i] = yield* values[i].deserialize(stream); - } - return result; - } - }; - } - PROTO.Tuple = Tuple; - function Optional(value) { - return { - *serialize(optional, stream) { - yield* PROTO.Boolean.serialize(optional !== undefined, stream); - if (optional !== undefined) { - yield* value.serialize(optional, stream); - } - }, - *deserialize(stream) { - const defined = yield* PROTO.Boolean.deserialize(stream); - if (defined) { - return yield* value.deserialize(stream); - } - return undefined; - } - }; - } - PROTO.Optional = Optional; - function Map(key, value) { - return { - *serialize(map, stream) { - yield* PROTO.UVarInt32.serialize(map.size, stream); - for (const [k, v] of map.entries()) { - yield* key.serialize(k, stream); - yield* value.serialize(v, stream); - } - }, - *deserialize(stream) { - const size = yield* PROTO.UVarInt32.deserialize(stream); - const result = new globalThis.Map(); - for (let i = 0; i < size; i++) { - const k = yield* key.deserialize(stream); - const v = yield* value.deserialize(stream); - result.set(k, v); - } - return result; - } - }; - } - PROTO.Map = Map; - function Set(value) { - return { - *serialize(set, stream) { - yield* PROTO.UVarInt32.serialize(set.size, stream); - for (const [_, v] of set.entries()) { - yield* value.serialize(v, stream); - } - }, - *deserialize(stream) { - const size = yield* PROTO.UVarInt32.deserialize(stream); - const result = new globalThis.Set(); - for (let i = 0; i < size; i++) { - const v = yield* value.deserialize(stream); - result.add(v); - } - return result; - } - }; - } - PROTO.Set = Set; - PROTO.Endpoint = PROTO.String; - PROTO.Header = PROTO.Object({ - guid: PROTO.String, - encoding: PROTO.String, - index: PROTO.UVarInt32, - final: PROTO.Boolean - }); -})(PROTO || (PROTO = {})); -export var NET; -(function (NET) { - const FRAG_MAX = 2048; - const ENCODING = 'mcbe-ipc:v3'; - const ENDPOINTS = new Map(); - function* serialize(byte_queue, max_size = Infinity) { - const uint8array = byte_queue.to_uint8array(); - const result = []; - let acc_str = ''; - let acc_size = 0; - for (let i = 0; i < uint8array.length; i++) { - const char_code = uint8array[i] | (uint8array[++i] << 8); - const utf16_size = char_code <= 0x7f ? 1 : char_code <= 0x7ff ? 2 : char_code <= 0xffff ? 3 : 4; - const char_size = char_code > 0xff ? utf16_size : 2; - if (acc_size + char_size > max_size) { - result.push(acc_str); - acc_str = ''; - acc_size = 0; - } - if (char_code > 0xff) { - acc_str += String.fromCharCode(char_code); - acc_size += utf16_size; - } - else { - acc_str += char_code.toString(16).padStart(2, '0').toUpperCase(); - acc_size += 2; - } - yield; - } - result.push(acc_str); - return result; - } - NET.serialize = serialize; - function* deserialize(strings) { - const result = []; - for (let i = 0; i < strings.length; i++) { - const str = strings[i]; - for (let j = 0; j < str.length; j++) { - const char_code = str.charCodeAt(j); - if (char_code <= 0xff) { - const hex = str[j] + str[++j]; - const hex_code = parseInt(hex, 16); - result.push(hex_code & 0xff); - result.push(hex_code >> 8); - } - else { - result.push(char_code & 0xff); - result.push(char_code >> 8); - } - yield; - } - yield; - } - return PROTO.ByteQueue.from_uint8array(new Uint8Array(result)); - } - NET.deserialize = deserialize; - system.afterEvents.scriptEventReceive.subscribe(event => { - system.runJob((function* () { - const [serialized_endpoint, serialized_header] = event.id.split(':'); - const endpoint_stream = yield* PROTO.MIPS.deserialize(serialized_endpoint); - const endpoint = yield* PROTO.Endpoint.deserialize(endpoint_stream); - const listeners = ENDPOINTS.get(endpoint); - if (event.sourceType === ScriptEventSource.Server && listeners) { - const header_stream = yield* PROTO.MIPS.deserialize(serialized_header); - const header = yield* PROTO.Header.deserialize(header_stream); - for (let i = 0; i < listeners.length; i++) { - yield* listeners[i](header, event.message); - } - } - })()); - }); - function create_listener(endpoint, listener) { - let listeners = ENDPOINTS.get(endpoint); - if (!listeners) { - listeners = new Array(); - ENDPOINTS.set(endpoint, listeners); - } - listeners.push(listener); - return () => { - const idx = listeners.indexOf(listener); - if (idx !== -1) - listeners.splice(idx, 1); - if (listeners.length === 0) { - ENDPOINTS.delete(endpoint); - } - }; - } - function generate_id() { - const r = (Math.random() * 0x100000000) >>> 0; - return ((r & 0xff).toString(16).padStart(2, '0') + - ((r >> 8) & 0xff).toString(16).padStart(2, '0') + - ((r >> 16) & 0xff).toString(16).padStart(2, '0') + - ((r >> 24) & 0xff).toString(16).padStart(2, '0')).toUpperCase(); - } - function* emit(endpoint, serializer, value) { - const guid = generate_id(); - const endpoint_stream = new PROTO.ByteQueue(); - yield* PROTO.Endpoint.serialize(endpoint, endpoint_stream); - const serialized_endpoint = yield* PROTO.MIPS.serialize(endpoint_stream); - const RUN = function* (header, serialized_packet) { - const header_stream = new PROTO.ByteQueue(); - yield* PROTO.Header.serialize(header, header_stream); - const serialized_header = yield* PROTO.MIPS.serialize(header_stream); - world - .getDimension('overworld') - .runCommand(`scriptevent ${serialized_endpoint}:${serialized_header} ${serialized_packet}`); - }; - const packet_stream = new PROTO.ByteQueue(); - yield* serializer.serialize(value, packet_stream); - const serialized_packets = yield* serialize(packet_stream, FRAG_MAX); - for (let i = 0; i < serialized_packets.length; i++) { - const serialized_packet = serialized_packets[i]; - yield* RUN({ guid, encoding: ENCODING, index: i, final: i === serialized_packets.length - 1 }, serialized_packet); - } - } - NET.emit = emit; - function listen(endpoint, serializer, callback) { - const buffer = new Map(); - const listener = function* (payload, serialized_packet) { - let fragment = buffer.get(payload.guid); - if (!fragment) { - fragment = { size: -1, serialized_packets: [], data_size: 0 }; - buffer.set(payload.guid, fragment); - } - if (payload.final) { - fragment.size = payload.index + 1; - } - fragment.serialized_packets[payload.index] = serialized_packet; - fragment.data_size += payload.index + 1; - if (fragment.size !== -1 && fragment.data_size === (fragment.size * (fragment.size + 1)) / 2) { - const stream = yield* deserialize(fragment.serialized_packets); - const value = yield* serializer.deserialize(stream); - yield* callback(value); - buffer.delete(payload.guid); - } - }; - return create_listener(endpoint, listener); - } - NET.listen = listen; -})(NET || (NET = {})); -export var IPC; -(function (IPC) { - /** Sends a message with `args` to `channel` */ - function send(channel, serializer, value) { - system.runJob(NET.emit(`ipc:${channel}:send`, serializer, value)); - } - IPC.send = send; - /** Sends an `invoke` message through IPC, and expects a result asynchronously. */ - function invoke(channel, serializer, value, deserializer) { - system.runJob(NET.emit(`ipc:${channel}:invoke`, serializer, value)); - return new Promise(resolve => { - const terminate = NET.listen(`ipc:${channel}:handle`, deserializer, function* (value) { - resolve(value); - terminate(); - }); - }); - } - IPC.invoke = invoke; - /** Listens to `channel`. When a new message arrives, `listener` will be called with `listener(args)`. */ - function on(channel, deserializer, listener) { - return NET.listen(`ipc:${channel}:send`, deserializer, function* (value) { - listener(value); - }); - } - IPC.on = on; - /** Listens to `channel` once. When a new message arrives, `listener` will be called with `listener(args)`, and then removed. */ - function once(channel, deserializer, listener) { - const terminate = NET.listen(`ipc:${channel}:send`, deserializer, function* (value) { - listener(value); - terminate(); - }); - return terminate; - } - IPC.once = once; - /** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */ - function handle(channel, deserializer, serializer, listener) { - return NET.listen(`ipc:${channel}:invoke`, deserializer, function* (value) { - const result = listener(value); - yield* NET.emit(`ipc:${channel}:handle`, serializer, result); - }); - } - IPC.handle = handle; -})(IPC || (IPC = {})); -export default IPC; diff --git a/packs/BP/scripts/options/easyPlace.js b/packs/BP/scripts/options/easyPlace.js index 93fcbb9..b4d8a72 100644 --- a/packs/BP/scripts/options/easyPlace.js +++ b/packs/BP/scripts/options/easyPlace.js @@ -1,7 +1,7 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server'; import { structureCollection } from '../classes/Structure/StructureCollection'; -import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, blockIdToItemStackMap } from '../data'; +import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, blockIdToItemStackMap } from './easyPlaceConversions'; import { fetchMatchingItemSlot, placeBlock } from '../utils'; import { Builders } from '../classes/Builder/Builders'; diff --git a/packs/BP/scripts/data.js b/packs/BP/scripts/options/easyPlaceConversions.js similarity index 97% rename from packs/BP/scripts/data.js rename to packs/BP/scripts/options/easyPlaceConversions.js index e205aa4..57e919f 100644 --- a/packs/BP/scripts/data.js +++ b/packs/BP/scripts/options/easyPlaceConversions.js @@ -41,7 +41,6 @@ export const resetToBlockStates = { cluster_count: 0, respawn_anchor_charge: 0, turtle_egg_count: 0, - cluster_count: 0, composter_fill_level: 0, end_portal_eye_bit: false }; @@ -55,5 +54,6 @@ export const blockIdToItemStackMap = { export const specialItemPlacementConversions = { 'water_bucket': 'bucket', - 'lava_bucket': 'bucket' + 'lava_bucket': 'bucket', + 'powder_snow_bucket': 'bucket' }; \ No newline at end of file diff --git a/packs/BP/scripts/options/fastEasyPlace.js b/packs/BP/scripts/options/fastEasyPlace.js index 08b8ea4..bf8ccd3 100644 --- a/packs/BP/scripts/options/fastEasyPlace.js +++ b/packs/BP/scripts/options/fastEasyPlace.js @@ -1,7 +1,7 @@ import { BuilderOption } from '../classes/Builder/BuilderOption'; import { BlockPermutation, EntityComponentTypes, EquipmentSlot, GameMode, ItemStack, system, world } from '@minecraft/server'; import { bannedBlocks, bannedToValidBlockMap, whitelistedBlockStates, resetToBlockStates, bannedDimensionBlocks, - blockIdToItemStackMap } from '../data'; + blockIdToItemStackMap } from './easyPlaceConversions'; import { placeBlock, fetchMatchingItemSlot } from '../utils'; import { Raycaster } from '../classes/Raycaster'; import { Builders } from '../classes/Builder/Builders'; diff --git a/packs/BP/scripts/options/materialGrabber.js b/packs/BP/scripts/options/materialGrabber.js index 3fe72ee..d4bf889 100644 --- a/packs/BP/scripts/options/materialGrabber.js +++ b/packs/BP/scripts/options/materialGrabber.js @@ -140,7 +140,8 @@ function playSoundEffect(player, transferCount) { function tryTransferAmountToPlayer(slot, playerContainer, materials, grabAmount) { const itemStack = slot.getItem(); if (canAddItem(playerContainer, itemStack)) { - const itemStackToAdd = new ItemStack(itemStack.typeId, grabAmount); + const itemStackToAdd = itemStack.clone(); + itemStackToAdd.amount = grabAmount; addItem(playerContainer, itemStackToAdd); materials.remove(itemStack.typeId, grabAmount); removeAmount(slot, grabAmount); @@ -185,7 +186,8 @@ function partiallyFilledSlotPass(inventory, itemStack) { const remainderAmount = Math.max(0, (slot.amount + itemStack.amount) - slot.maxAmount); slot.amount += itemStack.amount - remainderAmount; if (remainderAmount > 0) { - const remainderStack = new ItemStack(itemStack.typeId, remainderAmount); + const remainderStack = itemStack.clone(); + remainderStack.amount = remainderAmount; addItem(inventory, remainderStack); } return true; diff --git a/packs/BP/scripts/utils.js b/packs/BP/scripts/utils.js index d9eb049..5a2e54c 100644 --- a/packs/BP/scripts/utils.js +++ b/packs/BP/scripts/utils.js @@ -1,6 +1,6 @@ import { system, EntityComponentTypes, LiquidType, ItemStack } from '@minecraft/server'; import { FormCancelationReason } from '@minecraft/server-ui'; -import { specialItemPlacementConversions } from './data'; +import { specialItemPlacementConversions } from './options/easyPlaceConversions'; import { blocks, block_sounds } from './blocks'; export async function forceShow(player, form, timeout = Infinity) { diff --git a/packs/RP/manifest.json b/packs/RP/manifest.json index f844e7a..8591865 100644 --- a/packs/RP/manifest.json +++ b/packs/RP/manifest.json @@ -1,11 +1,11 @@ { "format_version": 2, "header": { - "name": "Construct [RP] v1.0.6", + "name": "Construct [RP] v1.0.7", "description": "Survival building addon by §aForestOfLight§r.", "uuid": "375ec465-3dc1-429f-8b4c-a337889e1ed4", - "version": [1, 0, 6], - "min_engine_version": [1,17,1] + "version": [1, 0, 7], + "min_engine_version": [1,26,0] }, "modules": [ { @@ -17,9 +17,12 @@ "dependencies": [ { "uuid": "8c0c0153-d8b9-482a-889f-aef922b8fe58", // Construct BP - "version": [1, 0, 6] + "version": [1, 0, 7] } ], + "capabilities": [ + "pbr" + ], "metadata": { "authors": [ "ForestOfLight" ], "license": "MIT" diff --git a/packs/RP/texts/en_US.lang b/packs/RP/texts/en_US.lang index 7cfc03c..e487fa4 100644 --- a/packs/RP/texts/en_US.lang +++ b/packs/RP/texts/en_US.lang @@ -68,28 +68,27 @@ construct.mainmenu.instance.exists=§cInstance '%s' already exists. Try again wi construct.mainmenu.instance.notfound=§cStructure ID '%s' not found. If you're looking for a structure that you put in the structures folder, please restart your world and try again. ## Insert string: structure name construct.mainmenu.title=§l§2Construct ## This is the name of the pack. construct.mainmenu.selectinstance=Select an instance: +construct.mainmenu.howto=How to Add/Remove Structures construct.mainmenu.settings=Builder Settings construct.mainmenu.newinstance=Create New Instance construct.mainmenu.newinstance.prompt=Enter a name for the new instance: construct.mainmenu.newinstance.placeholder=example_instance ## The placeholder text in the textbox where the user enters the instance name. construct.mainmenu.selectstructure.header=Select a structure: construct.mainmenu.selectstructure.other=Other -construct.mainmenu.selectstructure.howto=How to Add/Remove Structures construct.mainmenu.selectstructure.other.prompt=Enter the Structure ID: construct.mainmenu.selectstructure.other.placeholder=example_structure ## The placeholder text in the textbox where the user enters the structure name. -construct.mainmenu.selectstructure.howto.add.header=§aHow to Add Structures: -construct.mainmenu.selectstructure.howto.add.structureblock=§7- Save a structure using a §fstructure block§7 or the §f/structure§7 command. -construct.mainmenu.selectstructure.howto.add.or=§7§lOR§r -construct.mainmenu.selectstructure.howto.add.mcstructure=§7- Add a §f.mcstructure§7 file to this pack's §fstructures folder§7. When selecting your structure, select the §fOther§7 option and then use the filename (without '.mcstructure') as the §fStructure ID§7. After its first use, it will be added to the list of structures. -construct.mainmenu.selectstructure.howto.remove.header=§cHow to Remove Structures: -construct.mainmenu.selectstructure.howto.remove.body=§7- Use the §f/structure delete§7 command to remove a structure from the world. +construct.mainmenu.howto.add.header=§aHow to Add Structures: +construct.mainmenu.howto.add.structureblock=§7- Save a structure using a §fstructure block§7 or the §f/structure§7 command. +construct.mainmenu.howto.add.or=§7§lOR§r +construct.mainmenu.howto.add.mcstructure=§7- Add a §f.mcstructure§7 file to this pack's §fstructures folder§7. When selecting your structure, select the §fOther§7 option and then use the filename (without '.mcstructure') as the §fStructure ID§7. After its first use, it will be added to the list of structures. +construct.mainmenu.howto.remove.header=§cHow to Remove Structures: +construct.mainmenu.howto.remove.body=§7- Use the §f/structure delete§7 command to remove a structure from the world. ## Commands -construct.commands.construct=Opens the Construct menu. -construct.commands.item=Gives you the Construct item. Use it to open the Construct menu. -construct.commands.item.denyorigin=This command can only be used by players. -construct.commands.item.fail=§cFailed to give you the Construct item. -construct.commands.item.success=§aYou recieved the Construct item! Use it to open the Construct menu. +construct.commands.construct=Gives you the Construct item. Use it to open the Construct menu. +construct.commands.construct.denyorigin=This command can only be used by players. +construct.commands.construct.fail=§cFailed to give you the Construct item. +construct.commands.construct.success=§aYou recieved the Construct item! Use it to open the Construct menu. ## Options construct.option.enabled= is now enabled!