change error handling for websocket client
fix eslint errors in Tiles.js
This commit is contained in:
parent
28be86718d
commit
e31d27bb35
|
@ -80,6 +80,9 @@ function addShrunkenSubtileToTile(
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* this was a failed try, it ended up being slow
|
||||
* and low quality
|
||||
function addShrunkenIndexedSubtilesToTile(
|
||||
palette,
|
||||
tileSize,
|
||||
|
@ -102,9 +105,7 @@ function addShrunkenIndexedSubtilesToTile(
|
|||
let clr;
|
||||
const linePad = (tileSize + 1) * (subtilesInTile - 1);
|
||||
let amountFullRows = Math.floor(inpTileLength / subtilesInTile);
|
||||
/*
|
||||
* use available data
|
||||
*/
|
||||
// use available data
|
||||
for (let row = 0; row < amountFullRows; row += 1) {
|
||||
channelOffset = (offset + row * tileSize) * 3;
|
||||
const max = channelOffset + subtileSize * 3;
|
||||
|
@ -123,9 +124,7 @@ function addShrunkenIndexedSubtilesToTile(
|
|||
posB += subtilesInTile;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* padding the rest
|
||||
*/
|
||||
// padding the rest
|
||||
[tr, tg, tb] = rgb;
|
||||
if (inpTileLength % subtilesInTile) {
|
||||
channelOffset = (offset + amountFullRows * tileSize) * 3;
|
||||
|
@ -158,6 +157,7 @@ function addShrunkenIndexedSubtilesToTile(
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* @param subtilesInTile how many subtiles are in a tile (per dimension)
|
||||
|
@ -297,7 +297,13 @@ export async function createZoomTileFromChunk(
|
|||
|
||||
if (na.length !== TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL) {
|
||||
na.forEach((element) => {
|
||||
deleteSubtilefromTile(TILE_SIZE, palette, TILE_ZOOM_LEVEL, element, tileRGBBuffer);
|
||||
deleteSubtilefromTile(
|
||||
TILE_SIZE,
|
||||
palette,
|
||||
TILE_ZOOM_LEVEL,
|
||||
element,
|
||||
tileRGBBuffer,
|
||||
);
|
||||
});
|
||||
|
||||
const filename = tileFileName(canvasTileFolder, [maxTiledZoom - 1, x, y]);
|
||||
|
@ -357,7 +363,12 @@ export async function createZoomedTile(
|
|||
}
|
||||
try {
|
||||
const chunk = await sharp(chunkfile).removeAlpha().raw().toBuffer();
|
||||
addShrunkenSubtileToTile(TILE_ZOOM_LEVEL, [dx, dy], chunk, tileRGBBuffer);
|
||||
addShrunkenSubtileToTile(
|
||||
TILE_ZOOM_LEVEL,
|
||||
[dx, dy],
|
||||
chunk,
|
||||
tileRGBBuffer,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
// eslint-disable-next-line max-len
|
||||
|
@ -369,7 +380,13 @@ export async function createZoomedTile(
|
|||
|
||||
if (na.length !== TILE_ZOOM_LEVEL * TILE_ZOOM_LEVEL) {
|
||||
na.forEach((element) => {
|
||||
deleteSubtilefromTile(TILE_SIZE / TILE_ZOOM_LEVEL, palette, TILE_ZOOM_LEVEL, element, tileRGBBuffer);
|
||||
deleteSubtilefromTile(
|
||||
TILE_SIZE / TILE_ZOOM_LEVEL,
|
||||
palette,
|
||||
TILE_ZOOM_LEVEL,
|
||||
element,
|
||||
tileRGBBuffer,
|
||||
);
|
||||
});
|
||||
|
||||
const filename = tileFileName(canvasTileFolder, [z, x, y]);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
/* @flow
|
||||
*/
|
||||
|
||||
// allow the websocket to be noisy on the console
|
||||
/* eslint-disable no-console */
|
||||
|
@ -15,36 +13,36 @@ import RegisterChunk from './packets/RegisterChunk';
|
|||
import RegisterMultipleChunks from './packets/RegisterMultipleChunks';
|
||||
import DeRegisterChunk from './packets/DeRegisterChunk';
|
||||
import ChangedMe from './packets/ChangedMe';
|
||||
import Ping from './packets/Ping';
|
||||
|
||||
const chunks = [];
|
||||
|
||||
class SocketClient extends EventEmitter {
|
||||
url: string;
|
||||
ws: WebSocket;
|
||||
canvasId: number;
|
||||
channelId: number;
|
||||
timeConnected: number;
|
||||
isConnected: number;
|
||||
isConnecting: boolean;
|
||||
msgQueue: Array;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
console.log('Creating WebSocketClient');
|
||||
this.isConnecting = false;
|
||||
this.isConnected = false;
|
||||
this.ws = null;
|
||||
this.canvasId = '0';
|
||||
this.channelId = 0;
|
||||
/*
|
||||
* properties set in connect and open:
|
||||
* this.timeLastConnecting
|
||||
* this.timeLastPing
|
||||
* this.timeLastSent
|
||||
*/
|
||||
this.readyState = WebSocket.CLOSED;
|
||||
this.msgQueue = [];
|
||||
|
||||
this.checkHealth = this.checkHealth.bind(this);
|
||||
setInterval(this.checkHealth, 2000);
|
||||
}
|
||||
|
||||
async connect() {
|
||||
this.isConnecting = true;
|
||||
this.readyState = WebSocket.CONNECTING;
|
||||
if (this.ws) {
|
||||
console.log('WebSocket already open, not starting');
|
||||
}
|
||||
this.timeConnected = Date.now();
|
||||
this.timeLastConnecting = Date.now();
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const url = `${protocol}//${window.location.hostname}${
|
||||
window.location.port ? `:${window.location.port}` : ''
|
||||
|
@ -54,18 +52,34 @@ class SocketClient extends EventEmitter {
|
|||
this.ws.onopen = this.onOpen.bind(this);
|
||||
this.ws.onmessage = this.onMessage.bind(this);
|
||||
this.ws.onclose = this.onClose.bind(this);
|
||||
this.ws.onerror = this.onError.bind(this);
|
||||
this.ws.onerror = (err) => {
|
||||
console.error('Socket encountered error, closing socket', err);
|
||||
};
|
||||
}
|
||||
|
||||
checkHealth() {
|
||||
if (this.readyState === WebSocket.OPEN) {
|
||||
const now = Date.now();
|
||||
if (now - 14000 > this.timeLastPing) {
|
||||
// server didn't send anything, probably dead
|
||||
console.log('Server is silent, killing websocket');
|
||||
this.readyState = WebSocket.CLOSING;
|
||||
this.ws.close();
|
||||
}
|
||||
if (now - 10000 > this.timeLastSent) {
|
||||
// make sure we send something at least all 12s
|
||||
this.sendWhenReady(Ping.dehydrate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendWhenReady(msg) {
|
||||
if (this.isConnected) {
|
||||
this.timeLastSent = Date.now();
|
||||
if (this.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(msg);
|
||||
} else {
|
||||
console.log('Tried sending message when websocket was closed!');
|
||||
this.msgQueue.push(msg);
|
||||
if (!this.isConnecting) {
|
||||
this.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,8 +90,11 @@ class SocketClient extends EventEmitter {
|
|||
}
|
||||
|
||||
onOpen() {
|
||||
this.isConnecting = false;
|
||||
this.isConnected = true;
|
||||
const now = Date.now();
|
||||
this.timeLastPing = now;
|
||||
this.timeLastSent = now;
|
||||
this.readyState = WebSocket.OPEN;
|
||||
|
||||
this.emit('open', {});
|
||||
if (this.canvasId !== null) {
|
||||
this.ws.send(RegisterCanvas.dehydrate(this.canvasId));
|
||||
|
@ -87,11 +104,6 @@ class SocketClient extends EventEmitter {
|
|||
this.ws.send(RegisterMultipleChunks.dehydrate(chunks));
|
||||
}
|
||||
|
||||
onError(err) {
|
||||
console.error('Socket encountered error, closing socket', err);
|
||||
this.ws.close();
|
||||
}
|
||||
|
||||
setCanvas(canvasId) {
|
||||
/* canvasId can be string or integer, thanks to
|
||||
* JSON not allowing integer keys
|
||||
|
@ -111,14 +123,18 @@ class SocketClient extends EventEmitter {
|
|||
const chunkid = (i << 8) | j;
|
||||
chunks.push(chunkid);
|
||||
const buffer = RegisterChunk.dehydrate(chunkid);
|
||||
if (this.isConnected) this.ws.send(buffer);
|
||||
if (this.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
deRegisterChunk(cell) {
|
||||
const [i, j] = cell;
|
||||
const chunkid = (i << 8) | j;
|
||||
const buffer = DeRegisterChunk.dehydrate(chunkid);
|
||||
if (this.isConnected) this.ws.send(buffer);
|
||||
if (this.readyState === WebSocket.OPEN) {
|
||||
this.ws.send(buffer);
|
||||
}
|
||||
const pos = chunks.indexOf(chunkid);
|
||||
if (~pos) chunks.splice(pos, 1);
|
||||
}
|
||||
|
@ -129,17 +145,15 @@ class SocketClient extends EventEmitter {
|
|||
* @param pixel Array of [[offset, color],...] pixels within chunk
|
||||
*/
|
||||
requestPlacePixels(
|
||||
i: number, j: number,
|
||||
pixels: Array,
|
||||
i, j,
|
||||
pixels,
|
||||
) {
|
||||
const buffer = PixelUpdate.dehydrate(i, j, pixels);
|
||||
this.sendWhenReady(buffer);
|
||||
}
|
||||
|
||||
sendChatMessage(message, channelId) {
|
||||
if (this.isConnected) {
|
||||
this.ws.send(JSON.stringify([message, channelId]));
|
||||
}
|
||||
this.sendWhenReady(JSON.stringify([message, channelId]));
|
||||
}
|
||||
|
||||
onMessage({ data: message }) {
|
||||
|
@ -196,6 +210,10 @@ class SocketClient extends EventEmitter {
|
|||
this.emit('pixelReturn', PixelReturn.hydrate(data));
|
||||
break;
|
||||
case OnlineCounter.OP_CODE:
|
||||
/*
|
||||
* using online counter as sign-of-life ping
|
||||
*/
|
||||
this.timeLastPing = Date.now();
|
||||
this.emit('onlineCounter', OnlineCounter.hydrate(data));
|
||||
break;
|
||||
case CoolDownPacket.OP_CODE:
|
||||
|
@ -215,9 +233,9 @@ class SocketClient extends EventEmitter {
|
|||
onClose(e) {
|
||||
this.emit('close');
|
||||
this.ws = null;
|
||||
this.isConnected = false;
|
||||
this.readyState = WebSocket.CONNECTING;
|
||||
// reconnect in 1s if last connect was longer than 7s ago, else 5s
|
||||
const timeout = this.timeConnected < Date.now() - 7000 ? 1000 : 5000;
|
||||
const timeout = this.timeLastConnecting < Date.now() - 7000 ? 1000 : 5000;
|
||||
console.warn(
|
||||
`Socket is closed. Reconnect will be attempted in ${timeout} ms.`,
|
||||
e.reason,
|
||||
|
@ -226,18 +244,14 @@ class SocketClient extends EventEmitter {
|
|||
setTimeout(() => this.connect(), 5000);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.ws.close();
|
||||
}
|
||||
|
||||
reconnect() {
|
||||
if (this.isConnected) {
|
||||
this.isConnected = false;
|
||||
if (this.readyState === WebSocket.OPEN) {
|
||||
this.readyState = WebSocket.CLOSING;
|
||||
console.log('Restarting WebSocket');
|
||||
this.ws.onclose = null;
|
||||
this.ws.onmessage = null;
|
||||
this.ws.close();
|
||||
this.ws = null;
|
||||
this.ws.close();
|
||||
this.connect();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ class SocketServer {
|
|||
this.broadcast = this.broadcast.bind(this);
|
||||
this.broadcastPixelBuffer = this.broadcastPixelBuffer.bind(this);
|
||||
this.reloadUser = this.reloadUser.bind(this);
|
||||
this.ping = this.ping.bind(this);
|
||||
this.onlineCounterBroadcast = this.onlineCounterBroadcast.bind(this);
|
||||
this.checkHealth = this.checkHealth.bind(this);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
|
@ -83,9 +83,8 @@ class SocketServer {
|
|||
});
|
||||
|
||||
wss.on('connection', async (ws, req) => {
|
||||
ws.isAlive = true;
|
||||
ws.timeLastMsg = Date.now();
|
||||
ws.canvasId = null;
|
||||
ws.startDate = Date.now();
|
||||
const user = await authenticateClient(req);
|
||||
if (!user) {
|
||||
ws.close();
|
||||
|
@ -103,16 +102,13 @@ class SocketServer {
|
|||
logger.error(`WebSocket Client Error for ${ws.name}: ${e.message}`);
|
||||
});
|
||||
|
||||
ws.on('pong', () => {
|
||||
ws.isAlive = true;
|
||||
});
|
||||
|
||||
ws.on('close', () => {
|
||||
ipCounter.delete(ip);
|
||||
this.deleteAllChunks(ws);
|
||||
});
|
||||
|
||||
ws.on('message', (data, isBinary) => {
|
||||
ws.timeLastMsg = Date.now();
|
||||
if (isBinary) {
|
||||
this.onBinaryMessage(data, ws);
|
||||
} else {
|
||||
|
@ -179,7 +175,7 @@ class SocketServer {
|
|||
});
|
||||
|
||||
setInterval(this.onlineCounterBroadcast, 10 * 1000);
|
||||
setInterval(this.ping, 15 * 1000);
|
||||
setInterval(this.checkHealth, 15 * 1000);
|
||||
}
|
||||
|
||||
verifyClient(info, done) {
|
||||
|
@ -351,16 +347,15 @@ class SocketServer {
|
|||
});
|
||||
}
|
||||
|
||||
ping() {
|
||||
checkHealth() {
|
||||
const ts = Date.now() - 15000;
|
||||
this.wss.clients.forEach((ws) => {
|
||||
if (!ws.isAlive) {
|
||||
if (ws.user) {
|
||||
logger.info(`Killing dead websocket from ${ws.user.ip}`);
|
||||
}
|
||||
if (
|
||||
ws.readyState === WebSocket.OPEN
|
||||
&& ts > ws.timeLastMsg
|
||||
) {
|
||||
logger.info(`Killing dead websocket from ${ws.user.ip}`);
|
||||
ws.terminate();
|
||||
} else {
|
||||
ws.isAlive = false;
|
||||
ws.ping(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,9 +5,6 @@ export default {
|
|||
|
||||
dehydrate() {
|
||||
// Server (sender)
|
||||
const buffer = new ArrayBuffer(1);
|
||||
const view = new DataView(buffer);
|
||||
view.setInt8(0, OP_CODE);
|
||||
return buffer;
|
||||
return new Uint8Array([OP_CODE]).buffer;
|
||||
},
|
||||
};
|
||||
|
|
10
src/socket/packets/Ping.js
Normal file
10
src/socket/packets/Ping.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const OP_CODE = 0xB0;
|
||||
|
||||
export default {
|
||||
OP_CODE,
|
||||
|
||||
dehydrate() {
|
||||
// Client (sender)
|
||||
return new Uint8Array([OP_CODE]).buffer;
|
||||
},
|
||||
};
|
Loading…
Reference in New Issue
Block a user