Skip to content

Adds a new Minecraft service type - #1055

Open
mwhax7 wants to merge 6 commits into
bastienwirtz:mainfrom
mwhax7:main
Open

Adds a new Minecraft service type#1055
mwhax7 wants to merge 6 commits into
bastienwirtz:mainfrom
mwhax7:main

Conversation

@mwhax7

@mwhax7 mwhax7 commented Jul 8, 2026

Copy link
Copy Markdown

Description

Adds a new Minecraft service type that displays the status of a Minecraft server using mcsrvstat.us API.

  • running / ❌ stopped indicator (with green/red dot)
  • 🖥️ Server software (e.g., Paper, Spigot)
  • 📦 Server version (e.g., 1.21.8)
  • 👥 Players online / max

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring

Checklist:

  • I've read & comply with the contributing guidelines.
  • I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
  • I have made corresponding changes to the documentation (README.md).
  • I've checked my modifications for any breaking changes, especially in the config.yml file.

Result:

⁉️ A working docker/implementation is already available if you'd like to test it: https://github.com/mwhax7/homer-minecraft (old homer version)

🚧 I know Homer has received many improvements since then, such as the new icon system and likely other enhancements as well. My implementation could certainly be improved to better match the current codebase and aesthetics, but I simply haven't taken the time to update it yet.

@tanasegabriel

Copy link
Copy Markdown
Collaborator

FYI, see #1060 for a proposed fix for the CI failures.

Comment thread docs/customservices.md Outdated
Comment thread src/components/services/Minecraft.vue Outdated
Comment thread src/components/services/Minecraft.vue
const port = this.item.port || 25565;

window
.fetch(`https://api.mcsrvstat.us/2/${host}:${port}`)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ?

@mwhax7 mwhax7 Jul 16, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

mwhax7 and others added 3 commits July 16, 2026 15:17
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>
Comment thread docs/customservices.md

```yaml
- name: "Minecraft"
url: "127.0.0.1:25565"

@tanasegabriel tanasegabriel Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 user

Maybe 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.

Comment thread .nvmrc
@@ -0,0 +1 @@
24

@tanasegabriel tanasegabriel Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
{{ software }} | v{{ version }} | {{ players.online }}/{{ players.max }} players
{{ details }}

That relies on a details computed, see below.

Comment on lines +31 to +39
data: () => ({
status: "",
software: "",
version: "",
players: {
online: 0,
max: 0,
},
}),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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";

Comment on lines +40 to +46
created() {
// Set up auto-update method for the scheduler
this.autoUpdateMethod = this.fetchServerStatus;

// Initial data fetch
this.fetchServerStatus();
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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();
},

Comment on lines +48 to +70
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";
});
},

@tanasegabriel tanasegabriel Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Suggested change
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";
}
},

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants