Adds a new Minecraft service type - #1055
Conversation
|
FYI, see #1060 for a proposed fix for the CI failures. |
| const port = this.item.port || 25565; | ||
|
|
||
| window | ||
| .fetch(`https://api.mcsrvstat.us/2/${host}:${port}`) |
There was a problem hiding this comment.
It might be worth mentionning in the card documentation that you rely on an external service.
Isn't it possible to fetch stats directly from the host:port ?
There was a problem hiding this comment.
Unfortunately not. Minecraft uses its own TCP protocol instead of HTTP, so direct queries aren't possible. An intermediary service such as mcsrvstat is therefore required, unless you have another idea to integrate this kind of request?
I know some Minecraft plugins can expose an HTTP API, but this would require additional server-side configuration and wouldn't work for every server (especially vanilla or servers where plugins cannot be installed). An intermediary service remains the only generic solution to query any Minecraft server without modifications.
Co-authored-by: Bastien Wirtz <bastien.wirtz@gmail.com>
Co-authored-by: Bastien Wirtz <bastien.wirtz@gmail.com>
Co-authored-by: Bastien Wirtz <bastien.wirtz@gmail.com>
|
|
||
| ```yaml | ||
| - name: "Minecraft" | ||
| url: "127.0.0.1:25565" |
There was a problem hiding this comment.
url (used when clicking on the card) and endpoint (used for the API calls, if missing, url is used) are the options that are commonly used here - can we use that approach?
I seems the card itself is using host and port, which are not documented here. I think the doc here is correct, but rather we should consider adjusting the service to match that.
That would also potentially allow people to define the external service used, instead of us being prescriptive here, so something like this:
- name: "Minecraft"
type: "Minecraft"
logo: "assets/tools/minecraft.png"
url: "127.0.0.1:25565"
endpoint: "https://api.mcsrvstat.us/3" # potentally we can default to this, if unset by the userMaybe also worth adding some notes, letting users know that this relies on an external service?
Minecraft servers speak their own TCP protocol rather than HTTP, so a browser cannot query them directly. This card therefore relies on an intermediary HTTP service, defaulting to mcsrvstat.us.
Important
This service relies on an external service, the address in url is sent to a third party every time the dashboard is loaded, and the lookup happens from their network rather than yours.
Note
software and version are optional in the API response and are free form text, so they are left out of the card when the server does not report them.
| @@ -0,0 +1 @@ | |||
| 24 | |||
There was a problem hiding this comment.
This can be removed, if you can sync your fork, builds should not be blocked by the absence of this.
| {{ item.subtitle }} | ||
| </template> | ||
| <template v-else-if="status === 'running'"> | ||
| {{ software }} | v{{ version }} | {{ players.online }}/{{ players.max }} players |
There was a problem hiding this comment.
The subtitle assumes both fields are always present, but they are optional in the API response and free form. Querying mc.hypixel.net right now returns no software at all, and version as the string "Requires MC 1.8 / 1.21", so this renders as Unknown | vRequires MC 1.8 / 1.21 | .... Worth building the line from whatever is actually present, and dropping the v prefix since version is not a version number.
| {{ software }} | v{{ version }} | {{ players.online }}/{{ players.max }} players | |
| {{ details }} |
That relies on a details computed, see below.
| data: () => ({ | ||
| status: "", | ||
| software: "", | ||
| version: "", | ||
| players: { | ||
| online: 0, | ||
| max: 0, | ||
| }, | ||
| }), |
There was a problem hiding this comment.
Two computed properties would help here. server normalises url into the bare host[:port] the status API expects, so people can write a normal URL. details renders only the fields the server actually reported.
| data: () => ({ | |
| status: "", | |
| software: "", | |
| version: "", | |
| players: { | |
| online: 0, | |
| max: 0, | |
| }, | |
| }), | |
| data: () => ({ | |
| status: "", | |
| software: "", | |
| version: "", | |
| players: { | |
| online: 0, | |
| max: 0, | |
| }, | |
| }), | |
| computed: { | |
| // The status API takes a bare `host[:port]`, so drop any scheme and path. | |
| server: function () { | |
| return (this.item.url || "") | |
| .replace(/^[a-z][a-z0-9+.-]*:\/\//i, "") | |
| .replace(/\/.*$/, ""); | |
| }, | |
| details: function () { | |
| const players = `${this.players.online}/${this.players.max} players`; | |
| return [this.software, this.version, players].filter(Boolean).join(" | "); | |
| }, | |
| }, |
This also needs const DEFAULT_API = "https://api.mcsrvstat.us/3";
| created() { | ||
| // Set up auto-update method for the scheduler | ||
| this.autoUpdateMethod = this.fetchServerStatus; | ||
|
|
||
| // Initial data fetch | ||
| this.fetchServerStatus(); | ||
| }, |
There was a problem hiding this comment.
Following up on my comment on the docs: this is where the default endpoint can live. The service.js mixin sets this.endpoint = this.item.endpoint || this.item.url in its own created, and mixin hooks run before the component's, so overriding it here gives a sensible default that the user can replace with endpoint.
| created() { | |
| // Set up auto-update method for the scheduler | |
| this.autoUpdateMethod = this.fetchServerStatus; | |
| // Initial data fetch | |
| this.fetchServerStatus(); | |
| }, | |
| created() { | |
| // Minecraft speaks its own TCP protocol, so an HTTP intermediary is required. | |
| // Point `endpoint` at a self hosted one to avoid the third party default. | |
| if (!this.item.endpoint) { | |
| this.endpoint = DEFAULT_API; | |
| } | |
| // Set up auto-update method for the scheduler | |
| this.autoUpdateMethod = this.fetchServerStatus; | |
| // Initial data fetch | |
| this.fetchServerStatus(); | |
| }, |
| fetchServerStatus: async function () { | ||
| const host = this.item.host || "127.0.0.1"; | ||
| const port = this.item.port || 25565; | ||
|
|
||
| window | ||
| .fetch(`https://api.mcsrvstat.us/2/${host}:${port}`) | ||
| .then((response) => response.json()) | ||
| .then((data) => { | ||
| if (data.online) { | ||
| this.status = "running"; | ||
| this.software = data.software || "Unknown"; | ||
| this.version = data.version || "N/A"; | ||
| this.players.online = data.players?.online || 0; | ||
| this.players.max = data.players?.max || 0; | ||
| } else { | ||
| this.status = "stopped"; | ||
| } | ||
| }) | ||
| .catch((e) => { | ||
| console.log(e); | ||
| this.status = "error"; | ||
| }); | ||
| }, |
There was a problem hiding this comment.
A few things bundled together here.
window.fetch bypasses the service.js mixin, so per item headers, useCredentials and successCodes are all skipped. Maybe that's fine for an external service though, so I'm open for pushback here!
Also, he 127.0.0.1 fallback is worth removin. The lookup happens on the API provider's network rather than yours, so an unset host means mcsrvstat resolves 127.0.0.1 from its own side instead of reaching your server. Failing loudly is more useful than silently querying the wrong thing, I'd say?
| fetchServerStatus: async function () { | |
| const host = this.item.host || "127.0.0.1"; | |
| const port = this.item.port || 25565; | |
| window | |
| .fetch(`https://api.mcsrvstat.us/2/${host}:${port}`) | |
| .then((response) => response.json()) | |
| .then((data) => { | |
| if (data.online) { | |
| this.status = "running"; | |
| this.software = data.software || "Unknown"; | |
| this.version = data.version || "N/A"; | |
| this.players.online = data.players?.online || 0; | |
| this.players.max = data.players?.max || 0; | |
| } else { | |
| this.status = "stopped"; | |
| } | |
| }) | |
| .catch((e) => { | |
| console.log(e); | |
| this.status = "error"; | |
| }); | |
| }, | |
| fetchServerStatus: async function () { | |
| if (!this.server) { | |
| console.error( | |
| `Minecraft: "${this.item.name}" is missing the url option`, | |
| ); | |
| this.status = "error"; | |
| return; | |
| } | |
| try { | |
| const data = await this.fetch(this.server); | |
| if (!data.online) { | |
| this.status = "stopped"; | |
| return; | |
| } | |
| this.status = "running"; | |
| // Both are optional and free form: plenty of servers report neither. | |
| this.software = data.software || ""; | |
| this.version = data.version || ""; | |
| this.players.online = data.players?.online || 0; | |
| this.players.max = data.players?.max || 0; | |
| } catch (e) { | |
| console.error(e); | |
| this.status = "error"; | |
| } | |
| }, |
Description
Adds a new
Minecraftservice type that displays the status of a Minecraft server using mcsrvstat.us API.Type of change
Checklist:
README.md).config.ymlfile.Result: