Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions demo-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ window.addEventListener('load', () => {
client.addEventListener('open', () => {
console.info('Connection open.');
});

// Listen for connection close
client.addEventListener('close', () => {
console.info('Connection closed.');
});
});
1,262 changes: 552 additions & 710 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "netcode",
"version": "1.0.2",
"version": "1.0.3",
"description": "NetCode for web video games",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
Expand All @@ -20,16 +20,16 @@
],
"author": "Thomas Jarrand <thomas.jarrand@gmail.com> (https://thomas.jarrand.fr)",
"license": "MIT",
"dependencies": {
"tom32i-event-emitter.js": "^2.0.1",
"websocket-driver": "^0.7.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"eslint": "^5.3.0",
"webpack": "^4.16.4",
"webpack-cli": "^3.1.0"
},
"dependencies": {
"tom32i-event-emitter.js": "^2.0.1",
"websocket-driver": "^0.7.0"
"eslint": "^5.8.0",
"webpack": "^4.25.1",
"webpack-cli": "^3.1.2"
}
}
2 changes: 1 addition & 1 deletion src/client/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export default class Client extends EventEmitter {
*/
close() {
this.socket.close();
this.onClose();
//this.onClose();
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/encoder/BinaryEncoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ export default class BinaryEncoder {
* @return {Object}
*/
decode(buffer) {
if (!buffer.byteLength) {
throw new Error('Empty buffer');
}

const id = this.idCodec.decode(buffer, 0);
const handler = this.handlersById[id];

Expand Down
17 changes: 17 additions & 0 deletions src/encoder/codec/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Codec from 'netcode/src/encoder/codec/Codec';
import BooleanCodec from 'netcode/src/encoder/codec/BooleanCodec';
import Int8Codec from 'netcode/src/encoder/codec/Int8Codec';
import Int16Codec from 'netcode/src/encoder/codec/Int16Codec';
import Int32Codec from 'netcode/src/encoder/codec/Int32Codec';
import LongIntCodec from 'netcode/src/encoder/codec/LongIntCodec';
import StringCodec from 'netcode/src/encoder/codec/StringCodec';

module.exports = {
Codec,
BooleanCodec,
Int8Codec,
Int16Codec,
Int32Codec,
LongIntCodec,
StringCodec,
};
50 changes: 45 additions & 5 deletions src/server/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@ export default class Client extends EventEmitter {
* @param {WebSocket} socket
* @param {String} ip
* @param {Encoder} encoder
* @param {Number} pingFrequency Ping frequency in milliseconds
*/
constructor(socket, ip, encoder) {
constructor(socket, ip, encoder, pingFrequency = 0) {
super();

this.socket = socket;
this.id = ++INDEX;
this.ip = ip;
this.socket = socket;
this.encoder = encoder;
this.id = ++INDEX;
this.pingInterval = null;

this.onMessage = this.onMessage.bind(this);
this.onError = this.onError.bind(this);
this.onClose = this.onClose.bind(this);
this.ping = this.ping.bind(this);

this.socket.on('message', this.onMessage);
this.socket.on('error', this.onError);
this.socket.on('close', this.onClose);

if (pingFrequency > 0) {
this.socket.on('open', () => this.startPing(pingFrequency));
}

this.socket.send = this.encoder.constructor.binaryType === 'arraybuffer' ? this.socket.binary : this.socket.text;

this.socket.start();
Expand All @@ -48,15 +55,46 @@ export default class Client extends EventEmitter {
}
}

/**
* Start ping at given interval
*
* @param {Number} frequency Ping frequency in milliseconds
*/
startPing(frequency) {
if (frequency) {
this.pingInterval = setInterval(this.ping, frequency);
}
}

ping() {
if (this.socket) {
this.socket.ping();
}
}

/**
* Stop ping interval
*/
stopPing() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
this.pingInterval = null;
}
}

/**
* On message
*
* @param {Event} event
*/
onMessage(event) {
const { name, data } = this.encoder.decode(event.data);
try {
const { name, data } = this.encoder.decode(event.data);

this.emit(name, data, this);
this.emit(name, data, this);
} catch (error) {
console.error('[ERROR] Could not parse message:', error, '\n\r\tEvent: ', event);
}
}

/**
Expand All @@ -66,12 +104,14 @@ export default class Client extends EventEmitter {
*/
onError(event) {
console.error(`Client ${this.id}: `, event.message);
this.close();
}

/**
* On close
*/
onClose() {
this.stopPing();
this.socket = null;
this.emit('close', this);
}
Expand Down
26 changes: 14 additions & 12 deletions src/server/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import Client from 'netcode/src/server/Client';

export default class Server extends EventEmitter {
/**
* Constructor
*
* @param {Number} port
* @param {String} host
* @param {Number} port Port
* @param {String} host Host
* @param {JsonEncoder|BinaryEncoder} encoder
* @param {Number} ping Ping frequency in seconds (0 for no ping)
* @param {Number} maxLength Paquet max length in bit
* @param {Array} protocols Supported protocols
*/
constructor(port = 8080, host = 'localhost', encoder = new JsonEncoder()) {
constructor(port = 8080, host = 'localhost', encoder = new JsonEncoder(), ping = 0, maxLength = Math.pow(2, 9) - 1, protocols = ['websocket']) {
super();

this.onUpgrade = this.onUpgrade.bind(this);
Expand All @@ -22,6 +24,11 @@ export default class Server extends EventEmitter {
this.encoder = encoder;
this.server = http.createServer();
this.clients = new Map();
this.ping = ping;
this.options = {
maxLength,
protocols,
};

this.server.on('error', this.onError);
this.server.on('upgrade', this.onUpgrade);
Expand Down Expand Up @@ -69,24 +76,19 @@ export default class Server extends EventEmitter {
* @param {Request} request
* @param {Socket} socket
* @param {Object} body
*
* @return {Boolean}
*/
onUpgrade(request, socket, body) {
if (!WebSocket.isWebSocket(request)) {
return socket.end();
}

const ip = request.headers['x-real-ip'] || request.connection.remoteAddress;
const options = { maxLength: Math.pow(2, 9) - 1, protocols: ['websocket'] };
const driver = WebSocket.http(request, options);
const driver = WebSocket.http(request, this.options);

driver.io.write(body);
socket.pipe(driver.io).pipe(socket);

this.addClient(new Client(driver, ip, this.encoder));

return true;
this.addClient(new Client(driver, ip, this.encoder, this.ping * 1000));
}

/**
Expand Down