feat: Add an automatic app launcher (#541)

* refactor: Change OpenImageFolder to use a winapi call

It will open the given file in any existing explorer instances instead of opening a new shell every time, handle longer paths, and work with third-party filesystem viewers with winapi hooks(well, this one).

* feat: Add an automatic app launcher

The launcer will automatically launch apps in the 'startup' folder under the VRCX appdata folder when VRChat has started, and close them when VRChat dies(or is closed).

* refactor: Add new class for monitoring VRC processes

This replaces the old AppApi functionality that would poll all processes twice to grab the status of both VRChat and vrserver.exe every... *checks app.js* 500ms.

It also raises events for when a monitored process is started/closed, mainly for the new AppLauncher feature, which is now using this class instead of monitoring vrchat itself.

* refactor: Add tooltip for launch folder button

* docs: Add some notes on potential issues with Process.HasExited

* Change CheckGameRunning from polling to events

---------

Co-authored-by: Teacup <git@teadev.xyz>
This commit is contained in:
Natsumi
2023-05-10 16:57:25 +12:00
committed by GitHub
parent 4387719907
commit bb31ce5736
10 changed files with 537 additions and 66 deletions

View File

@@ -4885,6 +4885,7 @@ speechSynthesis.getVoices();
this.checkForVRCXUpdate();
}
});
AppApi.CheckGameRunning();
API.$on('SHOW_WORLD_DIALOG', (tag) => this.showWorldDialog(tag));
API.$on('SHOW_WORLD_DIALOG_SHORTNAME', (tag) =>
this.verifyShortName('', tag)
@@ -5045,45 +5046,12 @@ speechSynthesis.getVoices();
this.nextClearVRCXCacheCheck = this.clearVRCXCacheFrequency;
this.clearVRCXCache();
}
AppApi.CheckGameRunning().then(
([isGameRunning, isSteamVRRunning]) => {
this.updateOpenVR(isGameRunning, isSteamVRRunning);
if (isGameRunning !== this.isGameRunning) {
this.isGameRunning = isGameRunning;
if (isGameRunning) {
API.currentUser.$online_for = Date.now();
API.currentUser.$offline_for = '';
} else {
configRepository.setBool(
'isGameNoVR',
this.isGameNoVR
);
API.currentUser.$online_for = '';
API.currentUser.$offline_for = Date.now();
this.autoVRChatCacheManagement();
this.checkIfGameCrashed();
this.ipcTimeout = 0;
}
this.lastLocationReset();
this.clearNowPlaying();
this.updateVRLastLocation();
workerTimers.setTimeout(
() => this.checkVRChatDebugLogging(),
60000
);
this.nextDiscordUpdate = 0;
}
if (isSteamVRRunning !== this.isSteamVRRunning) {
this.isSteamVRRunning = isSteamVRRunning;
}
if (--this.nextDiscordUpdate <= 0) {
this.nextDiscordUpdate = 7;
if (this.discordActive) {
this.updateDiscord();
}
}
if (--this.nextDiscordUpdate <= 0) {
this.nextDiscordUpdate = 7;
if (this.discordActive) {
this.updateDiscord();
}
);
}
}
} catch (err) {
API.isRefreshFriendsLoading = false;
@@ -5092,6 +5060,43 @@ speechSynthesis.getVoices();
workerTimers.setTimeout(() => this.updateLoop(), 500);
};
$app.methods.updateIsGameRunning = function (
isGameRunning,
isSteamVRRunning
) {
console.log(
`updateIsGameRunning isGameRunning:${isGameRunning} isSteamVRRunning:${isSteamVRRunning}`
);
if (isGameRunning !== this.isGameRunning) {
this.isGameRunning = isGameRunning;
if (isGameRunning) {
API.currentUser.$online_for = Date.now();
API.currentUser.$offline_for = '';
} else {
configRepository.setBool('isGameNoVR', this.isGameNoVR);
API.currentUser.$online_for = '';
API.currentUser.$offline_for = Date.now();
this.autoVRChatCacheManagement();
this.checkIfGameCrashed();
this.ipcTimeout = 0;
}
this.lastLocationReset();
this.clearNowPlaying();
this.updateVRLastLocation();
workerTimers.setTimeout(
() => this.checkVRChatDebugLogging(),
60000
);
this.nextDiscordUpdate = 0;
console.log('isGameRunning changed', isGameRunning);
}
if (isSteamVRRunning !== this.isSteamVRRunning) {
this.isSteamVRRunning = isSteamVRRunning;
console.log('isSteamVRRunning changed', isSteamVRRunning);
}
this.updateOpenVR();
};
$app.data.debug = false;
$app.data.debugWebRequests = false;
$app.data.debugWebSocket = false;
@@ -9631,10 +9636,12 @@ speechSynthesis.getVoices();
case 'openvr-init':
this.isGameNoVR = false;
configRepository.setBool('isGameNoVR', this.isGameNoVR);
this.updateOpenVR();
break;
case 'desktop-mode':
this.isGameNoVR = true;
configRepository.setBool('isGameNoVR', this.isGameNoVR);
this.updateOpenVR();
break;
case 'udon-exception':
console.log('UdonException', gameLog.data);
@@ -13384,7 +13391,7 @@ speechSynthesis.getVoices();
this.updateVRConfigVars();
this.updateVRLastLocation();
AppApi.ExecuteVrOverlayFunction('notyClear', '');
this.updateOpenVR(this.isGameRunning, this.isSteamVRRunning);
this.updateOpenVR();
};
$app.methods.saveSortFavoritesOption = function () {
this.getLocalWorldFavorites();
@@ -13523,6 +13530,7 @@ speechSynthesis.getVoices();
if (!this.timeoutHudOverlay) {
AppApi.ExecuteVrOverlayFunction('updateHudTimeout', '[]');
}
this.updateOpenVR();
};
$app.data.logResourceLoad = configRepository.getBool(
'VRCX_logResourceLoad'
@@ -13973,12 +13981,12 @@ speechSynthesis.getVoices();
});
};
$app.methods.updateOpenVR = function (isGameRunning, isSteamVRRunning) {
$app.methods.updateOpenVR = function () {
if (
this.openVR &&
!this.isGameNoVR &&
isSteamVRRunning &&
(isGameRunning || this.openVRAlways)
this.isSteamVRRunning &&
(this.isGameRunning || this.openVRAlways)
) {
var hmdOverlay = false;
if (
@@ -15006,6 +15014,9 @@ speechSynthesis.getVoices();
});
$app.methods.showUserDialog = function (userId) {
if (!userId) {
return;
}
this.$nextTick(() => adjustDialogZ(this.$refs.userDialog.$el));
var D = this.userDialog;
D.id = userId;
@@ -20586,6 +20597,12 @@ speechSynthesis.getVoices();
this.VRChatConfigFile.screenshot_res_width = res.width;
};
// Auto Launch Shortcuts
$app.methods.openShortcutFolder = function () {
AppApi.OpenShortcutFolder();
};
// Screenshot Helper
$app.methods.saveScreenshotHelper = function () {
@@ -20747,7 +20764,7 @@ speechSynthesis.getVoices();
};
$app.methods.openImageFolder = function (path) {
AppApi.OpenImageFolder(path).then(() => {
AppApi.OpenFolderAndSelectItem(path).then(() => {
this.$message({
message: 'Opened image folder',
type: 'success'
@@ -20804,6 +20821,7 @@ speechSynthesis.getVoices();
this.progressPieFilter
);
this.updateVRLastLocation();
this.updateOpenVR();
};
$app.methods.showYouTubeApiDialog = function () {

View File

@@ -1418,6 +1418,9 @@ html
el-button(size="small" icon="el-icon-s-operation" @click="showVRChatConfig()") VRChat config.json
el-button(size="small" icon="el-icon-s-operation" @click="showLaunchOptions()") {{ $t('view.settings.advanced.advanced.launch_options') }}
el-button(size="small" icon="el-icon-picture" @click="showScreenshotMetadataDialog()") {{ $t('view.settings.advanced.advanced.screenshot_metadata') }}
el-button(size="small" icon="el-icon-folder" @click="openShortcutFolder()") {{ $t('view.settings.advanced.advanced.auto_launch') }}
el-tooltip(placement="top" style="margin-left:5px" :content="$t('view.settings.advanced.advanced.auto_launch_tooltip')")
i.el-icon-warning
div.options-container
span.sub-header {{ $t('view.settings.advanced.advanced.primary_password.header') }}
div.options-container-item
@@ -3880,7 +3883,7 @@ html
img(v-lazy="screenshotMetadataDialog.metadata.nextFilePath" style="height:700px")
br
template(v-if="screenshotMetadataDialog.metadata.error")
pre(v-text="screenshotMetadataDialog.metadata.error")
pre(v-text="screenshotMetadataDialog.metadata.error" style="white-space:pre-wrap;font-size:12px")
br
span(v-for="user in screenshotMetadataDialog.metadata.players" style="margin-top:5px")
span.x-link(v-text="user.displayName" @click="lookupUser(user)")

View File

@@ -354,6 +354,8 @@
"header": "Advanced",
"launch_options": "Launch Options",
"screenshot_metadata": "Screenshot Metadata",
"auto_launch": "Auto-Launch Folder",
"auto_launch_tooltip": "To auto-launch apps with VRChat, place shortcuts in this folder.",
"pending_offline": {
"header": "Pending Offline",
"description": "Delay before marking user as offline (fixes false positives)",