import { nextTick, ref, watch } from 'vue'; import { defineStore } from 'pinia'; import { toast } from 'vue-sonner'; import { instanceRequest } from '../api'; import { parseLocation } from '../shared/utils'; import { watchState } from '../service/watchState'; import configRepository from '../service/config'; export const useLaunchStore = defineStore('Launch', () => { const isLaunchOptionsDialogVisible = ref(false); const isOpeningInstance = ref(false); const launchDialogData = ref({ visible: false, loading: false, tag: '', shortName: '' }); watch( () => watchState.isLoggedIn, () => { isLaunchOptionsDialogVisible.value = false; }, { flush: 'sync' } ); function showLaunchOptions() { isLaunchOptionsDialogVisible.value = true; } /** * * @param {string} tag * @param {string} shortName * @returns {Promise} */ async function showLaunchDialog(tag, shortName = null) { launchDialogData.value = { visible: true, // flag, use for trigger adjustDialogZ loading: true, tag, shortName }; nextTick(() => (launchDialogData.value.loading = false)); } /** * * @param {string} location * @param {string} shortName * @returns {Promise} launchUrl */ async function getLaunchUrl(location, shortName) { const L = parseLocation(location); if ( shortName && L.instanceType !== 'public' && L.groupAccessType !== 'public' ) { return `vrchat://launch?ref=vrcx.app&id=${location}&shortName=${shortName}`; } // fetch shortName let newShortName = ''; const response = await instanceRequest.getInstanceShortName({ worldId: L.worldId, instanceId: L.instanceId }); if (response.json) { if (response.json.shortName) { newShortName = response.json.shortName; } else { newShortName = response.json.secureName; } } if (newShortName) { return `vrchat://launch?ref=vrcx.app&id=${location}&shortName=${newShortName}`; } return `vrchat://launch?ref=vrcx.app&id=${location}`; } /** * launch.exe &attach=1 * @param {string} location * @param {string} shortName * @returns {Promise} */ async function tryOpenInstanceInVrc(location, shortName) { if (isOpeningInstance.value) { return; } isOpeningInstance.value = true; let launchUrl = ''; let result = false; try { launchUrl = await getLaunchUrl(location, shortName); result = await AppApi.TryOpenInstanceInVrc(launchUrl); } catch (e) { console.error(e); } console.log('Attach Game', launchUrl, result); if (!result) { toast.warning( 'Failed open instance in VRChat, falling back to self invite' ); // self invite fallback try { const L = parseLocation(location); await instanceRequest.selfInvite({ instanceId: L.instanceId, worldId: L.worldId, shortName }); toast.success('Self invite sent'); } catch (e) { console.error(e); } } setTimeout(() => { isOpeningInstance.value = false; }, 1000); } /** * * @param {string} location * @param {string} shortName * @param {boolean} desktopMode * @returns {Promise} */ async function launchGame(location, shortName, desktopMode) { const launchUrl = await getLaunchUrl(location, shortName); const args = [launchUrl]; const launchArguments = await configRepository.getString('launchArguments'); const vrcLaunchPathOverride = await configRepository.getString( 'vrcLaunchPathOverride' ); if (launchArguments) { args.push(launchArguments); } if (desktopMode) { args.push('--no-vr'); } try { if (vrcLaunchPathOverride && !LINUX) { const result = await AppApi.StartGameFromPath( vrcLaunchPathOverride, args.join(' ') ); if (!result) { toast.error( 'Failed to launch VRChat, invalid custom path set' ); } else { toast.success('VRChat launched'); } } else { const result = await AppApi.StartGame(args.join(' ')); if (!result) { toast.error( 'Failed to find VRChat, set a custom path in launch options' ); } else { toast.success('VRChat launched'); } } } catch (e) { console.error(e); toast.error(`Failed to launch VRChat: ${e.message}`); } console.log('Launch Game', args.join(' '), desktopMode); } return { isLaunchOptionsDialogVisible, isOpeningInstance, launchDialogData, showLaunchOptions, showLaunchDialog, getLaunchUrl, launchGame, tryOpenInstanceInVrc }; });