diff --git a/README.md b/README.md
index 5e4b037..cbd13d2 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,104 @@
Netcode
=======
-A simple client & server binary-encoded websocket communication system for web video games.
+> A simple JavaScript client & server binary-encoded websocket communication system aimed towards web video games development.
+
+Features:
+- 🔌 Server / Client duo, for node and the browser, that handle Websocket connection and communication.
+- ⚡️ Handle the binary encoding and decoding of your data, with performances in mind.
+- 📢 Listen for event dispatched over websocket with simple `on`/`off` event emitter system.
+- 💬 Fallback to JSON for easy debugging.
+- 🗜[COMMING SOON] set a tick-rate and group events for lesser data consumption.
+
+## Requirements
+
+- Node >= v8.0.0
## Installation
`npm add netcode`
-## Usage
+## Get started
+
+### Define a list of events
+
+The server and the client __must__ share the same events.
+An event is defined by its _unique_ name and the corresponding codec, responsible for encoding and decoding the data.
+
+```javascript
+// events.js
+import Int8Codec from 'netcode/src/encoder/codec/Int8Codec';
+import StringCodec from 'netcode/src/encoder/codec/StringCodec';
+
+export default [
+ ['id', new Int8Codec()],
+ ['say', new StringCodec()],
+];
+```
+
+In this example, the event list define how to send the following events over websocket:
+
+- `client.send('id', 255);`
+- `client.send('say', 'Hello world!');`
+
+Then you'll be able to listen to this events on the client as follow:
+
+- `client.on('id', id => { /* Do something */ });`
+- `client.on('say', sentence => { /* Do something */ });`
+
+Now let's create a server and an client that use this event list.
+
+### Setup a Server
+
+We setup a server specifying the port and host on which the server will listen and the type of encoder to use.
+Here we use a BinaryEncoder to communicate in binary over websocket, with the previously configured event list.
+
+```javascript
+import Server from 'netcode/src/server/Server';
+import BinaryEncoder from 'netcode/src/server/BinaryEncoder';
+import events from './envents';
+
+// Listen on localhost:8080
+const server = new Server(8080, 'localhost', new BinaryEncoder(events));
+
+server.on('client:join', client => {
+ client.on('say', sentence => console.log(sentence));
+ client.send('id', client.id);
+});
+```
+
+Now we've got a server running at `localhost:8080` that listen for a `say` text event and send a `id` integer event to every client that connects.
+
+_See an [full example of server setup](demo-server.js)._
+
+### Write a Client
+
+Now we write a client, for the browser, that connects to our running server on `ws://localhost:8080` and use a BinaryEncoder with the same event list as the server.
+
+```javascript
+import Client from 'netcode/src/client/Client';
+import BinaryEncoder from 'netcode/src/client/BinaryEncoder';
+import events from './envents';
+
+const client = new Client('ws://localhost:8080', new BinaryEncoder(events))
+
+client.on('open', () => {
+ client.on('id', id => console.log(`My id is ${id}.`));
+ client.send('say', 'Hello world!');
+});
+```
+
+Now we've got client that listen for the `id` event and sent a sentence in a `say` event.
+
+Connection is alive and well!
-### Server side (node)
+_See an [full example of client setup](demo-client.js)._
-See an [example of server setup](demo-server.js).
+## Complete documentation
-### Client side (browser)
+To go further, see in-depth documentation and how-to's.
-See an [example of client setup](demo-client.js).
+- [Full API reference](doc/API.md).
+- [Default codecs and how-to write your own](doc/codecs.md).
+- [About packaging and setting up your own webpack configuration](doc/packaging.md).
+- [Use netcode over a custom domain name and/or secured SSL connection](doc/ssl.md).
diff --git a/demo-client.js b/demo-client.js
index 56bdef7..0afcf21 100644
--- a/demo-client.js
+++ b/demo-client.js
@@ -4,7 +4,6 @@ window.addEventListener('load', () => {
// Register your events
const encoder = new BinaryEncoder([
- ['open', new Codec()],
['id', new Int16Codec()],
['ping', new LongIntCodec(6)],
['pong', new LongIntCodec(6)],
@@ -17,12 +16,12 @@ window.addEventListener('load', () => {
let ping;
// Listen for a "pong" event
- client.addEventListener('pong', pong => {
+ client.on('pong', pong => {
console.info('pong: %s ms', pong - ping);
});
// Listen for an "id" event
- client.addEventListener('id', id => {
+ client.on('id', id => {
console.log('connected with id %s', id);
ping = Date.now();
@@ -31,7 +30,7 @@ window.addEventListener('load', () => {
});
// Listen for an "inverse" event
- client.addEventListener('inverse', status => {
+ client.on('inverse', status => {
// Answer with an "inverse" event
client.send('inverse', !status);
console.log('Inverse received: %s', status);
@@ -41,17 +40,19 @@ window.addEventListener('load', () => {
});
// Listen for a "greeting" event
- client.addEventListener('greeting', message => {
+ client.on('greeting', message => {
console.log('Servers geets you: "%s"', message);
});
// Listen for oppening connection
- client.addEventListener('open', () => {
+ client.on('open', () => {
console.info('Connection open.');
+
+ setTimeout(() => client.close(), 20 * 1000);
});
// Listen for connection close
- client.addEventListener('close', () => {
+ client.on('close', () => {
console.info('Connection closed.');
});
});
diff --git a/demo-server.js b/demo-server.js
index 057f4cb..d1acdf1 100644
--- a/demo-server.js
+++ b/demo-server.js
@@ -2,7 +2,6 @@ const { Server, BinaryEncoder, Codec, Int16Codec, LongIntCodec, BooleanCodec, St
// Register your events
const encoder = new BinaryEncoder([
- ['open', new Codec()],
['id', new Int16Codec()],
['ping', new LongIntCodec(6)],
['pong', new LongIntCodec(6)],
@@ -14,11 +13,11 @@ const encoder = new BinaryEncoder([
const server = new Server(process.argv[2], 'localhost', encoder);
// Listen for new clients
-server.addListener('client:join', client => {
+server.on('client:join', client => {
console.log('Client %s joined.', client.id);
// Listen for "ping" event
- client.addListener('ping', ping => {
+ client.on('ping', ping => {
// Answer with a "pong" event
client.send('pong', Date.now());
console.log('Client %s ping received: %s.', client.id, ping);
@@ -28,12 +27,12 @@ server.addListener('client:join', client => {
});
// Listen for "inverse" event
- client.addListener('inverse', status => {
+ client.on('inverse', status => {
console.log('Client %s inverse received: %s.', client.id, status);
});
// Listen for "greeting" event
- client.addListener('greeting', message => {
+ client.on('greeting', message => {
console.log('Client %s geets you: "%s"', client.id, message);
// Send a "greeting" event
client.send('greeting', 'Hello, I\'m server!');
@@ -44,7 +43,7 @@ server.addListener('client:join', client => {
});
// Listen for disconnecting clients
-server.addListener('client:leave', client => {
+server.on('client:leave', client => {
console.log('Client %s left.', client.id);
});
diff --git a/doc/API.md b/doc/API.md
new file mode 100644
index 0000000..ec01b30
--- /dev/null
+++ b/doc/API.md
@@ -0,0 +1,79 @@
+## Server
+
+### Constructor
+
+The `Server` class takes the following arguments:
+
+| Parameter | Type | Default value | Description |
+| --------- | ------------------------------ | ----------------- | ---------------------------------------------------- |
+| port | _Number_ | 8080 | Port to listen on. |
+| host | _String_ | 0.0.0.0 | Host to listen on. |
+| encoder | _BinaryEncoder \| JsonEncoder_ | new JsonEncoder() | Encoder to use to read/write event messages. |
+| ping | _Number_ | 30 | Ping frequency in seconds (0 for no ping). |
+| maxLength | _Number_ | 512 | Paquet max length in bit (should be a power of two). |
+| protocols | _Array String[]_ | `['websocket']` | Protocols tu use |
+
+### Methods
+
+#### on(name, callback)
+
+Listen for event.
+
+- `name {String}` The name of the event to listen to.
+- `callback {Function}` The callback to execute when the event occure.
+
+
+#### off(name, callback)
+
+Remove listener for this event / callback.
+
+_Note: If you're on node `< 10.0.0`, use `removeListener` method instead of `off`_
+
+### Events
+
+| Name | Callback parameters | Description |
+|------|---------------------|---|
+| `ready` | | Server is listening and ready to accept connections. |
+| `client:join` | client _Client_ | New connected client. |
+| `client:leave` | client _Client_ | Client left. |
+| `error` | error _Error_ | An error occured. |
+
+## Client
+
+### Methods
+
+#### send(name, data)
+
+Send data to the other end of the Websocket.
+
+- `name {String}` The name of the event (must be one from the list passed to the BinaryEncoder).
+- `data {Number|String|Boolean|Object}` Any data handled by the corresponding Codec.
+
+#### close()
+
+Close the connexion.
+
+#### on(name, callback)
+
+Listen for event.
+
+- `name {String}` The name of the event to listen to.
+- `callback {Function}` The callback to execute when the event occure.
+
+
+#### off(name, callback)
+
+Remove listener for this event / callback.
+
+_Note: If you're on node `< 10.0.0`, use `removeListener` method instead of `off`_
+
+### Events
+
+| Name | Callback parameters | Description |
+|---|---|---|
+| `open` | client _Client_ | Client connection is open and ready to transmit. |
+| `error` | - error _Error_
- client _Client_ | An error occurred. |
+| `close` | client _{Client_} | Client connection is closed. |
+| * | - eventData _{Number\|String\|Boolean\|Object}_
- client _Client_ | Every event sent through the websocket pipe will emit an event on the other end of the socket. |
+
+_Note: `open`, `error` and `close` are reserved event names and the Encoder will throw an exeption if you define a custom event with either of these names._
\ No newline at end of file
diff --git a/doc/codecs.md b/doc/codecs.md
new file mode 100644
index 0000000..985e88a
--- /dev/null
+++ b/doc/codecs.md
@@ -0,0 +1,54 @@
+# Codecs
+
+Codecs are responsible for encoding your events data into ArrayBuffer that will be transmitted over the binary websocket connection and decoded back into usable data on the other side.
+
+You will probably need to write your own codecs but a few standard needs are alreay adressed by the simple codecs that follow:
+
+## Packaged codecs
+
+Packaged codecs are available in the `netcode/server` and `netcode/client` packages or as source code in `netcode/src/encoder/codec` folder (see [Note on packaging](#Note on packaging))
+
+| Class | Data format | Example | Size (in byte) |
+| -------------------------- | ---------------------------------- | ------------------------------------------------------------ | ----------------------- |
+| `Codec` | No data (just send the event name) | `['pause', new Codec()]`
`send('pause')` | 0 |
+| `BooleanCodec` | `true\|false` | `['active', new BooleanCodec()]`
`send('active', true)` | 1 |
+| `StringCodec` | String up to 255 characters | `['player:name', new StringCodec()]`
`send('player:name', 'DarkShadow73')` | 1 + (String length * 2) |
+| `LongStringCodec` | String up to 65536 characters | `['url', new LongStringCodec()]`
`send('url', 'https://my.long.url/hash/xxx...')` | 2 + (String length * 2) |
+| `Int8Codec` | Integer from 0 to 255 | `['id', new Int8Codec()]`
`send('id', 42)` | 1 |
+| `Int16Codec` | Integer from 0 to 65536 | `['score', new Int16Codec()]`
`send('score', 9999)` | 2 |
+| `Int32Codec` | Integer from 0 to 4294967295 | `['position', new Int32Codec()]`
`send('position', 4294967295)` | 4 |
+| `LongIntCodec(byteLength)` | Integer encoded as string | `['timestamp', new LongIntCodec(13)`]
`send('timestamp', Date.now())` | byteLength |
+
+## Custom codecs
+
+Some of your events will certainly have more complex data structure that just one of the above single scalar example. Good news is you can write your own codecs!
+
+Your custom codec must extends the `Codec` class of Netcode and implement the 3 following methods:
+
+- `getByteLength(data)`: return the number of byte in the event for the given data (your event byte length may vary over the data, like for abitrary strings for example).
+- `encoder(buffer, offset, data)` encode the given data into the provided buffer, starting at the given offset (in byte).
+- `decode(buffer, offset)`: read and return the data contained in the provided buffer, starting at the given offset.
+
+Let's say we want to send an event with the given format:
+
+`send('position', { id: player.id, x: 12345, y: 2354 });`
+
+We'll need to rite a codec that can transmit these values in an ArrayBuffer.
+
+For this example, I'm gonna chose to encode:
+
+- The player ID as an unsigned integer on 1 byte (UInt8)
+- The x and y position on 2 byte each (Uint16)
+
+So the byte length of my event is fixed: 1 + 2 + 2 = 5 bytes. Let implement the `getByteLengthMethod`
+
+```
+getByteLength() {
+ return Uint8Array.BYTES_PER_ELEMENT + Uint16Array.BYTES_PER_ELEMENT * 2;
+}
+```
+
+## Debugging
+
+The BinaryEncoder should emit errors if anything wrong happen durring encoding or decoding, but even so, working with binary data can be tricky.
+You can replace your `BinaryEncoder` with a `JsonEncoder` at any time without any other change to you code to switch to a JSON communication and to help you find out what's wrong with transmitted data.
diff --git a/doc/packaging.md b/doc/packaging.md
new file mode 100644
index 0000000..804239e
--- /dev/null
+++ b/doc/packaging.md
@@ -0,0 +1,89 @@
+# Note on packaging
+
+Netcode provides you with 2 pre-packaged versions of the library for _Node_ and for the _browser_, so you can use it out of the box.
+You can also _import the ES6 source code_ directly and manage the packaging yourself with a tool like Webpack.
+
+Let's see an example of both these setups:
+
+## Using the pre-packaged libraries
+
+On the server-side (node):
+
+```javascript
+const {
+ Server,
+ BinaryEncoder,
+ Int16Codec,
+ BooleanCodec,
+ StringCodec,
+ // ...
+} = require('netcode/server');`
+```
+
+And in the browser as well:
+
+```html
+