Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e2ebb06
Ping feature (#2)
Tom32i Nov 7, 2018
26ddba3
Improving documentation
Tom32i Nov 10, 2018
f4918b7
Split doc
Tom32i Nov 11, 2018
1616d70
Merge pull request #3 from Tom32i/doc
Tom32i Nov 11, 2018
f463119
Store host and port as properties
Tom32i Nov 30, 2018
3e5fadf
v1.0.4
Tom32i Nov 30, 2018
0912537
Support emojis in StringCodec
Tom32i Dec 2, 2018
467a60b
Added client directory to handle id generation
Tom32i Dec 3, 2018
ab1e7c5
Added tests for all Codecs
Tom32i Dec 3, 2018
1a2c2ea
Merge pull request #7 from Tom32i/client-directory
Tom32i Dec 3, 2018
f491536
v1.0.6
Tom32i Dec 3, 2018
5c7d083
Hotfix: import
Tom32i Dec 3, 2018
2e0b011
Renamed test
Tom32i Dec 3, 2018
ab1a16e
Fixed node deprecation for Buffer
Tom32i Dec 5, 2018
801cf86
Fixed typo
Tom32i Dec 5, 2018
4a423bc
Moving to babel 7
Tom32i Dec 13, 2018
ec24843
Handler errors
Tom32i Dec 13, 2018
aa12017
v1.0.10
Tom32i Dec 13, 2018
2f8bcc7
Merge pull request #8 from Tom32i/babel7
Tom32i Dec 13, 2018
5c60dad
Updated doc
Tom32i Dec 13, 2018
2264637
Don't crash when a null value is passed to the StringEncoder.
Dec 21, 2018
a1575bc
Moved to ws package for Websocket implementation
Tom32i Dec 23, 2018
2c3146a
Updated doc
Dec 27, 2018
21d9e9d
Merge pull request #10 from Tom32i/ws
Tom32i Dec 27, 2018
6f0e590
Fixed security warning on dependencies
Apr 6, 2020
edd35cf
Improvements
Tom32i Apr 24, 2020
3095178
Bind start
Tom32i Apr 24, 2020
22ee735
Updated dependencies
Tom32i Apr 24, 2020
236fa0e
Updated dependencies
Jul 20, 2020
c84368b
Updated scipts
Jul 20, 2020
fa47a80
Updated event-emitter
Jul 20, 2020
52811d2
Updated dependencies
Aug 27, 2020
8691950
Update README.md
Tom32i Oct 3, 2020
ad3ba32
Fixed typo + full codec example
Nov 19, 2020
1420a62
Create LICENSE
Tom32i Dec 11, 2020
12faf6a
Security package update
Tom32i Dec 13, 2020
07dc064
Npm security update
May 11, 2021
212572d
v2.0.5
May 11, 2021
abf5acb
Updated dependencies
Tom32i Jan 19, 2023
42c3172
Better text encoding
Tom32i Oct 19, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"env": {
"browser": true,
"es6": true,
"es2021": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2017,
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
Expand All @@ -30,6 +30,10 @@
"no-console": [
"error",
{ "allow": ["info", "error"] }
],
"spaced-comment": [
"error",
"always"
]
}
}
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2020 Thomas Jarrand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
22 changes: 17 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
.SILENT:
.PHONY: test

# Install dependencies
install:
npm install

# Launch watch
watch:
./node_modules/.bin/webpack --watch --mode=development
npx webpack --watch --mode=development

# Build lib
build:
./node_modules/.bin/webpack --mode=production

start: demo-client demo-server
npx webpack --mode=production

# Launch demo client
demo-client:
php -S 0.0.0.0:8000 -t .

# Launch demo server
demo-server:
node ./demo-server.js 8002

# Lint and code style fix
lint:
./node_modules/.bin/eslint src/* --ext .js,.json --fix
npx eslint src/* --ext .js,.json --fix

# Test
test: build
npx mocha

# Publish package
publish: build
npm publish . --access public
97 changes: 91 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,103 @@
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.

## 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/encoder/BinaryEncoder';
import events from './events';

// 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/encoder/BinaryEncoder';
import events from './events';

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).
31 changes: 21 additions & 10 deletions demo-client.js
Original file line number Diff line number Diff line change
@@ -1,52 +1,63 @@

window.addEventListener('load', () => {
const { Client, BinaryEncoder, Codec, Int16Codec, LongIntCodec, BooleanCodec, StringCodec } = netcode;
const { Client, BinaryEncoder, Codec, Int8Codec, Int16Codec, LongIntCodec, BooleanCodec, StringCodec } = netcode;

// Register your events
const encoder = new BinaryEncoder([
['open', new Codec()],
['id', new Int16Codec()],
['ping', new LongIntCodec(6)],
['pong', new LongIntCodec(6)],
['inverse', new BooleanCodec()],
['greeting', new StringCodec()],
['total', new Int8Codec()],
]);

// Create the client
const client = new Client('ws://localhost:8002', encoder);
const client = new Client('ws://127.0.0.1:8002', encoder);
let ping;

// Listen for a "pong" event
client.addEventListener('pong', pong => {
client.on('pong', ({ detail: pong }) => {
console.info('pong: %s ms', pong - ping);
});

// Listen for an "id" event
client.addEventListener('id', id => {
client.on('id', ({ detail: id }) => {
console.log('connected with id %s', id);
ping = Date.now();

// Send a "ping" event
client.send('ping', ping);
});

// Listen for a "total" event
client.on('total', ({ detail: total }) => {
console.log(`There is ${total} people connected.`)
});

// Listen for an "inverse" event
client.addEventListener('inverse', status => {
client.on('inverse', ({ detail: status }) => {
// Answer with an "inverse" event
client.send('inverse', !status);
console.log('Inverse received: %s', status);

// Send a "greeting" event
client.send('greeting', 'Hello, I\'m client!');
client.send('greeting', 'Hello, I\'m client 😊!');
});

// Listen for a "greeting" event
client.addEventListener('greeting', message => {
client.on('greeting', ({ detail: 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.on('close', () => {
console.info('Connection closed.');
});
});
29 changes: 20 additions & 9 deletions demo-server.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
const { Server, BinaryEncoder, Codec, Int16Codec, LongIntCodec, BooleanCodec, StringCodec } = require('./server');
const { Server, BinaryEncoder, Codec, Int8Codec, Int16Codec, LongIntCodec, BooleanCodec, StringCodec } = require('./server');

// Register your events
const encoder = new BinaryEncoder([
['open', new Codec()],
['id', new Int16Codec()],
['ping', new LongIntCodec(6)],
['pong', new LongIntCodec(6)],
['inverse', new BooleanCodec()],
['greeting', new StringCodec()],
['total', new Int8Codec()],
]);

// Create the server
const server = new Server(process.argv[2], 'localhost', encoder);
const port = process.argv[2];
const server = new Server(port, '127.0.0.1', encoder, 3);

// 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);
Expand All @@ -28,24 +29,34 @@ 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!');
client.send('greeting', 'Hello, I\'m server 😊!');
});

// Send event "id" to the client
client.send('id', client.id);

broadcastTotal();
});

// Listen for disconnecting clients
server.addListener('client:leave', client => {
server.on('client:leave', client => {
console.log('Client %s left.', client.id);
broadcastTotal();
});

server.on('ready', () => console.log('Listening on port %s', port));

function broadcastTotal() {
const { length } = server.clients;
server.clients.forEach(client => client.send('total', length));
}

module.exports = server;
Loading