replace socket.io usage with WebSockets
This commit is contained in:
parent
2ee9c8b919
commit
ca9e127e3e
@ -81,9 +81,6 @@ export class App {
|
|||||||
document.getElementById('stat_totalPlayers').innerText = 0
|
document.getElementById('stat_totalPlayers').innerText = 0
|
||||||
document.getElementById('stat_networks').innerText = 0
|
document.getElementById('stat_networks').innerText = 0
|
||||||
|
|
||||||
// Modify page state to display loading overlay
|
|
||||||
this.caption.set('Lost connection!')
|
|
||||||
|
|
||||||
this.setPageReady(false)
|
this.setPageReady(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +1,107 @@
|
|||||||
import { App } from './app'
|
import { App } from './app'
|
||||||
|
|
||||||
import io from 'socket.io-client'
|
|
||||||
|
|
||||||
const app = new App()
|
const app = new App()
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
const socket = io.connect({
|
const webSocket = new WebSocket('ws://' + location.host)
|
||||||
reconnect: true,
|
|
||||||
reconnectDelay: 1000,
|
|
||||||
reconnectionAttempts: 10
|
|
||||||
})
|
|
||||||
|
|
||||||
// The backend will automatically push data once connected
|
// The backend will automatically push data once connected
|
||||||
socket.on('connect', function () {
|
webSocket.onopen = () => {
|
||||||
app.caption.set('Loading...')
|
app.caption.set('Loading...')
|
||||||
})
|
}
|
||||||
|
|
||||||
socket.on('disconnect', function () {
|
webSocket.onclose = (event) => {
|
||||||
app.handleDisconnect()
|
app.handleDisconnect()
|
||||||
|
|
||||||
|
// Modify page state to display loading overlay
|
||||||
|
// Code 1006 denotes "Abnormal closure", most likely from the server or client losing connection
|
||||||
|
// See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||||
|
// Treat other codes as active errors (besides connectivity errors) when displaying the message
|
||||||
|
if (event.code === 1006) {
|
||||||
|
app.caption.set('Lost connection!')
|
||||||
|
} else {
|
||||||
|
app.caption.set('Disconnected due to error.')
|
||||||
|
}
|
||||||
|
|
||||||
// Reset modified DOM structures
|
// Reset modified DOM structures
|
||||||
document.getElementById('big-graph-mobile-load-request').style.display = 'none'
|
document.getElementById('big-graph-mobile-load-request').style.display = 'none'
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('historyGraph', function (data) {
|
// TODO: Reconnect behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
webSocket.onmessage = (message) => {
|
||||||
|
const payload = JSON.parse(message.data)
|
||||||
|
|
||||||
|
switch (payload.message) {
|
||||||
|
case 'init':
|
||||||
|
app.setPublicConfig(payload.config)
|
||||||
|
|
||||||
|
// Display the main page component
|
||||||
|
// Called here instead of syncComplete so the DOM can be drawn prior to the graphs being drawn
|
||||||
|
// Otherwise flot.js will cause visual alignment bugs
|
||||||
|
app.setPageReady(true)
|
||||||
|
|
||||||
|
// Allow the graphDisplayManager to control whether or not the historical graph is loaded
|
||||||
|
// Defer to isGraphVisible from the publicConfig to understand if the frontend will ever receive a graph payload
|
||||||
|
if (app.publicConfig.isGraphVisible) {
|
||||||
|
if (app.graphDisplayManager.isVisible) {
|
||||||
|
// Send request as a plain text string to avoid the server needing to parse JSON
|
||||||
|
// This is mostly to simplify the backend server's need for error handling
|
||||||
|
webSocket.send('requestHistoryGraph')
|
||||||
|
} else {
|
||||||
|
document.getElementById('big-graph-mobile-load-request').style.display = 'block'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.servers.forEach(app.addServer)
|
||||||
|
|
||||||
|
if (payload.mojangServices) {
|
||||||
|
app.mojangUpdater.updateStatus(payload.mojangServices)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init payload contains all data needed to render the page
|
||||||
|
// Alert the app it is ready
|
||||||
|
app.handleSyncComplete()
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'updateServer': {
|
||||||
|
// The backend may send "update" events prior to receiving all "add" events
|
||||||
|
// A server has only been added once it's ServerRegistration is defined
|
||||||
|
// Checking undefined protects from this race condition
|
||||||
|
const serverRegistration = app.serverRegistry.getServerRegistration(payload.serverId)
|
||||||
|
|
||||||
|
if (serverRegistration) {
|
||||||
|
serverRegistration.updateServerStatus(payload, false, app.publicConfig.minecraftVersions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use update payloads to conditionally append data to graph
|
||||||
|
// Skip any incoming updates if the graph is disabled
|
||||||
|
if (payload.updateHistoryGraph && app.graphDisplayManager.isVisible) {
|
||||||
|
// Update may not be successful, safely append 0 points
|
||||||
|
const playerCount = payload.result ? payload.result.players.online : 0
|
||||||
|
|
||||||
|
app.graphDisplayManager.addGraphPoint(serverRegistration.serverId, payload.timestamp, playerCount)
|
||||||
|
|
||||||
|
// Only redraw the graph if not mutating hidden data
|
||||||
|
if (serverRegistration.isVisible) {
|
||||||
|
app.graphDisplayManager.requestRedraw()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'updateMojangServices': {
|
||||||
|
app.mojangUpdater.updateStatus(payload)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'historyGraph': {
|
||||||
// Consider the graph visible since a payload has been received
|
// Consider the graph visible since a payload has been received
|
||||||
// This is used for the manual graph load request behavior
|
// This is used for the manual graph load request behavior
|
||||||
app.graphDisplayManager.isVisible = true
|
app.graphDisplayManager.isVisible = true
|
||||||
|
|
||||||
app.graphDisplayManager.buildPlotInstance(data)
|
app.graphDisplayManager.buildPlotInstance(payload.graphData)
|
||||||
|
|
||||||
// Build checkbox elements for graph controls
|
// Build checkbox elements for graph controls
|
||||||
let lastRowCounter = 0
|
let lastRowCounter = 0
|
||||||
@ -60,64 +133,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
// Bind click event for updating graph data
|
// Bind click event for updating graph data
|
||||||
app.graphDisplayManager.initEventListeners()
|
app.graphDisplayManager.initEventListeners()
|
||||||
})
|
break
|
||||||
|
|
||||||
socket.on('add', function (data) {
|
|
||||||
data.forEach(app.addServer)
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('update', function (data) {
|
|
||||||
// The backend may send "update" events prior to receiving all "add" events
|
|
||||||
// A server has only been added once it's ServerRegistration is defined
|
|
||||||
// Checking undefined protects from this race condition
|
|
||||||
const serverRegistration = app.serverRegistry.getServerRegistration(data.serverId)
|
|
||||||
|
|
||||||
if (serverRegistration) {
|
|
||||||
serverRegistration.updateServerStatus(data, false, app.publicConfig.minecraftVersions)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use update payloads to conditionally append data to graph
|
|
||||||
// Skip any incoming updates if the graph is disabled
|
|
||||||
if (data.updateHistoryGraph && app.graphDisplayManager.isVisible) {
|
|
||||||
// Update may not be successful, safely append 0 points
|
|
||||||
const playerCount = data.result ? data.result.players.online : 0
|
|
||||||
|
|
||||||
app.graphDisplayManager.addGraphPoint(serverRegistration.serverId, data.timestamp, playerCount)
|
|
||||||
|
|
||||||
// Only redraw the graph if not mutating hidden data
|
|
||||||
if (serverRegistration.isVisible) {
|
|
||||||
app.graphDisplayManager.requestRedraw()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('updateMojangServices', function (data) {
|
|
||||||
app.mojangUpdater.updateStatus(data)
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on('setPublicConfig', function (data) {
|
|
||||||
app.setPublicConfig(data)
|
|
||||||
|
|
||||||
// Display the main page component
|
|
||||||
// Called here instead of syncComplete so the DOM can be drawn prior to the graphs being drawn
|
|
||||||
// Otherwise flot.js will cause visual alignment bugs
|
|
||||||
app.setPageReady(true)
|
|
||||||
|
|
||||||
// Allow the graphDisplayManager to control whether or not the historical graph is loaded
|
|
||||||
// Defer to isGraphVisible from the publicConfig to understand if the frontend will ever receive a graph payload
|
|
||||||
if (data.isGraphVisible) {
|
|
||||||
if (app.graphDisplayManager.isVisible) {
|
|
||||||
socket.emit('requestHistoryGraph')
|
|
||||||
} else {
|
|
||||||
document.getElementById('big-graph-mobile-load-request').style.display = 'block'
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Fired once the backend has sent all requested data
|
|
||||||
socket.on('syncComplete', function () {
|
|
||||||
app.handleSyncComplete()
|
|
||||||
})
|
|
||||||
|
|
||||||
window.addEventListener('resize', function () {
|
window.addEventListener('resize', function () {
|
||||||
app.percentageBar.redraw()
|
app.percentageBar.redraw()
|
||||||
@ -128,7 +147,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
document.getElementById('big-graph-mobile-load-request-button').addEventListener('click', function () {
|
document.getElementById('big-graph-mobile-load-request-button').addEventListener('click', function () {
|
||||||
// Send a graph data request to the backend
|
// Send a graph data request to the backend
|
||||||
socket.emit('requestHistoryGraph')
|
// Send request as a plain text string to avoid the server needing to parse JSON
|
||||||
|
// This is mostly to simplify the backend server's need for error handling
|
||||||
|
webSocket.send('requestHistoryGraph')
|
||||||
|
|
||||||
// Hide the activation link to avoid multiple requests
|
// Hide the activation link to avoid multiple requests
|
||||||
document.getElementById('big-graph-mobile-load-request').style.display = 'none'
|
document.getElementById('big-graph-mobile-load-request').style.display = 'none'
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
"pingAll": 3000,
|
"pingAll": 3000,
|
||||||
"connectTimeout": 2500
|
"connectTimeout": 2500
|
||||||
},
|
},
|
||||||
"logToDatabase": false,
|
"logToDatabase": true,
|
||||||
"graphDuration": 86400000
|
"graphDuration": 86400000
|
||||||
}
|
}
|
||||||
|
30
lib/app.js
30
lib/app.js
@ -2,6 +2,7 @@ const Database = require('./database')
|
|||||||
const MojangUpdater = require('./mojang')
|
const MojangUpdater = require('./mojang')
|
||||||
const PingController = require('./ping')
|
const PingController = require('./ping')
|
||||||
const Server = require('./server')
|
const Server = require('./server')
|
||||||
|
const MessageOf = require('./message')
|
||||||
|
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
const minecraftVersions = require('../minecraft_versions')
|
const minecraftVersions = require('../minecraft_versions')
|
||||||
@ -36,7 +37,8 @@ class App {
|
|||||||
|
|
||||||
handleClientConnection = (client) => {
|
handleClientConnection = (client) => {
|
||||||
if (config.logToDatabase) {
|
if (config.logToDatabase) {
|
||||||
client.on('requestHistoryGraph', () => {
|
client.on('message', (message) => {
|
||||||
|
if (message === 'requestHistoryGraph') {
|
||||||
// Send historical graphData built from all serverRegistrations
|
// Send historical graphData built from all serverRegistrations
|
||||||
const graphData = {}
|
const graphData = {}
|
||||||
|
|
||||||
@ -44,11 +46,18 @@ class App {
|
|||||||
graphData[serverRegistration.serverId] = serverRegistration.graphData
|
graphData[serverRegistration.serverId] = serverRegistration.graphData
|
||||||
})
|
})
|
||||||
|
|
||||||
client.emit('historyGraph', graphData)
|
// Send graphData in object wrapper to avoid needing to explicity filter
|
||||||
|
// any header data being appended by #MessageOf since the graph data is fed
|
||||||
|
// directly into the flot.js graphing system
|
||||||
|
client.send(MessageOf('historyGraph', {
|
||||||
|
graphData: graphData
|
||||||
|
}))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
client.emit('setPublicConfig', (() => {
|
const initMessage = {
|
||||||
|
config: (() => {
|
||||||
// Remap minecraftVersion entries into name values
|
// Remap minecraftVersion entries into name values
|
||||||
const minecraftVersionNames = {}
|
const minecraftVersionNames = {}
|
||||||
Object.keys(minecraftVersions).forEach(function (key) {
|
Object.keys(minecraftVersions).forEach(function (key) {
|
||||||
@ -62,17 +71,12 @@ class App {
|
|||||||
minecraftVersions: minecraftVersionNames,
|
minecraftVersions: minecraftVersionNames,
|
||||||
isGraphVisible: config.logToDatabase
|
isGraphVisible: config.logToDatabase
|
||||||
}
|
}
|
||||||
})())
|
})(),
|
||||||
|
mojangServices: this.mojangUpdater.getLastUpdate(),
|
||||||
|
servers: this.serverRegistrations.map(serverRegistration => serverRegistration.getPingHistory())
|
||||||
|
}
|
||||||
|
|
||||||
// Send last Mojang update, if any
|
client.send(MessageOf('init', initMessage))
|
||||||
this.mojangUpdater.sendLastUpdate(client)
|
|
||||||
|
|
||||||
// Send pingHistory of all ServerRegistrations
|
|
||||||
client.emit('add', this.serverRegistrations.map(serverRegistration => serverRegistration.getPingHistory()))
|
|
||||||
|
|
||||||
// Always send last
|
|
||||||
// This tells the frontend to do final processing and render
|
|
||||||
client.emit('syncComplete')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
lib/message.js
Normal file
6
lib/message.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = function MessageOf (name, data) {
|
||||||
|
return JSON.stringify({
|
||||||
|
message: name,
|
||||||
|
...data
|
||||||
|
})
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
const request = require('request')
|
const request = require('request')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
const MessageOf = require('./message')
|
||||||
|
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
|
|
||||||
@ -65,14 +66,12 @@ class MojangUpdater {
|
|||||||
if (this._hasUpdated) {
|
if (this._hasUpdated) {
|
||||||
this._hasUpdated = false
|
this._hasUpdated = false
|
||||||
|
|
||||||
this._app.server.broadcast('updateMojangServices', this._services)
|
this._app.server.broadcast(MessageOf('updateMojangServices', this._services))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendLastUpdate (client) {
|
getLastUpdate () {
|
||||||
if (this._services) {
|
return this._services
|
||||||
client.emit('updateMojangServices', this._services)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleServiceUpdate (url, color) {
|
handleServiceUpdate (url, color) {
|
||||||
|
@ -4,6 +4,7 @@ const minecraftJavaPing = require('mc-ping-updated')
|
|||||||
const minecraftBedrockPing = require('mcpe-ping-fixed')
|
const minecraftBedrockPing = require('mcpe-ping-fixed')
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
const MessageOf = require('./message')
|
||||||
|
|
||||||
const config = require('../config')
|
const config = require('../config')
|
||||||
|
|
||||||
@ -125,7 +126,9 @@ class PingController {
|
|||||||
// Generate a combined update payload
|
// Generate a combined update payload
|
||||||
// This includes any modified fields and flags used by the frontend
|
// This includes any modified fields and flags used by the frontend
|
||||||
// This will not be cached and can contain live metadata
|
// This will not be cached and can contain live metadata
|
||||||
this._app.server.broadcast('update', serverRegistration.getUpdate(timestamp, resp, err, version, updateHistoryGraph))
|
const updateMessage = serverRegistration.getUpdate(timestamp, resp, err, version, updateHistoryGraph)
|
||||||
|
|
||||||
|
this._app.server.broadcast(MessageOf('updateServer', updateMessage))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
const http = require('http')
|
const http = require('http')
|
||||||
|
|
||||||
|
const WebSocket = require('ws')
|
||||||
const finalHttpHandler = require('finalhandler')
|
const finalHttpHandler = require('finalhandler')
|
||||||
const serveStatic = require('serve-static')
|
const serveStatic = require('serve-static')
|
||||||
const io = require('socket.io')
|
|
||||||
|
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
|
||||||
@ -12,52 +12,65 @@ function getRemoteAddr (req) {
|
|||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
constructor (clientSocketHandler) {
|
constructor (clientSocketHandler) {
|
||||||
this._clientSocketHandler = clientSocketHandler
|
this.createHttpServer()
|
||||||
this._connectedSockets = 0
|
this.createWebSocketServer(clientSocketHandler)
|
||||||
|
}
|
||||||
|
|
||||||
this._http = http.createServer(this.handleHttpRequest)
|
createHttpServer () {
|
||||||
|
const distServeStatic = serveStatic('dist/')
|
||||||
|
const faviconsServeStatic = serveStatic('favicons/')
|
||||||
|
|
||||||
this._distServeStatic = serveStatic('dist/')
|
this._http = http.createServer((req, res) => {
|
||||||
this._faviconsServeStatic = serveStatic('favicons/')
|
logger.log('info', '%s requested: %s', getRemoteAddr(req), req.url)
|
||||||
|
|
||||||
|
// Attempt to handle req using distServeStatic, otherwise fail over to faviconServeStatic
|
||||||
|
// If faviconServeStatic fails, pass to finalHttpHandler to terminate
|
||||||
|
distServeStatic(req, res, () => {
|
||||||
|
faviconsServeStatic(req, res, finalHttpHandler(req, res))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
createWebSocketServer (proxyClientSocketHandler) {
|
||||||
|
this._wss = new WebSocket.Server({
|
||||||
|
server: this._http
|
||||||
|
})
|
||||||
|
|
||||||
|
this._wss.on('connection', (client, req) => {
|
||||||
|
logger.log('info', '%s connected, total clients: %d', getRemoteAddr(req), this.getConnectedClients())
|
||||||
|
|
||||||
|
// Bind disconnect event for logging
|
||||||
|
client.on('close', () => {
|
||||||
|
logger.log('info', '%s disconnected, total clients: %d', getRemoteAddr(req), this.getConnectedClients())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Pass client off to proxy handler
|
||||||
|
proxyClientSocketHandler(client)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
listen (host, port) {
|
listen (host, port) {
|
||||||
this._http.listen(port, host)
|
this._http.listen(port, host)
|
||||||
|
|
||||||
this._io = io.listen(this._http)
|
|
||||||
this._io.on('connect', this.handleClientSocket)
|
|
||||||
|
|
||||||
logger.log('info', 'Started on %s:%d', host, port)
|
logger.log('info', 'Started on %s:%d', host, port)
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast (event, payload) {
|
broadcast (payload) {
|
||||||
this._io.sockets.emit(event, payload)
|
this._wss.clients.forEach(client => {
|
||||||
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHttpRequest = (req, res) => {
|
|
||||||
logger.log('info', '%s requested: %s', getRemoteAddr(req), req.url)
|
|
||||||
|
|
||||||
// Attempt to handle req using distServeStatic, otherwise fail over to faviconServeStatic
|
|
||||||
// If faviconServeStatic fails, pass to finalHttpHandler to terminate
|
|
||||||
this._distServeStatic(req, res, () => {
|
|
||||||
this._faviconsServeStatic(req, res, finalHttpHandler(req, res))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
handleClientSocket = (client) => {
|
getConnectedClients () {
|
||||||
this._connectedSockets++
|
let count = 0
|
||||||
|
this._wss.clients.forEach(client => {
|
||||||
logger.log('info', '%s connected, total clients: %d', getRemoteAddr(client.request), this._connectedSockets)
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
|
count++
|
||||||
// Bind disconnect event for logging
|
}
|
||||||
client.on('disconnect', () => {
|
|
||||||
this._connectedSockets--
|
|
||||||
|
|
||||||
logger.log('info', '%s disconnected, total clients: %d', getRemoteAddr(client.request), this._connectedSockets)
|
|
||||||
})
|
})
|
||||||
|
return count
|
||||||
// Pass client off to proxy handler
|
|
||||||
this._clientSocketHandler(client)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
package-lock.json
generated
30
package-lock.json
generated
@ -1836,6 +1836,15 @@
|
|||||||
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
|
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bufferutil": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"node-gyp-build": "~3.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bufferview": {
|
"bufferview": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/bufferview/-/bufferview-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/bufferview/-/bufferview-1.0.1.tgz",
|
||||||
@ -5876,6 +5885,12 @@
|
|||||||
"integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==",
|
"integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node-gyp-build": {
|
||||||
|
"version": "3.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz",
|
||||||
|
"integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"node-libs-browser": {
|
"node-libs-browser": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
|
||||||
@ -8782,6 +8797,15 @@
|
|||||||
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
"integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"version": "5.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz",
|
||||||
|
"integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"node-gyp-build": "~3.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"util": {
|
"util": {
|
||||||
"version": "0.11.1",
|
"version": "0.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||||
@ -9041,9 +9065,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "7.2.3",
|
"version": "7.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.5.tgz",
|
||||||
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
|
"integrity": "sha512-C34cIU4+DB2vMyAbmEKossWq2ZQDr6QEyuuCzWrM9zfw1sGc0mYiJ0UnG9zzNykt49C2Fi34hvr2vssFQRS6EA=="
|
||||||
},
|
},
|
||||||
"xml-name-validator": {
|
"xml-name-validator": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
"mcpe-ping-fixed": "0.0.3",
|
"mcpe-ping-fixed": "0.0.3",
|
||||||
"request": "2.88.2",
|
"request": "2.88.2",
|
||||||
"serve-static": "^1.14.1",
|
"serve-static": "^1.14.1",
|
||||||
"socket.io": "2.3.0",
|
|
||||||
"sqlite3": "4.1.1",
|
"sqlite3": "4.1.1",
|
||||||
"winston": "^2.0.0"
|
"winston": "^2.0.0",
|
||||||
|
"ws": "^7.2.5"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@ -44,5 +44,9 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "eslint assets/js/*.js && parcel build assets/html/index.html",
|
"build": "eslint assets/js/*.js && parcel build assets/html/index.html",
|
||||||
"dev": "parcel build assets/html/index.html --no-minify"
|
"dev": "parcel build assets/html/index.html --no-minify"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": "^5.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user