Skip to content
Draft
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
13 changes: 9 additions & 4 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}
"plugins": [
[
"@babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}
4 changes: 4 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"pingAll": 3000,
"connectTimeout": 2500
},
"versions": {
"type": "mc-versions",
"updateInterval": 86400000
},
"logFailedPings": true,
"logToDatabase": false,
"graphDuration": 86400000,
Expand Down
10 changes: 2 additions & 8 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { TimeTracker } = require('./time')
const MessageOf = require('./message')

const config = require('../config')
const minecraftVersions = require('../minecraft_versions')
const versions = require('./versions')

class App {
serverRegistrations = []
Expand Down Expand Up @@ -57,19 +57,13 @@ class App {

const initMessage = {
config: (() => {
// Remap minecraftVersion entries into name values
const minecraftVersionNames = {}
Object.keys(minecraftVersions).forEach(function (key) {
minecraftVersionNames[key] = minecraftVersions[key].map(version => version.name)
})

// Send configuration data for rendering the page
return {
graphDurationLabel: config.graphDurationLabel || (Math.floor(config.graphDuration / (60 * 60 * 1000)) + 'h'),
graphMaxLength: TimeTracker.getMaxGraphDataLength(),
serverGraphMaxLength: TimeTracker.getMaxServerGraphDataLength(),
servers: this.serverRegistrations.map(serverRegistration => serverRegistration.getPublicData()),
minecraftVersions: minecraftVersionNames,
minecraftVersions: versions.getProvider().names,
isGraphVisible: config.logToDatabase
}
})(),
Expand Down
11 changes: 7 additions & 4 deletions lib/servers.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const { GRAPH_UPDATE_TIME_GAP, TimeTracker } = require('./time')
const { getPlayerCountOrNull } = require('./util')

const config = require('../config')
const minecraftVersions = require('../minecraft_versions')
const versions = require('./versions')

class ServerRegistration {
serverId
Expand Down Expand Up @@ -213,14 +213,17 @@ class ServerRegistration {
protocolIndex: 0
}
}
const protocolVersions = minecraftVersions[this.data.type]
const protocolVersions = versions.getProvider().protocolNumbers[this.data.type]
if (typeof this._nextProtocolIndex === 'undefined' || this._nextProtocolIndex + 1 >= protocolVersions.length) {
this._nextProtocolIndex = 0
} else {
this._nextProtocolIndex++
// Find index of next proto number (skip versions with same proto number)
const prevNumber = protocolVersions[this._nextProtocolIndex]
this._nextProtocolIndex = protocolVersions.findIndex(number => number > prevNumber)
}

return {
protocolId: protocolVersions[this._nextProtocolIndex].protocolId,
protocolId: protocolVersions[this._nextProtocolIndex],
protocolIndex: this._nextProtocolIndex
}
}
Expand Down
120 changes: 120 additions & 0 deletions lib/versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const fs = require('fs/promises')
const path = require('path')
const fetch = require('node-fetch')

const config = require('../config.json')

const DEFAULT_CONFIG = { type: 'remote', updateInterval: 86400000 }
const LOCAL_VERSIONS_PATH = path.resolve(__dirname, '../minecraft_versions.json')

/**
* The global versions provider.
*
* @type {VersionsProvider}
*/
let provider

class VersionsProvider {
// editionId -> [versionValue] mappings
names = {};
protocolNumbers = {};

setEditionData (edition, names, protocolNumbers) {
this.names[edition] = names
this.protocolNumbers[edition] = protocolNumbers
}

/**
* Updates the version data.
*
* @returns {Promise} a promise containing the update result
*/
async update () {}
}

class LocalVersionsProvider extends VersionsProvider {
async update () {
const raw = await fs.readFile(LOCAL_VERSIONS_PATH, 'utf8')
const editions = JSON.parse(raw)

for (const [edition, versions] of Object.entries(editions)) {
this.setEditionData(
edition,
versions.map(version => version.name),
versions.map(version => version.protocolId)
)
}
}
}

const REMOTE_EDITION_NAMES = {
java: 'PC',
bedrock: 'PE',
education: 'EDU'
}

class RemoteVersionsProvider extends VersionsProvider {
constructor (endpoint) {
super()
this.endpoint = endpoint
}

async update () {
const res = await fetch(this.endpoint)
const { editions } = await res.json()

for (const [rawEdition, data] of Object.entries(editions)) {
const edition = REMOTE_EDITION_NAMES[rawEdition]
const versions = data.versions
.filter(version => version.protocolNumber !== null)
// Sort versions chronologically (mimic local behavior)
.reverse()

if (edition === 'PC') {
// Ignore versions older than 1.7.2
// We cannot rely on protocol numbers since they are reused across versions.
const onePointSeven = versions.findIndex(version => version.name === '1.7.2')
versions.splice(0, onePointSeven)
}

this.setEditionData(
edition,
versions.map(version => version.name),
versions.map(version => version.protocolNumber)
)
}
}
}

module.exports = {
initProvider: async function () {
const { type, updateInterval } = config.versions || DEFAULT_CONFIG

switch (type) {
case 'local':
provider = new LocalVersionsProvider()
break
case 'mc-versions':
// See https://github.com/hugmanrique/mc-versions
provider = new RemoteVersionsProvider('https://raw.githubusercontent.com/hugmanrique/mc-versions/main/versions.json')
break
default:
throw new Error(`Unknown versions provider type "${type}"`)
}
if (updateInterval <= 0) {
throw new Error(`Invalid non-positive versions provider update interval: ${updateInterval}`)
}

async function update () {
await provider.update()
setTimeout(update, updateInterval) // Schedule next update
}
await update() // Wait for initial update (populates data)
},
getProvider: function () {
if (!provider) {
throw new Error('Versions provider has not been initialized')
}
return provider
}
}
17 changes: 10 additions & 7 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const ServerRegistration = require('./lib/servers')
const logger = require('./lib/logger')

const config = require('./config')
const versions = require('./lib/versions')
const servers = require('./servers')

const app = new App()
Expand All @@ -30,12 +31,14 @@ if (!config.serverGraphDuration) {
config.serverGraphDuration = 3 * 60 * 10000
}

if (!config.logToDatabase) {
logger.log('warn', 'Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.')
versions.initProvider().then(() => {
if (!config.logToDatabase) {
logger.log('warn', 'Database logging is not enabled. You can enable it by setting "logToDatabase" to true in config.json. This requires sqlite3 to be installed.')

app.handleReady()
} else {
app.loadDatabase(() => {
app.handleReady()
})
}
} else {
app.loadDatabase(() => {
app.handleReady()
})
}
}).catch(err => logger.error('Cannot load versions data', err))
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"finalhandler": "^1.1.2",
"mcpe-ping-fixed": "0.0.3",
"mcping-js": "^1.4.1",
"node-fetch": "^2.6.1",
"request": "2.88.2",
"serve-static": "^1.14.1",
"sqlite3": "4.2.0",
Expand Down
Loading