diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc25d8c --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# NUClearNet.js + +[![Node.js CI](https://github.com/Fastcode/NUClearNet.js/actions/workflows/node.js.yml/badge.svg)](https://github.com/Fastcode/NUClearNet.js/actions/workflows/node.js.yml) + +Node.js module for interacting with the [NUClear](https://github.com/Fastcode/NUClear) network. + +## Installation + +The package contains a native module, so you'll need a working C++ compiler on your system to install and build it. + +``` +npm install nuclearnet.js --save +``` + +## Usage + +The following example show a typical usage pattern of creating a network instance, listening for join, leave, and packet events, and sending data. + +```js +const { NUClearNet } = require('nuclearnet.js'); + +// Create a network instance +const net = new NUClearNet(); + +// Set a join listener to be notified when peers join the network. +// This should be done before calling `connect()` for the first time, to get join events +// from peers that are already on the network. +net.on('nuclear_join', function (peer) { + console.log(`peer ${peer.name} has joined`); +}); + +// Set a leave listener to be notified when peers leave the network +net.on('nuclear_leave', function (peer) { + console.log(`peer ${peer.name} has left`); +}); + +// Listen for all incoming packets +net.on('nuclear_packet', function (packet) { + const packetType = packet.type !== undefined ? packet.type : 'unknown type'; + console.log(`got a packet (${packetType}) of length ${packet.payload.length} from peer ${packet.peer.name}`); +}); + +// Listen for packets of a specific type +net.on('packet_type_a', function (packet) { + console.log(`got a packet (packet_type_a) of length ${packet.payload.length} from peer ${packet.peer.name}`); + + // Send a response to the peer + net.send({ + target: packet.peer.name, + reliable: true, + type: 'packet_type_b', + payload: Buffer.from('hello there!'), + }); +}); + +// Connect to the network using the peer name "My Name" +net.connect({ name: 'My Name' }); +``` + +## API + +See [`index.d.ts`](./index.d.ts) for types and API details. diff --git a/index.d.ts b/index.d.ts index 849aab3..3a1a047 100644 --- a/index.d.ts +++ b/index.d.ts @@ -21,119 +21,163 @@ * NUClearNet options for connecting to the network */ export interface NUClearNetOptions { - /// The name of this node on the NUClearNetwork - name: string - /// The announce address for this network defaults to 239.226.152.162 - address?: string - /// The announce port for this network defaults to 7447 - port?: number - /// The MTU of the network. Used for splitting packets optimally - mtu?: number + /** The name of this node on the NUClearNetwork */ + name: string; + + /** The announce address for this network. Defaults to `239.226.152.162`. */ + address?: string; + + /** The announce port for this network. Defaults to `7447`. */ + port?: number; + + /** The MTU of the network. Used for splitting packets optimally. */ + mtu?: number; } /** * Data provided for sending information on the network */ export interface NUClearNetSend { - /// The type of the message to send, either as a string that will be hashed or as a 64 bit hash directly - type: string|Buffer - /// The data to send - payload: Buffer - /// The target to send the packet to, undefined means send to everyone on the network - target?: string - /// If the packet should be sent using a more reliable protocol which handles retransmissions - reliable?: boolean + /** + * The type of the message to send: either a string that will be hashed, + * or a Buffer with the 64 bit hash. + */ + type: string | Buffer; + + /** The data to send */ + payload: Buffer; + + /** + * The target to send the packet to. If `undefined`, the packet will be sent + * to everyone on the network. + */ + target?: string; + + /** + * If `true`, the packet should be sent using a reliable protocol + * which handles retransmissions. + */ + reliable?: boolean; } /** - * Information for a peer on the NUClear network + * Information about a peer on the NUClear network */ export interface NUClearNetPeer { - /// The name the peer has on the network. See NUClearNetOptions.name - name: string - /// The IP address of the peer either as dotted decimal for IPv4 or an IPv6 string - address: string - /// The port the peer has connected on. - /// This will be an ephemeral port and the combination of address+port will be unique for a peer - port: number + /** The name the peer has on the network. See `NUClearNetOptions.name` */ + name: string; + + /** The IP address of the peer: either a dotted decimal IPv4 string, or an IPv6 string. */ + address: string; + + /** + * The port the peer has connected on. This will be an ephemeral port. + * The combination of `address` + `port` will be unique for a peer. + */ + port: number; } /** - * A data packet that is received by the NUClear network + * A data packet that received on the NUClear network */ export interface NUClearNetPacket { - /// The peer the packet was sent from - peer: NUClearNetPeer - /// The hashcode of the packets type - hash: Buffer - /// The bytes that were sent from the peer - payload: Buffer - /// If the peer sent this as a reliable transmission (see NUClearNetSend.reliable) - reliable: boolean + /** The peer the packet was sent from */ + peer: NUClearNetPeer; + + /** The hash code of the packet's type */ + hash: Buffer; + + /** The data that was sent from the peer */ + payload: Buffer; + + /** + * Will be set to `true`, if the peer sent the packet with reliable transmission + * (see `NUClearNetSend.reliable`). + */ + reliable: boolean; } /** - * A packet that will be received using on('message.type') will always have a known string type - * It will be the same string as provided in the on statement + * A packet that is received using `on('message.type')` will always have a known string type: + * the same string that was provided in the on statement. */ export interface NUClearNetTypedPacket extends NUClearNetPacket { - /// The type that was provided in the on statement for this packet - type: string + /** The type that was provided in the on statement for this packet */ + type: string; } /** - * When using on('nuclear_packet') the type as a string may or may not be known depending on if another - * user has requested this type. + * When using `on('nuclear_packet')` the type as a string may or may not be known depending on + * whether another user has requested the same type. This interface represents that type of packet. */ export interface NUClearNetMaybeTypedPacket extends NUClearNetPacket { - /// The type that was provided in an on statement, or undefined if nobody has executed an on statement for this type - type: string|undefined + /** + * The type that was provided in an on statement, or `undefined` if nobody has executed + * an on statement for this type. + */ + type: string | undefined; } /** - * Create a new NUClearNet instance - * Must be connected before it will provide data however you should set your join and leave callbacks before calling - * connect. - * If connect is called first you will not receive the join events from already connected peers. + * Represents a NUClearNet network client. + * + * Usage notes: + * - Before it will provide data, the instance must be connected first via `.connect()`. + * - Join and leave callbacks should be set before calling `.connect()`. If not, join + * events from already connected peers will not be received. + * - After calling `.destroy()`, the instance should not be used again. */ export declare class NUClearNet { - /// Stores the `connect()` options. Is an empty object until `connect()` is called. - options: Partial; - - public constructor() - - /// Will fire when a peer joins or leaves the network. - public on(event: 'nuclear_join'|'nuclear_leave', callback: (peer: NUClearNetPeer) => void): this - - /// Will fire when NUClearNet receives any packet - public on(event: 'nuclear_packet', callback: (packet: NUClearNetMaybeTypedPacket) => void): this - - /// Will fire when the requested packet is received - public on(event: string, callback: (packet: NUClearNetTypedPacket) => void): this - - /// Hash the provided string using the hashing method of NUClearNet. - /// These hashes will be identical to those used by NUClearNet - public hash(data: string): Buffer - - /// Stop listening for a type - /// Note that if this is the last listener for a type, the type will revert to being an undefined type - /// for NUClearNetMaybeTypedPacket - public removeListener(event: string, listener: Function): this - - /// Connect to the NUClearNetwork - /// If this function is called multiple times - /// it will disconnect from the previous network and reconnect to a new one - public connect(options: NUClearNetOptions): void - - /// Disconnect from the NUClearNetwork - /// Does not disconnect listeners and reconnecting will resume events - public disconnect(): void - - /// Disconnect and destroy this NUClearNetwork instance, clearing all listeners - /// Attempting to use this instance after calling destroy() will throw an error. - public destroy(): void - - /// Send the packet over the NUClearNetwork - /// This function will throw an error if the network is not connected - public send(options: NUClearNetSend): void + /** Stores the `connect()` options. Is an empty object until `connect()` is called. */ + options: Partial; + + /** Create a new NUClearNet instance. */ + public constructor(); + + /** Emitted when a peer joins or leaves the network. */ + public on(event: 'nuclear_join' | 'nuclear_leave', callback: (peer: NUClearNetPeer) => void): this; + + /** Emitted when NUClearNet receives any packet */ + public on(event: 'nuclear_packet', callback: (packet: NUClearNetMaybeTypedPacket) => void): this; + + /** Emitted when the given packet is received */ + public on(event: string, callback: (packet: NUClearNetTypedPacket) => void): this; + + /** + * Hash the provided string using the NUClearNet hashing method. + * These hashes will be identical to those used by NUClear + */ + public hash(data: string): Buffer; + + /** + * Stop listening for the given type. + * Note that after removing the last listener for a type, the type will revert to + * being an undefined type for `NUClearNetMaybeTypedPacket`. + */ + public removeListener(event: string, listener: Function): this; + + /** + * Connect this instance to the NUclear network. + * Subsequent calls to this function will disconnect from the previous network and + * reconnect to a new one. + */ + public connect(options: NUClearNetOptions): void; + + /** + * Disconnect this instance from the NUClear network. + * Does not remove event listeners, therefore reconnecting will resume events. + */ + public disconnect(): void; + + /** + * Disconnect and destroy this NUClearNetwork instance, clearing all event listeners. + * Attempting to use this instance after calling `destroy()` will throw an error. + */ + public destroy(): void; + + /** + * Send the given packet over the NUClear network. + * Will throw if the network is not connected. + */ + public send(options: NUClearNetSend): void; } diff --git a/tests/test.js b/tests/test.js index 38cfdb0..803c7d0 100644 --- a/tests/test.js +++ b/tests/test.js @@ -125,7 +125,7 @@ test('NUClearNet.send() throws if used before connect()', () => { const net = new NUClearNet(); assert.throws(() => { - net.send('nuclearnet'); + net.send({}); }, /The network is not currently connected/); net.destroy();