Updated for v1.0.0
Instances are the serverside of nengi. Instances handle connections from clients, receive commands, and send out data.
const instance = new nengi.Instance(nengiConfig, webConfig)
instance.onConnect((client, clientData, callback) => {})
instance.onDisconnect(client => {})
instance.addEntity(entity)
instance.removeEntity(entity)
instance.addLocalMessage(localMessage)
instance.sendMessage(message, clientOrClients)
instance.getNextCommand()
instance.update()
const instance = new nengi.Instance(nengiConfig, webConfig)
The same nengiConfig
must be used for both nengi.Instance nengi.Client. The nengiConfig contains the common language that allow the client and server to communicate. See the manual page for nengiConfig for more information.
The webConfig
can be either { port }
which will create an http+websocket server and start listening, or can have an good ol' node { httpServer }
passed into it.
See the manual page for Entity for more information.
See the manual page for LocalMessage for more information.
See the manual page for Message for more information.
instance.onConnect((client, clientData, callback) => {
callback({ accepted: true, text: 'Welcome!' })
// or callback({ accepted: fase, text: 'some error message' })
})
This `client` object is created on connect, and the very same `client` object is associated with any commands they send. Feel free to attach state to the client object when they first connect, and access that state later when processing their commands.
The clientData
object contains data sent by the game client along with its connection handshake. The clientData.fromClient
is populated by the second argument sent via client.connect(address, { foo: 'bar' })
. This can be used to submit an authToken or other means of identifying a player.
{
fromClient: {
foo: 'bar',
}
}
Keep in mind that clientData.fromClient
originates from the game client, and is therefore inherently unauthoritative and potentially fraudulent. Be sure to sanitize whatever it is!
A client's connection is accepted when the we invoke the callback with { accepted: true, text: '' }
and the connection is denied if we invoke the callback with { accepted: false, text: '' }
. The text
property exists so that we can provide the client application a reason for denied connections.
Any user code (your code) in the onConnect handler will silently fail if it throws an exception. The client will not connect, the server will not crash, and everything will be generally confusing. This is a BUG caused by an overly drastic security feature that I added, and hopefully will be fixed in the future. If you believe you have encountered this problem, then I suggest using breakpoints or console log statements all over the onConnect user code until the offending line is found.
In between ticks on the server, commands sent from clients build up into a queue. This is how we process that queue:
let cmd = null
while (cmd = this.instance.getNextCommand()) {
const tick = cmd.tick
const client = cmd.client
for (let i = 0; i < cmd.commands.length; i++) {
const command = cmd.commands[i]
if (command.protocol.name === 'Something') {
// do something to the game
}
}
}
I recommend processing commands at the *start* of each tick on the server.
instance.update()
Invoking instance.update
will send out a snapshot of the instance state to each client. I recommend doing this at the *end* of each tick on the server.