124 lines
2.9 KiB
JavaScript
124 lines
2.9 KiB
JavaScript
import D from 'debug';
|
|
import io from 'socket.io-client';
|
|
|
|
import {compose, EventEmitter} from '@avocado/core';
|
|
|
|
import * as SocketIoParser from '../packet/socket.io-parser';
|
|
|
|
const debug = D('@avocado/net:socket');
|
|
|
|
const decorate = compose(
|
|
EventEmitter,
|
|
);
|
|
|
|
export class SocketClient extends decorate(class {}) {
|
|
|
|
constructor(address, options = {}) {
|
|
super();
|
|
this.address = address;
|
|
this.isConnected = false;
|
|
this.isReconnecting = false;
|
|
this.options = {
|
|
parser: SocketIoParser,
|
|
path: '/avocado',
|
|
perMessageDeflate: false,
|
|
reconnection: false,
|
|
...options
|
|
};
|
|
this.socket = null;
|
|
this.connect();
|
|
}
|
|
|
|
connect() {
|
|
if (this.socket) {
|
|
this.socket.destroy();
|
|
}
|
|
this.socket = io(this.address, {
|
|
...this.options,
|
|
});
|
|
this.socket.on('connect', () => {
|
|
debug('connect');
|
|
this.isConnected = true;
|
|
this.emit('connect');
|
|
});
|
|
this.socket.on('connect_error', () => {
|
|
debug('connect_error');
|
|
this.tryReconnect();
|
|
});
|
|
this.socket.on('connect_timeout', () => {
|
|
debug('connect_timeout');
|
|
this.tryReconnect();
|
|
});
|
|
this.socket.on('disconnect', () => {
|
|
this.isConnected = false;
|
|
this.emit('disconnect');
|
|
debug('disconnect');
|
|
this.tryReconnect();
|
|
});
|
|
const {all, idFrom} = require('../packet/packets.scwp');
|
|
const entries = Object.entries(all());
|
|
for (let i = 0; i < entries.length; i++) {
|
|
const [, M] = entries[i];
|
|
const {default: Packet} = M;
|
|
const id = idFrom(M);
|
|
this.socket.on(id, (data, fn) => {
|
|
const packet = new Packet(data);
|
|
debug('recieved packet %o', packet);
|
|
this.emit('packet', packet, fn);
|
|
});
|
|
}
|
|
}
|
|
|
|
disconnect() {
|
|
this.socket.disconnect();
|
|
}
|
|
|
|
get id() {
|
|
return this.socket ? this.socket.id : undefined;
|
|
}
|
|
|
|
send(packet, fn) {
|
|
debug('sending packet %o', packet);
|
|
const {idFrom} = require('../packet/registrar');
|
|
const id = idFrom(packet.constructor);
|
|
this.socket.binary(true).emit(id, packet.data, fn);
|
|
}
|
|
|
|
get session() {
|
|
return this.socket.handshake.session;
|
|
}
|
|
|
|
to(channel) {
|
|
return {
|
|
send: (packet) => {
|
|
const {idFrom} = require('../packet/registrar');
|
|
const id = idFrom(packet.constructor);
|
|
this.socket.binary(true).to(channel).emit(id, packet.data);
|
|
},
|
|
};
|
|
}
|
|
|
|
tryReconnect() {
|
|
if (this.isReconnecting) {
|
|
return;
|
|
}
|
|
this.isReconnecting = true;
|
|
let backoff = 100;
|
|
const tryToReconnect = () => {
|
|
debug('try to reconnect');
|
|
if (this.isConnected) {
|
|
debug('reconnected');
|
|
this.emit('reconnect');
|
|
this.isReconnecting = false;
|
|
return;
|
|
}
|
|
this.connect();
|
|
backoff = Math.min(2000, backoff * 1.5);
|
|
debug('backoff', backoff);
|
|
setTimeout(tryToReconnect, backoff);
|
|
}
|
|
setTimeout(tryToReconnect, 0);
|
|
}
|
|
|
|
}
|