feat: bump MCBE-IPC to 3.4.2

This commit is contained in:
ForestOfLight
2026-05-30 00:24:43 +02:00
Unverified
parent 2b90020e37
commit 5177f7c986
2 changed files with 314 additions and 235 deletions
+50 -34
View File
@@ -2,7 +2,7 @@
* @license * @license
* MIT License * MIT License
* *
* Copyright (c) 2025 OmniacDev * Copyright (c) 2026 OmniacDev
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,11 +23,15 @@
* SOFTWARE. * SOFTWARE.
*/ */
export declare namespace PROTO { export declare namespace PROTO {
interface Serializable<T> { interface Serializer<T> {
serialize(value: T, stream: ByteQueue): Generator<void, void, void>; serialize(value: T, stream: Buffer): Generator<void, void, void>;
deserialize(stream: ByteQueue): Generator<void, T, void>;
} }
class ByteQueue { interface Deserializer<T> {
deserialize(stream: Buffer): Generator<void, T, void>;
}
interface Serializable<T> extends Serializer<T>, Deserializer<T> {
}
class Buffer {
private _buffer; private _buffer;
private _data_view; private _data_view;
private _length; private _length;
@@ -36,15 +40,20 @@ export declare namespace PROTO {
get front(): number; get front(): number;
get data_view(): DataView; get data_view(): DataView;
constructor(size?: number); constructor(size?: number);
write(...values: number[]): void; reserve(amount: number): number;
read(amount?: number): number[]; consume(amount: number): number;
write(byte: number): void;
write(bytes: Uint8Array): void;
read(): number;
read(amount: number): Uint8Array;
ensure_capacity(size: number): void; ensure_capacity(size: number): void;
static from_uint8array(array: Uint8Array): ByteQueue; static from_uint8array(array: Uint8Array): Buffer;
to_uint8array(): Uint8Array; to_uint8array(): Uint8Array;
} }
namespace MIPS { namespace MIPS {
function serialize(byte_queue: PROTO.ByteQueue): Generator<void, string, void>; function is_valid(str: string): boolean;
function deserialize(str: string): Generator<void, PROTO.ByteQueue, void>; function serialize(stream: PROTO.Buffer): Generator<void, string, void>;
function deserialize(str: string): Generator<void, PROTO.Buffer, void>;
} }
const Void: PROTO.Serializable<void>; const Void: PROTO.Serializable<void>;
const Null: PROTO.Serializable<null>; const Null: PROTO.Serializable<null>;
@@ -56,48 +65,55 @@ export declare namespace PROTO {
const UInt16: PROTO.Serializable<number>; const UInt16: PROTO.Serializable<number>;
const UInt32: PROTO.Serializable<number>; const UInt32: PROTO.Serializable<number>;
const UVarInt32: PROTO.Serializable<number>; const UVarInt32: PROTO.Serializable<number>;
const VarInt32: PROTO.Serializable<number>;
const Float32: PROTO.Serializable<number>; const Float32: PROTO.Serializable<number>;
const Float64: PROTO.Serializable<number>; const Float64: PROTO.Serializable<number>;
const String: PROTO.Serializable<string>; const String: PROTO.Serializable<string>;
const Boolean: PROTO.Serializable<boolean>; const Boolean: PROTO.Serializable<boolean>;
const UInt8Array: PROTO.Serializable<Uint8Array>; const UInt8Array: PROTO.Serializable<Uint8Array>;
const Date: PROTO.Serializable<Date>; const Date: PROTO.Serializable<Date>;
function Object<T extends object>(obj: { function Object<T extends object>(s: {
[K in keyof T]: PROTO.Serializable<T[K]>; [K in keyof T]: PROTO.Serializable<T[K]>;
}): PROTO.Serializable<T>; }): PROTO.Serializable<T>;
function Array<T>(value: PROTO.Serializable<T>): PROTO.Serializable<T[]>; function Array<T>(s: PROTO.Serializable<T>): PROTO.Serializable<T[]>;
function Tuple<T extends any[]>(...values: { function Tuple<T extends any[]>(...s: {
[K in keyof T]: PROTO.Serializable<T[K]>; [K in keyof T]: PROTO.Serializable<T[K]>;
}): PROTO.Serializable<T>; }): PROTO.Serializable<T>;
function Optional<T>(value: PROTO.Serializable<T>): PROTO.Serializable<T | undefined>; function Optional<T>(s: PROTO.Serializable<T>): PROTO.Serializable<T | undefined>;
function Map<K, V>(key: PROTO.Serializable<K>, value: PROTO.Serializable<V>): PROTO.Serializable<Map<K, V>>; function Map<K, V>(kS: PROTO.Serializable<K>, vS: PROTO.Serializable<V>): PROTO.Serializable<Map<K, V>>;
function Set<V>(value: PROTO.Serializable<V>): PROTO.Serializable<Set<V>>; function Set<V>(s: PROTO.Serializable<V>): PROTO.Serializable<Set<V>>;
type Endpoint = string; function Cached<V>(s: PROTO.Serializable<V>, depth?: number): PROTO.Serializable<V>;
type Header = {
guid: string;
encoding: string;
index: number;
final: boolean;
};
const Endpoint: PROTO.Serializable<Endpoint>;
const Header: PROTO.Serializable<Header>;
} }
export declare namespace NET { export declare namespace NET {
function serialize(byte_queue: PROTO.ByteQueue, max_size?: number): Generator<void, string[], void>; type Meta = {
function deserialize(strings: string[]): Generator<void, PROTO.ByteQueue, void>; guid: string;
function emit<S extends PROTO.Serializable<T>, T>(endpoint: string, serializer: S & PROTO.Serializable<T>, value: T): Generator<void, void, void>; signature: string;
function listen<T, S extends PROTO.Serializable<T>>(endpoint: string, serializer: S & PROTO.Serializable<T>, callback: (value: T) => Generator<void, void, void>): () => void; };
const Meta: PROTO.Serializable<Meta>;
export const SIGNATURE: string;
export let FRAG_MAX: number;
export function serialize(buffer: PROTO.Buffer, max_size?: number): Generator<void, string[], void>;
export function deserialize(strings: string[]): Generator<void, PROTO.Buffer, void>;
export interface EmitOptions {
metaOverride?: Partial<Meta>;
}
export function emit<S>(endpoint: string, serializer: PROTO.Serializer<S>, value: NoInfer<S>, options?: EmitOptions): Generator<void, void, void>;
export interface ListenOptions {
filter?: (meta: Meta) => boolean;
}
export function listen<D>(endpoint: string, deserializer: PROTO.Deserializer<D>, callback: (value: NoInfer<D>, meta: Meta) => Generator<void, void, void>, options?: ListenOptions): () => void;
export {};
} }
export declare namespace IPC { export declare namespace IPC {
/** Sends a message with `args` to `channel` */ /** Sends a message with `args` to `channel` */
function send<S extends PROTO.Serializable<T>, T>(channel: string, serializer: S & PROTO.Serializable<T>, value: T): void; function send<S>(channel: string, serializer: PROTO.Serializer<S>, value: NoInfer<S>): void;
/** Sends an `invoke` message through IPC, and expects a result asynchronously. */ /** Sends an `invoke` message through IPC, and expects a result asynchronously. */
function invoke<TS extends PROTO.Serializable<T>, T, RS extends PROTO.Serializable<R>, R>(channel: string, serializer: TS & PROTO.Serializable<T>, value: T, deserializer: RS & PROTO.Serializable<R>): Promise<R>; function invoke<S, D>(channel: string, serializer: PROTO.Serializer<S>, value: NoInfer<S>, deserializer: PROTO.Deserializer<D>): Promise<NoInfer<D>>;
/** Listens to `channel`. When a new message arrives, `listener` will be called with `listener(args)`. */ /** Listens to `channel`. When a new message arrives, `listener` will be called with `listener(args)`. */
function on<S extends PROTO.Serializable<T>, T>(channel: string, deserializer: S & PROTO.Serializable<T>, listener: (value: T) => void): () => void; function on<D>(channel: string, deserializer: PROTO.Deserializer<D>, listener: (value: NoInfer<D>) => void): () => void;
/** Listens to `channel` once. When a new message arrives, `listener` will be called with `listener(args)`, and then removed. */ /** Listens to `channel` once. When a new message arrives, `listener` will be called with `listener(args)`, and then removed. */
function once<S extends PROTO.Serializable<T>, T>(channel: string, deserializer: S & PROTO.Serializable<T>, listener: (value: T) => void): () => void; function once<D>(channel: string, deserializer: PROTO.Deserializer<D>, listener: (value: NoInfer<D>) => void): () => void;
/** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */ /** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */
function handle<TS extends PROTO.Serializable<T>, T, RS extends PROTO.Serializable<R>, R>(channel: string, deserializer: TS & PROTO.Serializable<T>, serializer: RS & PROTO.Serializable<R>, listener: (value: T) => R): () => void; function handle<D, S>(channel: string, deserializer: PROTO.Deserializer<D>, serializer: PROTO.Serializer<S>, listener: (value: NoInfer<D>) => NoInfer<S>): () => void;
} }
export default IPC; export default IPC;
+264 -201
View File
@@ -2,7 +2,7 @@
* @license * @license
* MIT License * MIT License
* *
* Copyright (c) 2025 OmniacDev * Copyright (c) 2026 OmniacDev
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
import { ScriptEventSource, system, world } from '@minecraft/server'; import { ScriptEventSource, system } from '@minecraft/server';
var UTIL;
(function (UTIL) {
function generate_id() {
const r = (Math.random() * 0x100000000) >>> 0;
return r.toString(16).padStart(8, '0').toUpperCase();
}
UTIL.generate_id = generate_id;
})(UTIL || (UTIL = {}));
export var PROTO; export var PROTO;
(function (PROTO) { (function (PROTO) {
class ByteQueue { class Buffer {
get end() { get end() {
return this._length + this._offset; return this._length + this._offset;
} }
@@ -41,20 +49,39 @@ export var PROTO;
this._length = 0; this._length = 0;
this._offset = 0; this._offset = 0;
} }
write(...values) { reserve(amount) {
this.ensure_capacity(values.length); this.ensure_capacity(amount);
this._buffer.set(values, this.end); const end = this.end;
this._length += values.length; this._length += amount;
return end;
} }
read(amount = 1) { consume(amount) {
if (this._length > 0) { if (amount > this._length)
const max_amount = amount > this._length ? this._length : amount; throw new Error('not enough bytes');
const values = this._buffer.subarray(this._offset, this._offset + max_amount); const front = this.front;
this._length -= max_amount; this._length -= amount;
this._offset += max_amount; this._offset += amount;
return globalThis.Array.from(values); return front;
}
write(input) {
if (typeof input === 'number') {
const offset = this.reserve(1);
this._buffer[offset] = input;
}
else {
const offset = this.reserve(input.length);
this._buffer.set(input, offset);
}
}
read(amount) {
if (amount === undefined) {
const offset = this.consume(1);
return this._buffer[offset];
}
else {
const offset = this.consume(amount);
return this._buffer.slice(offset, offset + amount);
} }
return [];
} }
ensure_capacity(size) { ensure_capacity(size) {
if (this.end + size > this._buffer.length) { if (this.end + size > this._buffer.length) {
@@ -66,22 +93,26 @@ export var PROTO;
} }
} }
static from_uint8array(array) { static from_uint8array(array) {
const byte_queue = new ByteQueue(); const buffer = new Buffer();
byte_queue._buffer = array; buffer._buffer = array;
byte_queue._length = array.length; buffer._length = array.length;
byte_queue._offset = 0; buffer._offset = 0;
byte_queue._data_view = new DataView(array.buffer); buffer._data_view = new DataView(array.buffer);
return byte_queue; return buffer;
} }
to_uint8array() { to_uint8array() {
return this._buffer.subarray(this._offset, this.end); return this._buffer.subarray(this._offset, this.end);
} }
} }
PROTO.ByteQueue = ByteQueue; PROTO.Buffer = Buffer;
let MIPS; let MIPS;
(function (MIPS) { (function (MIPS) {
function* serialize(byte_queue) { function is_valid(str) {
const uint8array = byte_queue.to_uint8array(); return str.startsWith('(0x') && str.endsWith(')');
}
MIPS.is_valid = is_valid;
function* serialize(stream) {
const uint8array = stream.to_uint8array();
let str = '(0x'; let str = '(0x';
for (let i = 0; i < uint8array.length; i++) { for (let i = 0; i < uint8array.length; i++) {
const hex = uint8array[i].toString(16).padStart(2, '0').toUpperCase(); const hex = uint8array[i].toString(16).padStart(2, '0').toUpperCase();
@@ -93,17 +124,17 @@ export var PROTO;
} }
MIPS.serialize = serialize; MIPS.serialize = serialize;
function* deserialize(str) { function* deserialize(str) {
if (str.startsWith('(0x') && str.endsWith(')')) { if (is_valid(str)) {
const result = []; const buffer = new Buffer();
const hex_str = str.slice(3, str.length - 1); const hex_str = str.slice(3, str.length - 1);
for (let i = 0; i < hex_str.length; i++) { for (let i = 0; i < hex_str.length; i++) {
const hex = hex_str[i] + hex_str[++i]; const hex = hex_str[i] + hex_str[++i];
result.push(parseInt(hex, 16)); buffer.write(parseInt(hex, 16));
yield; yield;
} }
return ByteQueue.from_uint8array(new Uint8Array(result)); return buffer;
} }
return new ByteQueue(); return new Buffer();
} }
MIPS.deserialize = deserialize; MIPS.deserialize = deserialize;
})(MIPS = PROTO.MIPS || (PROTO.MIPS = {})); })(MIPS = PROTO.MIPS || (PROTO.MIPS = {}));
@@ -125,120 +156,98 @@ export var PROTO;
}; };
PROTO.Int8 = { PROTO.Int8 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 1; stream.data_view.setInt8(stream.reserve(1), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setInt8(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getInt8(stream.front); return stream.data_view.getInt8(stream.consume(1));
stream.read(1);
return value;
} }
}; };
PROTO.Int16 = { PROTO.Int16 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 2; stream.data_view.setInt16(stream.reserve(2), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setInt16(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getInt16(stream.front); return stream.data_view.getInt16(stream.consume(2));
stream.read(2);
return value;
} }
}; };
PROTO.Int32 = { PROTO.Int32 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 4; stream.data_view.setInt32(stream.reserve(4), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setInt32(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getInt32(stream.front); return stream.data_view.getInt32(stream.consume(4));
stream.read(4);
return value;
} }
}; };
PROTO.UInt8 = { PROTO.UInt8 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 1; stream.data_view.setUint8(stream.reserve(1), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setUint8(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getUint8(stream.front); return stream.data_view.getUint8(stream.consume(1));
stream.read(1);
return value;
} }
}; };
PROTO.UInt16 = { PROTO.UInt16 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 2; stream.data_view.setUint16(stream.reserve(2), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setUint16(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getUint16(stream.front); return stream.data_view.getUint16(stream.consume(2));
stream.read(2);
return value;
} }
}; };
PROTO.UInt32 = { PROTO.UInt32 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 4; stream.data_view.setUint32(stream.reserve(4), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setUint32(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getUint32(stream.front); return stream.data_view.getUint32(stream.consume(4));
stream.read(4);
return value;
} }
}; };
PROTO.UVarInt32 = { PROTO.UVarInt32 = {
*serialize(value, stream) { *serialize(value, stream) {
value >>>= 0;
while (value >= 0x80) { while (value >= 0x80) {
stream.write((value & 0x7f) | 0x80); stream.write((value & 0x7f) | 0x80);
value >>= 7; value >>>= 7;
yield; yield;
} }
stream.write(value); stream.write(value);
}, },
*deserialize(stream) { *deserialize(stream) {
let value = 0; let value = 0;
let size = 0; for (let size = 0; size < 5; size++) {
let byte; const byte = stream.read();
do {
byte = stream.read()[0];
value |= (byte & 0x7f) << (size * 7); value |= (byte & 0x7f) << (size * 7);
size += 1;
yield; yield;
} while ((byte & 0x80) !== 0 && size < 10); if ((byte & 0x80) == 0)
return value; break;
}
return value >>> 0;
}
};
PROTO.VarInt32 = {
*serialize(value, stream) {
const zigzag = (value << 1) ^ (value >> 31);
yield* PROTO.UVarInt32.serialize(zigzag, stream);
},
*deserialize(stream) {
const zigzag = yield* PROTO.UVarInt32.deserialize(stream);
return (zigzag >>> 1) ^ -(zigzag & 1);
} }
}; };
PROTO.Float32 = { PROTO.Float32 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 4; stream.data_view.setFloat32(stream.reserve(4), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setFloat32(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getFloat32(stream.front); return stream.data_view.getFloat32(stream.consume(4));
stream.read(4);
return value;
} }
}; };
PROTO.Float64 = { PROTO.Float64 = {
*serialize(value, stream) { *serialize(value, stream) {
const length = 8; stream.data_view.setFloat64(stream.reserve(8), value);
stream.write(...globalThis.Array(length).fill(0));
stream.data_view.setFloat64(stream.end - length, value);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.data_view.getFloat64(stream.front); return stream.data_view.getFloat64(stream.consume(8));
stream.read(8);
return value;
} }
}; };
PROTO.String = { PROTO.String = {
@@ -264,18 +273,17 @@ export var PROTO;
stream.write(value ? 1 : 0); stream.write(value ? 1 : 0);
}, },
*deserialize(stream) { *deserialize(stream) {
const value = stream.read()[0]; return stream.read() !== 0;
return value === 1;
} }
}; };
PROTO.UInt8Array = { PROTO.UInt8Array = {
*serialize(value, stream) { *serialize(value, stream) {
yield* PROTO.UVarInt32.serialize(value.length, stream); yield* PROTO.UVarInt32.serialize(value.length, stream);
stream.write(...value); stream.write(value);
}, },
*deserialize(stream) { *deserialize(stream) {
const length = yield* PROTO.UVarInt32.deserialize(stream); const length = yield* PROTO.UVarInt32.deserialize(stream);
return new Uint8Array(stream.read(length)); return stream.read(length);
} }
}; };
PROTO.Date = { PROTO.Date = {
@@ -286,94 +294,91 @@ export var PROTO;
return new globalThis.Date(yield* PROTO.Float64.deserialize(stream)); return new globalThis.Date(yield* PROTO.Float64.deserialize(stream));
} }
}; };
function Object(obj) { function Object(s) {
return { return {
*serialize(value, stream) { *serialize(value, stream) {
for (const key in obj) { for (const key in s) {
yield* obj[key].serialize(value[key], stream); yield* s[key].serialize(value[key], stream);
} }
}, },
*deserialize(stream) { *deserialize(stream) {
const result = {}; const result = {};
for (const key in obj) { for (const key in s) {
result[key] = yield* obj[key].deserialize(stream); result[key] = yield* s[key].deserialize(stream);
} }
return result; return result;
} }
}; };
} }
PROTO.Object = Object; PROTO.Object = Object;
function Array(value) { function Array(s) {
return { return {
*serialize(array, stream) { *serialize(value, stream) {
const actualValue = typeof value === 'function' ? value() : value; yield* PROTO.UVarInt32.serialize(value.length, stream);
yield* PROTO.UVarInt32.serialize(array.length, stream); for (const item of value) {
for (const item of array) { yield* s.serialize(item, stream);
yield* actualValue.serialize(item, stream);
} }
}, },
*deserialize(stream) { *deserialize(stream) {
const actualValue = typeof value === 'function' ? value() : value;
const result = []; const result = [];
const length = yield* PROTO.UVarInt32.deserialize(stream); const length = yield* PROTO.UVarInt32.deserialize(stream);
for (let i = 0; i < length; i++) { for (let i = 0; i < length; i++) {
result[i] = yield* actualValue.deserialize(stream); result[i] = yield* s.deserialize(stream);
} }
return result; return result;
} }
}; };
} }
PROTO.Array = Array; PROTO.Array = Array;
function Tuple(...values) { function Tuple(...s) {
return { return {
*serialize(tuple, stream) { *serialize(value, stream) {
for (let i = 0; i < values.length; i++) { for (let i = 0; i < s.length; i++) {
yield* values[i].serialize(tuple[i], stream); yield* s[i].serialize(value[i], stream);
} }
}, },
*deserialize(stream) { *deserialize(stream) {
const result = []; const result = [];
for (let i = 0; i < values.length; i++) { for (let i = 0; i < s.length; i++) {
result[i] = yield* values[i].deserialize(stream); result[i] = yield* s[i].deserialize(stream);
} }
return result; return result;
} }
}; };
} }
PROTO.Tuple = Tuple; PROTO.Tuple = Tuple;
function Optional(value) { function Optional(s) {
return { return {
*serialize(optional, stream) { *serialize(value, stream) {
yield* PROTO.Boolean.serialize(optional !== undefined, stream); const def = value !== undefined;
if (optional !== undefined) { yield* PROTO.Boolean.serialize(def, stream);
yield* value.serialize(optional, stream); if (def)
} yield* s.serialize(value, stream);
}, },
*deserialize(stream) { *deserialize(stream) {
const defined = yield* PROTO.Boolean.deserialize(stream); const def = yield* PROTO.Boolean.deserialize(stream);
if (defined) { if (def)
return yield* value.deserialize(stream); return yield* s.deserialize(stream);
}
return undefined; return undefined;
} }
}; };
} }
PROTO.Optional = Optional; PROTO.Optional = Optional;
function Map(key, value) { function Map(kS, vS) {
return { return {
*serialize(map, stream) { *serialize(value, stream) {
yield* PROTO.UVarInt32.serialize(map.size, stream); yield* PROTO.UVarInt32.serialize(value.size, stream);
for (const [k, v] of map.entries()) { for (const [k, v] of value) {
yield* key.serialize(k, stream); yield* kS.serialize(k, stream);
yield* value.serialize(v, stream); yield* vS.serialize(v, stream);
} }
}, },
*deserialize(stream) { *deserialize(stream) {
const size = yield* PROTO.UVarInt32.deserialize(stream); const size = yield* PROTO.UVarInt32.deserialize(stream);
const result = new globalThis.Map(); const result = new globalThis.Map();
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const k = yield* key.deserialize(stream); const k = yield* kS.deserialize(stream);
const v = yield* value.deserialize(stream); const v = yield* vS.deserialize(stream);
result.set(k, v); result.set(k, v);
} }
return result; return result;
@@ -381,19 +386,19 @@ export var PROTO;
}; };
} }
PROTO.Map = Map; PROTO.Map = Map;
function Set(value) { function Set(s) {
return { return {
*serialize(set, stream) { *serialize(set, stream) {
yield* PROTO.UVarInt32.serialize(set.size, stream); yield* PROTO.UVarInt32.serialize(set.size, stream);
for (const [_, v] of set.entries()) { for (const v of set) {
yield* value.serialize(v, stream); yield* s.serialize(v, stream);
} }
}, },
*deserialize(stream) { *deserialize(stream) {
const size = yield* PROTO.UVarInt32.deserialize(stream); const size = yield* PROTO.UVarInt32.deserialize(stream);
const result = new globalThis.Set(); const result = new globalThis.Set();
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
const v = yield* value.deserialize(stream); const v = yield* s.deserialize(stream);
result.add(v); result.add(v);
} }
return result; return result;
@@ -401,21 +406,52 @@ export var PROTO;
}; };
} }
PROTO.Set = Set; PROTO.Set = Set;
PROTO.Endpoint = PROTO.String; function Cached(s, depth = 16) {
PROTO.Header = PROTO.Object({ const cache = new globalThis.Map();
guid: PROTO.String, return {
encoding: PROTO.String, *serialize(value, stream) {
index: PROTO.UVarInt32, const hit = cache.get(value);
final: PROTO.Boolean if (hit !== undefined) {
}); stream.write(hit);
cache.delete(value);
cache.set(value, hit);
}
else {
const buffer = new PROTO.Buffer();
yield* s.serialize(value, buffer);
const bytes = buffer.to_uint8array();
stream.write(bytes);
cache.set(value, bytes);
if (cache.size > depth) {
const first = cache.keys().next().value;
cache.delete(first);
}
}
},
*deserialize(stream) {
return yield* s.deserialize(stream);
}
};
}
PROTO.Cached = Cached;
})(PROTO || (PROTO = {})); })(PROTO || (PROTO = {}));
export var NET; export var NET;
(function (NET) { (function (NET) {
const FRAG_MAX = 2048; const Endpoint = PROTO.String;
const ENCODING = 'mcbe-ipc:v3'; const Meta = PROTO.Object({
const ENDPOINTS = new Map(); guid: PROTO.String,
function* serialize(byte_queue, max_size = Infinity) { signature: PROTO.String
const uint8array = byte_queue.to_uint8array(); });
const Header = PROTO.Object({
meta: Meta,
index: PROTO.UVarInt32,
final: PROTO.Boolean
});
const LISTENERS = new Map();
NET.SIGNATURE = 'mcbe-ipc:v3';
NET.FRAG_MAX = 2048;
function* serialize(buffer, max_size = Infinity) {
const uint8array = buffer.to_uint8array();
const result = []; const result = [];
let acc_str = ''; let acc_str = '';
let acc_size = 0; let acc_size = 0;
@@ -443,7 +479,7 @@ export var NET;
} }
NET.serialize = serialize; NET.serialize = serialize;
function* deserialize(strings) { function* deserialize(strings) {
const result = []; const buffer = new PROTO.Buffer();
for (let i = 0; i < strings.length; i++) { for (let i = 0; i < strings.length; i++) {
const str = strings[i]; const str = strings[i];
for (let j = 0; j < str.length; j++) { for (let j = 0; j < str.length; j++) {
@@ -451,40 +487,49 @@ export var NET;
if (char_code <= 0xff) { if (char_code <= 0xff) {
const hex = str[j] + str[++j]; const hex = str[j] + str[++j];
const hex_code = parseInt(hex, 16); const hex_code = parseInt(hex, 16);
result.push(hex_code & 0xff); buffer.write(hex_code & 0xff);
result.push(hex_code >> 8); buffer.write(hex_code >> 8);
} }
else { else {
result.push(char_code & 0xff); buffer.write(char_code & 0xff);
result.push(char_code >> 8); buffer.write(char_code >> 8);
} }
yield; yield;
} }
yield; yield;
} }
return PROTO.ByteQueue.from_uint8array(new Uint8Array(result)); return buffer;
} }
NET.deserialize = deserialize; NET.deserialize = deserialize;
system.afterEvents.scriptEventReceive.subscribe(event => { system.afterEvents.scriptEventReceive.subscribe(event => {
system.runJob((function* () { system.runJob((function* () {
if (event.sourceType !== ScriptEventSource.Server)
return;
const [serialized_endpoint, serialized_header] = event.id.split(':'); const [serialized_endpoint, serialized_header] = event.id.split(':');
if (!PROTO.MIPS.is_valid(serialized_endpoint))
return;
const endpoint_stream = yield* PROTO.MIPS.deserialize(serialized_endpoint); const endpoint_stream = yield* PROTO.MIPS.deserialize(serialized_endpoint);
const endpoint = yield* PROTO.Endpoint.deserialize(endpoint_stream); const endpoint = yield* Endpoint.deserialize(endpoint_stream);
const listeners = ENDPOINTS.get(endpoint); const listeners = LISTENERS.get(endpoint);
if (event.sourceType === ScriptEventSource.Server && listeners) { if (listeners !== undefined && PROTO.MIPS.is_valid(serialized_header)) {
const header_stream = yield* PROTO.MIPS.deserialize(serialized_header); const header_stream = yield* PROTO.MIPS.deserialize(serialized_header);
const header = yield* PROTO.Header.deserialize(header_stream); const header = yield* Header.deserialize(header_stream);
for (let i = 0; i < listeners.length; i++) { for (const listener of [...listeners]) {
yield* listeners[i](header, event.message); try {
yield* listener(header, event.message);
}
catch (e) {
console.error(`[MCBE-IPC] listener error while handling packet on "${endpoint}":`, e);
}
} }
} }
})()); })());
}); });
function create_listener(endpoint, listener) { function register(endpoint, listener) {
let listeners = ENDPOINTS.get(endpoint); let listeners = LISTENERS.get(endpoint);
if (!listeners) { if (listeners === undefined) {
listeners = new Array(); listeners = new Array();
ENDPOINTS.set(endpoint, listeners); LISTENERS.set(endpoint, listeners);
} }
listeners.push(listener); listeners.push(listener);
return () => { return () => {
@@ -492,60 +537,61 @@ export var NET;
if (idx !== -1) if (idx !== -1)
listeners.splice(idx, 1); listeners.splice(idx, 1);
if (listeners.length === 0) { if (listeners.length === 0) {
ENDPOINTS.delete(endpoint); LISTENERS.delete(endpoint);
} }
}; };
} }
function generate_id() { function* emit(endpoint, serializer, value, options) {
const r = (Math.random() * 0x100000000) >>> 0; const guid = options?.metaOverride?.guid ?? UTIL.generate_id();
return ((r & 0xff).toString(16).padStart(2, '0') + const signature = options?.metaOverride?.signature ?? NET.SIGNATURE;
((r >> 8) & 0xff).toString(16).padStart(2, '0') + const endpoint_stream = new PROTO.Buffer();
((r >> 16) & 0xff).toString(16).padStart(2, '0') + yield* Endpoint.serialize(endpoint, endpoint_stream);
((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 serialized_endpoint = yield* PROTO.MIPS.serialize(endpoint_stream);
const RUN = function* (header, serialized_packet) { const packet_stream = new PROTO.Buffer();
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); yield* serializer.serialize(value, packet_stream);
const serialized_packets = yield* serialize(packet_stream, FRAG_MAX); const serialized_packets = yield* serialize(packet_stream, NET.FRAG_MAX);
for (let i = 0; i < serialized_packets.length; i++) { for (let i = 0; i < serialized_packets.length; i++) {
const serialized_packet = serialized_packets[i]; const serialized_packet = serialized_packets[i];
yield* RUN({ guid, encoding: ENCODING, index: i, final: i === serialized_packets.length - 1 }, serialized_packet); const header = {
meta: { guid, signature },
index: i,
final: i === serialized_packets.length - 1
};
const header_stream = new PROTO.Buffer();
yield* Header.serialize(header, header_stream);
const serialized_header = yield* PROTO.MIPS.serialize(header_stream);
system.sendScriptEvent(`${serialized_endpoint}:${serialized_header}`, serialized_packet);
} }
} }
NET.emit = emit; NET.emit = emit;
function listen(endpoint, serializer, callback) { function listen(endpoint, deserializer, callback, options) {
const buffer = new Map(); const buffer = new Map();
const listener = function* (payload, serialized_packet) { const listener = function* (header, fragment) {
let fragment = buffer.get(payload.guid); let packet = buffer.get(header.meta.guid);
if (!fragment) { if (packet === undefined) {
fragment = { size: -1, serialized_packets: [], data_size: 0 }; if (options?.filter?.(header.meta) === false)
buffer.set(payload.guid, fragment); return;
packet = { size: -1, fragments: [], received: 0 };
buffer.set(header.meta.guid, packet);
} }
if (payload.final) { if (header.final) {
fragment.size = payload.index + 1; packet.size = header.index + 1;
} }
fragment.serialized_packets[payload.index] = serialized_packet; if (packet.fragments[header.index] === undefined) {
fragment.data_size += payload.index + 1; packet.fragments[header.index] = fragment;
if (fragment.size !== -1 && fragment.data_size === (fragment.size * (fragment.size + 1)) / 2) { packet.received++;
const stream = yield* deserialize(fragment.serialized_packets); }
const value = yield* serializer.deserialize(stream); else {
yield* callback(value); throw new Error(`received duplicate fragment ${header.index} for packet ${header.meta.guid}`);
buffer.delete(payload.guid); }
if (packet.size !== -1 && packet.size === packet.received) {
const stream = yield* deserialize(packet.fragments);
const value = yield* deserializer.deserialize(stream);
yield* callback(value, header.meta);
buffer.delete(header.meta.guid);
} }
}; };
return create_listener(endpoint, listener); return register(endpoint, listener);
} }
NET.listen = listen; NET.listen = listen;
})(NET || (NET = {})); })(NET || (NET = {}));
@@ -558,12 +604,22 @@ export var IPC;
IPC.send = send; IPC.send = send;
/** Sends an `invoke` message through IPC, and expects a result asynchronously. */ /** Sends an `invoke` message through IPC, and expects a result asynchronously. */
function invoke(channel, serializer, value, deserializer) { function invoke(channel, serializer, value, deserializer) {
system.runJob(NET.emit(`ipc:${channel}:invoke`, serializer, value)); const id = UTIL.generate_id();
return new Promise(resolve => { return new Promise(resolve => {
const terminate = NET.listen(`ipc:${channel}:handle`, deserializer, function* (value) { const terminate = NET.listen(`ipc:${channel}:handle`, deserializer, function* (value, meta) {
if (meta.signature.includes(`+correlation`) && meta.guid !== id)
return;
resolve(value); resolve(value);
terminate(); terminate();
}, {
filter: meta => !meta.signature.includes(`+correlation`) || meta.guid === id
}); });
system.runJob(NET.emit(`ipc:${channel}:invoke`, serializer, value, {
metaOverride: {
guid: id,
signature: `${NET.SIGNATURE}+correlation`
}
}));
}); });
} }
IPC.invoke = invoke; IPC.invoke = invoke;
@@ -585,9 +641,16 @@ export var IPC;
IPC.once = once; IPC.once = once;
/** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */ /** Adds a handler for an `invoke` IPC. This handler will be called whenever `invoke(channel, ...args)` is called */
function handle(channel, deserializer, serializer, listener) { function handle(channel, deserializer, serializer, listener) {
return NET.listen(`ipc:${channel}:invoke`, deserializer, function* (value) { return NET.listen(`ipc:${channel}:invoke`, deserializer, function* (value, meta) {
const result = listener(value); const result = listener(value);
yield* NET.emit(`ipc:${channel}:handle`, serializer, result); yield* NET.emit(`ipc:${channel}:handle`, serializer, result, {
metaOverride: meta.signature.includes(`+correlation`)
? {
guid: meta.guid,
signature: `${NET.SIGNATURE}+correlation`
}
: undefined
});
}); });
} }
IPC.handle = handle; IPC.handle = handle;