From 68c9ad5ef23f0d4a47e214633c73b4fda2c0e442 Mon Sep 17 00:00:00 2001 From: ForestOfLight Date: Wed, 11 Feb 2026 13:56:51 -0800 Subject: [PATCH] decouple from Canopy --- README.md | 4 - packs/BP/scripts/config.js | 8 - .../BP/scripts/lib/canopy/CanopyExtension.js | 166 ----- packs/BP/scripts/lib/canopy/Command.js | 103 --- packs/BP/scripts/lib/canopy/Rule.js | 70 --- packs/BP/scripts/lib/canopy/extension.ipc.js | 70 --- packs/BP/scripts/lib/ipc/ipc.d.ts | 103 --- packs/BP/scripts/lib/ipc/ipc.js | 593 ------------------ 8 files changed, 1117 deletions(-) delete mode 100644 packs/BP/scripts/config.js delete mode 100644 packs/BP/scripts/lib/canopy/CanopyExtension.js delete mode 100644 packs/BP/scripts/lib/canopy/Command.js delete mode 100644 packs/BP/scripts/lib/canopy/Rule.js delete mode 100644 packs/BP/scripts/lib/canopy/extension.ipc.js delete mode 100644 packs/BP/scripts/lib/ipc/ipc.d.ts delete mode 100644 packs/BP/scripts/lib/ipc/ipc.js diff --git a/README.md b/README.md index 0dab28d..167698e 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,6 @@ 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! 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;