@footgun/network
v0.5.1
Published
A node.js library for low latency, realtime networking over UDP.
Maintainers
Readme
@footgun/network
A node.js library for low latency, realtime networking over UDP.
Features:
- low latency message delivery
- does not leak memory
unreliable-unorderedchannelsreliable-orderedchannels- per-endpoint Round Trip Time (RTT) estimation
- send/receive transfer speed estimations
- data oriented design
- has unit and soak tests
- minimal (~1k lines of code)
- pure es module
Usage
How to set up a run loop, send/receive messages
import * as Network from '@footgun/network'
import dgram from 'dgram'
async function main () {
const socket = dgram.createSocket('udp4')
// listen for data, local: address port
await bind(socket, '0.0.0.0', 3000)
// create endpoint, remote: address port
const endpoint = Network.create(socket, '0.0.0.0', 3000)
// unreliable channels are great for high volume eventually consistent data like player positions, etc.
Network.addChannel(endpoint, Network.CHANNEL_UNRELIABLE)
// reliable channels are useful for lower volume events that need guaranteed, in order delivery
// e.g., I'm entering a vehicle, I'm shooting a missile, etc.
Network.addChannel(endpoint, Network.CHANNEL_RELIABLE)
// listen for new data on the UDP socket
socket.on('message', function (message, rinfo) {
Network.readPacket(endpoint, message) // process all received data through the endpoint
})
// the channels ids are based on the order they were added to the endpoint.
// so for example above we first called Network.addChannel(...) with CHANNEL_UNRELIABLE so it's channelid is 0
const unreliableChannelId = 0
const reliableChannelId = 1
// define a structure that will hold messages read from the network
const messages = new Array(2048)
for (let i=0; i < messages.length; i++) {
messages[i] = {
len: 0,
msg: new Uint8Array(1024),
}
}
const gameLoop = function () {
// send one message over unreliable channel
const [ x, y ] = [ 100, 106 ]
const playerPosMsg = new Uint8Array([ x, y ]) // a simple 2 byte message to send
Network.sendMessage(endpoint, unreliableChannelId, playerPosMsg, playerPosMsg.byteLength)
// send one message over reliable channel
const enterVehicleMsg = new Uint8Array([ 23, 106, 255, 0, 24, 14, 91 ])
Network.sendMessage(endpoint, reliableChannelId, enterVehicleMsg, enterVehicleMsg.byteLength)
// receive messages over the unreliable channel (each message is a Uint8Array)
let msgCount = Network.readMessages(endpoint, unreliableChannelId, messages)
// process each message received over the uneliable channel
for (let i=0; i < msgCount; i++) {
const um = messages[i] // um.msg is the data, um.len is how many bytes are in it
}
// also an array of messages, but the reliable channels will ensure the order is
// maintained, so if packets are dropped or arrive out of order you can still have
// confidence you'll see a consistent ordered message stream here that matches what
// the sending side put in.
msgCount = Network.readMessages(endpoint, reliableChannelId, messages)
// process each message received over the reliable channel
for (let i=0; i < msgCount; i++) {
const um = messages[i] // um.msg is the data, um.len is how many bytes are in it
}
// package all queued messages into packets and send them over the underlying UDP socket
Network.transmitPackets(endpoint)
// stats:
// network round trip in milliseconds
//console.log('RTT:', endpoint.RTT)
// upload rate in bytes/second
//console.log('upload:', endpoint.bandwidth.sendSpeed)
// download rate in bytes/second
//console.log('download:', endpoint.bandwidth.recvSpeed)
setTimeout(gameLoop, 1)
}
gameLoop() // start running the game
}
main()
async function bind (socket, listenAddress, listenPort) {
return new Promise(function (resolve, reject) {
socket.bind(listenPort, listenAddress, function (er) {
if (er)
return reject(er)
const address = socket.address()
console.log(`Server listening at ${address.address}:${address.port}.`)
resolve()
})
})
}
References
- https://gafferongames.com/post/reliability_ordering_and_congestion_avoidance_over_udp
- https://github.com/mas-bandwidth/yojimbo
- https://github.com/padenot/ringbuf.js
