Skip to content
Merged
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
12 changes: 9 additions & 3 deletions src/app/home/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
</button>
</div>
<div class="home-container">
<img [src]="icon" alt="No Image Found">
<mat-card>
<mat-card-header>
<mat-card-title>
{{ serverIp }}
<mat-card-title style="text-align: center">
{{ serverIp }} <br />
{{ version }}
</mat-card-title>
</mat-card-header>
<mat-card-content>
Expand All @@ -39,7 +41,11 @@
{{ running ? "Stop" : "Start" }} Server
</button>
@if (error) {
<p style="color: red;">{{ error }}</p>
<p style="color: red">{{ error }}</p>
} @if (playerCount !== "undefined/undefined"){
<h2>Players: {{ playerCount }}</h2>
} @for (player of playerList; track $index) {
<h3>{{ $index + ". " + player.name_clean }}</h3>
}
</div>
</mat-card-content>
Expand Down
4 changes: 4 additions & 0 deletions src/app/home/home.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,8 @@
position: absolute;
top: 10px;
left: 80px;
}

.icon {
width: 50px;
}
31 changes: 26 additions & 5 deletions src/app/home/home.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { MatChipsModule } from '@angular/material/chips';
import { ServerService } from '../server.service';
import { MatIconModule } from '@angular/material/icon';
import { AuthService } from '../auth.service';
import { McstatusService } from '../mcstatus.service';
import { MinecraftPlayerSample } from '../../mcstatus.model';

@Component({
selector: 'app-home',
Expand All @@ -28,18 +30,37 @@ export class Home implements OnInit {
serverIp = localStorage.getItem('serverIp');
error = '';

private statusInterval?: ReturnType<typeof setInterval>;
version: string | undefined = undefined;
playerCount: string | undefined = undefined;
playerList: MinecraftPlayerSample[] | undefined = undefined;
icon: string | null | undefined = undefined

private infoInterval?: ReturnType<typeof setInterval>;

private serverService: ServerService = inject(ServerService);
private authService: AuthService = inject(AuthService);
private mcstatusService: McstatusService = inject(McstatusService);

ngOnInit() {
this.loading = true;
this.fetchInfo();
this.infoInterval = setInterval(() => this.fetchInfo(), 1000);
}

private fetchInfo() {
this.mcstatusService.loadStatusInfo()
this.fetchStatus();
this.statusInterval = setInterval(() => this.fetchStatus(), 5000);
this.fetchMcstatusInfo();
}

private fetchMcstatusInfo() {
this.version = this.mcstatusService.getVersion();
this.playerCount = this.mcstatusService.getPlayerCount();
this.playerList = this.mcstatusService.getPlayerList();
this.icon = this.mcstatusService.getServerIcon()
}

fetchStatus() {
private fetchStatus() {
this.serverService.getStatus().subscribe({
next: (res) => {
this.running = res.running;
Expand All @@ -55,7 +76,7 @@ export class Home implements OnInit {

toggleServer() {
if (this.running === null) return;
clearInterval(this.statusInterval);
clearInterval(this.infoInterval);
this.loading = true;

const action$ = this.running
Expand All @@ -64,7 +85,7 @@ export class Home implements OnInit {

action$.subscribe({
next: () =>
(this.statusInterval = setInterval(() => this.fetchStatus(), 5000)),
(this.infoInterval = setInterval(() => this.fetchStatus(), 5000)),
error: () => (this.loading = false),
});
}
Expand Down
19 changes: 19 additions & 0 deletions src/app/mcstatus.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { TestBed } from '@angular/core/testing';

import { McstatusService } from './mcstatus.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';

describe('McstatusService', () => {
let service: McstatusService;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule]
});
service = TestBed.inject(McstatusService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
38 changes: 38 additions & 0 deletions src/app/mcstatus.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Mcstatus } from '../mcstatus.model';

@Injectable({
providedIn: 'root',
})
export class McstatusService {
// https://mcstatus.io/docs

private readonly http = inject(HttpClient);
private readonly URL = 'https://api.mcstatus.io/v2/status/java/';
private IP: string | null = null;
private status: Mcstatus | null = null;

public loadStatusInfo() {
this.IP = localStorage.getItem('serverIp');
this.http
.get<Mcstatus>(`${this.URL}/${this.IP?.replace("https://", "")}`)
.subscribe((res) => (this.status = res));
}

public getVersion() {
return this.status?.version?.name_clean;
}

public getPlayerCount() {
return `${this.status?.players?.online}/${this.status?.players?.max}`;
}

public getPlayerList() {
return this.status?.players?.list;
}

public getServerIcon() {
return this.status?.icon
}
}
122 changes: 122 additions & 0 deletions src/mcstatus.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
export interface Mcstatus {
/** Whether the server is online or offline
* Not used since we get the status directly from the machine */
online: boolean;

/** Resolved hostname of the server */
host: string;

/** Resolved port of the server */
port: number;

/** Resolved IP address of the hostname (can be null if resolution failed) */
ip_address: string | null;

/** Whether this address is blocked by Mojang (EULA violation) */
eula_blocked: boolean;

/** Unix ms timestamp when the status was retrieved */
retrieved_at: number;

/** Unix ms timestamp when this status cache expires */
expires_at: number;

/** Server version data (missing if server is offline) */
version?: MinecraftVersion;

/** Player information (missing if server is offline) */
players?: MinecraftPlayers;

/** Message of the day / description (missing if server is offline) */
motd?: MinecraftMotd;

/**
* Base64-encoded PNG data of the 64x64 server icon.
* May be null if no icon is set. Missing if server is offline.
*/
icon?: string | null;

/**
* Forge mods loaded on the server.
* Usually empty if no Forge is installed.
*/
mods: MinecraftMod[];

/**
* Software the server is running (null if query lookup fails).
* Missing if server is offline.
*/
software?: string | null;

/**
* Plugins running on the server (missing if server is offline).
*/
plugins?: MinecraftPlugin[];

/**
* Result of SRV record lookup.
* Always present but can be null if no SRV record was found.
*/
srv_record: MinecraftSrvRecord | null;
}

export interface MinecraftVersion {
/** Raw version name with formatting codes */
name_raw: string;
/** Clean version name without formatting codes */
name_clean: string;
/** HTML-formatted version name */
name_html: string;
/** Protocol version used to identify supported client versions */
protocol: number;
}

export interface MinecraftPlayers {
/** Online player count */
online: number;
/** Maximum allowed players */
max: number;
/** Sample list of online players (may be empty) */
list: MinecraftPlayerSample[];
}

export interface MinecraftPlayerSample {
/** UUID of the player */
uuid: string;
/** Username with possible formatting codes */
name_raw: string;
/** Username without formatting codes */
name_clean: string;
/** HTML-formatted username */
name_html: string;
}

export interface MinecraftMotd {
/** Raw MOTD with formatting codes */
raw: string;
/** Clean text-only MOTD */
clean: string;
/** HTML representation of the MOTD */
html: string;
}

export interface MinecraftMod {
/** Name of the mod */
name: string;
/** Version of the mod */
version: string;
}

export interface MinecraftPlugin {
/** Name of the plugin */
name: string;
/** Semantic version of the plugin (can be null) */
version: string | null;
}

export interface MinecraftSrvRecord {
/** Hostname returned by the SRV lookup */
host: string;
/** Port returned by the SRV lookup */
port: number;
}