Add API to get server status information.

This commit is contained in:
Daniel Scalzi 2020-08-27 19:37:41 -04:00
parent feb9b98b2a
commit 3838729da7
No known key found for this signature in database
GPG Key ID: D18EA3FB4B142A57
5 changed files with 395 additions and 141 deletions

247
package-lock.json generated
View File

@ -1541,9 +1541,9 @@
"dev": true
},
"@types/react": {
"version": "16.9.46",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.46.tgz",
"integrity": "sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg==",
"version": "16.9.48",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.48.tgz",
"integrity": "sha512-4ykBVswgYitPGMXFRxJCHkxJDU2rjfU3/zw67f8+dB7sNdVJXsrwqoYxz/stkAucymnEEbRPFmX7Ce5Mc/kJCw==",
"dev": true,
"requires": {
"@types/prop-types": "*",
@ -1551,9 +1551,9 @@
},
"dependencies": {
"csstype": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz",
"integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz",
"integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==",
"dev": true
}
}
@ -1745,12 +1745,12 @@
"dev": true
},
"@typescript-eslint/eslint-plugin": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.0.tgz",
"integrity": "sha512-Bbeg9JAnSzZ85Y0gpInZscSpifA6SbEgRryaKdP5ZlUjhTKsvZS4GUIE6xAZCjhNTrf4zXXsySo83ZdHL7it0w==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz",
"integrity": "sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "3.10.0",
"@typescript-eslint/experimental-utils": "3.10.1",
"debug": "^4.1.1",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
@ -1759,45 +1759,45 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.0.tgz",
"integrity": "sha512-e5ZLSTuXgqC/Gq3QzK2orjlhTZVXzwxDujQmTBOM1NIVBZgW3wiIZjaXuVutk9R4UltFlwC9UD2+bdxsA7yyNg==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz",
"integrity": "sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/types": "3.10.0",
"@typescript-eslint/typescript-estree": "3.10.0",
"@typescript-eslint/types": "3.10.1",
"@typescript-eslint/typescript-estree": "3.10.1",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.0.tgz",
"integrity": "sha512-iJyf3f2HVwscvJR7ySGMXw2DJgIAPKEz8TeU17XVKzgJRV4/VgCeDFcqLzueRe7iFI2gv+Tln4AV88ZOnsCNXg==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.10.1.tgz",
"integrity": "sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "3.10.0",
"@typescript-eslint/types": "3.10.0",
"@typescript-eslint/typescript-estree": "3.10.0",
"@typescript-eslint/experimental-utils": "3.10.1",
"@typescript-eslint/types": "3.10.1",
"@typescript-eslint/typescript-estree": "3.10.1",
"eslint-visitor-keys": "^1.1.0"
}
},
"@typescript-eslint/types": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.0.tgz",
"integrity": "sha512-ktUWSa75heQNwH85GRM7qP/UUrXqx9d6yIdw0iLO9/uE1LILW+i+3+B64dUodUS2WFWLzKTlwfi9giqrODibWg==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.10.1.tgz",
"integrity": "sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.0.tgz",
"integrity": "sha512-yjuY6rmVHRhcUKgXaSPNVloRueGWpFNhxR5EQLzxXfiFSl1U/+FBqHhbaGwtPPEgCSt61QNhZgiFjWT27bgAyw==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz",
"integrity": "sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==",
"dev": true,
"requires": {
"@typescript-eslint/types": "3.10.0",
"@typescript-eslint/visitor-keys": "3.10.0",
"@typescript-eslint/types": "3.10.1",
"@typescript-eslint/visitor-keys": "3.10.1",
"debug": "^4.1.1",
"glob": "^7.1.6",
"is-glob": "^4.0.1",
@ -1807,9 +1807,9 @@
}
},
"@typescript-eslint/visitor-keys": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.0.tgz",
"integrity": "sha512-g4qftk8lWb/rHZe9uEp8oZSvsJhUvR2cfp7F7qE6DyUD2SsovEs8JDQTRP1xHzsD+pERsEpYNqkDgQXW6+ob5A==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz",
"integrity": "sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
@ -4663,8 +4663,7 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
"optional": true
"dev": true
},
"eslint": {
"version": "7.7.0",
@ -7260,55 +7259,12 @@
"dev": true
},
"log-symbols": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
"integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
"integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
"dev": true,
"requires": {
"chalk": "^2.4.2"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
"chalk": "^4.0.0"
}
},
"logform": {
@ -7724,23 +7680,23 @@
"integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="
},
"mocha": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.1.tgz",
"integrity": "sha512-p7FuGlYH8t7gaiodlFreseLxEmxTgvyG9RgPHODFPySNhwUehu8NIb0vdSt3WFckSneswZ0Un5typYcWElk7HQ==",
"version": "8.1.2",
"resolved": "https://registry.npmjs.org/mocha/-/mocha-8.1.2.tgz",
"integrity": "sha512-I8FRAcuACNMLQn3lS4qeWLxXqLvGf6r2CaLstDpZmMUUSmvW6Cnm1AuHxgbc7ctZVRcfwspCRbDHymPsi3dkJw==",
"dev": true,
"requires": {
"ansi-colors": "4.1.1",
"browser-stdout": "1.3.1",
"chokidar": "3.3.1",
"debug": "3.2.6",
"chokidar": "3.4.2",
"debug": "4.1.1",
"diff": "4.0.2",
"escape-string-regexp": "1.0.5",
"find-up": "4.1.0",
"escape-string-regexp": "4.0.0",
"find-up": "5.0.0",
"glob": "7.1.6",
"growl": "1.10.5",
"he": "1.2.0",
"js-yaml": "3.13.1",
"log-symbols": "3.0.0",
"js-yaml": "3.14.0",
"log-symbols": "4.0.0",
"minimatch": "3.0.4",
"ms": "2.1.2",
"object.assign": "4.1.0",
@ -7797,9 +7753,9 @@
}
},
"chokidar": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
"integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
"integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
"dev": true,
"requires": {
"anymatch": "~3.1.1",
@ -7809,7 +7765,7 @@
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.3.0"
"readdirp": "~3.4.0"
}
},
"cliui": {
@ -7823,21 +7779,6 @@
"wrap-ansi": "^5.1.0"
}
},
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@ -7847,6 +7788,16 @@
"to-regex-range": "^5.0.1"
}
},
"find-up": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"requires": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
}
},
"fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
@ -7878,48 +7829,40 @@
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"js-yaml": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
"integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
"locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
"p-locate": "^5.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"p-limit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
"integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
"p-limit": "^3.0.2"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"readdirp": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
"integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
"integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
"dev": true,
"requires": {
"picomatch": "^2.0.7"
"picomatch": "^2.2.1"
}
},
"serialize-javascript": {
@ -7994,6 +7937,40 @@
"requires": {
"locate-path": "^3.0.0"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
}
}
},

