Resolve circular dependencies
This commit is contained in:
parent
2820805f35
commit
23bfebbfe5
|
@ -5,7 +5,7 @@ import { using } from 'bluebird';
|
||||||
import type { User } from '../data/models';
|
import type { User } from '../data/models';
|
||||||
import { redlock } from '../data/redis';
|
import { redlock } from '../data/redis';
|
||||||
import { getChunkOfPixel, getOffsetOfPixel } from './utils';
|
import { getChunkOfPixel, getOffsetOfPixel } from './utils';
|
||||||
import { broadcastPixel } from '../socket/websockets';
|
import webSockets from '../socket/websockets';
|
||||||
import logger from './logger';
|
import logger from './logger';
|
||||||
import RedisCanvas from '../data/models/RedisCanvas';
|
import RedisCanvas from '../data/models/RedisCanvas';
|
||||||
import { registerPixelChange } from './tileserver';
|
import { registerPixelChange } from './tileserver';
|
||||||
|
@ -29,7 +29,7 @@ export function setPixel(
|
||||||
const [i, j] = getChunkOfPixel([x, y], canvasSize);
|
const [i, j] = getChunkOfPixel([x, y], canvasSize);
|
||||||
const offset = getOffsetOfPixel(x, y, canvasSize);
|
const offset = getOffsetOfPixel(x, y, canvasSize);
|
||||||
RedisCanvas.setPixelInChunk(i, j, offset, color, canvasId);
|
RedisCanvas.setPixelInChunk(i, j, offset, color, canvasId);
|
||||||
broadcastPixel(canvasId, i, j, offset, color);
|
webSockets.broadcastPixel(canvasId, i, j, offset, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,6 +22,17 @@ export function sum(values: Array<number>): number {
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* returns random integer
|
||||||
|
* @param min Minimum of random integer
|
||||||
|
* @param max Maximum of random integer
|
||||||
|
* @return random integer between min and max (min <= ret <= max)
|
||||||
|
*/
|
||||||
|
export function getRandomInt(min, max) {
|
||||||
|
const range = max - min + 1;
|
||||||
|
return min + (Math.floor(Math.random() * range));
|
||||||
|
}
|
||||||
|
|
||||||
export function distMax([x1, y1]: Cell, [x2, y2]: Cell): number {
|
export function distMax([x1, y1]: Cell, [x2, y2]: Cell): number {
|
||||||
return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
|
return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ class RedisCanvas {
|
||||||
RedisCanvas.registerChunkChange(canvasId, [i, j]);
|
RedisCanvas.registerChunkChange(canvasId, [i, j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async getPixel(
|
static async getPixelIfExists(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
canvasId: number,
|
canvasId: number,
|
||||||
|
@ -81,7 +81,6 @@ class RedisCanvas {
|
||||||
// 3rd bit -> protected or not
|
// 3rd bit -> protected or not
|
||||||
// rest (5 bits) -> index of color
|
// rest (5 bits) -> index of color
|
||||||
const canvasSize = canvases[canvasId].size;
|
const canvasSize = canvases[canvasId].size;
|
||||||
const canvasAlpha = canvases[canvasId].alpha;
|
|
||||||
const [i, j] = getChunkOfPixel([x, y], canvasSize);
|
const [i, j] = getChunkOfPixel([x, y], canvasSize);
|
||||||
const offset = getOffsetOfPixel(x, y, canvasSize);
|
const offset = getOffsetOfPixel(x, y, canvasSize);
|
||||||
const args = [
|
const args = [
|
||||||
|
@ -91,9 +90,19 @@ class RedisCanvas {
|
||||||
`#${offset}`,
|
`#${offset}`,
|
||||||
];
|
];
|
||||||
const result: ?number = await redis.sendCommandAsync('bitfield', args);
|
const result: ?number = await redis.sendCommandAsync('bitfield', args);
|
||||||
if (!result) return canvasAlpha;
|
if (!result) return null;
|
||||||
const color = result[0];
|
const color = result[0];
|
||||||
return color || canvasAlpha;
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getPixel(
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
canvasId: number,
|
||||||
|
): Promise<number> {
|
||||||
|
const canvasAlpha = canvases[canvasId].alpha;
|
||||||
|
const clr = RedisCanvas.getPixelIfExists(x, y, canvasId);
|
||||||
|
return (clr == null) ? canvasAlpha : clr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import { broadcastMinecraftLink } from '../../../socket/websockets';
|
import webSockets from '../../../socket/websockets';
|
||||||
|
|
||||||
|
|
||||||
export default async (req: Request, res: Response) => {
|
export default async (req: Request, res: Response) => {
|
||||||
|
@ -40,5 +40,5 @@ export default async (req: Request, res: Response) => {
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
broadcastMinecraftLink(name, minecraftid, accepted);
|
webSockets.broadcastMinecraftLink(name, minecraftid, accepted);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
|
|
||||||
import canvases from '../../canvases.json';
|
import canvases from '../../canvases.json';
|
||||||
import { broadcastMinecraftTP } from '../../socket/websockets';
|
import webSockets from '../../socket/websockets';
|
||||||
|
|
||||||
const CANVAS_MAX_XY = (canvases[0].size / 2);
|
const CANVAS_MAX_XY = (canvases[0].size / 2);
|
||||||
const CANVAS_MIN_XY = -CANVAS_MAX_XY;
|
const CANVAS_MIN_XY = -CANVAS_MAX_XY;
|
||||||
|
@ -47,7 +47,7 @@ export default async (req: Request, res: Response) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await broadcastMinecraftTP(minecraftid, x, y);
|
webSockets.broadcastMinecraftTP(minecraftid, x, y);
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
|
|
@ -8,17 +8,15 @@
|
||||||
* @flow */
|
* @flow */
|
||||||
|
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
|
||||||
|
import WebSocketEvents from './WebSocketEvents';
|
||||||
|
import webSockets from './websockets';
|
||||||
import { getIPFromRequest } from '../utils/ip';
|
import { getIPFromRequest } from '../utils/ip';
|
||||||
import Minecraft from '../core/minecraft';
|
import Minecraft from '../core/minecraft';
|
||||||
import { drawUnsafe, setPixel } from '../core/draw';
|
import { drawUnsafe, setPixel } from '../core/draw';
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
import redis from '../data/redis';
|
|
||||||
import ChatHistory from './ChatHistory';
|
|
||||||
import { broadcastChatMessage, notifyChangedMe } from './websockets';
|
|
||||||
import { APISOCKET_KEY } from '../core/config';
|
import { APISOCKET_KEY } from '../core/config';
|
||||||
|
|
||||||
function heartbeat() {
|
function heartbeat() {
|
||||||
|
@ -39,13 +37,14 @@ async function verifyClient(info, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class APISocketServer extends EventEmitter {
|
class APISocketServer extends WebSocketEvents {
|
||||||
wss: WebSocket.Server;
|
wss: WebSocket.Server;
|
||||||
mc: Minecraft;
|
mc: Minecraft;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
logger.info('Starting API websocket server');
|
logger.info('Starting API websocket server');
|
||||||
|
webSockets.addListener(this);
|
||||||
|
|
||||||
const wss = new WebSocket.Server({
|
const wss = new WebSocket.Server({
|
||||||
perMessageDeflate: false,
|
perMessageDeflate: false,
|
||||||
|
@ -79,7 +78,9 @@ class APISocketServer extends EventEmitter {
|
||||||
setInterval(this.ping, 45 * 1000);
|
setInterval(this.ping, 45 * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastChatMessage(name, msg, ws = null) {
|
broadcastChatMessage(name, msg, sendapi, ws = null) {
|
||||||
|
if (!sendapi) return;
|
||||||
|
|
||||||
const sendmsg = JSON.stringify(['msg', name, msg]);
|
const sendmsg = JSON.stringify(['msg', name, msg]);
|
||||||
this.wss.clients.forEach((client) => {
|
this.wss.clients.forEach((client) => {
|
||||||
if (client !== ws && client.subChat && client.readyState === WebSocket.OPEN) {
|
if (client !== ws && client.subChat && client.readyState === WebSocket.OPEN) {
|
||||||
|
@ -127,7 +128,8 @@ class APISocketServer extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastPixelBuffer(chunkid, buffer) {
|
broadcastPixelBuffer(canvasId, chunkid, buffer) {
|
||||||
|
if (canvasId !== 0) return;
|
||||||
const frame = WebSocket.Sender.frame(buffer, {
|
const frame = WebSocket.Sender.frame(buffer, {
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
mask: false,
|
mask: false,
|
||||||
|
@ -228,21 +230,21 @@ class APISocketServer extends EventEmitter {
|
||||||
const [minecraftname, msg] = packet;
|
const [minecraftname, msg] = packet;
|
||||||
const user = this.mc.minecraftname2User(minecraftname);
|
const user = this.mc.minecraftname2User(minecraftname);
|
||||||
const chatname = (user.id) ? `[MC] ${user.regUser.name}` : `[MC] ${minecraftname}`;
|
const chatname = (user.id) ? `[MC] ${user.regUser.name}` : `[MC] ${minecraftname}`;
|
||||||
broadcastChatMessage(chatname, msg, false);
|
webSockets.broadcastChatMessage(chatname, msg, false);
|
||||||
this.broadcastChatMessage(chatname, msg, ws);
|
this.broadcastChatMessage(chatname, msg, true, ws);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (command == 'chat') {
|
if (command == 'chat') {
|
||||||
const [name, msg] = packet;
|
const [name, msg] = packet;
|
||||||
broadcastChatMessage(name, msg, false);
|
webSockets.broadcastChatMessage(name, msg, false);
|
||||||
this.broadcastChatMessage(name, msg, ws);
|
this.broadcastChatMessage(name, msg, true, ws);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (command == 'linkacc') {
|
if (command == 'linkacc') {
|
||||||
const [minecraftid, minecraftname, name] = packet;
|
const [minecraftid, minecraftname, name] = packet;
|
||||||
const ret = await this.mc.linkacc(minecraftid, minecraftname, name);
|
const ret = await this.mc.linkacc(minecraftid, minecraftname, name);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
notifyChangedMe(name);
|
webSockets.notifyChangedMe(name);
|
||||||
}
|
}
|
||||||
ws.send(JSON.stringify([
|
ws.send(JSON.stringify([
|
||||||
'linkret',
|
'linkret',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
|
||||||
import WebSocket from 'ws';
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
|
@ -16,12 +15,12 @@ import DeRegisterChunk from './packets/DeRegisterChunk';
|
||||||
import DeRegisterMultipleChunks from './packets/DeRegisterMultipleChunks';
|
import DeRegisterMultipleChunks from './packets/DeRegisterMultipleChunks';
|
||||||
import RequestChatHistory from './packets/RequestChatHistory';
|
import RequestChatHistory from './packets/RequestChatHistory';
|
||||||
import CoolDownPacket from './packets/CoolDownPacket';
|
import CoolDownPacket from './packets/CoolDownPacket';
|
||||||
import PixelUpdate from './packets/PixelUpdate';
|
|
||||||
import ChangedMe from './packets/ChangedMe';
|
import ChangedMe from './packets/ChangedMe';
|
||||||
|
|
||||||
import ChatHistory from './ChatHistory';
|
import ChatHistory from './ChatHistory';
|
||||||
import authenticateClient from './verifyClient';
|
import authenticateClient from './verifyClient';
|
||||||
import { broadcastChatMessage } from './websockets';
|
import WebSocketEvents from './WebSocketEvents';
|
||||||
|
import webSockets from './websockets';
|
||||||
|
|
||||||
|
|
||||||
const ipCounter: Counter<string> = new Counter();
|
const ipCounter: Counter<string> = new Counter();
|
||||||
|
@ -46,7 +45,7 @@ async function verifyClient(info, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class SocketServer extends EventEmitter {
|
class SocketServer extends WebSocketEvents {
|
||||||
wss: WebSocket.Server;
|
wss: WebSocket.Server;
|
||||||
CHUNK_CLIENTS: Map<number, Array>;
|
CHUNK_CLIENTS: Map<number, Array>;
|
||||||
|
|
||||||
|
@ -55,6 +54,7 @@ class SocketServer extends EventEmitter {
|
||||||
super();
|
super();
|
||||||
this.CHUNK_CLIENTS = new Map();
|
this.CHUNK_CLIENTS = new Map();
|
||||||
logger.info('Starting websocket server');
|
logger.info('Starting websocket server');
|
||||||
|
webSockets.addListener(this);
|
||||||
|
|
||||||
const wss = new WebSocket.Server({
|
const wss = new WebSocket.Server({
|
||||||
perMessageDeflate: false,
|
perMessageDeflate: false,
|
||||||
|
@ -92,14 +92,20 @@ class SocketServer extends EventEmitter {
|
||||||
this.deleteAllChunks(ws);
|
this.deleteAllChunks(ws);
|
||||||
});
|
});
|
||||||
ws.on('message', (message) => {
|
ws.on('message', (message) => {
|
||||||
if (typeof message === 'string') { this.onTextMessage(message, ws); } else { this.onBinaryMessage(message, ws); }
|
if (typeof message === 'string') {
|
||||||
|
this.onTextMessage(message, ws);
|
||||||
|
} else {
|
||||||
|
this.onBinaryMessage(message, ws);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.onlineCounterBroadcast = this.onlineCounterBroadcast.bind(this);
|
||||||
this.ping = this.ping.bind(this);
|
this.ping = this.ping.bind(this);
|
||||||
this.killOld = this.killOld.bind(this);
|
this.killOld = this.killOld.bind(this);
|
||||||
|
|
||||||
setInterval(this.killOld, 10 * 60 * 1000);
|
setInterval(this.killOld, 10 * 60 * 1000);
|
||||||
|
setInterval(this.onlineCounterBroadcast, 10 * 1000);
|
||||||
// https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
|
// https://github.com/websockets/ws#how-to-detect-and-close-broken-connections
|
||||||
setInterval(this.ping, 45 * 1000);
|
setInterval(this.ping, 45 * 1000);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +136,12 @@ class SocketServer extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcastText(text: string) {
|
broadcastOnlineCounter(buffer: Buffer) {
|
||||||
|
this.broadcast(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastChatMessage(name: string, message: string) {
|
||||||
|
const text = JSON.stringify([name, message]);
|
||||||
this.wss.clients.forEach((ws) => {
|
this.wss.clients.forEach((ws) => {
|
||||||
if (ws.readyState == WebSocket.OPEN) {
|
if (ws.readyState == WebSocket.OPEN) {
|
||||||
ws.send(text);
|
ws.send(text);
|
||||||
|
@ -171,10 +182,6 @@ class SocketServer extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getConnections(): number {
|
|
||||||
return this.wss.clients.size || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
killOld() {
|
killOld() {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
this.wss.clients.forEach((ws) => {
|
this.wss.clients.forEach((ws) => {
|
||||||
|
@ -192,13 +199,18 @@ class SocketServer extends EventEmitter {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onlineCounterBroadcast() {
|
||||||
|
const online = this.wss.clients.size || 0;
|
||||||
|
webSockets.broadcastOnlineCounter(online);
|
||||||
|
}
|
||||||
|
|
||||||
onTextMessage(message, ws) {
|
onTextMessage(message, ws) {
|
||||||
if (ws.name && message) {
|
if (ws.name && message) {
|
||||||
const waitLeft = ws.rateLimiter.tick();
|
const waitLeft = ws.rateLimiter.tick();
|
||||||
if (waitLeft) {
|
if (waitLeft) {
|
||||||
ws.send(JSON.stringify(['info', `You are sending messages too fast, you have to wait ${Math.floor(waitLeft / 1000)}s :(`]));
|
ws.send(JSON.stringify(['info', `You are sending messages too fast, you have to wait ${Math.floor(waitLeft / 1000)}s :(`]));
|
||||||
} else {
|
} else {
|
||||||
broadcastChatMessage(ws.name, message);
|
webSockets.broadcastChatMessage(ws.name, message);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.info('Got empty message or message from unidentified ws');
|
logger.info('Got empty message or message from unidentified ws');
|
||||||
|
@ -256,7 +268,7 @@ class SocketServer extends EventEmitter {
|
||||||
|
|
||||||
pushChunk(chunkid, ws) {
|
pushChunk(chunkid, ws) {
|
||||||
if (!this.CHUNK_CLIENTS.has(chunkid)) {
|
if (!this.CHUNK_CLIENTS.has(chunkid)) {
|
||||||
this.CHUNK_CLIENTS.set(chunkid, new Array());
|
this.CHUNK_CLIENTS.set(chunkid, []);
|
||||||
}
|
}
|
||||||
const clients = this.CHUNK_CLIENTS.get(chunkid);
|
const clients = this.CHUNK_CLIENTS.get(chunkid);
|
||||||
const pos = clients.indexOf(ws);
|
const pos = clients.indexOf(ws);
|
||||||
|
|
33
src/socket/WebSocketEvents.js
Normal file
33
src/socket/WebSocketEvents.js
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
/* @flow
|
||||||
|
*
|
||||||
|
* Parent class for socket servers
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
/* eslint-disable class-methods-use-this */
|
||||||
|
|
||||||
|
class WebSocketEvents {
|
||||||
|
broadcast(message: Buffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastPixelBuffer(canvasId: number, chunkid: number, buffer: Buffer) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastChatMessage(name: string, message: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastMinecraftLink(name: string, minecraftid: string, accepted: boolean) {
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyChangedMe(name: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastMinecraftTP(minecraftid: string, x: number, y: number) {
|
||||||
|
}
|
||||||
|
|
||||||
|
broadcastOnlineCounter(data: Buffer) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WebSocketEvents;
|
|
@ -23,5 +23,6 @@ export default {
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,12 +2,6 @@
|
||||||
|
|
||||||
|
|
||||||
import type { ColorIndex } from '../../core/Palette';
|
import type { ColorIndex } from '../../core/Palette';
|
||||||
import {
|
|
||||||
getChunkOfPixel,
|
|
||||||
getOffsetOfPixel,
|
|
||||||
getPixelFromChunkOffset,
|
|
||||||
} from '../../core/utils';
|
|
||||||
|
|
||||||
|
|
||||||
type PixelUpdatePacket = {
|
type PixelUpdatePacket = {
|
||||||
x: number,
|
x: number,
|
||||||
|
@ -42,5 +36,6 @@ export default {
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
import logger from '../core/logger';
|
|
||||||
import session from '../core/session';
|
import session from '../core/session';
|
||||||
import passport from '../core/passport';
|
import passport from '../core/passport';
|
||||||
import { User } from '../data/models';
|
import { User } from '../data/models';
|
||||||
|
@ -19,7 +18,6 @@ router.use(session);
|
||||||
* (cut IPv6 to subnet to prevent abuse)
|
* (cut IPv6 to subnet to prevent abuse)
|
||||||
*/
|
*/
|
||||||
router.use(async (req, res, next) => {
|
router.use(async (req, res, next) => {
|
||||||
const { session } = req;
|
|
||||||
const ip = await getIPFromRequest(req);
|
const ip = await getIPFromRequest(req);
|
||||||
const trueIp = ip || '0.0.0.1';
|
const trueIp = ip || '0.0.0.1';
|
||||||
req.trueIp = trueIp;
|
req.trueIp = trueIp;
|
||||||
|
@ -33,9 +31,9 @@ router.use(passport.initialize());
|
||||||
router.use(passport.session());
|
router.use(passport.session());
|
||||||
|
|
||||||
|
|
||||||
export function authenticateClient(req) {
|
function authenticateClient(req) {
|
||||||
return new Promise(
|
return new Promise(
|
||||||
((resolve, reject) => {
|
((resolve) => {
|
||||||
router(req, {}, () => {
|
router(req, {}, () => {
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
resolve(req.user);
|
resolve(req.user);
|
||||||
|
|
|
@ -5,123 +5,127 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import url from 'url';
|
|
||||||
|
|
||||||
import logger from '../core/logger';
|
import logger from '../core/logger';
|
||||||
import { SECOND } from '../core/constants';
|
|
||||||
import { getChunkOfPixel } from '../core/utils';
|
|
||||||
|
|
||||||
import ChatHistory from './ChatHistory';
|
import ChatHistory from './ChatHistory';
|
||||||
import SocketServer from './SocketServer';
|
|
||||||
import APISocketServer from './APISocketServer';
|
|
||||||
import OnlineCounter from './packets/OnlineCounter';
|
import OnlineCounter from './packets/OnlineCounter';
|
||||||
import PixelUpdate from './packets/PixelUpdate';
|
import PixelUpdate from './packets/PixelUpdate';
|
||||||
|
|
||||||
const usersocket = new SocketServer();
|
|
||||||
const apisocket = new APISocketServer();
|
|
||||||
|
|
||||||
/*
|
class WebSockets {
|
||||||
* broadcast message via websocket
|
listeners: Array<Object>;
|
||||||
* @param message Message to send
|
|
||||||
*/
|
|
||||||
export async function broadcast(message: Buffer) {
|
|
||||||
if (usersocket) usersocket.broadcast(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
constructor() {
|
||||||
* broadcast pixel message via websocket
|
this.listeners = [];
|
||||||
* @param canvasIdent ident of canvas
|
}
|
||||||
* @param i x coordinates of chunk
|
|
||||||
* @param j y coordinates of chunk
|
|
||||||
* @param offset offset of pixel within this chunk
|
|
||||||
* @param color colorindex
|
|
||||||
*/
|
|
||||||
export async function broadcastPixel(
|
|
||||||
canvasId: number,
|
|
||||||
i: number,
|
|
||||||
j: number,
|
|
||||||
offset: number,
|
|
||||||
color: number,
|
|
||||||
) {
|
|
||||||
const chunkid = (i << 8) | j;
|
|
||||||
const buffer = PixelUpdate.dehydrate(i, j, offset, color);
|
|
||||||
if (usersocket) usersocket.broadcastPixelBuffer(canvasId, chunkid, buffer);
|
|
||||||
if (apisocket && canvasId == 0) apisocket.broadcastPixelBuffer(chunkid, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
addListener(listener) {
|
||||||
* broadcast chat message
|
this.listeners.push(listener);
|
||||||
* @param name chatname
|
}
|
||||||
* @param message Message to send
|
|
||||||
* @param sendapi If chat message should get boradcasted to api websocket
|
|
||||||
* (usefull if the api is supposed to not answer to its own messages)
|
|
||||||
*/
|
|
||||||
export async function broadcastChatMessage(name: string, message: string, sendapi: boolean = true) {
|
|
||||||
logger.info(`Received chat message ${message} from ${name}`);
|
|
||||||
ChatHistory.addMessage(name, message);
|
|
||||||
if (usersocket) usersocket.broadcastText(JSON.stringify([name, message]));
|
|
||||||
if (sendapi && apisocket) apisocket.broadcastChatMessage(name, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* broadcast minecraft linking to API
|
* broadcast message via websocket
|
||||||
* @param name pixelplanetname
|
* @param message Message to send
|
||||||
* @param minecraftid minecraftid
|
*/
|
||||||
* @param accepted If link request got accepted
|
broadcast(message: Buffer) {
|
||||||
*/
|
this.listeners.forEach(
|
||||||
export async function broadcastMinecraftLink(name: string, minecraftid: string, accepted: boolean) {
|
(listener) => listener.broadcast(message),
|
||||||
if (apisocket) apisocket.broadcastMinecraftLink(name, minecraftid, accepted);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notify user on websocket that he should rerequest api/message
|
* broadcast pixel message via websocket
|
||||||
* Currently just used for getting minecraft link message.
|
* @param canvasIdent ident of canvas
|
||||||
*/
|
* @param i x coordinates of chunk
|
||||||
export async function notifyChangedMe(name: string) {
|
* @param j y coordinates of chunk
|
||||||
if (usersocket) usersocket.notifyChangedMe(name);
|
* @param offset offset of pixel within this chunk
|
||||||
}
|
* @param color colorindex
|
||||||
|
*/
|
||||||
|
broadcastPixel(
|
||||||
|
canvasId: number,
|
||||||
|
i: number,
|
||||||
|
j: number,
|
||||||
|
offset: number,
|
||||||
|
color: number,
|
||||||
|
) {
|
||||||
|
const chunkid = (i << 8) | j;
|
||||||
|
const buffer = PixelUpdate.dehydrate(i, j, offset, color);
|
||||||
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.broadcastPixelBuffer(canvasId, chunkid, buffer),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* broadcast mc tp request to API
|
* broadcast chat message
|
||||||
* @param minecraftid minecraftid
|
* @param name chatname
|
||||||
* @param x x coords
|
* @param message Message to send
|
||||||
* @param y y coords
|
* @param sendapi If chat message should get boradcasted to api websockets
|
||||||
*/
|
* (usefull if the api is supposed to not answer to its own messages)
|
||||||
export async function broadcastMinecraftTP(minecraftid, x, y) {
|
*/
|
||||||
if (apisocket) apisocket.broadcastMinecraftTP(minecraftid, x, y);
|
broadcastChatMessage(
|
||||||
}
|
name: string,
|
||||||
|
message: string,
|
||||||
|
sendapi: boolean = true,
|
||||||
|
) {
|
||||||
|
logger.info(`Received chat message ${message} from ${name}`);
|
||||||
|
ChatHistory.addMessage(name, message);
|
||||||
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.broadcastChatMessage(name, message, sendapi),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* send websocket package of online counter every x seconds
|
* broadcast minecraft linking to API
|
||||||
*/
|
* @param name pixelplanetname
|
||||||
function startOnlineCounterBroadcast() {
|
* @param minecraftid minecraftid
|
||||||
setInterval(() => {
|
* @param accepted If link request got accepted
|
||||||
if (usersocket) {
|
*/
|
||||||
const online = usersocket.getConnections();
|
broadcastMinecraftLink(
|
||||||
const buffer = OnlineCounter.dehydrate({ online });
|
name: string,
|
||||||
usersocket.broadcast(buffer);
|
minecraftid: string,
|
||||||
if (apisocket) apisocket.broadcastOnlineCounter(buffer);
|
accepted: boolean,
|
||||||
}
|
) {
|
||||||
}, 15 * SECOND);
|
this.listeners.forEach(
|
||||||
}
|
(listener) => listener.broadcastMinecraftLink(
|
||||||
startOnlineCounterBroadcast();
|
name,
|
||||||
|
minecraftid,
|
||||||
|
accepted,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* websocket upgrade / establishing connection
|
* Notify user on websocket that he should rerequest api/message
|
||||||
* Get hooked up to httpServer and routes to the right socket
|
* Currently just used for getting minecraft link message.
|
||||||
*/
|
*/
|
||||||
export function wsupgrade(request, socket, head) {
|
notifyChangedMe(name: string) {
|
||||||
const { pathname } = url.parse(request.url);
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.notifyChangedMe(name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (pathname === '/ws') {
|
/*
|
||||||
usersocket.wss.handleUpgrade(request, socket, head, (ws) => {
|
* broadcast mc tp request to API
|
||||||
usersocket.wss.emit('connection', ws, request);
|
* @param minecraftid minecraftid
|
||||||
});
|
* @param x x coords
|
||||||
} else if (pathname === '/mcws') {
|
* @param y y coords
|
||||||
apisocket.wss.handleUpgrade(request, socket, head, (ws) => {
|
*/
|
||||||
apisocket.wss.emit('connection', ws, request);
|
broadcastMinecraftTP(minecraftid, x, y) {
|
||||||
});
|
this.listeners.forEach(
|
||||||
} else {
|
(listener) => listener.broadcastMinecraftTP(minecraftid, x, y),
|
||||||
socket.destroy();
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* broadcast online counter
|
||||||
|
* @param online Number of users online
|
||||||
|
*/
|
||||||
|
broadcastOnlineCounter(online: number) {
|
||||||
|
const buffer = OnlineCounter.dehydrate({ online });
|
||||||
|
this.listeners.forEach(
|
||||||
|
(listener) => listener.broadcastOnlineCounter(buffer),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const webSockets = new WebSockets();
|
||||||
|
export default webSockets;
|
||||||
|
|
29
src/web.js
29
src/web.js
|
@ -1,5 +1,6 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
|
import url from 'url';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import compression from 'compression';
|
import compression from 'compression';
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
@ -14,6 +15,9 @@ import logger from './core/logger';
|
||||||
import rankings from './core/ranking';
|
import rankings from './core/ranking';
|
||||||
import models from './data/models';
|
import models from './data/models';
|
||||||
|
|
||||||
|
import SocketServer from './socket/SocketServer';
|
||||||
|
import APISocketServer from './socket/APISocketServer';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
api,
|
api,
|
||||||
tiles,
|
tiles,
|
||||||
|
@ -28,7 +32,6 @@ import { SECOND, MONTH } from './core/constants';
|
||||||
import { PORT, DISCORD_INVITE } from './core/config';
|
import { PORT, DISCORD_INVITE } from './core/config';
|
||||||
|
|
||||||
import { ccToCoords } from './utils/location';
|
import { ccToCoords } from './utils/location';
|
||||||
import { wsupgrade } from './socket/websockets';
|
|
||||||
import { startAllCanvasLoops } from './core/tileserver';
|
import { startAllCanvasLoops } from './core/tileserver';
|
||||||
|
|
||||||
startAllCanvasLoops();
|
startAllCanvasLoops();
|
||||||
|
@ -41,8 +44,30 @@ app.disable('x-powered-by');
|
||||||
// Call Garbage Collector every 30 seconds
|
// Call Garbage Collector every 30 seconds
|
||||||
setInterval(forceGC, 15 * 60 * SECOND);
|
setInterval(forceGC, 15 * 60 * SECOND);
|
||||||
|
|
||||||
// create websocket
|
// create http server
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// websockets
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
const usersocket = new SocketServer();
|
||||||
|
const apisocket = new APISocketServer();
|
||||||
|
function wsupgrade(request, socket, head) {
|
||||||
|
const { pathname } = url.parse(request.url);
|
||||||
|
|
||||||
|
if (pathname === '/ws') {
|
||||||
|
usersocket.wss.handleUpgrade(request, socket, head, (ws) => {
|
||||||
|
usersocket.wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
} else if (pathname === '/mcws') {
|
||||||
|
apisocket.wss.handleUpgrade(request, socket, head, (ws) => {
|
||||||
|
apisocket.wss.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
server.on('upgrade', wsupgrade);
|
server.on('upgrade', wsupgrade);
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user