mirror of
https://github.com/MCSManager/MCSManager.git
synced 2024-11-21 03:12:10 +08:00
Feat: get minecraft players
This commit is contained in:
parent
d9a62e13d0
commit
27e63eb203
@ -1,6 +1,6 @@
|
||||
import Instance from "../../instance/instance";
|
||||
import InstanceCommand from "../base/command";
|
||||
import { MCServerStatus } from "common";
|
||||
import { MCServerStatus, toNumber } from "common";
|
||||
|
||||
export default class PingJavaMinecraftServerCommand extends InstanceCommand {
|
||||
constructor() {
|
||||
@ -14,15 +14,17 @@ export default class PingJavaMinecraftServerCommand extends InstanceCommand {
|
||||
const result = await new MCServerStatus(instance.config.pingConfig.port, host).getStatus();
|
||||
if (result.online) {
|
||||
instance.info.mcPingOnline = true;
|
||||
instance.info.currentPlayers = result.current_players;
|
||||
instance.info.maxPlayers = result.max_players;
|
||||
instance.info.currentPlayers = toNumber(result.current_players) ?? 0;
|
||||
instance.info.maxPlayers = toNumber(result.max_players) ?? 0;
|
||||
instance.info.version = result.version;
|
||||
instance.info.latency = toNumber(result.latency) ?? 0;
|
||||
} else {
|
||||
instance.resetPingInfo();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} catch (error) {
|
||||
instance.resetPingInfo();
|
||||
// ignore error
|
||||
}
|
||||
return null;
|
||||
|
@ -18,5 +18,6 @@ export default class PingMinecraftServerTask implements ILifeCycleTask {
|
||||
async stop(instance: Instance) {
|
||||
instance.resetPingInfo();
|
||||
clearInterval(this.task);
|
||||
this.task = undefined;
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,14 @@ export interface InstanceDetail {
|
||||
started: number;
|
||||
status: INSTANCE_STATUS_CODE;
|
||||
info: {
|
||||
mcPingOnline: boolean;
|
||||
currentPlayers: number;
|
||||
fileLock: number;
|
||||
maxPlayers: number;
|
||||
openFrpStatus: boolean;
|
||||
playersChart: any[];
|
||||
version: string;
|
||||
fileLock: number;
|
||||
playersChart: Array<{ value: string }>;
|
||||
openFrpStatus: boolean;
|
||||
latency: number;
|
||||
};
|
||||
config: IGlobalInstanceConfig;
|
||||
watcher?: number;
|
||||
|
@ -18,8 +18,10 @@ import {
|
||||
WarningOutlined,
|
||||
InfoCircleOutlined,
|
||||
FrownOutlined,
|
||||
RedoOutlined
|
||||
RedoOutlined,
|
||||
UserOutlined
|
||||
} from "@ant-design/icons-vue";
|
||||
|
||||
import BetweenMenus from "@/components/BetweenMenus.vue";
|
||||
import { router } from "@/config/router";
|
||||
import { remoteInstances, remoteNodeList } from "@/services/apis";
|
||||
@ -468,23 +470,24 @@ onMounted(async () => {
|
||||
</template>
|
||||
<template #body>
|
||||
<a-typography-paragraph>
|
||||
<div>
|
||||
{{ t("TXT_CODE_e70a8e24") }}
|
||||
<span v-if="item.moreInfo?.isRunning" class="color-success">
|
||||
<CheckCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else-if="item.moreInfo?.isStopped" class="color-info">
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<ExclamationCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
{{ t("TXT_CODE_68831be6") }}
|
||||
{{ item.moreInfo?.instanceTypeText }}
|
||||
<div class="mb-6">
|
||||
<a-tag :color="item.moreInfo?.isRunning ? 'green' : ''">
|
||||
<span v-if="item.moreInfo?.isRunning">
|
||||
<CheckCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else-if="item.moreInfo?.isStopped" class="color-info">
|
||||
<ExclamationCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
<span v-else>
|
||||
<ExclamationCircleOutlined />
|
||||
{{ item.moreInfo?.statusText }}
|
||||
</span>
|
||||
</a-tag>
|
||||
<a-tag>
|
||||
{{ item.moreInfo?.instanceTypeText }}
|
||||
</a-tag>
|
||||
</div>
|
||||
<div>
|
||||
{{ t("TXT_CODE_d31a684c") }}
|
||||
@ -494,6 +497,13 @@ onMounted(async () => {
|
||||
{{ t("TXT_CODE_ae747cc0") }}
|
||||
{{ parseTimestamp(item.config.endTime) }}
|
||||
</div>
|
||||
<div v-if="item.info.mcPingOnline">
|
||||
<span>{{ t("在线数:") }}</span>
|
||||
<span style="vertical-align: middle">
|
||||
<UserOutlined />
|
||||
{{ item.info.currentPlayers }} / {{ item.info.maxPlayers }}
|
||||
</span>
|
||||
</div>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
</CardPanel>
|
||||
@ -530,6 +540,7 @@ onMounted(async () => {
|
||||
}
|
||||
.instance-card {
|
||||
cursor: pointer;
|
||||
min-height: 170px;
|
||||
}
|
||||
.instance-card:hover {
|
||||
border: 1px solid var(--color-gray-8);
|
||||
|
@ -36,6 +36,17 @@ const getInstanceName = computed(() => {
|
||||
}
|
||||
});
|
||||
|
||||
const instanceGameServerInfo = computed(() => {
|
||||
if (instanceInfo.value?.info?.mcPingOnline) {
|
||||
return {
|
||||
players: `${instanceInfo.value?.info.currentPlayers} / ${instanceInfo.value?.info.maxPlayers}`,
|
||||
version: instanceInfo.value?.info.version
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (instanceId && daemonId) {
|
||||
await execute({
|
||||
@ -60,6 +71,12 @@ onMounted(async () => {
|
||||
<a-typography-paragraph>
|
||||
{{ t("TXT_CODE_68831be6") }}{{ instanceTypeText }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-if="instanceGameServerInfo">
|
||||
{{ t("玩家数:") }}{{ instanceGameServerInfo.players }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph v-if="instanceGameServerInfo">
|
||||
{{ t("游戏版本:") }}{{ instanceGameServerInfo.version }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
<div style="display: flex; gap: 10px">
|
||||
<div>
|
||||
@ -112,12 +129,13 @@ onMounted(async () => {
|
||||
</div>
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
{{ t("TXT_CODE_ae747cc0") }}{{ parseTimestamp(instanceInfo?.config.endTime) }}
|
||||
<span>{{ t("TXT_CODE_ae747cc0") }}</span>
|
||||
<span>{{ parseTimestamp(instanceInfo?.config.endTime) || t("无限制") }}</span>
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-paragraph v-if="!instanceGameServerInfo">
|
||||
{{ t("TXT_CODE_8b8e08a6") }}{{ parseTimestamp(instanceInfo?.config.createDatetime) }}
|
||||
</a-typography-paragraph>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-paragraph v-if="!instanceGameServerInfo">
|
||||
<span>{{ t("TXT_CODE_cec321b4") }}{{ instanceInfo?.config.oe.toUpperCase() }} </span>
|
||||
<span class="ml-6">
|
||||
{{ t("TXT_CODE_400a4210") }}{{ instanceInfo?.config.ie.toUpperCase() }}
|
||||
@ -126,7 +144,7 @@ onMounted(async () => {
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text> {{ t("TXT_CODE_30051f9b") }} </a-typography-text>
|
||||
<a-typography-text :copyable="{ text: instanceInfo?.instanceUuid }"> </a-typography-text>
|
||||
<a-typography-text class="ml-10"> {{ t("TXT_CODE_5f2d2e30") }} </a-typography-text>
|
||||
<a-typography-text class="ml-20"> {{ t("TXT_CODE_5f2d2e30") }} </a-typography-text>
|
||||
<a-typography-text :copyable="{ text: daemonId }"> </a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
</template>
|
||||
|
@ -11,25 +11,27 @@ import {
|
||||
ControlOutlined,
|
||||
DashboardOutlined,
|
||||
FieldTimeOutlined,
|
||||
FolderOpenOutlined
|
||||
FolderOpenOutlined,
|
||||
UsergroupDeleteOutlined
|
||||
} from "@ant-design/icons-vue";
|
||||
import InnerCard from "@/components/InnerCard.vue";
|
||||
import { LayoutCardHeight } from "../../config/originLayoutConfig";
|
||||
import { useAppStateStore } from "@/stores/useAppStateStore";
|
||||
import { useAppRouters } from "@/hooks/useAppRouters";
|
||||
import { useLayoutCardTools } from "../../hooks/useCardTools";
|
||||
import { useInstanceInfo } from "@/hooks/useInstance";
|
||||
import { TYPE_MINECRAFT_JAVA, useInstanceInfo } from "@/hooks/useInstance";
|
||||
import TermConfig from "./dialogs/TermConfig.vue";
|
||||
import EventConfig from "./dialogs/EventConfig.vue";
|
||||
import PingConfig from "./dialogs/PingConfig.vue";
|
||||
import RconSettings from "./dialogs/RconSettings.vue";
|
||||
import InstanceDetail from "./dialogs/InstanceDetail.vue";
|
||||
import { GLOBAL_INSTANCE_NAME } from "../../config/const";
|
||||
import type { RouteLocationPathRaw } from "vue-router";
|
||||
import { TYPE_UNIVERSAL, TYPE_WEB_SHELL } from "../../hooks/useInstance";
|
||||
import McPingSettings from "./dialogs/McPingSettings.vue";
|
||||
|
||||
const terminalConfigDialog = ref<InstanceType<typeof TermConfig>>();
|
||||
const rconSettingsDialog = ref<InstanceType<typeof RconSettings>>();
|
||||
const mcSettingsDialog = ref<InstanceType<typeof McPingSettings>>();
|
||||
const eventConfigDialog = ref<InstanceType<typeof EventConfig>>();
|
||||
const pingConfigDialog = ref<InstanceType<typeof PingConfig>>();
|
||||
const instanceDetailsDialog = ref<InstanceType<typeof InstanceDetail>>();
|
||||
@ -102,6 +104,14 @@ const btns = computed(() => {
|
||||
},
|
||||
condition: () => state.settings.canFileManager || isAdmin.value
|
||||
},
|
||||
{
|
||||
title: t("Minecraft 状态获取"),
|
||||
icon: UsergroupDeleteOutlined,
|
||||
click: () => {
|
||||
mcSettingsDialog.value?.openDialog();
|
||||
},
|
||||
condition: () => instanceInfo.value?.config.type.includes(TYPE_MINECRAFT_JAVA) ?? false
|
||||
},
|
||||
{
|
||||
title: t("TXT_CODE_656a85d8"),
|
||||
icon: BuildOutlined,
|
||||
@ -234,6 +244,14 @@ const btns = computed(() => {
|
||||
:daemon-id="daemonId"
|
||||
@update="refreshInstanceInfo"
|
||||
/>
|
||||
|
||||
<McPingSettings
|
||||
ref="mcSettingsDialog"
|
||||
:instance-info="instanceInfo"
|
||||
:instance-id="instanceId"
|
||||
:daemon-id="daemonId"
|
||||
@update="refreshInstanceInfo"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -115,7 +115,7 @@ const loadImages = async () => {
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
return reportErrorMsg(err.message);
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
@ -143,7 +143,7 @@ const loadNetworkModes = async () => {
|
||||
});
|
||||
if (modes.value) networkModes.value = modes.value;
|
||||
} catch (err: any) {
|
||||
return reportErrorMsg(err.message);
|
||||
// ignore
|
||||
}
|
||||
};
|
||||
|
||||
|
102
frontend/src/widgets/instance/dialogs/McPingSettings.vue
Normal file
102
frontend/src/widgets/instance/dialogs/McPingSettings.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue";
|
||||
import { t } from "@/lang/i18n";
|
||||
import type { InstanceDetail } from "@/types";
|
||||
import { updateInstanceConfig } from "@/services/apis/instance";
|
||||
import { message, type FormInstance } from "ant-design-vue";
|
||||
import { reportErrorMsg } from "@/tools/validator";
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
|
||||
const props = defineProps<{
|
||||
instanceInfo?: InstanceDetail;
|
||||
instanceId?: string;
|
||||
daemonId?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(["update"]);
|
||||
const formData = reactive({
|
||||
ip: "",
|
||||
port: "",
|
||||
type: 1
|
||||
});
|
||||
|
||||
const open = ref(false);
|
||||
const openDialog = () => {
|
||||
open.value = true;
|
||||
formData.ip = props.instanceInfo?.config?.pingConfig.ip || "";
|
||||
formData.port = String(props.instanceInfo?.config?.pingConfig.port || "");
|
||||
formData.type = props.instanceInfo?.config?.pingConfig.type ?? 1;
|
||||
};
|
||||
|
||||
const { execute, isLoading } = updateInstanceConfig();
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
await formRef.value?.validateFields();
|
||||
await execute({
|
||||
params: {
|
||||
uuid: props.instanceId ?? "",
|
||||
daemonId: props.daemonId ?? ""
|
||||
},
|
||||
data: {
|
||||
pingConfig: {
|
||||
ip: formData.ip,
|
||||
port: Number(formData.port),
|
||||
type: formData.type
|
||||
}
|
||||
}
|
||||
});
|
||||
emit("update");
|
||||
open.value = false;
|
||||
return message.success(t("TXT_CODE_d3de39b4"));
|
||||
} catch (err: any) {
|
||||
return reportErrorMsg(err.message);
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a-modal
|
||||
v-model:open="open"
|
||||
centered
|
||||
:title="t('Minecraft 状态获取')"
|
||||
:confirm-loading="isLoading"
|
||||
:ok-text="t('TXT_CODE_abfe9512')"
|
||||
@ok="submit"
|
||||
>
|
||||
<div>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("MCSManager 将使用 Minecraft Ping 协议尝试获取服务器在线人数,版本,延迟等信息。") }}
|
||||
<br />
|
||||
{{ t("完成配置后,实例运行时,每分钟刷新一次服务器状态。") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-form ref="formRef" :model="formData" layout="vertical">
|
||||
<a-form-item name="port">
|
||||
<a-typography-title :level="5">{{ t("TXT_CODE_890aa44c") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_a4748cb0") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="formData.port" :placeholder="t('TXT_CODE_e2dc0156')" />
|
||||
</a-form-item>
|
||||
<a-form-item name="ip">
|
||||
<a-typography-title :level="5">{{ t("服务器地址") }}</a-typography-title>
|
||||
<a-typography-paragraph>
|
||||
<a-typography-text type="secondary">
|
||||
{{ t("TXT_CODE_8e2be926") }}
|
||||
</a-typography-text>
|
||||
</a-typography-paragraph>
|
||||
<a-input v-model:value="formData.ip" :placeholder="t('默认值:localhost')" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user