mirror of
https://github.com/dscalzi/HeliosLauncher.git
synced 2024-12-22 11:42:14 -08:00
Show player count on landing page.
The server status needs to be stored in the redux store as it needs to be determined before mounting the Landing component.
This commit is contained in:
parent
cb68c34abe
commit
67e42ead78
63
package-lock.json
generated
63
package-lock.json
generated
@ -1362,14 +1362,20 @@
|
||||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.0.tgz",
|
||||
"integrity": "sha512-bfL5365QSCmH6cPeFT7Ywclj8C7LiF7sO6mUGzZhtAMV7iID1Euq6740u/SRi4C80NOnVz/CEfK8/HO+nCAPJg==",
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.1.3.tgz",
|
||||
"integrity": "sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.4",
|
||||
"debug": "^4.1.1",
|
||||
"espree": "^7.3.0",
|
||||
"globals": "^12.1.0",
|
||||
"ignore": "^4.0.6",
|
||||
"import-fresh": "^3.2.1",
|
||||
"js-yaml": "^3.13.1",
|
||||
"lodash": "^4.17.19",
|
||||
"minimatch": "^3.0.4",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -1385,11 +1391,26 @@
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "12.4.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
|
||||
"integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.8.1"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
|
||||
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1577,9 +1598,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.54",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.54.tgz",
|
||||
"integrity": "sha512-ge4xZ3vSBornVYlDnk7yZ0gK6ChHf/CHB7Gl1I0Jhah8DDnEQqBzgohYG4FX4p81TNirSETOiSyn+y1r9/IR6w=="
|
||||
"version": "12.12.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.55.tgz",
|
||||
"integrity": "sha512-Vd6xQUVvPCTm7Nx1N7XHcpX6t047ltm7TgcsOr4gFHjeYgwZevo+V7I1lfzHnj5BT5frztZ42+RTG4MwYw63dw=="
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.3",
|
||||
@ -4722,13 +4743,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"eslint": {
|
||||
"version": "7.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.0.tgz",
|
||||
"integrity": "sha512-qgtVyLZqKd2ZXWnLQA4NtVbOyH56zivOAdBFWE54RFkSZjokzNrcP4Z0eVWsZ+84ByXv+jL9k/wE1ENYe8xRFw==",
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.8.1.tgz",
|
||||
"integrity": "sha512-/2rX2pfhyUG0y+A123d0ccXtMm7DV7sH1m3lk9nk2DZ2LReq39FXHueR9xZwshE5MdfSf0xunSaMWRqyIA6M1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"@eslint/eslintrc": "^0.1.0",
|
||||
"@eslint/eslintrc": "^0.1.3",
|
||||
"ajv": "^6.10.0",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
@ -6072,18 +6093,18 @@
|
||||
}
|
||||
},
|
||||
"got": {
|
||||
"version": "11.5.2",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.5.2.tgz",
|
||||
"integrity": "sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==",
|
||||
"version": "11.6.0",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-11.6.0.tgz",
|
||||
"integrity": "sha512-ErhWb4IUjQzJ3vGs3+RR12NWlBDDkRciFpAkQ1LPUxi6OnwhGj07gQxjPsyIk69s7qMihwKrKquV6VQq7JNYLA==",
|
||||
"requires": {
|
||||
"@sindresorhus/is": "^3.0.0",
|
||||
"@sindresorhus/is": "^3.1.1",
|
||||
"@szmarczak/http-timer": "^4.0.5",
|
||||
"@types/cacheable-request": "^6.0.1",
|
||||
"@types/responselike": "^1.0.0",
|
||||
"cacheable-lookup": "^5.0.3",
|
||||
"cacheable-request": "^7.0.1",
|
||||
"decompress-response": "^6.0.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.0",
|
||||
"http2-wrapper": "^1.0.0-beta.5.2",
|
||||
"lowercase-keys": "^2.0.0",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"responselike": "^2.0.0"
|
||||
@ -11440,9 +11461,9 @@
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz",
|
||||
"integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz",
|
||||
"integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
@ -11564,9 +11585,9 @@
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.7",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
|
||||
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
|
||||
"integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"unicode-canonical-property-names-ecmascript": {
|
||||
|
10
package.json
10
package.json
@ -35,7 +35,7 @@
|
||||
"electron-updater": "^4.3.4",
|
||||
"fs-extra": "^9.0.1",
|
||||
"github-syntax-dark": "^0.5.0",
|
||||
"got": "^11.5.2",
|
||||
"got": "^11.6.0",
|
||||
"jquery": "^3.5.1",
|
||||
"lodash": "^4.17.20",
|
||||
"moment": "^2.27.0",
|
||||
@ -58,7 +58,7 @@
|
||||
"@types/jquery": "^3.5.1",
|
||||
"@types/lodash": "^4.14.161",
|
||||
"@types/mocha": "^8.0.3",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/node": "^12.12.55",
|
||||
"@types/react": "^16.9.49",
|
||||
"@types/react-dom": "^16.9.8",
|
||||
"@types/react-redux": "^7.1.9",
|
||||
@ -77,7 +77,7 @@
|
||||
"electron-devtools-installer": "^3.1.1",
|
||||
"electron-webpack": "^2.8.2",
|
||||
"electron-webpack-ts": "^4.0.1",
|
||||
"eslint": "^7.8.0",
|
||||
"eslint": "^7.8.1",
|
||||
"eslint-plugin-react": "^7.20.6",
|
||||
"helios-distribution-types": "1.0.0-pre.1",
|
||||
"mocha": "^8.1.3",
|
||||
@ -89,9 +89,9 @@
|
||||
"react-transition-group": "^4.4.1",
|
||||
"redux": "^4.0.5",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"ts-node": "^9.0.0",
|
||||
"tsconfig-paths": "^3.9.0",
|
||||
"typescript": "^3.9.7",
|
||||
"typescript": "^4.0.2",
|
||||
"webpack": "^4.44.1"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -53,13 +53,34 @@ export class HeliosDistribution {
|
||||
export class HeliosServer {
|
||||
|
||||
public readonly modules: HeliosModule[]
|
||||
public readonly hostname: string
|
||||
public readonly port: number
|
||||
|
||||
constructor(
|
||||
public readonly rawServer: Server
|
||||
) {
|
||||
const { hostname, port } = this.parseAddress()
|
||||
this.hostname = hostname
|
||||
this.port = port
|
||||
this.modules = rawServer.modules.map(m => new HeliosModule(m, rawServer.id))
|
||||
}
|
||||
|
||||
private parseAddress(): { hostname: string, port: number } {
|
||||
// Srv record lookup here if needed.
|
||||
if(this.rawServer.address.includes(':')) {
|
||||
const pieces = this.rawServer.address.split(':')
|
||||
const port = Number(pieces[1])
|
||||
|
||||
if(!Number.isInteger(port)) {
|
||||
throw new Error(`Malformed server address for ${this.rawServer.id}. Port must be an integer!`)
|
||||
}
|
||||
|
||||
return { hostname: pieces[0], port }
|
||||
} else {
|
||||
return { hostname: this.rawServer.address, port: 25565 }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class HeliosModule {
|
||||
|
@ -35,17 +35,17 @@ export interface ServerStatus {
|
||||
* Get the handshake packet.
|
||||
*
|
||||
* @param protocol The client's protocol version.
|
||||
* @param address The server address.
|
||||
* @param hostname The server hostname.
|
||||
* @param port The server port.
|
||||
*
|
||||
* @see https://wiki.vg/Server_List_Ping#Handshake
|
||||
*/
|
||||
function getHandshakePacket(protocol: number, address: string, port: number): Buffer {
|
||||
function getHandshakePacket(protocol: number, hostname: string, port: number): Buffer {
|
||||
|
||||
return ServerBoundPacket.build()
|
||||
.writeVarInt(0x00) // Packet Id
|
||||
.writeVarInt(protocol)
|
||||
.writeString(address)
|
||||
.writeString(hostname)
|
||||
.writeUnsignedShort(port)
|
||||
.writeVarInt(1) // State, 1 = status
|
||||
.toBuffer()
|
||||
@ -80,19 +80,19 @@ function unifyStatusResponse(resp: ServerStatus): ServerStatus {
|
||||
return resp
|
||||
}
|
||||
|
||||
export function getServerStatus(protocol: number, address: string, port = 25565): Promise<ServerStatus | null> {
|
||||
export function getServerStatus(protocol: number, hostname: string, port = 25565): Promise<ServerStatus | undefined> {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const socket = connect(port, address, () => {
|
||||
socket.write(getHandshakePacket(protocol, address, port))
|
||||
const socket = connect(port, hostname, () => {
|
||||
socket.write(getHandshakePacket(protocol, hostname, port))
|
||||
socket.write(getRequestPacket())
|
||||
})
|
||||
|
||||
socket.setTimeout(5000, () => {
|
||||
socket.destroy()
|
||||
logger.error(`Server Status Socket timed out (${address}:${port})`)
|
||||
reject(new Error(`Server Status Socket timed out (${address}:${port})`))
|
||||
logger.error(`Server Status Socket timed out (${hostname}:${port})`)
|
||||
reject(new Error(`Server Status Socket timed out (${hostname}:${port})`))
|
||||
})
|
||||
|
||||
const maxTries = 2
|
||||
@ -122,7 +122,7 @@ export function getServerStatus(protocol: number, address: string, port = 25565)
|
||||
|
||||
if(iterations > maxTries) {
|
||||
socket.destroy()
|
||||
reject(new Error(`Data read from ${address}:${port} exceeded ${maxTries} iterations, closing connection.`))
|
||||
reject(new Error(`Data read from ${hostname}:${port} exceeded ${maxTries} iterations, closing connection.`))
|
||||
return
|
||||
}
|
||||
++iterations
|
||||
@ -141,7 +141,7 @@ export function getServerStatus(protocol: number, address: string, port = 25565)
|
||||
const result = inboundPacket.readString()
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(result)
|
||||
const parsed: ServerStatus = JSON.parse(result)
|
||||
socket.end()
|
||||
resolve(unifyStatusResponse(parsed))
|
||||
} catch(err) {
|
||||
@ -164,17 +164,17 @@ export function getServerStatus(protocol: number, address: string, port = 25565)
|
||||
|
||||
if(err.code === 'ENOTFOUND') {
|
||||
// ENOTFOUND = Unable to resolve.
|
||||
logger.error(`Server ${address}:${port} not found!`)
|
||||
resolve(null)
|
||||
logger.error(`Server ${hostname}:${port} not found!`)
|
||||
resolve(undefined)
|
||||
return
|
||||
} else if(err.code === 'ECONNREFUSED') {
|
||||
// ECONNREFUSED = Unable to connect to port.
|
||||
logger.error(`Server ${address}:${port} refused to connect, is the port correct?`)
|
||||
resolve(null)
|
||||
logger.error(`Server ${hostname}:${port} refused to connect, is the port correct?`)
|
||||
resolve(undefined)
|
||||
return
|
||||
} else {
|
||||
logger.error(`Error trying to pull server status (${address}:${port})`, err)
|
||||
resolve(null)
|
||||
logger.error(`Error trying to pull server status (${hostname}:${port})`, err)
|
||||
resolve(undefined)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
@ -21,7 +21,7 @@ import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overl
|
||||
|
||||
import { LoggerUtil } from 'common/logging/loggerutil'
|
||||
import { DistributionAPI } from 'common/distribution/DistributionAPI'
|
||||
import { getServerStatus } from 'common/mojang/net/ServerStatusAPI'
|
||||
import { getServerStatus, ServerStatus } from 'common/mojang/net/ServerStatusAPI'
|
||||
import { Distribution } from 'helios-distribution-types'
|
||||
import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory'
|
||||
|
||||
@ -40,6 +40,7 @@ interface ApplicationProps {
|
||||
overlayQueue: OverlayPushAction<unknown>[]
|
||||
distribution: HeliosDistribution
|
||||
selectedServer: HeliosServer
|
||||
selectedServerStatus: ServerStatus
|
||||
}
|
||||
|
||||
interface ApplicationState {
|
||||
@ -79,7 +80,7 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
||||
}
|
||||
}
|
||||
|
||||
getViewElement(): JSX.Element {
|
||||
private getViewElement = (): JSX.Element => {
|
||||
// TODO debug remove
|
||||
console.log('loading', this.props.currentView, this.state.workingView)
|
||||
switch(this.state.workingView) {
|
||||
@ -89,7 +90,11 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
||||
</>
|
||||
case View.LANDING:
|
||||
return <>
|
||||
<Landing distribution={this.props.distribution} selectedServer={this.props.selectedServer} />
|
||||
<Landing
|
||||
distribution={this.props.distribution}
|
||||
selectedServer={this.props.selectedServer}
|
||||
selectedServerStatus={this.props.selectedServerStatus}
|
||||
/>
|
||||
</>
|
||||
case View.LOGIN:
|
||||
return <>
|
||||
@ -164,10 +169,19 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
||||
return
|
||||
} else {
|
||||
const distro = new HeliosDistribution(rawDisto)
|
||||
this.props.setDistribution(distro)
|
||||
// TODO TEMP USE CONFIG
|
||||
// TODO TODO TODO TODO
|
||||
this.props.setSelectedServer(distro.servers[0])
|
||||
const selectedServer: HeliosServer = distro.servers[0]
|
||||
const { hostname, port } = selectedServer
|
||||
let selectedServerStatus
|
||||
try {
|
||||
selectedServerStatus = await getServerStatus(47, hostname, port)
|
||||
} catch(err) {
|
||||
Application.logger.error('Failed to refresh server status', selectedServerStatus)
|
||||
}
|
||||
this.props.setDistribution(distro)
|
||||
this.props.setSelectedServer(selectedServer)
|
||||
this.props.setSelectedServerStatus(selectedServerStatus)
|
||||
}
|
||||
|
||||
// TODO Setup hook for distro refresh every ~ 5 mins.
|
||||
@ -186,16 +200,16 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
|
||||
})
|
||||
// TODO temp
|
||||
setTimeout(() => {
|
||||
//this.props.setView(View.WELCOME)
|
||||
this.props.pushGenericOverlay({
|
||||
title: 'Load Distribution',
|
||||
description: 'This is a test.',
|
||||
dismissible: false,
|
||||
acknowledgeCallback: async () => {
|
||||
const serverStatus = await getServerStatus(47, 'play.hypixel.net', 25565)
|
||||
console.log(serverStatus)
|
||||
}
|
||||
})
|
||||
// this.props.setView(View.WELCOME)
|
||||
// this.props.pushGenericOverlay({
|
||||
// title: 'Load Distribution',
|
||||
// description: 'This is a test.',
|
||||
// dismissible: false,
|
||||
// acknowledgeCallback: async () => {
|
||||
// const serverStatus = await getServerStatus(47, 'play.hypixel.net', 25565)
|
||||
// console.log(serverStatus)
|
||||
// }
|
||||
// })
|
||||
// this.props.pushGenericOverlay({
|
||||
// title: 'Test Title 2',
|
||||
// description: 'Test Description',
|
||||
|
@ -1,3 +1,19 @@
|
||||
.serverStatusWrapper-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
.serverStatusWrapper-enter-active {
|
||||
opacity: 1;
|
||||
transition: opacity 500ms, transform 500ms;
|
||||
}
|
||||
.serverStatusWrapper-exit {
|
||||
opacity: 1;
|
||||
}
|
||||
.serverStatusWrapper-exit-active {
|
||||
opacity: 0;
|
||||
transition: opacity 500ms, transform 500ms;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Landing View (Structural Styles) *
|
||||
|
@ -1,10 +1,12 @@
|
||||
import * as React from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { CSSTransition } from 'react-transition-group'
|
||||
|
||||
import { StoreType } from '../../redux/store'
|
||||
import { AppActionDispatch } from '../..//redux/actions/appActions'
|
||||
import { OverlayActionDispatch } from '../../redux/actions/overlayActions'
|
||||
import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory'
|
||||
import { ServerStatus, getServerStatus } from 'common/mojang/net/ServerStatusAPI'
|
||||
import { MojangStatus, MojangStatusColor } from 'common/mojang/rest/internal/MojangStatus'
|
||||
import { MojangResponse } from 'common/mojang/rest/internal/MojangResponse'
|
||||
import { MojangRestAPI } from 'common/mojang/rest/MojangRestAPI'
|
||||
@ -18,16 +20,19 @@ import './Landing.css'
|
||||
interface LandingProps {
|
||||
distribution: HeliosDistribution
|
||||
selectedServer: HeliosServer
|
||||
selectedServerStatus: ServerStatus
|
||||
}
|
||||
|
||||
interface LandingState {
|
||||
mojangStatuses: MojangStatus[]
|
||||
outdatedServerStatus: boolean
|
||||
}
|
||||
|
||||
const mapState = (state: StoreType): Partial<LandingProps> => {
|
||||
return {
|
||||
distribution: state.app.distribution!,
|
||||
selectedServer: state.app.selectedServer!
|
||||
selectedServer: state.app.selectedServer!,
|
||||
selectedServerStatus: state.app.selectedServerStatus!
|
||||
}
|
||||
}
|
||||
const mapDispatch = {
|
||||
@ -42,11 +47,13 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
private static readonly logger = LoggerUtil.getLogger('LandingTSX')
|
||||
|
||||
private mojangStatusInterval!: NodeJS.Timeout
|
||||
private serverStatusInterval!: NodeJS.Timeout
|
||||
|
||||
constructor(props: InternalLandingProps) {
|
||||
super(props)
|
||||
this.state = {
|
||||
mojangStatuses: []
|
||||
mojangStatuses: [],
|
||||
outdatedServerStatus: false
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,12 +67,21 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
await this.loadMojangStatuses()
|
||||
}, 300000)
|
||||
|
||||
this.serverStatusInterval = setInterval(async () => {
|
||||
Landing.logger.info('Refreshing selected server status..')
|
||||
this.setState({
|
||||
...this.state,
|
||||
outdatedServerStatus: true
|
||||
})
|
||||
}, 300000)
|
||||
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
|
||||
// Clean up intervals.
|
||||
clearInterval(this.mojangStatusInterval)
|
||||
clearInterval(this.serverStatusInterval)
|
||||
|
||||
}
|
||||
|
||||
@ -92,6 +108,34 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
|
||||
}
|
||||
|
||||
private syncServerStatus = async (): Promise<void> => {
|
||||
let serverStatus: ServerStatus | undefined
|
||||
|
||||
if(this.props.selectedServer != null) {
|
||||
const { hostname, port } = this.props.selectedServer
|
||||
try {
|
||||
serverStatus = await getServerStatus(
|
||||
47,
|
||||
hostname,
|
||||
port
|
||||
)
|
||||
} catch(err) {
|
||||
Landing.logger.error('Error while refreshing server status', err)
|
||||
}
|
||||
|
||||
} else {
|
||||
serverStatus = undefined
|
||||
}
|
||||
|
||||
this.props.setSelectedServerStatus(serverStatus)
|
||||
}
|
||||
private finishServerSync = async (): Promise<void> => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
outdatedServerStatus: false
|
||||
})
|
||||
}
|
||||
|
||||
private getMainMojangStatusColor = (): string => {
|
||||
const essential = this.state.mojangStatuses.filter(s => s.essential)
|
||||
|
||||
@ -137,9 +181,14 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
this.props.pushServerSelectOverlay({
|
||||
servers: this.props.distribution.servers,
|
||||
selectedId: this.props.selectedServer.rawServer.id,
|
||||
onSelection: (serverId: string) => {
|
||||
onSelection: async (serverId: string) => {
|
||||
Landing.logger.info('Server Selection Change:', serverId)
|
||||
this.props.setSelectedServer(this.props.distribution.getServerById(serverId)!)
|
||||
const next: HeliosServer = this.props.distribution.getServerById(serverId)!
|
||||
this.props.setSelectedServer(next)
|
||||
this.setState({
|
||||
...this.state,
|
||||
outdatedServerStatus: true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -152,6 +201,19 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
}
|
||||
}
|
||||
|
||||
private getSelectedServerStatusText = (): string => {
|
||||
return this.props.selectedServerStatus != null ? 'PLAYERS' : 'SERVER'
|
||||
}
|
||||
|
||||
private getSelectedServerCount = (): string => {
|
||||
if(this.props.selectedServerStatus != null) {
|
||||
const { online, max } = this.props.selectedServerStatus.players
|
||||
return `${online}/${max}`
|
||||
} else {
|
||||
return 'OFFLINE'
|
||||
}
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
return <>
|
||||
|
||||
@ -252,10 +314,21 @@ class Landing extends React.Component<InternalLandingProps, LandingState> {
|
||||
<div id="left">
|
||||
<div className="bot_wrapper">
|
||||
<div id="content">
|
||||
<div id="server_status_wrapper">
|
||||
<span className="bot_label" id="landingPlayerLabel">SERVER</span>
|
||||
<span id="player_count">OFFLINE</span>
|
||||
</div>
|
||||
|
||||
<CSSTransition
|
||||
in={!this.state.outdatedServerStatus}
|
||||
timeout={500}
|
||||
classNames="serverStatusWrapper"
|
||||
unmountOnExit
|
||||
onEnter={this.syncServerStatus}
|
||||
onExited={this.finishServerSync}
|
||||
>
|
||||
<div id="server_status_wrapper">
|
||||
<span className="bot_label" id="landingPlayerLabel">{this.getSelectedServerStatusText()}</span>
|
||||
<span id="player_count">{this.getSelectedServerCount()}</span>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
|
||||
<div className="bot_divider"></div>
|
||||
<div id="mojangStatusWrapper">
|
||||
<span className="bot_label">MOJANG STATUS</span>
|
||||
|
@ -10,7 +10,7 @@ import '../shared-select/SharedSelect.css'
|
||||
export interface ServerSelectOverlayProps {
|
||||
servers: HeliosServer[]
|
||||
selectedId: string
|
||||
onSelection: (serverId: string) => void
|
||||
onSelection: (serverId: string) => Promise<void>
|
||||
}
|
||||
|
||||
interface ServerSelectOverlayState {
|
||||
@ -36,7 +36,7 @@ class ServerSelectOverlay extends React.Component<InternalServerSelectOverlayPro
|
||||
|
||||
private onSelectClick = async (): Promise<void> => {
|
||||
try {
|
||||
this.props.onSelection(this.state.selectedId)
|
||||
await this.props.onSelection(this.state.selectedId)
|
||||
} catch(err) {
|
||||
this.logger.error('Uncaught error in server select confirmation.', err)
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ ReactDOM.render(
|
||||
overlayQueue={store.getState().overlayQueue}
|
||||
distribution={store.getState().app.distribution!}
|
||||
selectedServer={store.getState().app.selectedServer!}
|
||||
selectedServerStatus={store.getState().app.selectedServerStatus!}
|
||||
/>
|
||||
</Provider>
|
||||
</AppContainer>,
|
||||
|
@ -1,37 +1,51 @@
|
||||
import { Action } from 'redux'
|
||||
import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory'
|
||||
import { ServerStatus } from 'common/mojang/net/ServerStatusAPI'
|
||||
|
||||
export enum AppActionType {
|
||||
SetDistribution = 'SET_DISTRIBUTION',
|
||||
SetSelectedServer = 'SET_SELECTED_SERVER'
|
||||
SetSelectedServer = 'SET_SELECTED_SERVER',
|
||||
SetSelectedServerStatus = 'SET_SELECTED_SERVER_STATUS'
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface AppAction extends Action {}
|
||||
|
||||
export interface SetDistributionAction extends AppAction {
|
||||
payload: HeliosDistribution
|
||||
payload?: HeliosDistribution
|
||||
}
|
||||
|
||||
export interface SetSelectedServerAction extends AppAction {
|
||||
payload: HeliosServer
|
||||
payload?: HeliosServer
|
||||
}
|
||||
|
||||
export function setDistribution(distribution: HeliosDistribution): SetDistributionAction {
|
||||
export interface SetSelectedServerStatusAction extends AppAction {
|
||||
payload?: ServerStatus
|
||||
}
|
||||
|
||||
export function setDistribution(distribution?: HeliosDistribution): SetDistributionAction {
|
||||
return {
|
||||
type: AppActionType.SetDistribution,
|
||||
payload: distribution
|
||||
}
|
||||
}
|
||||
|
||||
export function setSelectedServer(server: HeliosServer): SetSelectedServerAction {
|
||||
export function setSelectedServer(server?: HeliosServer): SetSelectedServerAction {
|
||||
return {
|
||||
type: AppActionType.SetSelectedServer,
|
||||
payload: server
|
||||
}
|
||||
}
|
||||
|
||||
export const AppActionDispatch = {
|
||||
setDistribution: (d: HeliosDistribution): SetDistributionAction => setDistribution(d),
|
||||
setSelectedServer: (s: HeliosServer): SetSelectedServerAction => setSelectedServer(s)
|
||||
export function setSelectedServerStatus(serverStatus?: ServerStatus): SetSelectedServerStatusAction {
|
||||
return {
|
||||
type: AppActionType.SetSelectedServerStatus,
|
||||
payload: serverStatus
|
||||
}
|
||||
}
|
||||
|
||||
export const AppActionDispatch = {
|
||||
setDistribution: (d?: HeliosDistribution): SetDistributionAction => setDistribution(d),
|
||||
setSelectedServer: (s?: HeliosServer): SetSelectedServerAction => setSelectedServer(s),
|
||||
setSelectedServerStatus: (ss?: ServerStatus): SetSelectedServerStatusAction => setSelectedServerStatus(ss)
|
||||
}
|
@ -1,15 +1,18 @@
|
||||
import { AppActionType, AppAction, SetDistributionAction, SetSelectedServerAction } from '../actions/appActions'
|
||||
import { AppActionType, AppAction, SetDistributionAction, SetSelectedServerAction, SetSelectedServerStatusAction } from '../actions/appActions'
|
||||
import { Reducer } from 'redux'
|
||||
import { HeliosDistribution, HeliosServer } from 'common/distribution/DistributionFactory'
|
||||
import { ServerStatus } from 'common/mojang/net/ServerStatusAPI'
|
||||
|
||||
export interface AppState {
|
||||
distribution: HeliosDistribution | null
|
||||
selectedServer: HeliosServer | null
|
||||
distribution?: HeliosDistribution
|
||||
selectedServer?: HeliosServer
|
||||
selectedServerStatus?: ServerStatus
|
||||
}
|
||||
|
||||
const defaultAppState: AppState = {
|
||||
distribution: null,
|
||||
selectedServer: null
|
||||
distribution: undefined,
|
||||
selectedServer: undefined,
|
||||
selectedServerStatus: undefined
|
||||
}
|
||||
|
||||
const AppReducer: Reducer<AppState, AppAction> = (state = defaultAppState, action) => {
|
||||
@ -24,6 +27,11 @@ const AppReducer: Reducer<AppState, AppAction> = (state = defaultAppState, actio
|
||||
...state,
|
||||
selectedServer: (action as SetSelectedServerAction).payload
|
||||
}
|
||||
case AppActionType.SetSelectedServerStatus:
|
||||
return {
|
||||
...state,
|
||||
selectedServerStatus: (action as SetSelectedServerStatusAction).payload
|
||||
}
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user