View File

@ -57,7 +57,7 @@
"@types/lodash": "^4.14.160",
"@types/mocha": "^8.0.3",
"@types/node": "^12.12.54",
"@types/react": "^16.9.46",
"@types/react": "^16.9.48",
"@types/react-dom": "^16.9.8",
"@types/react-redux": "^7.1.9",
"@types/react-transition-group": "^4.4.0",
@ -65,8 +65,8 @@
"@types/tar-fs": "^2.0.0",
"@types/triple-beam": "^1.3.2",
"@types/winreg": "^1.2.30",
"@typescript-eslint/eslint-plugin": "^3.10.0",
"@typescript-eslint/parser": "^3.10.0",
"@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1",
"chai": "^4.2.0",
"cross-env": "^7.0.2",
"electron": "^9.2.1",
@ -77,7 +77,7 @@
"eslint": "^7.7.0",
"eslint-plugin-react": "^7.20.6",
"helios-distribution-types": "1.0.0-pre.1",
"mocha": "^8.1.1",
"mocha": "^8.1.2",
"nock": "^13.0.4",
"react": "^16.13.0",
"react-dom": "^16.13.0",

View File

@ -0,0 +1,262 @@
/* eslint-disable no-control-regex */
import { connect } from 'net'
import { LoggerUtil } from 'common/logging/loggerutil'
const logger = LoggerUtil.getLogger('ServerStatusUtil')
export interface ServerStatus {
version: {
name: string
protocol: number
}
players: {
max: number
online: number
sample: {
name: string
id: string
}[]
}
description: {
text: string
}
favicon: string
modinfo?: { // Only for modded servers
type: string // Ex. FML
modList: {
modid: string
version: string
}[]
}
}
/**
* Utility Class to construct a packet conforming to Minecraft's
* protocol. All data types are BE except VarInt and VarLong.
*
* @see https://wiki.vg/Protocol
*/
class ServerBoundPacket {
private buffer: number[]
protected constructor() {
this.buffer = []
}
public static build(): ServerBoundPacket {
return new ServerBoundPacket()
}
/**
* Packet is prefixed with its data length as a VarInt.
*
* @see https://wiki.vg/Protocol#Packet_format
*/
public toBuffer(): Buffer {
const finalizedPacket = new ServerBoundPacket()
finalizedPacket.writeVarInt(this.buffer.length)
finalizedPacket.writeBytes(...this.buffer)
return Buffer.from(finalizedPacket.buffer)
}
public writeBytes(...bytes: number[]): ServerBoundPacket {
this.buffer.push(...bytes)
return this
}
/**
* @see https://wiki.vg/Protocol#VarInt_and_VarLong
*/
public writeVarInt(value: number): ServerBoundPacket {
do {
let temp = value & 0b01111111
value >>>= 7
if (value != 0) {
temp |= 0b10000000
}
this.writeBytes(temp)
} while (value != 0)
return this
}
/**
* Strings are prefixed with their length as a VarInt.
*
* @see https://wiki.vg/Protocol#Data_types
*/
public writeString(string: string): ServerBoundPacket {
this.writeVarInt(string.length)
for (let i=0; i<string.length; i++) {
this.writeBytes(string.codePointAt(i)!)
}
return this
}
public writeUnsignedShort(short: number): ServerBoundPacket {
const buf = Buffer.alloc(2)
buf.writeUInt16BE(short, 0)
this.writeBytes(...buf)
return this
}
}
/**
* Utility Class to read a client-bound packet conforming to
* Minecraft's protocol. All data types are BE except VarInt
* and VarLong.
*
* @see https://wiki.vg/Protocol
*/
class ClientBoundPacket {
private buffer: number[]
constructor(buffer: Buffer) {
this.buffer = [...buffer]
}
public readByte(): number {
return this.buffer.shift()!
}
public readBytes(length: number): number[] {
const value = this.buffer.slice(0, length)
this.buffer.splice(0, length)
return value
}
public readVarInt(): number {
let numRead = 0
let result = 0
let read
do {
read = this.readByte()
const value = (read & 0b01111111)
result |= (value << (7 * numRead))
numRead++
if (numRead > 5) {
throw new Error('VarInt is too big')
}
} while ((read & 0b10000000) != 0)
return result
}
public readString(): string {
const length = this.readVarInt()
const data = this.readBytes(length)
let value = ''
for (let i=0; i<data.length; i++) {
value += String.fromCharCode(data[i])
}
return value
}
}
/**
* Get the handshake packet.
*
* @param protocol The client's protocol version.
* @param address The server address.
* @param port The server port.
*
* @see https://wiki.vg/Server_List_Ping#Handshake
*/
function getHandshakePacket(protocol: number, address: string, port: number): Buffer {
return ServerBoundPacket.build()
.writeVarInt(0x00) // Packet Id
.writeVarInt(protocol)
.writeString(address)
.writeUnsignedShort(port)
.writeVarInt(1) // State, 1 = status
.toBuffer()
}
/**
* Get the request packet.
*
* @see https://wiki.vg/Server_List_Ping#Request
*/
function getRequestPacket(): Buffer {
return ServerBoundPacket.build()
.writeVarInt(0x00)
.toBuffer()
}
export function getServerStatus(protocol: number, address: string, port = 25565): Promise<ServerStatus | null> {
return new Promise((resolve, reject) => {
const socket = connect(port, address, () => {
socket.write(getHandshakePacket(protocol, address, port))
socket.write(getRequestPacket())
})
socket.setTimeout(2500, () => {
socket.destroy()
logger.error(`Server Status Socket timed out (${address}:${port})`)
reject(new Error(`Server Status Socket timed out (${address}:${port})`))
})
socket.on('data', (data) => {
const inboundPacket = new ClientBoundPacket(data)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const packetLength = inboundPacket.readVarInt() // First VarInt is packet length.
const packetType = inboundPacket.readVarInt() // Second VarInt is packet type.
if(packetType !== 0x00) {
// TODO
socket.destroy()
reject(new Error(`Invalid response. Expected packet type ${0x00}, received ${packetType}!`))
return
}
const res = inboundPacket.readString() // Remainder of Buffer is the server status json.
socket.end()
resolve(JSON.parse(res))
})
socket.on('error', (err: NodeJS.ErrnoException) => {
socket.destroy()
if(err.code === 'ENOTFOUND') {
// ENOTFOUND = Unable to resolve.
logger.error(`Server ${address}:${port} not found!`)
resolve(null)
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)
return
} else {
logger.error(`Error trying to pull server status (${address}:${port}})`, err)
resolve(null)
return
}
})
})
}

