Add Discord RPC options

This commit is contained in:
Natsumi
2025-08-23 11:45:29 +12:00
parent 4f94582731
commit c04047078c
6 changed files with 180 additions and 27 deletions

View File

@@ -156,7 +156,8 @@ namespace VRCX
string buttonText, string buttonText,
string buttonUrl, string buttonUrl,
string appId, string appId,
int activityType) int activityType,
int statusDisplayType)
{ {
_lock.EnterWriteLock(); _lock.EnterWriteLock();
try try
@@ -212,7 +213,7 @@ namespace VRCX
} }
_presence.Type = (ActivityType)activityType; _presence.Type = (ActivityType)activityType;
_presence.StatusDisplay = StatusDisplayType.Details; _presence.StatusDisplay = (StatusDisplayType)statusDisplayType;
Button[] buttons = []; Button[] buttons = [];
if (!string.IsNullOrEmpty(buttonUrl)) if (!string.IsNullOrEmpty(buttonUrl))

View File

@@ -516,13 +516,17 @@
"discord_presence": { "discord_presence": {
"discord_presence": { "discord_presence": {
"header": "Discord Presence", "header": "Discord Presence",
"description": "* Only active while VRChat is running.", "description": "* Only active whilst in an instance.",
"enable": "Enable", "enable": "Enable",
"enable_tooltip": "Recommended to disable Rich Presence in VRChat config.json to stop it from conflicting", "enable_tooltip": "Recommended to disable Rich Presence in VRChat config.json to stop it from conflicting",
"instance_type_player_count": "Instance type/player count", "instance_type_player_count": "Instance type & player count",
"join_button": "Join button (public only)", "join_button": "Join button (public only)",
"show_details_in_private": "Show world details in private", "show_details_in_private": "Show world details while in private",
"show_images": "Show world images" "show_images": "Show world images",
"show_current_platform": "Show current platform",
"world_integration": "World integration",
"world_integration_tooltip": "Show \"Watching\\Listening to\" video for Popcorn Palace, PyPyDance, VRDancing and LS Media",
"display_world_name_as_discord_status": "Display world name as Discord status"
}, },
"rpc": { "rpc": {
"vr": "VR", "vr": "VR",

View File

@@ -18,11 +18,31 @@ class ActivityType {
} }
} }
class StatusDisplayType {
static _Name = 0;
static _State = 1;
static _Details = 2;
static get Name() {
return this._Name;
}
static get State() {
return this._State;
}
static get Details() {
return this._Details;
}
}
Object.freeze(ActivityType); Object.freeze(ActivityType);
Object.freeze(StatusDisplayType);
Object.defineProperty(ActivityType, '_Playing', { writable: false }); Object.defineProperty(ActivityType, '_Playing', { writable: false });
Object.defineProperty(ActivityType, '_Listening', { writable: false }); Object.defineProperty(ActivityType, '_Listening', { writable: false });
Object.defineProperty(ActivityType, '_Watching', { writable: false }); Object.defineProperty(ActivityType, '_Watching', { writable: false });
Object.defineProperty(ActivityType, '_Competing', { writable: false }); Object.defineProperty(ActivityType, '_Competing', { writable: false });
Object.defineProperty(StatusDisplayType, '_Name', { writable: false });
Object.defineProperty(StatusDisplayType, '_State', { writable: false });
Object.defineProperty(StatusDisplayType, '_Details', { writable: false });
export { ActivityType }; export { ActivityType, StatusDisplayType };

View File

@@ -14,7 +14,10 @@ import { useGameLogStore } from '../gameLog';
import { useLocationStore } from '../location'; import { useLocationStore } from '../location';
import { useUpdateLoopStore } from '../updateLoop'; import { useUpdateLoopStore } from '../updateLoop';
import { useUserStore } from '../user'; import { useUserStore } from '../user';
import { ActivityType } from '../../shared/constants/discord'; import {
ActivityType,
StatusDisplayType
} from '../../shared/constants/discord';
import { useI18n } from 'vue-i18n-bridge'; import { useI18n } from 'vue-i18n-bridge';
export const useDiscordPresenceSettingsStore = defineStore( export const useDiscordPresenceSettingsStore = defineStore(
@@ -33,6 +36,9 @@ export const useDiscordPresenceSettingsStore = defineStore(
discordHideInvite: true, discordHideInvite: true,
discordJoinButton: false, discordJoinButton: false,
discordHideImage: false, discordHideImage: false,
discordShowPlatform: true,
discordWorldIntegration: true,
discordWorldNameAsDiscordStatus: false,
isDiscordActive: false, isDiscordActive: false,
lastLocationDetails: { lastLocationDetails: {
tag: '', tag: '',
@@ -55,13 +61,22 @@ export const useDiscordPresenceSettingsStore = defineStore(
discordInstance, discordInstance,
discordHideInvite, discordHideInvite,
discordJoinButton, discordJoinButton,
discordHideImage discordHideImage,
discordShowPlatform,
discordWorldIntegration,
discordWorldNameAsDiscordStatus
] = await Promise.all([ ] = await Promise.all([
configRepository.getBool('discordActive', false), configRepository.getBool('discordActive', false),
configRepository.getBool('discordInstance', true), configRepository.getBool('discordInstance', true),
configRepository.getBool('discordHideInvite', true), configRepository.getBool('discordHideInvite', true),
configRepository.getBool('discordJoinButton', false), configRepository.getBool('discordJoinButton', false),
configRepository.getBool('discordHideImage', false) configRepository.getBool('discordHideImage', false),
configRepository.getBool('discordShowPlatform', true),
configRepository.getBool('discordWorldIntegration', true),
configRepository.getBool(
'discordWorldNameAsDiscordStatus',
false
)
]); ]);
state.discordActive = discordActive; state.discordActive = discordActive;
@@ -69,6 +84,10 @@ export const useDiscordPresenceSettingsStore = defineStore(
state.discordHideInvite = discordHideInvite; state.discordHideInvite = discordHideInvite;
state.discordJoinButton = discordJoinButton; state.discordJoinButton = discordJoinButton;
state.discordHideImage = discordHideImage; state.discordHideImage = discordHideImage;
state.discordShowPlatform = discordShowPlatform;
state.discordWorldIntegration = discordWorldIntegration;
state.discordWorldNameAsDiscordStatus =
discordWorldNameAsDiscordStatus;
} }
const discordActive = computed(() => state.discordActive); const discordActive = computed(() => state.discordActive);
@@ -76,6 +95,13 @@ export const useDiscordPresenceSettingsStore = defineStore(
const discordHideInvite = computed(() => state.discordHideInvite); const discordHideInvite = computed(() => state.discordHideInvite);
const discordJoinButton = computed(() => state.discordJoinButton); const discordJoinButton = computed(() => state.discordJoinButton);
const discordHideImage = computed(() => state.discordHideImage); const discordHideImage = computed(() => state.discordHideImage);
const discordShowPlatform = computed(() => state.discordShowPlatform);
const discordWorldIntegration = computed(
() => state.discordWorldIntegration
);
const discordWorldNameAsDiscordStatus = computed(
() => state.discordWorldNameAsDiscordStatus
);
function setDiscordActive() { function setDiscordActive() {
state.discordActive = !state.discordActive; state.discordActive = !state.discordActive;
@@ -106,6 +132,28 @@ export const useDiscordPresenceSettingsStore = defineStore(
state.discordHideImage state.discordHideImage
); );
} }
function setDiscordShowPlatform() {
state.discordShowPlatform = !state.discordShowPlatform;
configRepository.setBool(
'discordShowPlatform',
state.discordShowPlatform
);
}
function setDiscordWorldIntegration() {
state.discordWorldIntegration = !state.discordWorldIntegration;
configRepository.setBool(
'discordWorldIntegration',
state.discordWorldIntegration
);
}
function setDiscordWorldNameAsDiscordStatus() {
state.discordWorldNameAsDiscordStatus =
!state.discordWorldNameAsDiscordStatus;
configRepository.setBool(
'discordWorldNameAsDiscordStatus',
state.discordWorldNameAsDiscordStatus
);
}
initDiscordPresenceSettings(); initDiscordPresenceSettings();
@@ -162,9 +210,30 @@ export const useDiscordPresenceSettingsStore = defineStore(
); );
} }
let platform = gameStore.isGameNoVR let platform = '';
? t('view.settings.discord_presence.rpc.desktop') if (state.discordShowPlatform) {
: t('view.settings.discord_presence.rpc.vr'); if (gameStore.isGameRunning) {
platform = gameStore.isGameNoVR
? ` (${t('view.settings.discord_presence.rpc.desktop')})`
: ` (${t('view.settings.discord_presence.rpc.vr')})`;
} else {
switch (userStore.currentUser.presence.platform) {
case 'web':
break;
case 'standalonewindows':
platform = ` (PC)`;
break;
case 'android':
platform = ` (Android)`;
break;
case 'ios':
platform = ` (iOS)`;
break;
default:
platform = ` (${userStore.currentUser.presence.platform})`;
}
}
}
if (L.groupAccessType) { if (L.groupAccessType) {
if (L.groupAccessType === 'public') { if (L.groupAccessType === 'public') {
state.lastLocationDetails.groupAccessType = t( state.lastLocationDetails.groupAccessType = t(
@@ -179,26 +248,26 @@ export const useDiscordPresenceSettingsStore = defineStore(
switch (L.accessType) { switch (L.accessType) {
case 'public': case 'public':
state.lastLocationDetails.joinUrl = getLaunchURL(L); state.lastLocationDetails.joinUrl = getLaunchURL(L);
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_public')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_public')} #${L.instanceName}${platform}`;
break; break;
case 'invite+': case 'invite+':
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_invite_plus')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_invite_plus')} #${L.instanceName}${platform}`;
break; break;
case 'invite': case 'invite':
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_invite')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_invite')} #${L.instanceName}${platform}`;
break; break;
case 'friends': case 'friends':
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_friend')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_friend')} #${L.instanceName}${platform}`;
break; break;
case 'friends+': case 'friends+':
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_friend_plus')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_friend_plus')} #${L.instanceName}${platform}`;
break; break;
case 'group': case 'group':
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_group')} #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_group')} #${L.instanceName}${platform}`;
try { try {
const groupName = await getGroupName(L.groupId); const groupName = await getGroupName(L.groupId);
if (groupName) { if (groupName) {
state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_group')}${state.lastLocationDetails.groupAccessType}(${groupName}) #${L.instanceName} (${platform})`; state.lastLocationDetails.accessName = `${t('dialog.new_instance.access_type_group')}${state.lastLocationDetails.groupAccessType}(${groupName}) #${L.instanceName}${platform}`;
} }
} catch (e) { } catch (e) {
console.error( console.error(
@@ -252,6 +321,9 @@ export const useDiscordPresenceSettingsStore = defineStore(
let stateText = state.lastLocationDetails.accessName; let stateText = state.lastLocationDetails.accessName;
let endTime = 0; let endTime = 0;
let activityType = ActivityType.Playing; let activityType = ActivityType.Playing;
let statusDisplayType = state.discordWorldNameAsDiscordStatus
? StatusDisplayType.Details
: StatusDisplayType.Name;
let appId = '883308884863901717'; let appId = '883308884863901717';
let bigIcon = 'vrchat'; let bigIcon = 'vrchat';
let detailsUrl = state.lastLocationDetails.worldLink; let detailsUrl = state.lastLocationDetails.worldLink;
@@ -282,7 +354,10 @@ export const useDiscordPresenceSettingsStore = defineStore(
buttonUrl = ''; buttonUrl = '';
} }
if (isRpcWorld(state.lastLocationDetails.tag)) { if (
isRpcWorld(state.lastLocationDetails.tag) &&
state.discordWorldIntegration
) {
// custom world rpc // custom world rpc
if ( if (
state.lastLocationDetails.worldId === state.lastLocationDetails.worldId ===
@@ -293,6 +368,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
'wrld_04899f23-e182-4a8d-b2c7-2c74c7c15534' 'wrld_04899f23-e182-4a8d-b2c7-2c74c7c15534'
) { ) {
activityType = ActivityType.Listening; activityType = ActivityType.Listening;
statusDisplayType = StatusDisplayType.Details;
appId = '784094509008551956'; appId = '784094509008551956';
bigIcon = 'pypy'; bigIcon = 'pypy';
} else if ( } else if (
@@ -302,6 +378,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
'wrld_dd6d2888-dbdc-47c2-bc98-3d631b2acd7c' 'wrld_dd6d2888-dbdc-47c2-bc98-3d631b2acd7c'
) { ) {
activityType = ActivityType.Listening; activityType = ActivityType.Listening;
statusDisplayType = StatusDisplayType.Details;
appId = '846232616054030376'; appId = '846232616054030376';
bigIcon = 'vr_dancing'; bigIcon = 'vr_dancing';
} else if ( } else if (
@@ -311,6 +388,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
'wrld_2d40da63-8f1f-4011-8a9e-414eb8530acd' 'wrld_2d40da63-8f1f-4011-8a9e-414eb8530acd'
) { ) {
activityType = ActivityType.Listening; activityType = ActivityType.Listening;
statusDisplayType = StatusDisplayType.Details;
appId = '939473404808007731'; appId = '939473404808007731';
bigIcon = 'zuwa_zuwa_dance'; bigIcon = 'zuwa_zuwa_dance';
} else if ( } else if (
@@ -324,6 +402,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
'wrld_f767d1c8-b249-4ecc-a56f-614e433682c8' 'wrld_f767d1c8-b249-4ecc-a56f-614e433682c8'
) { ) {
activityType = ActivityType.Watching; activityType = ActivityType.Watching;
statusDisplayType = StatusDisplayType.Details;
appId = '968292722391785512'; appId = '968292722391785512';
bigIcon = 'ls_media'; bigIcon = 'ls_media';
} else if ( } else if (
@@ -333,6 +412,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
'wrld_27c7e6b2-d938-447e-a270-3d1a873e2cf3' 'wrld_27c7e6b2-d938-447e-a270-3d1a873e2cf3'
) { ) {
activityType = ActivityType.Watching; activityType = ActivityType.Watching;
statusDisplayType = StatusDisplayType.Details;
appId = '1095440531821170820'; appId = '1095440531821170820';
if ( if (
!state.discordHideImage && !state.discordHideImage &&
@@ -374,6 +454,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
appId = '883308884863901717'; // default VRChat app id appId = '883308884863901717'; // default VRChat app id
bigIcon = 'vrchat'; bigIcon = 'vrchat';
activityType = ActivityType.Playing; activityType = ActivityType.Playing;
statusDisplayType = StatusDisplayType.Name;
} }
if (details.length < 2) { if (details.length < 2) {
// 글자 수가 짧으면 업데이트가 안된다.. // 글자 수가 짧으면 업데이트가 안된다..
@@ -399,7 +480,8 @@ export const useDiscordPresenceSettingsStore = defineStore(
buttonText, // button text buttonText, // button text
buttonUrl, // button url buttonUrl, // button url
appId, // app id appId, // app id
activityType // activity type activityType, // activity type
statusDisplayType // status display type
); );
} }
@@ -423,12 +505,18 @@ export const useDiscordPresenceSettingsStore = defineStore(
discordHideInvite, discordHideInvite,
discordJoinButton, discordJoinButton,
discordHideImage, discordHideImage,
discordShowPlatform,
discordWorldIntegration,
discordWorldNameAsDiscordStatus,
setDiscordActive, setDiscordActive,
setDiscordInstance, setDiscordInstance,
setDiscordHideInvite, setDiscordHideInvite,
setDiscordJoinButton, setDiscordJoinButton,
setDiscordHideImage, setDiscordHideImage,
setDiscordShowPlatform,
setDiscordWorldIntegration,
setDiscordWorldNameAsDiscordStatus,
updateDiscord, updateDiscord,
saveDiscordOption saveDiscordOption
}; };

View File

@@ -139,7 +139,8 @@ declare global {
buttonText: string, buttonText: string,
buttonUrl: string, buttonUrl: string,
appId: string, appId: string,
activityType: number activityType: number,
statusDisplayType: number
): Promise<void>; ): Promise<void>;
SetActive(active: boolean): Promise<boolean>; SetActive(active: boolean): Promise<boolean>;
}; };

View File

@@ -1189,14 +1189,25 @@
<div class="options-container-item"> <div class="options-container-item">
<span>{{ t('view.settings.discord_presence.discord_presence.description') }}</span> <span>{{ t('view.settings.discord_presence.discord_presence.description') }}</span>
</div> </div>
<div class="options-container-item" @click="showVRChatConfig" style="cursor: pointer">
<span>{{ t('view.settings.discord_presence.discord_presence.enable_tooltip') }}</span>
</div>
<br />
<simple-switch <simple-switch
:label="t('view.settings.discord_presence.discord_presence.enable')" :label="t('view.settings.discord_presence.discord_presence.enable')"
:value="discordActive" :value="discordActive"
:tooltip="t('view.settings.discord_presence.discord_presence.enable_tooltip')"
@change=" @change="
setDiscordActive(); setDiscordActive();
saveDiscordOption(); saveDiscordOption();
" /> " />
<simple-switch
:label="t('view.settings.discord_presence.discord_presence.world_integration')"
:value="discordWorldIntegration"
:disabled="!discordActive"
@change="
setDiscordWorldIntegration();
saveDiscordOption();
" />
<simple-switch <simple-switch
:label="t('view.settings.discord_presence.discord_presence.instance_type_player_count')" :label="t('view.settings.discord_presence.discord_presence.instance_type_player_count')"
:value="discordInstance" :value="discordInstance"
@@ -1205,6 +1216,14 @@
setDiscordInstance(); setDiscordInstance();
saveDiscordOption(); saveDiscordOption();
" /> " />
<simple-switch
:label="t('view.settings.discord_presence.discord_presence.show_current_platform')"
:value="discordShowPlatform"
:disabled="!discordActive || !discordInstance"
@change="
setDiscordShowPlatform();
saveDiscordOption();
" />
<simple-switch <simple-switch
:label="t('view.settings.discord_presence.discord_presence.show_details_in_private')" :label="t('view.settings.discord_presence.discord_presence.show_details_in_private')"
:value="!discordHideInvite" :value="!discordHideInvite"
@@ -1229,6 +1248,16 @@
setDiscordHideImage(); setDiscordHideImage();
saveDiscordOption(); saveDiscordOption();
" /> " />
<simple-switch
:label="
t('view.settings.discord_presence.discord_presence.display_world_name_as_discord_status')
"
:value="discordWorldNameAsDiscordStatus"
:disabled="!discordActive"
@change="
setDiscordWorldNameAsDiscordStatus();
saveDiscordOption();
" />
</div> </div>
</el-tab-pane> </el-tab-pane>
@@ -1881,9 +1910,16 @@
const { cachedGroups } = storeToRefs(useGroupStore()); const { cachedGroups } = storeToRefs(useGroupStore());
const { cachedAvatars, cachedAvatarNames } = storeToRefs(useAvatarStore()); const { cachedAvatars, cachedAvatarNames } = storeToRefs(useAvatarStore());
const { showConsole } = useVrcxStore(); const { showConsole } = useVrcxStore();
const { discordActive, discordInstance, discordHideInvite, discordJoinButton, discordHideImage } = storeToRefs( const {
useDiscordPresenceSettingsStore() discordActive,
); discordInstance,
discordHideInvite,
discordJoinButton,
discordHideImage,
discordShowPlatform,
discordWorldIntegration,
discordWorldNameAsDiscordStatus
} = storeToRefs(useDiscordPresenceSettingsStore());
const { disableGameLogDialog } = useGameLogStore(); const { disableGameLogDialog } = useGameLogStore();
const { const {
setDiscordActive, setDiscordActive,
@@ -1891,6 +1927,9 @@
setDiscordHideInvite, setDiscordHideInvite,
setDiscordJoinButton, setDiscordJoinButton,
setDiscordHideImage, setDiscordHideImage,
setDiscordShowPlatform,
setDiscordWorldIntegration,
setDiscordWorldNameAsDiscordStatus,
saveDiscordOption saveDiscordOption
} = useDiscordPresenceSettingsStore(); } = useDiscordPresenceSettingsStore();
const { const {