Fix forever installing (#3135)
* Rough draft for fix for Mojang servers being down causing infinite installation * Add "pack installed" install step * Allow repairing an instance from Library to recover pack contents * Allow repair from instance page * Deduplicate repair code * Fix lint * Fix lint (for real this time) --------- Co-authored-by: Jai Agrawal <18202329+Geometrically@users.noreply.github.com>
This commit is contained in:
parent
abd679d716
commit
227386bb0d
@ -1,22 +1,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, onMounted, watch, onUnmounted } from 'vue'
|
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||||
import { RouterView, useRouter, useRoute } from 'vue-router'
|
import { RouterView, useRoute, useRouter } from 'vue-router'
|
||||||
import {
|
import {
|
||||||
ArrowBigUpDashIcon,
|
ArrowBigUpDashIcon,
|
||||||
LogInIcon,
|
CompassIcon,
|
||||||
|
DownloadIcon,
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
|
LeftArrowIcon,
|
||||||
LibraryIcon,
|
LibraryIcon,
|
||||||
|
LogInIcon,
|
||||||
|
LogOutIcon,
|
||||||
|
MaximizeIcon,
|
||||||
|
MinimizeIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
|
RestoreIcon,
|
||||||
|
RightArrowIcon,
|
||||||
SettingsIcon,
|
SettingsIcon,
|
||||||
XIcon,
|
XIcon,
|
||||||
DownloadIcon,
|
|
||||||
CompassIcon,
|
|
||||||
MinimizeIcon,
|
|
||||||
MaximizeIcon,
|
|
||||||
RestoreIcon,
|
|
||||||
LogOutIcon,
|
|
||||||
RightArrowIcon,
|
|
||||||
LeftArrowIcon,
|
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Button, ButtonStyled, Notifications, OverflowMenu } from '@modrinth/ui'
|
import { Avatar, Button, ButtonStyled, Notifications, OverflowMenu } from '@modrinth/ui'
|
||||||
import { useLoading, useTheming } from '@/store/state'
|
import { useLoading, useTheming } from '@/store/state'
|
||||||
@ -32,12 +32,12 @@ import ModrinthLoadingIndicator from '@/components/LoadingIndicatorBar.vue'
|
|||||||
import { handleError, useNotifications } from '@/store/notifications.js'
|
import { handleError, useNotifications } from '@/store/notifications.js'
|
||||||
import { command_listener, warning_listener } from '@/helpers/events.js'
|
import { command_listener, warning_listener } from '@/helpers/events.js'
|
||||||
import { type } from '@tauri-apps/plugin-os'
|
import { type } from '@tauri-apps/plugin-os'
|
||||||
import { isDev, getOS, restartApp } from '@/helpers/utils.js'
|
import { getOS, isDev, restartApp } from '@/helpers/utils.js'
|
||||||
import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
|
import { debugAnalytics, initAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
import { getVersion } from '@tauri-apps/api/app'
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
|
||||||
import { install_from_file } from './helpers/pack'
|
import { create_profile_and_install_from_file } from './helpers/pack'
|
||||||
import { useError } from '@/store/error.js'
|
import { useError } from '@/store/error.js'
|
||||||
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
|
||||||
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
import ModInstallModal from '@/components/ui/install_flow/ModInstallModal.vue'
|
||||||
@ -51,7 +51,7 @@ import { renderString } from '@modrinth/utils'
|
|||||||
import { useFetch } from '@/helpers/fetch.js'
|
import { useFetch } from '@/helpers/fetch.js'
|
||||||
import { check } from '@tauri-apps/plugin-updater'
|
import { check } from '@tauri-apps/plugin-updater'
|
||||||
import NavButton from '@/components/ui/NavButton.vue'
|
import NavButton from '@/components/ui/NavButton.vue'
|
||||||
import { get as getCreds, logout, login } from '@/helpers/mr_auth.js'
|
import { get as getCreds, login, logout } from '@/helpers/mr_auth.js'
|
||||||
import { get_user } from '@/helpers/cache.js'
|
import { get_user } from '@/helpers/cache.js'
|
||||||
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
|
import AppSettingsModal from '@/components/ui/modal/AppSettingsModal.vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@ -296,7 +296,7 @@ async function handleCommand(e) {
|
|||||||
if (e.event === 'RunMRPack') {
|
if (e.event === 'RunMRPack') {
|
||||||
// RunMRPack should directly install a local mrpack given a path
|
// RunMRPack should directly install a local mrpack given a path
|
||||||
if (e.path.endsWith('.mrpack')) {
|
if (e.path.endsWith('.mrpack')) {
|
||||||
await install_from_file(e.path).catch(handleError)
|
await create_profile_and_install_from_file(e.path).catch(handleError)
|
||||||
trackEvent('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,17 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { onUnmounted, ref, computed, onMounted } from 'vue'
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { SpinnerIcon, GameIcon, TimerIcon, StopCircleIcon, PlayIcon } from '@modrinth/assets'
|
import {
|
||||||
import { ButtonStyled, Avatar } from '@modrinth/ui'
|
DownloadIcon,
|
||||||
|
GameIcon,
|
||||||
|
PlayIcon,
|
||||||
|
SpinnerIcon,
|
||||||
|
StopCircleIcon,
|
||||||
|
TimerIcon,
|
||||||
|
} from '@modrinth/assets'
|
||||||
|
import { Avatar, ButtonStyled } from '@modrinth/ui'
|
||||||
import { convertFileSrc } from '@tauri-apps/api/core'
|
import { convertFileSrc } from '@tauri-apps/api/core'
|
||||||
import { kill, run } from '@/helpers/profile'
|
import { finish_install, kill, run } from '@/helpers/profile'
|
||||||
import { get_by_profile_path } from '@/helpers/process'
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
import { process_listener } from '@/helpers/events'
|
import { process_listener } from '@/helpers/events'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
@ -42,7 +49,8 @@ const modLoading = computed(
|
|||||||
currentEvent.value === 'installing' ||
|
currentEvent.value === 'installing' ||
|
||||||
(currentEvent.value === 'launched' && !playing.value),
|
(currentEvent.value === 'launched' && !playing.value),
|
||||||
)
|
)
|
||||||
const installing = computed(() => props.instance.install_stage !== 'installed')
|
const installing = computed(() => props.instance.install_stage.includes('installing'))
|
||||||
|
const installed = computed(() => props.instance.install_stage === 'installed')
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
@ -84,6 +92,12 @@ const stop = async (e, context) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const repair = async (e) => {
|
||||||
|
e?.stopPropagation()
|
||||||
|
|
||||||
|
await finish_install(props.instance)
|
||||||
|
}
|
||||||
|
|
||||||
const openFolder = async () => {
|
const openFolder = async () => {
|
||||||
await showProfileInFolder(props.instance.path)
|
await showProfileInFolder(props.instance.path)
|
||||||
}
|
}
|
||||||
@ -195,6 +209,15 @@ onUnmounted(() => unlisten())
|
|||||||
class="animate-spin w-8 h-8"
|
class="animate-spin w-8 h-8"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
/>
|
/>
|
||||||
|
<ButtonStyled v-else-if="!installed" size="large" color="brand" circular>
|
||||||
|
<button
|
||||||
|
v-tooltip="'Repair'"
|
||||||
|
class="transition-all scale-75 group-hover:scale-100 group-focus-within:scale-100 origin-bottom opacity-0 group-hover:opacity-100 group-focus-within:opacity-100 card-shadow"
|
||||||
|
@click="(e) => repair(e)"
|
||||||
|
>
|
||||||
|
<DownloadIcon />
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
<ButtonStyled v-else size="large" color="brand" circular>
|
<ButtonStyled v-else size="large" color="brand" circular>
|
||||||
<button
|
<button
|
||||||
v-tooltip="'Play'"
|
v-tooltip="'Play'"
|
||||||
|
|||||||
@ -199,16 +199,16 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
import ModalWrapper from '@/components/ui/modal/ModalWrapper.vue'
|
||||||
import {
|
import {
|
||||||
PlusIcon,
|
|
||||||
UploadIcon,
|
|
||||||
XIcon,
|
|
||||||
CodeIcon,
|
CodeIcon,
|
||||||
FolderOpenIcon,
|
FolderOpenIcon,
|
||||||
InfoIcon,
|
|
||||||
FolderSearchIcon,
|
FolderSearchIcon,
|
||||||
|
InfoIcon,
|
||||||
|
PlusIcon,
|
||||||
UpdatedIcon,
|
UpdatedIcon,
|
||||||
|
UploadIcon,
|
||||||
|
XIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { Avatar, Button, Chips, Checkbox } from '@modrinth/ui'
|
import { Avatar, Button, Checkbox, Chips } from '@modrinth/ui'
|
||||||
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
import { computed, onUnmounted, ref, shallowRef } from 'vue'
|
||||||
import { get_loaders } from '@/helpers/tags'
|
import { get_loaders } from '@/helpers/tags'
|
||||||
import { create } from '@/helpers/profile'
|
import { create } from '@/helpers/profile'
|
||||||
@ -218,7 +218,7 @@ import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
|
|||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import Multiselect from 'vue-multiselect'
|
import Multiselect from 'vue-multiselect'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { install_from_file } from '@/helpers/pack.js'
|
import { create_profile_and_install_from_file } from '@/helpers/pack.js'
|
||||||
import {
|
import {
|
||||||
get_default_launcher_path,
|
get_default_launcher_path,
|
||||||
get_importable_instances,
|
get_importable_instances,
|
||||||
@ -263,7 +263,7 @@ defineExpose({
|
|||||||
hide()
|
hide()
|
||||||
const { paths } = event.payload
|
const { paths } = event.payload
|
||||||
if (paths && paths.length > 0 && paths[0].endsWith('.mrpack')) {
|
if (paths && paths.length > 0 && paths[0].endsWith('.mrpack')) {
|
||||||
await install_from_file(paths[0]).catch(handleError)
|
await create_profile_and_install_from_file(paths[0]).catch(handleError)
|
||||||
trackEvent('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileDrop',
|
source: 'CreationModalFileDrop',
|
||||||
})
|
})
|
||||||
@ -419,7 +419,7 @@ const openFile = async () => {
|
|||||||
const newProject = await open({ multiple: false })
|
const newProject = await open({ multiple: false })
|
||||||
if (!newProject) return
|
if (!newProject) return
|
||||||
hide()
|
hide()
|
||||||
await install_from_file(newProject.path ?? newProject).catch(handleError)
|
await create_profile_and_install_from_file(newProject.path ?? newProject).catch(handleError)
|
||||||
|
|
||||||
trackEvent('InstanceCreate', {
|
trackEvent('InstanceCreate', {
|
||||||
source: 'CreationModalFileOpen',
|
source: 'CreationModalFileOpen',
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { XIcon, DownloadIcon } from '@modrinth/assets'
|
import { DownloadIcon, XIcon } from '@modrinth/assets'
|
||||||
import { Button } from '@modrinth/ui'
|
import { Button } from '@modrinth/ui'
|
||||||
import { install as pack_install } from '@/helpers/pack'
|
import { create_profile_and_install as pack_install } from '@/helpers/pack'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { trackEvent } from '@/helpers/analytics'
|
import { trackEvent } from '@/helpers/analytics'
|
||||||
import { handleError } from '@/store/state.js'
|
import { handleError } from '@/store/state.js'
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { invoke } from '@tauri-apps/api/core'
|
|||||||
import { create } from './profile'
|
import { create } from './profile'
|
||||||
|
|
||||||
// Installs pack from a version ID
|
// Installs pack from a version ID
|
||||||
export async function install(projectId, versionId, packTitle, iconUrl) {
|
export async function create_profile_and_install(projectId, versionId, packTitle, iconUrl) {
|
||||||
const location = {
|
const location = {
|
||||||
type: 'fromVersionId',
|
type: 'fromVersionId',
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
@ -28,8 +28,18 @@ export async function install(projectId, versionId, packTitle, iconUrl) {
|
|||||||
return await invoke('plugin:pack|pack_install', { location, profile })
|
return await invoke('plugin:pack|pack_install', { location, profile })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function install_to_existing_profile(projectId, versionId, title, profilePath) {
|
||||||
|
const location = {
|
||||||
|
type: 'fromVersionId',
|
||||||
|
project_id: projectId,
|
||||||
|
version_id: versionId,
|
||||||
|
title,
|
||||||
|
}
|
||||||
|
return await invoke('plugin:pack|pack_install', { location, profile: profilePath })
|
||||||
|
}
|
||||||
|
|
||||||
// Installs pack from a path
|
// Installs pack from a path
|
||||||
export async function install_from_file(path) {
|
export async function create_profile_and_install_from_file(path) {
|
||||||
const location = {
|
const location = {
|
||||||
type: 'fromFile',
|
type: 'fromFile',
|
||||||
path: path,
|
path: path,
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
* and deserialized into a usable JS object.
|
* and deserialized into a usable JS object.
|
||||||
*/
|
*/
|
||||||
import { invoke } from '@tauri-apps/api/core'
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
import { install_to_existing_profile } from '@/helpers/pack.js'
|
||||||
|
import { handleError } from '@/store/notifications.js'
|
||||||
|
|
||||||
/// Add instance
|
/// Add instance
|
||||||
/*
|
/*
|
||||||
@ -186,3 +188,17 @@ export async function edit(path, editProfile) {
|
|||||||
export async function edit_icon(path, iconPath) {
|
export async function edit_icon(path, iconPath) {
|
||||||
return await invoke('plugin:profile|profile_edit_icon', { path, iconPath })
|
return await invoke('plugin:profile|profile_edit_icon', { path, iconPath })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function finish_install(instance) {
|
||||||
|
if (instance.install_stage !== 'pack_installed') {
|
||||||
|
let linkedData = instance.linked_data
|
||||||
|
await install_to_existing_profile(
|
||||||
|
linkedData.project_id,
|
||||||
|
linkedData.version_id,
|
||||||
|
instance.name,
|
||||||
|
instance.path,
|
||||||
|
).catch(handleError)
|
||||||
|
} else {
|
||||||
|
await install(instance.path, false).catch(handleError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
7
apps/app-frontend/src/helpers/types.d.ts
vendored
7
apps/app-frontend/src/helpers/types.d.ts
vendored
@ -32,7 +32,12 @@ type GameInstance = {
|
|||||||
hooks: Hooks
|
hooks: Hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
type InstallStage = 'installed' | 'installing' | 'pack_installing' | 'not_installed'
|
type InstallStage =
|
||||||
|
| 'installed'
|
||||||
|
| 'minecraft_installing'
|
||||||
|
| 'pack_installed'
|
||||||
|
| 'pack_installing'
|
||||||
|
| 'not_installed'
|
||||||
|
|
||||||
type LinkedData = {
|
type LinkedData = {
|
||||||
project_id: ModrinthId
|
project_id: ModrinthId
|
||||||
|
|||||||
@ -30,9 +30,23 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<ButtonStyled v-if="instance.install_stage !== 'installed'" color="brand" size="large">
|
<ButtonStyled
|
||||||
|
v-if="instance.install_stage.includes('installing')"
|
||||||
|
color="brand"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
<button disabled>Installing...</button>
|
<button disabled>Installing...</button>
|
||||||
</ButtonStyled>
|
</ButtonStyled>
|
||||||
|
<ButtonStyled
|
||||||
|
v-else-if="instance.install_stage !== 'installed'"
|
||||||
|
color="brand"
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
<button @click="repairInstance()">
|
||||||
|
<DownloadIcon />
|
||||||
|
Repair
|
||||||
|
</button>
|
||||||
|
</ButtonStyled>
|
||||||
<ButtonStyled v-else-if="playing === true" color="red" size="large">
|
<ButtonStyled v-else-if="playing === true" color="red" size="large">
|
||||||
<button @click="stopInstance('InstancePage')">
|
<button @click="stopInstance('InstancePage')">
|
||||||
<StopCircleIcon />
|
<StopCircleIcon />
|
||||||
@ -137,38 +151,39 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
ContentPageHeader,
|
|
||||||
ButtonStyled,
|
ButtonStyled,
|
||||||
OverflowMenu,
|
ContentPageHeader,
|
||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
|
OverflowMenu,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import {
|
import {
|
||||||
UserPlusIcon,
|
|
||||||
ServerIcon,
|
|
||||||
PackageIcon,
|
|
||||||
SettingsIcon,
|
|
||||||
PlayIcon,
|
|
||||||
StopCircleIcon,
|
|
||||||
EditIcon,
|
|
||||||
FolderOpenIcon,
|
|
||||||
ClipboardCopyIcon,
|
|
||||||
PlusIcon,
|
|
||||||
ExternalIcon,
|
|
||||||
HashIcon,
|
|
||||||
GlobeIcon,
|
|
||||||
EyeIcon,
|
|
||||||
XIcon,
|
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
UpdatedIcon,
|
ClipboardCopyIcon,
|
||||||
MoreVerticalIcon,
|
DownloadIcon,
|
||||||
|
EditIcon,
|
||||||
|
ExternalIcon,
|
||||||
|
EyeIcon,
|
||||||
|
FolderOpenIcon,
|
||||||
GameIcon,
|
GameIcon,
|
||||||
|
GlobeIcon,
|
||||||
|
HashIcon,
|
||||||
|
MoreVerticalIcon,
|
||||||
|
PackageIcon,
|
||||||
|
PlayIcon,
|
||||||
|
PlusIcon,
|
||||||
|
ServerIcon,
|
||||||
|
SettingsIcon,
|
||||||
|
StopCircleIcon,
|
||||||
TimerIcon,
|
TimerIcon,
|
||||||
|
UpdatedIcon,
|
||||||
|
UserPlusIcon,
|
||||||
|
XIcon,
|
||||||
} from '@modrinth/assets'
|
} from '@modrinth/assets'
|
||||||
import { get, get_full_path, kill, run } from '@/helpers/profile'
|
import { finish_install, get, get_full_path, kill, run } from '@/helpers/profile'
|
||||||
import { get_by_profile_path } from '@/helpers/process'
|
import { get_by_profile_path } from '@/helpers/process'
|
||||||
import { process_listener, profile_listener } from '@/helpers/events'
|
import { process_listener, profile_listener } from '@/helpers/events'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { ref, onUnmounted, computed, watch } from 'vue'
|
import { computed, onUnmounted, ref, watch } from 'vue'
|
||||||
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
|
||||||
import { showProfileInFolder } from '@/helpers/utils.js'
|
import { showProfileInFolder } from '@/helpers/utils.js'
|
||||||
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
import ContextMenu from '@/components/ui/ContextMenu.vue'
|
||||||
@ -294,6 +309,10 @@ const stopInstance = async (context) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const repairInstance = async () => {
|
||||||
|
await finish_install(instance.value)
|
||||||
|
}
|
||||||
|
|
||||||
const handleRightClick = (event) => {
|
const handleRightClick = (event) => {
|
||||||
const baseOptions = [
|
const baseOptions = [
|
||||||
{ name: 'add_content' },
|
{ name: 'add_content' },
|
||||||
|
|||||||
@ -2,14 +2,14 @@ import { defineStore } from 'pinia'
|
|||||||
import {
|
import {
|
||||||
add_project_from_version,
|
add_project_from_version,
|
||||||
check_installed,
|
check_installed,
|
||||||
list,
|
|
||||||
get,
|
get,
|
||||||
get_projects,
|
get_projects,
|
||||||
|
list,
|
||||||
remove_project,
|
remove_project,
|
||||||
} from '@/helpers/profile.js'
|
} from '@/helpers/profile.js'
|
||||||
import { handleError } from '@/store/notifications.js'
|
import { handleError } from '@/store/notifications.js'
|
||||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||||
import { install as packInstall } from '@/helpers/pack.js'
|
import { create_profile_and_install as packInstall } from '@/helpers/pack.js'
|
||||||
import { trackEvent } from '@/helpers/analytics.js'
|
import { trackEvent } from '@/helpers/analytics.js'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::pack::install_from::{
|
|||||||
};
|
};
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
CacheBehaviour, CachedEntry, Credentials, JavaVersion, ProcessMetadata,
|
CacheBehaviour, CachedEntry, Credentials, JavaVersion, ProcessMetadata,
|
||||||
ProfileFile, ProjectType, SideType,
|
ProfileFile, ProfileInstallStage, ProjectType, SideType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::event::{emit::emit_profile, ProfilePayloadType};
|
use crate::event::{emit::emit_profile, ProfilePayloadType};
|
||||||
@ -225,7 +225,18 @@ pub async fn list() -> crate::Result<Vec<Profile>> {
|
|||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn install(path: &str, force: bool) -> crate::Result<()> {
|
pub async fn install(path: &str, force: bool) -> crate::Result<()> {
|
||||||
if let Some(profile) = get(path).await? {
|
if let Some(profile) = get(path).await? {
|
||||||
crate::launcher::install_minecraft(&profile, None, force).await?;
|
let result =
|
||||||
|
crate::launcher::install_minecraft(&profile, None, force).await;
|
||||||
|
if result.is_err()
|
||||||
|
&& profile.install_stage != ProfileInstallStage::Installed
|
||||||
|
{
|
||||||
|
edit(path, |prof| {
|
||||||
|
prof.install_stage = ProfileInstallStage::NotInstalled;
|
||||||
|
async { Ok(()) }
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
result?;
|
||||||
} else {
|
} else {
|
||||||
return Err(crate::ErrorKind::UnmanagedProfileError(path.to_string())
|
return Err(crate::ErrorKind::UnmanagedProfileError(path.to_string())
|
||||||
.as_error());
|
.as_error());
|
||||||
|
|||||||
@ -111,7 +111,7 @@ async fn replace_managed_modrinth(
|
|||||||
ignore_lock: bool,
|
ignore_lock: bool,
|
||||||
) -> crate::Result<()> {
|
) -> crate::Result<()> {
|
||||||
crate::profile::edit(profile_path, |profile| {
|
crate::profile::edit(profile_path, |profile| {
|
||||||
profile.install_stage = ProfileInstallStage::Installing;
|
profile.install_stage = ProfileInstallStage::MinecraftInstalling;
|
||||||
async { Ok(()) }
|
async { Ok(()) }
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@ -7,11 +7,7 @@ use crate::state::{
|
|||||||
Credentials, JavaVersion, ProcessMetadata, ProfileInstallStage,
|
Credentials, JavaVersion, ProcessMetadata, ProfileInstallStage,
|
||||||
};
|
};
|
||||||
use crate::util::io;
|
use crate::util::io;
|
||||||
use crate::{
|
use crate::{process, state as st, State};
|
||||||
process,
|
|
||||||
state::{self as st},
|
|
||||||
State,
|
|
||||||
};
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use daedalus as d;
|
use daedalus as d;
|
||||||
use daedalus::minecraft::{RuleAction, VersionInfo};
|
use daedalus::minecraft::{RuleAction, VersionInfo};
|
||||||
@ -199,7 +195,7 @@ pub async fn install_minecraft(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
crate::api::profile::edit(&profile.path, |prof| {
|
crate::api::profile::edit(&profile.path, |prof| {
|
||||||
prof.install_stage = ProfileInstallStage::Installing;
|
prof.install_stage = ProfileInstallStage::MinecraftInstalling;
|
||||||
|
|
||||||
async { Ok(()) }
|
async { Ok(()) }
|
||||||
})
|
})
|
||||||
@ -431,7 +427,7 @@ pub async fn launch_minecraft(
|
|||||||
profile: &Profile,
|
profile: &Profile,
|
||||||
) -> crate::Result<ProcessMetadata> {
|
) -> crate::Result<ProcessMetadata> {
|
||||||
if profile.install_stage == ProfileInstallStage::PackInstalling
|
if profile.install_stage == ProfileInstallStage::PackInstalling
|
||||||
|| profile.install_stage == ProfileInstallStage::Installing
|
|| profile.install_stage == ProfileInstallStage::MinecraftInstalling
|
||||||
{
|
{
|
||||||
return Err(crate::ErrorKind::LauncherError(
|
return Err(crate::ErrorKind::LauncherError(
|
||||||
"Profile is still installing".to_string(),
|
"Profile is still installing".to_string(),
|
||||||
|
|||||||
@ -308,7 +308,7 @@ where
|
|||||||
ProfileInstallStage::Installed
|
ProfileInstallStage::Installed
|
||||||
}
|
}
|
||||||
LegacyProfileInstallStage::Installing => {
|
LegacyProfileInstallStage::Installing => {
|
||||||
ProfileInstallStage::Installing
|
ProfileInstallStage::MinecraftInstalling
|
||||||
}
|
}
|
||||||
LegacyProfileInstallStage::PackInstalling => {
|
LegacyProfileInstallStage::PackInstalling => {
|
||||||
ProfileInstallStage::PackInstalling
|
ProfileInstallStage::PackInstalling
|
||||||
|
|||||||
@ -53,7 +53,9 @@ pub enum ProfileInstallStage {
|
|||||||
/// Profile is installed
|
/// Profile is installed
|
||||||
Installed,
|
Installed,
|
||||||
/// Profile's minecraft game is still installing
|
/// Profile's minecraft game is still installing
|
||||||
Installing,
|
MinecraftInstalling,
|
||||||
|
/// Pack is installed, but Minecraft installation has not begun
|
||||||
|
PackInstalled,
|
||||||
/// Profile created for pack, but the pack hasn't been fully installed yet
|
/// Profile created for pack, but the pack hasn't been fully installed yet
|
||||||
PackInstalling,
|
PackInstalling,
|
||||||
/// Profile is not installed
|
/// Profile is not installed
|
||||||
@ -64,7 +66,8 @@ impl ProfileInstallStage {
|
|||||||
pub fn as_str(&self) -> &'static str {
|
pub fn as_str(&self) -> &'static str {
|
||||||
match *self {
|
match *self {
|
||||||
Self::Installed => "installed",
|
Self::Installed => "installed",
|
||||||
Self::Installing => "installing",
|
Self::MinecraftInstalling => "minecraft_installing",
|
||||||
|
Self::PackInstalled => "pack_installed",
|
||||||
Self::PackInstalling => "pack_installing",
|
Self::PackInstalling => "pack_installing",
|
||||||
Self::NotInstalled => "not_installed",
|
Self::NotInstalled => "not_installed",
|
||||||
}
|
}
|
||||||
@ -73,7 +76,9 @@ impl ProfileInstallStage {
|
|||||||
pub fn from_str(val: &str) -> Self {
|
pub fn from_str(val: &str) -> Self {
|
||||||
match val {
|
match val {
|
||||||
"installed" => Self::Installed,
|
"installed" => Self::Installed,
|
||||||
"installing" => Self::Installing,
|
"minecraft_installing" => Self::MinecraftInstalling,
|
||||||
|
"installing" => Self::MinecraftInstalling, // Backwards compatibility
|
||||||
|
"pack_installed" => Self::PackInstalled,
|
||||||
"pack_installing" => Self::PackInstalling,
|
"pack_installing" => Self::PackInstalling,
|
||||||
"not_installed" => Self::NotInstalled,
|
"not_installed" => Self::NotInstalled,
|
||||||
_ => Self::NotInstalled,
|
_ => Self::NotInstalled,
|
||||||
@ -549,11 +554,11 @@ impl Profile {
|
|||||||
|
|
||||||
pub(crate) async fn refresh_all() -> crate::Result<()> {
|
pub(crate) async fn refresh_all() -> crate::Result<()> {
|
||||||
let state = crate::State::get().await?;
|
let state = crate::State::get().await?;
|
||||||
let all = Self::get_all(&state.pool).await?;
|
let mut all = Self::get_all(&state.pool).await?;
|
||||||
|
|
||||||
let mut keys = vec![];
|
let mut keys = vec![];
|
||||||
|
|
||||||
for profile in &all {
|
for profile in &mut all {
|
||||||
let path =
|
let path =
|
||||||
crate::api::profile::get_full_path(&profile.path).await?;
|
crate::api::profile::get_full_path(&profile.path).await?;
|
||||||
|
|
||||||
@ -586,6 +591,17 @@ impl Profile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if profile.install_stage == ProfileInstallStage::MinecraftInstalling
|
||||||
|
{
|
||||||
|
profile.install_stage = ProfileInstallStage::PackInstalled;
|
||||||
|
profile.upsert(&state.pool).await?;
|
||||||
|
} else if profile.install_stage
|
||||||
|
== ProfileInstallStage::PackInstalling
|
||||||
|
{
|
||||||
|
profile.install_stage = ProfileInstallStage::NotInstalled;
|
||||||
|
profile.upsert(&state.pool).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_hashes = CachedEntry::get_file_hash_many(
|
let file_hashes = CachedEntry::get_file_hash_many(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user