View File

@ -18,6 +18,7 @@ import Overlay from './overlay/Overlay'
import { OverlayPushAction, OverlayActionDispatch } from '../redux/actions/overlayActions'
import { DistributionAPI } from 'common/distribution/distribution'
import { getServerStatus } from 'common/util/ServerStatusUtil'
import './Application.css'
@ -129,6 +130,8 @@ class Application extends React.Component<ApplicationProps & typeof mapDispatch,
const distro = new DistributionAPI('C:\\Users\\user\\AppData\\Roaming\\Helios Launcher')
const x = await distro.testLoad()
console.log(x)
const serverStatus = await getServerStatus(47, 'mc.westeroscraft.com', 25565)
console.log(serverStatus)
}
})
this.props.pushGenericOverlay({

View File

@ -2,6 +2,8 @@ import * as React from 'react'
import { connect } from 'react-redux'
import { OverlayActionDispatch } from '../../../redux/actions/overlayActions'
import { LoggerUtil } from 'common/logging/loggerutil'
import './GenericOverlay.css'
export interface GenericOverlayProps {
@ -22,6 +24,8 @@ type InternalGenericOverlayProps = GenericOverlayProps & typeof mapDispatch
class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
private readonly logger = LoggerUtil.getLogger('GenericOverlay')
private getAcknowledgeText = (): string => {
return this.props.acknowledgeText || 'OK'
}
@ -32,14 +36,22 @@ class GenericOverlay extends React.Component<InternalGenericOverlayProps> {
private onAcknowledgeClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
if(this.props.acknowledgeCallback) {
await this.props.acknowledgeCallback(event)
try {
await this.props.acknowledgeCallback(event)
} catch(err) {
this.logger.error('Uncaught error in acknowledgement', err)
}
}
this.props.popOverlayContent()
}
private onDismissClick = async (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): Promise<void> => {
if(this.props.dismissCallback) {
await this.props.dismissCallback(event)
try {
await this.props.dismissCallback(event)
} catch(err) {
this.logger.error('Uncaught error in dismission', err)
}
}
this.props.popOverlayContent()
}