fix: support concurrent game windows and prepare beta12 GitHub releases

This commit is contained in:
Disy committed 2026-09-25 11:49:41 +08:00
1 parent 91f483b960
commit 1cf67602d8
33 files changed
+681 -293

No files matched your search

@@ -29,6 +29,7 @@ import { process_listener } from '@/helpers/events'
import { install_existing_instance, install_pack_to_existing_instance } from '@/helpers/install'
import { kill, run } from '@/helpers/instance'
import { get_by_instance_id } from '@/helpers/process'
import { isInstanceLaunching } from '@/helpers/instance-launch-state'
import type { GameInstance } from '@/helpers/types'
import { handleSevereError } from '@/store/error'
@@ -50,6 +51,8 @@ const handleMinecraftLaunchError = useMinecraftLaunchError()
const { offline } = useNetworkStatus()
const messages = defineMessages({
launchAnother: { id: 'app.instance.launch-another', defaultMessage: 'Launch another window' },
stopAll: { id: 'app.instance.stop-all', defaultMessage: 'Stop all windows of this instance' },
chooseInstance: {
id: 'app.home.minimal.choose-instance',
defaultMessage: 'Choose instance',
@@ -88,14 +91,17 @@ const selectedInstance = computed(() =>
props.instances.find((instance) => instance.id === props.selectedInstanceId),
)
const running = ref(false)
const loading = ref(false)
const currentEvent = ref<string | null>(null)
const installingId = ref<string>()
const installed = computed(() => selectedInstance.value?.install_stage === 'installed')
const installing = computed(
() => selectedInstance.value?.install_stage.includes('installing') ?? false,
)
const busy = computed(
() => loading.value || installing.value || (currentEvent.value === 'launched' && !running.value),
() =>
!!selectedInstance.value &&
(isInstanceLaunching(selectedInstance.value.id) ||
installingId.value === selectedInstance.value.id ||
installing.value),
)
const lastPlayed = computed(() => {
@@ -111,18 +117,17 @@ async function refreshProcessState() {
return
}
const processes = await get_by_instance_id(selectedInstance.value.id).catch((error) => {
const id = selectedInstance.value.id
const processes = await get_by_instance_id(id).catch((error) => {
handleError(error)
return []
})
running.value = processes.length > 0
if (selectedInstance.value?.id === id) running.value = processes.length > 0
}
async function playInstance() {
const instance = selectedInstance.value
if (!instance) return
loading.value = true
if (!instance || busy.value) return
try {
await run(instance.id)
} catch (error) {
@@ -132,7 +137,6 @@ async function playInstance() {
})
if (!handled) handleSevereError(error, { instanceId: instance.id })
} finally {
loading.value = false
await refreshProcessState()
}
}
@@ -142,14 +146,14 @@ async function stopInstance() {
if (!instance) return
await kill(instance.id).catch(handleError)
running.value = false
await refreshProcessState()
}
async function installInstance() {
const instance = selectedInstance.value
if (!instance) return
loading.value = true
installingId.value = instance.id
try {
if (
instance.install_stage !== 'pack_installed' &&
@@ -168,14 +172,14 @@ async function installInstance() {
} catch (error) {
handleError(error)
} finally {
loading.value = false
if (installingId.value === instance.id) installingId.value = undefined
}
}
watch(
() => props.selectedInstanceId,
() => {
currentEvent.value = null
running.value = false
void refreshProcessState()
},
)
@@ -184,9 +188,7 @@ await refreshProcessState()
const unlistenProcess = await process_listener((event: { instance_id: string; event: string }) => {
if (event.instance_id !== selectedInstance.value?.id) return
currentEvent.value = event.event
if (event.event === 'finished') running.value = false
else void refreshProcessState()
void refreshProcessState()
})
onUnmounted(() => {
@@ -237,13 +239,17 @@ onUnmounted(() => {
</router-link>
<div class="flex min-h-11 shrink-0 items-center justify-end gap-2">
<ButtonStyled v-if="running" color="red" size="large">
<button class="w-36 justify-center" @click="stopInstance">
<ButtonStyled v-if="running" color="red" size="large" circular>
<button
v-tooltip="formatMessage(messages.stopAll)"
:aria-label="formatMessage(messages.stopAll)"
:disabled="busy"
@click="stopInstance"
>
<StopCircleIcon aria-hidden="true" />
<span class="truncate">{{ formatMessage(commonMessages.stopButton) }}</span>
</button>
</ButtonStyled>
<ButtonStyled v-else-if="busy" size="large">
<ButtonStyled v-if="busy" size="large">
<button class="w-36 justify-center" disabled>
<SpinnerIcon class="animate-spin" aria-hidden="true" />
<span class="truncate">
@@ -256,7 +262,9 @@ onUnmounted(() => {
<ButtonStyled v-else-if="installed" color="brand" size="large">
<button class="w-36 justify-center" @click="playInstance">
<PlayIcon class="translate-x-px" aria-hidden="true" />
<span class="truncate">{{ formatMessage(commonMessages.playButton) }}</span>
<span class="truncate">{{
formatMessage(running ? messages.launchAnother : commonMessages.playButton)
}}</span>
</button>
</ButtonStyled>
<ButtonStyled v-else color="brand" size="large">