From 9d99b69affc5c3021612f1a024b808c691e92b5f Mon Sep 17 00:00:00 2001 From: TheWander02 <48934424+thewander02@users.noreply.github.com> Date: Wed, 16 Oct 2024 02:08:01 -0700 Subject: [PATCH] feat(frontend): wip player managment --- .../components/ui/servers/PanelPlayerList.vue | 89 +++++++++++++++++++ apps/frontend/src/composables/pyroServers.ts | 4 +- .../src/pages/servers/manage/[id].vue | 56 +++++++++++- .../src/pages/servers/manage/[id]/index.vue | 48 ++++++++-- 4 files changed, 186 insertions(+), 11 deletions(-) create mode 100644 apps/frontend/src/components/ui/servers/PanelPlayerList.vue diff --git a/apps/frontend/src/components/ui/servers/PanelPlayerList.vue b/apps/frontend/src/components/ui/servers/PanelPlayerList.vue new file mode 100644 index 000000000..34576bc99 --- /dev/null +++ b/apps/frontend/src/components/ui/servers/PanelPlayerList.vue @@ -0,0 +1,89 @@ + + + Players + + + + + + + + + {{ player.name }} + + {{ player.created_at }} + + + + + + + Op + Deop + Whitelist + Dewhitelist + Kick + Ban + + + + + + + + + diff --git a/apps/frontend/src/composables/pyroServers.ts b/apps/frontend/src/composables/pyroServers.ts index 127efa7b1..f7198c3ac 100644 --- a/apps/frontend/src/composables/pyroServers.ts +++ b/apps/frontend/src/composables/pyroServers.ts @@ -659,7 +659,9 @@ const modules: any = { data.image = (await processImage(data.project?.icon_url)) ?? undefined; const motd = await getMotd(); if (motd === "A Minecraft Server") { - await setMotd(`§b${data.project?.title} §f♦ §aModrinth Servers`); + await setMotd( + `§b${data.project?.title || data.loader + " " + data.mc_version} §f♦ §aModrinth Servers`, + ); } data.motd = motd; return data; diff --git a/apps/frontend/src/pages/servers/manage/[id].vue b/apps/frontend/src/pages/servers/manage/[id].vue index fef25733f..ee544f7c1 100644 --- a/apps/frontend/src/pages/servers/manage/[id].vue +++ b/apps/frontend/src/pages/servers/manage/[id].vue @@ -74,6 +74,7 @@ :console-output="consoleOutput" :socket="socket" :server="server" + :players="players" @reinstall="onReinstall" /> @@ -121,6 +122,9 @@ const ramData = ref([]); const isActioning = ref(false); const isServerRunning = computed(() => serverPowerState.value === "running"); const serverPowerState = ref("stopped"); +const players = ref([]); +const isInitialListCommand = ref(true); +const firstOk = ref(true); const stats = ref({ current: { @@ -194,7 +198,53 @@ const connectWebSocket = () => { const handleWebSocketMessage = (data: WSEvent) => { switch (data.event) { case "log": - consoleOutput.value.push(...data.message.split("\n").filter((l) => l.trim())); + // eslint-disable-next-line no-case-declarations + let log = data.message.split("\n").filter((l) => l.trim()); + + // eslint-disable-next-line no-case-declarations + const joinRegex = /(.+) joined the game/; + // eslint-disable-next-line no-case-declarations + const leaveRegex = /(.+) left the game/; + // eslint-disable-next-line no-case-declarations + const playerListRegex = /There are \d+ of a max of \d+ players online: (.+)/; + + log.forEach((line) => { + const joinMatch = line.match(joinRegex); + const leaveMatch = line.match(leaveRegex); + const playerListMatch = line.match(playerListRegex); + + if (joinMatch && joinMatch[1]) { + const player = joinMatch[1].split(" ")[3]; + console.log("Adding player", player); + if (!players.value.includes(player)) { + players.value.push(player); + } + } + + if (leaveMatch && leaveMatch[1]) { + const player = leaveMatch[1].split(" ")[3]; + console.log("Removing player", player); + players.value = players.value.filter((p) => p !== player); + } + + if (playerListMatch && playerListMatch[1]) { + players.value = playerListMatch[1].split(", "); + } + }); + + console.log(players.value); + + if (isInitialListCommand.value) { + log = log.filter((line) => { + if (line.includes("There are") && line.includes("players online")) { + isInitialListCommand.value = false; + return false; + } + return true; + }); + } + + consoleOutput.value.push(...log); break; case "stats": updateStats(data); @@ -210,6 +260,10 @@ const handleWebSocketMessage = (data: WSEvent) => { handleInstallationResult(data); break; case "auth-ok": + if (firstOk.value) { + socket.value?.send(JSON.stringify({ event: "command", cmd: "list" })); + firstOk.value = false; + } break; default: console.warn("Unhandled WebSocket event:", data); diff --git a/apps/frontend/src/pages/servers/manage/[id]/index.vue b/apps/frontend/src/pages/servers/manage/[id]/index.vue index 0f42cfaaa..9102a0eda 100644 --- a/apps/frontend/src/pages/servers/manage/[id]/index.vue +++ b/apps/frontend/src/pages/servers/manage/[id]/index.vue @@ -23,7 +23,7 @@ v-if="suggestions.length" id="command-suggestions" ref="suggestionsList" - class="z-20 mt-1 max-h-60 w-full list-none overflow-auto rounded-md border border-divider bg-bg-raised p-0 shadow-lg" + class="mt-1 max-h-60 w-full list-none overflow-auto rounded-md border border-divider bg-bg-raised p-0 shadow-lg" role="listbox" > {{ " ".repeat(commandInput.length - 1) }} {{ bestSuggestion }} - + + + + import { TerminalSquareIcon } from "@modrinth/assets"; +import { asyncComputed } from "@vueuse/core"; import type { ServerState, Stats } from "~/types/servers"; import type { Server } from "~/composables/pyroServers"; @@ -119,8 +121,28 @@ const props = defineProps<{ serverPowerState: ServerState; isServerRunning: boolean; server: Server<["general", "mods", "backups", "network", "startup", "ws", "fs"]>; + players: string[]; }>(); +const playerList = asyncComputed(async () => { + const results = await Promise.all( + props.players.map(async (name) => { + const ply = await $fetch(`https://api.ashcon.app/mojang/v2/user/${name}`, { + method: "GET", + retry: false, + }); + return { + name, + id: ply.uuid, + avatar: `https://crafatar.com/avatars/${ply.uuid}`, + name_history: ply.username_history, + created_at: ply.created_at, + }; + }), + ); + return results; +}); + const socket = ref(props.socket); watch(props, (newAttrs) => { @@ -490,7 +512,7 @@ const sendCommand = () => { const cmd = commandInput.value.trim(); if (!socket || !cmd) return; try { - socket.value?.send(JSON.stringify({ event: "command", cmd })); + sendConsoleCommand(cmd); commandInput.value = ""; suggestions.value = []; selectedSuggestionIndex.value = 0; @@ -499,6 +521,14 @@ const sendCommand = () => { } }; +const sendConsoleCommand = (cmd: string) => { + try { + socket.value?.send(JSON.stringify({ event: "command", cmd })); + } catch (error) { + console.error("Error sending command:", error); + } +}; + watch( () => selectedSuggestionIndex.value, (newVal) => {