Releases: OmniacDev/MCBE-IPC
Release v3.0.0
Moving from JSON to Binary
With v3.0.0, much of the library has been restructured to use binary serialization, rather than the previous JSON strings.
This brings a few performance improvements for complex value types, such as nested objects, and it also has a reduced footprint in the scriptevent messages, allowing for more data to be sent in a set amount of time, since less space is wasted with unnecessary data.
I personally find this version way nicer than previous versions, but I would like to hear everyone's opinions, and any suggestions I could make to further improve upon this.
This version will of course NOT be compatible with previous versions.
Serializable
Lets begin by exploring the "Serializable" types. These are types that can be serialized/deserialized, to and from their binary forms. v3.0.0 comes with a few of these already implemented (These are exported by the Proto
class);
- Int8
- Int16
- Int32
- UInt8
- UInt16
- UInt32
- Float32
- Float64
- VarInt
- String
- Boolean
- UInt8Array
- Date
- Object
- Array
- Tuple
- Optional
- Map
- Set
You can use these serializable types to create serializers for almost any type of value. Most of the types are simple static Serializable
instances, however, a few of them are methods, used to create a new Serializable
that reflects the input type structure.
For example, you can use the Proto.Object
method, to create a serializer for an arbitrary object type, such as the ConnectionSerializer used by the Connection & ConnectionManager classes for communication;
const ConnectionSerializer = Proto.Object({
from: Proto.String,
bytes: Proto.UInt8Array
})
Which is then used internally with NET.emit;
yield* NET.emit(`ipc:${$._to}:${channel}:send`, ConnectionSerializer, {
from: $._from, // $._from is a string
bytes // bytes is a UInt8Array, maybe encrypted
})
Advanced
The Serializable system is pretty flexible, and allows users to define fully custom serializers. The Serializable
interface is simple, having only two methods, serialize & deserialize. These are both generator functions;
export interface Serializable<T = any> {
serialize(value: T, stream: ByteArray): Generator<void, void, void>
deserialize(stream: ByteArray): Generator<void, T, void>
}
The ByteArray
class is a simple dynamic wrapper over a UInt8Array, with a few extra methods, such as read
and write
.
read
takes a parameter for the number of bytes to read, and then returns an array of bytes (numbers in range 0-255) with that many elements. If the number of elements requested is more than the remaining elements in the array, it only returns the remaining ones.
write
takes any amount of bytes (numbers in range 0-255), and writes them onto the end of the buffer.
General
Now that we've explored the Serializables a bit, we can move on to the changes to the methods within the IPC & NET modules.
All the IPC methods now take a Serializable
as an argument, with some taking more than one;
// serializer argument for the value being sent
function send<T>(channel: string, serializer: NET.Serializable<T>, value: T): void
// serializer argument for value being sent, and deserializer for expected return value.
function invoke<T, R>(channel: string, serializer: NET.Serializable<T>, value: T, deserializer: NET.Serializable<R>): Promise<R>;
These changes apply for all methods, including those in Connection
and ConnectionManager
classes.
Similarly, the NET.emit
and NET.listen
functions also take serializers as an argument.
Type Distribution
In previous versions, sharing types was essentially non-existant, due to the annoying way JS works. However, in this version, it has become INCREDIBLY easy. Since the system now relies on serializers which exist at runtime, we can create and export them from common files, that can be easily distributed to other users.
For example, this simple file can be placed in the same folder as the main IPC files, and can then be used in methods etc. This can then be shared as a file that the user just has to drop in, with no annoying modifications to the main IPC files needed to get it working.
import { Proto } from "./ipc";
export const TestSerializer = Proto.Object({
value: Proto.String,
tick: Proto.VarInt
})
What's Changed
- v3.0.0 - Binary by @OmniacDev in #9
Full Changelog: v2.0.0...v3.0.0
v2.0.0
Release v2.0.0
A lot has been optimized and re-implemented, so this version will be incompatible with any previous versions.
NET
Module
The NET
module has been heavily refactored, some key changes including:
- Custom data serialization, fully optimized for scriptevents
- Payload has been moved into the scriptevent ID, to maximize data throughput
- Scriptevent ID is now fully serialized, allowing completely custom namespaces, and supporting the payload
- Event listeners have been optimized to minimize delay
- Data fragmentation has been heavily optimized, reducing memory and time usage
For advanced users, the NET
module is also now available for use.
import { system } from '@minecraft/server'
import { NET } from './IPC/ipc'
// This listener will behave like IPC.once
const listen_terminator = NET.listen('namespace', 'event', 'channel', function* (args) {
console.log(args[0]) // 'args'
listen_terminator() // terminate listener
})
system.runJob(NET.emit('namespace', 'event', 'channel', ['args']))
What's Changed
- v2.0.0 by @OmniacDev in #8
Full Changelog: v1.7.0...v2.0.0
v1.7.0
Release v1.7.0
Optimizations
- Fixed a few oversights in previous versions, where sending large amounts of encrypted data caused performance issues.
- Other functions have also been optimized to increase their efficiency when handling large amounts of data.
- Adjusted the fragmentation to only limit the scriptevent's message size not the entire command.
Additions
ConnectionManager
has two new methods;send
andinvoke
Connection
has three new methods;on
,once
, andhandle
Connection
also has a newterminate
method. This can be used to close existing connections.
Other
Removed the CONFIG
export, all values are now hardcoded.
Since the project is getting large, I've added a minified JS file for those who want to keep their pack sizes low.
What's Changed
- v1.7.0 by @OmniacDev in #7
Full Changelog: v1.6.0...v1.7.0
v1.6.0
Auto Sizing
v1.6.0 addresses issues with the command length limit.
IPC will now dynamically resize the data being sent so it never goes above the MAX_CMD_LENGTH
value in CONFIG.FRAGMENTATION
(default: 2048
, modification is not recommended).
Previously, it tested the data string against a static MAX_STR_LENGTH
value, however, this didn't take into account the other parts of the command, such as the channel
, which resulted in inconsistencies and lost efficiency.
Other
- Only accept script events from server
What's Changed
- Auto Sizing by @OmniacDev in #6
Full Changelog: v1.5.0...v1.6.0
v1.5.0
Encryption
This update introduces encryption for direct IPC connections.
Usage
ConnectionManager
The ConnectionManager constructor now accepts an optional force_encryption
parameter. By enabling it, every incoming connection must be encrypted.
IPC.ConnectionManager.constructor(id: string, force_encryption?: boolean)
Example:
import IPC from 'ipc'
// Encrypted ConnectionManager. All incoming connections are encrypted
const encrypted_manager = new IPC.ConnectionManager('UUID', true)
// Non-encrypted ConnectionManager. All incoming connections are raw
const manager = new IPC.ConnectionManager('UUID'/*, false*/)
Connection
The connect()
method on the ConnectionManager now accepts an optional encrypted
parameter.
IPC.ConnectionManager.connect(to: string, encrypted?: boolean, timeout?: number): Promise<Connection>
Example:
import IPC from 'ipc'
const manager = new IPC.ConnectionManager('UUID')
manager.connect('Other UUID', true).then(connection => {
// connection is encrypted
})
manager.connect('Other UUID'/*, false*/).then(connection => {
// connection is not encrypted
})
Note
If a ConnectionManager has force_encryption
enabled, this does NOT mean each Connection made by that manager is also encrypted, as force_encryption
only applies to incoming connections, not outgoing ones.
How It Works
A Diffie-Hellman Key Exchange is used during the 2-way handshake between the ConnectionManagers to generate a shared private key.
What's Changed
- Encryption by @OmniacDev in #5
Full Changelog: v1.4.1...v1.5.0
v1.4.1
Patch: Direct handle returns incorrect argument
Full Changelog: v1.4.0...v1.4.1
v1.4.0
-
Added functionality for direct communication between packs.
Both packs must use the ConnectionManager for direct communication
// Manages direct communication. Input should be the UUID of your pack, or some other unique id const manager = new IPC.ConnectionManager('UUID') // ConnectionManager handles listening to channels manager.on('channel', args => {}) manager.once('channel', args => {}) manager.handle('channel', args => {}) // Connect to other packs using their unique ID manager.connect('Other UUID').then(connection => { // Connections handle sending connection.send('channel', 'test_message') connection.invoke('channel').then(result => {}) })
Example
// PACK 1 const manager = new IPC.ConnectionManager('5a456e5c-b064-4ea4-9626-8bb59b7dec0c') // Connect to Pack 2 manager.connect('6aad99ef-937d-49ac-a7c2-6d5528eda113').then(() => { console.warn('Connected to Pack 2') }) // Listen for messages on direct channel manager.on('direct_message', message => { console.warn(message) })
// PACK 2 const manager = new IPC.ConnectionManager('6aad99ef-937d-49ac-a7c2-6d5528eda113') // Connect to Pack 1 manager.connect('5a456e5c-b064-4ea4-9626-8bb59b7dec0c').then(connection => { console.warn('Connected to Pack 1') // Send a direct message to Pack 1 connection.send('direct_message', 'Direct Message from Pack 2 to Pack 1') })
Full Changelog: v1.3.1...v1.4.0
v1.3.1
Patch: use correct default size
Full Changelog: v1.3.0...v1.3.1
v1.3.0
- Many optimisations
- Increased MAX_STR_SIZE for payloads
Full Changelog: v1.2.0...v1.3.0
v1.2.0
- Removed Header, and moved necessary information into main payload
- Renamed Contents to Payload, now includes the channel and if it is the last payload
Slightly optimizes the size of the scriptevent messages, and removes one unnecessary scriptevent call
Full Changelog: v1.1.1...v1.2.0