mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-26 18:23:47 +02:00
271 lines
9.2 KiB
JavaScript
271 lines
9.2 KiB
JavaScript
import { toast } from 'vue-sonner';
|
|
|
|
import {
|
|
deleteVRChatCache as _deleteVRChatCache,
|
|
isRealInstance
|
|
} from '../shared/utils';
|
|
import { database } from '../services/database';
|
|
import { useAdvancedSettingsStore } from '../stores/settings/advanced';
|
|
import { useAvatarStore } from '../stores/avatar';
|
|
import { addAvatarWearTime } from './avatarCoordinator';
|
|
import { useGameLogStore } from '../stores/gameLog';
|
|
import { useGameStore } from '../stores/game';
|
|
import { useInstanceStore } from '../stores/instance';
|
|
import { useLaunchStore } from '../stores/launch';
|
|
import { useLocationStore } from '../stores/location';
|
|
import { runLastLocationResetFlow } from './locationCoordinator';
|
|
import { useModalStore } from '../stores/modal';
|
|
import { useNotificationStore } from '../stores/notification';
|
|
import { useUpdateLoopStore } from '../stores/updateLoop';
|
|
import { useUserStore } from '../stores/user';
|
|
import { useVrStore } from '../stores/vr';
|
|
import { useWorldStore } from '../stores/world';
|
|
|
|
import configRepository from '../services/config';
|
|
|
|
import * as workerTimers from 'worker-timers';
|
|
|
|
/**
|
|
* Runs shared side effects when game running state changes.
|
|
* @param {boolean} isGameRunning Whether VRChat is running.
|
|
*/
|
|
export async function runGameRunningChangedFlow(isGameRunning) {
|
|
const userStore = useUserStore();
|
|
const instanceStore = useInstanceStore();
|
|
const updateLoopStore = useUpdateLoopStore();
|
|
const gameLogStore = useGameLogStore();
|
|
const vrStore = useVrStore();
|
|
const gameStore = useGameStore();
|
|
|
|
if (isGameRunning) {
|
|
userStore.markCurrentUserGameStarted();
|
|
} else {
|
|
await configRepository.setBool('isGameNoVR', gameStore.isGameNoVR);
|
|
// persist last session data before markCurrentUserGameStopped resets $online_for
|
|
const sessionStart = userStore.currentUser.$online_for;
|
|
const offlineAt = Date.now();
|
|
if (sessionStart && sessionStart > 0) {
|
|
const sessionDuration = offlineAt - sessionStart;
|
|
// set store state synchronously so UI reads it immediately
|
|
gameStore.setLastSession(sessionDuration, offlineAt);
|
|
await Promise.all([
|
|
configRepository.setString('VRCX_lastGameSessionMs', String(sessionDuration)),
|
|
configRepository.setString('VRCX_lastGameOfflineAt', String(offlineAt))
|
|
]);
|
|
}
|
|
userStore.markCurrentUserGameStopped();
|
|
instanceStore.removeAllQueuedInstances();
|
|
runAutoVRChatCacheManagementFlow();
|
|
runCheckIfGameCrashedFlow();
|
|
updateLoopStore.setIpcTimeout(0);
|
|
addAvatarWearTime(userStore.currentUser.currentAvatar);
|
|
}
|
|
|
|
runLastLocationResetFlow();
|
|
gameLogStore.clearNowPlaying();
|
|
vrStore.updateVRLastLocation();
|
|
workerTimers.setTimeout(() => runCheckVRChatDebugLoggingFlow(), 60000);
|
|
updateLoopStore.setNextDiscordUpdate(0);
|
|
}
|
|
|
|
/**
|
|
* Orchestrates the game running state update from IPC.
|
|
* @param {boolean} isGameRunningArg Game running flag from IPC.
|
|
* @param {boolean} isSteamVRRunningArg SteamVR running flag from IPC.
|
|
*/
|
|
export async function runUpdateIsGameRunningFlow(
|
|
isGameRunningArg,
|
|
isSteamVRRunningArg
|
|
) {
|
|
const gameStore = useGameStore();
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
const vrStore = useVrStore();
|
|
|
|
if (advancedSettingsStore.gameLogDisabled) {
|
|
return;
|
|
}
|
|
if (isGameRunningArg !== gameStore.isGameRunning) {
|
|
gameStore.setIsGameRunning(isGameRunningArg);
|
|
await runGameRunningChangedFlow(isGameRunningArg);
|
|
console.log(new Date(), 'isGameRunning', isGameRunningArg);
|
|
}
|
|
|
|
if (isSteamVRRunningArg !== gameStore.isSteamVRRunning) {
|
|
gameStore.setIsSteamVRRunning(isSteamVRRunningArg);
|
|
console.log('isSteamVRRunning:', isSteamVRRunningArg);
|
|
}
|
|
vrStore.updateOpenVR();
|
|
}
|
|
|
|
/**
|
|
* Orchestrates the HMD AFK state update from IPC.
|
|
* @param {boolean} isHmdAfkArg HMD AFK flag from VR polling.
|
|
*/
|
|
export function runUpdateIsHmdAfkFlow(isHmdAfkArg) {
|
|
const gameStore = useGameStore();
|
|
|
|
if (isHmdAfkArg !== gameStore.isHmdAfk) {
|
|
gameStore.setIsHmdAfk(isHmdAfkArg);
|
|
console.log('isHmdAfk', isHmdAfkArg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs auto cache management if enabled.
|
|
*/
|
|
function runAutoVRChatCacheManagementFlow() {
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
|
|
if (advancedSettingsStore.autoSweepVRChatCache) {
|
|
runSweepVRChatCacheFlow();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sweeps VRChat cache and refreshes cache size display if config dialog visible.
|
|
*/
|
|
export async function runSweepVRChatCacheFlow() {
|
|
const gameStore = useGameStore();
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
|
|
try {
|
|
const output = await AssetBundleManager.SweepCache();
|
|
console.log('SweepCache', output);
|
|
} catch (e) {
|
|
console.error('SweepCache failed', e);
|
|
}
|
|
if (advancedSettingsStore.isVRChatConfigDialogVisible) {
|
|
gameStore.getVRChatCacheSize();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes VRChat cache for a given ref and refreshes related stores.
|
|
* @param {object} ref Avatar or world reference payload.
|
|
*/
|
|
export async function runDeleteVRChatCacheFlow(ref) {
|
|
const gameStore = useGameStore();
|
|
const worldStore = useWorldStore();
|
|
const avatarStore = useAvatarStore();
|
|
|
|
await _deleteVRChatCache(ref);
|
|
gameStore.getVRChatCacheSize();
|
|
worldStore.updateVRChatWorldCache();
|
|
avatarStore.updateVRChatAvatarCache();
|
|
}
|
|
|
|
/**
|
|
* Checks if VRChat crashed and attempts to relaunch.
|
|
*/
|
|
export function runCheckIfGameCrashedFlow() {
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
const locationStore = useLocationStore();
|
|
const gameStore = useGameStore();
|
|
|
|
if (!advancedSettingsStore.relaunchVRChatAfterCrash) {
|
|
return;
|
|
}
|
|
const { location } = locationStore.lastLocation;
|
|
AppApi.VrcClosedGracefully().then((result) => {
|
|
if (result || !isRealInstance(location)) {
|
|
return;
|
|
}
|
|
// check if relaunched less than 2mins ago (prevent crash loop)
|
|
if (
|
|
gameStore.state.lastCrashedTime &&
|
|
new Date().getTime() - gameStore.state.lastCrashedTime.getTime() <
|
|
120_000
|
|
) {
|
|
console.log('VRChat was recently crashed, not relaunching');
|
|
return;
|
|
}
|
|
gameStore.setLastCrashedTime(new Date());
|
|
// wait a bit for SteamVR to potentially close before deciding to relaunch
|
|
let restartDelay = 8000;
|
|
if (gameStore.isGameNoVR) {
|
|
// wait for game to close before relaunching
|
|
restartDelay = 2000;
|
|
}
|
|
workerTimers.setTimeout(
|
|
() => runRestartCrashedGameFlow(location),
|
|
restartDelay
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Restarts VRChat after a crash.
|
|
* @param {string} location Last known location to relaunch.
|
|
*/
|
|
function runRestartCrashedGameFlow(location) {
|
|
const gameStore = useGameStore();
|
|
const notificationStore = useNotificationStore();
|
|
const gameLogStore = useGameLogStore();
|
|
const launchStore = useLaunchStore();
|
|
|
|
if (!gameStore.isGameNoVR && !gameStore.isSteamVRRunning) {
|
|
console.log("SteamVR isn't running, not relaunching VRChat");
|
|
return;
|
|
}
|
|
AppApi.FocusWindow();
|
|
const message = 'VRChat crashed, attempting to rejoin last instance';
|
|
toast(message);
|
|
const entry = {
|
|
created_at: new Date().toJSON(),
|
|
type: 'Event',
|
|
data: message
|
|
};
|
|
database.addGamelogEventToDatabase(entry);
|
|
notificationStore.queueGameLogNoty(entry);
|
|
gameLogStore.addGameLog(entry);
|
|
launchStore.launchGame(location, '', gameStore.isGameNoVR);
|
|
}
|
|
|
|
/**
|
|
* Checks and re-enables VRChat debug logging if disabled.
|
|
*/
|
|
export async function runCheckVRChatDebugLoggingFlow() {
|
|
const gameStore = useGameStore();
|
|
const advancedSettingsStore = useAdvancedSettingsStore();
|
|
const modalStore = useModalStore();
|
|
|
|
if (advancedSettingsStore.gameLogDisabled) {
|
|
return;
|
|
}
|
|
try {
|
|
const loggingEnabled =
|
|
await gameStore.getVRChatRegistryKey('LOGGING_ENABLED');
|
|
if (loggingEnabled === null || typeof loggingEnabled === 'undefined') {
|
|
// key not found
|
|
return;
|
|
}
|
|
if (parseInt(loggingEnabled, 10) === 1) {
|
|
// already enabled
|
|
return;
|
|
}
|
|
const result = await AppApi.SetVRChatRegistryKey(
|
|
'LOGGING_ENABLED',
|
|
'1',
|
|
4
|
|
);
|
|
if (!result) {
|
|
// failed to set key
|
|
modalStore.alert({
|
|
description:
|
|
'VRCX has noticed VRChat debug logging is disabled. VRCX requires debug logging in order to function correctly. Please enable debug logging in VRChat quick menu settings > debug > enable debug logging, then rejoin the instance or restart VRChat.',
|
|
title: 'Enable debug logging'
|
|
});
|
|
console.error('Failed to enable debug logging', result);
|
|
return;
|
|
}
|
|
modalStore.alert({
|
|
description:
|
|
'VRCX has noticed VRChat debug logging is disabled and automatically re-enabled it. VRCX requires debug logging in order to function correctly.',
|
|
title: 'Enabled debug logging'
|
|
});
|
|
console.log('Enabled debug logging');
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|