Discord RPC localization, trigger BlockedOnPlayerJoined when joining an instance

This commit is contained in:
Natsumi
2025-08-02 07:46:56 +12:00
parent 2fea5f25b9
commit 806748388b
6 changed files with 74 additions and 40 deletions
+19 -3
View File
@@ -70,6 +70,22 @@ namespace VRCX
if (_client == null && _active) if (_client == null && _active)
{ {
_client = new DiscordRpcClient(_discordAppId); _client = new DiscordRpcClient(_discordAppId);
_client.OnReady += (sender, e) =>
{
_logger.Info("Discord Rich Presence connected: {User}", e.User.DisplayName);
};
_client.OnError += (sender, e) =>
{
_logger.Error("Discord Rich Presence error: {Error}", e.Message);
};
_client.OnConnectionFailed += (sender, e) =>
{
_logger.Error("Discord Rich Presence connection failed: {Error}", e.Type);
};
_client.OnConnectionEstablished += (sender, e) =>
{
_logger.Info("Discord Rich Presence connection established");
};
if (!_client.Initialize()) if (!_client.Initialize())
{ {
_client.Dispose(); _client.Dispose();
@@ -123,7 +139,7 @@ namespace VRCX
public void SetAssets( public void SetAssets(
string details, string details,
string state, string state,
string stateUrl, string detailsUrl,
string largeKey, string largeKey,
string largeText, string largeText,
@@ -156,8 +172,8 @@ namespace VRCX
} }
_presence.Details = LimitByteLength(details, 127); _presence.Details = LimitByteLength(details, 127);
// _presence.DetailsUrl _presence.DetailsUrl = !string.IsNullOrEmpty(detailsUrl) ? detailsUrl : null;
_presence.StateUrl = !string.IsNullOrEmpty(stateUrl) ? stateUrl : null; // _presence.StateUrl
_presence.State = LimitByteLength(state, 127); _presence.State = LimitByteLength(state, 127);
_presence.Assets ??= new Assets(); _presence.Assets ??= new Assets();
+7
View File
@@ -522,6 +522,13 @@
"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 in private",
"show_images": "Show world images" "show_images": "Show world images"
},
"rpc": {
"vr": "VR",
"desktop": "Desktop",
"join_button": "Join",
"powered_by_vrcx": "Powered by VRCX",
"private_world": "Private World"
} }
}, },
"pictures": { "pictures": {
+3 -3
View File
@@ -2188,9 +2188,9 @@ export const useNotificationStore = defineStore('Notification', () => {
let bias; let bias;
// remove join/leave notifications when switching worlds // remove join/leave notifications when switching worlds
if ( if (
noty.type === 'OnPlayerJoined' || noty.type === 'OnPlayerJoined'
noty.type === 'BlockedOnPlayerJoined' || // noty.type === 'BlockedOnPlayerJoined' ||
noty.type === 'MutedOnPlayerJoined' // noty.type === 'MutedOnPlayerJoined'
) { ) {
bias = locationStore.lastLocation.date + 30 * 1000; // 30 secs bias = locationStore.lastLocation.date + 30 * 1000; // 30 secs
if (Date.parse(noty.created_at) <= bias) { if (Date.parse(noty.created_at) <= bias) {
+1 -1
View File
@@ -125,7 +125,7 @@ export const useAdvancedSettingsStore = defineStore('AdvancedSettings', () => {
state.progressPie = progressPie; state.progressPie = progressPie;
state.progressPieFilter = progressPieFilter; state.progressPieFilter = progressPieFilter;
state.showConfirmationOnSwitchAvatar = showConfirmationOnSwitchAvatar; state.showConfirmationOnSwitchAvatar = showConfirmationOnSwitchAvatar;
state.gameLogDisabled = gameLogDisabled === 'true'; state.gameLogDisabled = gameLogDisabled;
state.ugcFolderPath = ugcFolderPath; state.ugcFolderPath = ugcFolderPath;
state.autoDeleteOldPrints = autoDeleteOldPrints; state.autoDeleteOldPrints = autoDeleteOldPrints;
state.notificationOpacity = notificationOpacity; state.notificationOpacity = notificationOpacity;
+42 -31
View File
@@ -14,18 +14,18 @@ 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 { useAdvancedSettingsStore } from './advanced';
import { ActivityType } from '../../shared/constants/discord'; import { ActivityType } from '../../shared/constants/discord';
import { useI18n } from 'vue-i18n-bridge';
export const useDiscordPresenceSettingsStore = defineStore( export const useDiscordPresenceSettingsStore = defineStore(
'DiscordPresenceSettings', 'DiscordPresenceSettings',
() => { () => {
const locationStore = useLocationStore(); const locationStore = useLocationStore();
const gameStore = useGameStore(); const gameStore = useGameStore();
const advancedSettingsStore = useAdvancedSettingsStore();
const gameLogStore = useGameLogStore(); const gameLogStore = useGameLogStore();
const userStore = useUserStore(); const userStore = useUserStore();
const updateLoopStore = useUpdateLoopStore(); const updateLoopStore = useUpdateLoopStore();
const { t } = useI18n();
const state = reactive({ const state = reactive({
discordActive: false, discordActive: false,
@@ -116,7 +116,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
currentLocation = locationStore.lastLocationDestination; currentLocation = locationStore.lastLocationDestination;
startTime = locationStore.lastLocationDestinationTime; startTime = locationStore.lastLocationDestinationTime;
} }
if (advancedSettingsStore.gameLogDisabled) { if (!currentLocation) {
// game log disabled, use API location // game log disabled, use API location
currentLocation = userStore.currentUser.$locationTag; currentLocation = userStore.currentUser.$locationTag;
startTime = userStore.currentUser.$location_at; startTime = userStore.currentUser.$location_at;
@@ -125,12 +125,7 @@ export const useDiscordPresenceSettingsStore = defineStore(
userStore.currentUser.$travelingToLocation; userStore.currentUser.$travelingToLocation;
} }
} }
if ( if (!state.discordActive || !isRealInstance(currentLocation)) {
!state.discordActive ||
(!gameStore.isGameRunning &&
!advancedSettingsStore.gameLogDisabled) ||
!isRealInstance(currentLocation)
) {
setIsDiscordActive(false); setIsDiscordActive(false);
return; return;
} }
@@ -167,36 +162,44 @@ export const useDiscordPresenceSettingsStore = defineStore(
); );
} }
let platform = gameStore.isGameNoVR ? 'Desktop' : 'VR'; let platform = gameStore.isGameNoVR
? t('view.settings.discord_presence.rpc.desktop')
: t('view.settings.discord_presence.rpc.vr');
if (L.groupAccessType) { if (L.groupAccessType) {
if (L.groupAccessType === 'public') { if (L.groupAccessType === 'public') {
state.lastLocationDetails.groupAccessType = 'Public'; state.lastLocationDetails.groupAccessType = t(
'dialog.new_instance.group_access_type_public'
);
} else if (L.groupAccessType === 'plus') { } else if (L.groupAccessType === 'plus') {
state.lastLocationDetails.groupAccessType = 'Plus'; state.lastLocationDetails.groupAccessType = t(
'dialog.new_instance.group_access_type_plus'
);
} }
} }
switch (L.accessType) { switch (L.accessType) {
case 'public': case 'public':
state.lastLocationDetails.joinUrl = getLaunchURL(L); state.lastLocationDetails.joinUrl = getLaunchURL(L);
state.lastLocationDetails.accessName = `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 = `Invite+ #${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 = `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 = `Friends #${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 = `Friends+ #${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 = `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);
state.lastLocationDetails.accessName = `Group${state.lastLocationDetails.groupAccessType}(${groupName}) #${L.instanceName} (${platform})`; if (groupName) {
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(
`Failed to get group name for ${L.groupId}`, `Failed to get group name for ${L.groupId}`,
@@ -220,27 +223,27 @@ export const useDiscordPresenceSettingsStore = defineStore(
let statusImage = ''; let statusImage = '';
switch (userStore.currentUser.status) { switch (userStore.currentUser.status) {
case 'active': case 'active':
statusName = 'Online'; statusName = t('dialog.user.status.active');
statusImage = 'active'; statusImage = 'active';
break; break;
case 'join me': case 'join me':
statusName = 'Join Me'; statusName = t('dialog.user.status.join_me');
statusImage = 'joinme'; statusImage = 'joinme';
break; break;
case 'ask me': case 'ask me':
statusName = 'Ask Me'; statusName = t('dialog.user.status.ask_me');
statusImage = 'askme'; statusImage = 'askme';
if (state.discordHideInvite) { if (state.discordHideInvite) {
hidePrivate = true; hidePrivate = true;
} }
break; break;
case 'busy': case 'busy':
statusName = 'Do Not Disturb'; statusName = t('dialog.user.status.busy');
statusImage = 'busy'; statusImage = 'busy';
hidePrivate = true; hidePrivate = true;
break; break;
default: default:
statusName = 'Offline'; statusName = t('dialog.user.status.offline');
statusImage = 'offline'; statusImage = 'offline';
hidePrivate = true; hidePrivate = true;
break; break;
@@ -251,7 +254,10 @@ export const useDiscordPresenceSettingsStore = defineStore(
let activityType = ActivityType.Playing; let activityType = ActivityType.Playing;
let appId = '883308884863901717'; let appId = '883308884863901717';
let bigIcon = 'vrchat'; let bigIcon = 'vrchat';
let stateUrl = state.lastLocationDetails.worldLink; let detailsUrl = state.lastLocationDetails.worldLink;
let poweredBy = t(
'view.settings.discord_presence.rpc.powered_by_vrcx'
);
let partyId = `${state.lastLocationDetails.worldId}:${state.lastLocationDetails.instanceName}`; let partyId = `${state.lastLocationDetails.worldId}:${state.lastLocationDetails.instanceName}`;
let partySize = locationStore.lastLocation.playerList.size; let partySize = locationStore.lastLocation.playerList.size;
@@ -259,13 +265,17 @@ export const useDiscordPresenceSettingsStore = defineStore(
if (partySize > partyMaxSize) { if (partySize > partyMaxSize) {
partyMaxSize = partySize; partyMaxSize = partySize;
} }
if (partySize === 0) {
partyMaxSize = 0;
}
if (!state.discordInstance) { if (!state.discordInstance) {
partySize = 0; partySize = 0;
partyMaxSize = 0; partyMaxSize = 0;
stateText = ''; stateText = '';
} }
let buttonText = t(
let buttonText = 'Join'; 'view.settings.discord_presence.rpc.join_button'
);
let buttonUrl = state.lastLocationDetails.joinUrl; let buttonUrl = state.lastLocationDetails.joinUrl;
if (!state.discordJoinButton) { if (!state.discordJoinButton) {
buttonText = ''; buttonText = '';
@@ -356,12 +366,13 @@ export const useDiscordPresenceSettingsStore = defineStore(
partyMaxSize = 0; partyMaxSize = 0;
buttonText = ''; buttonText = '';
buttonUrl = ''; buttonUrl = '';
stateUrl = ''; detailsUrl = '';
details = 'Private'; details = t('view.settings.discord_presence.rpc.private_world');
stateText = ''; stateText = '';
startTime = 0; startTime = 0;
endTime = 0; endTime = 0;
appId = '883308884863901717'; // default VRChat app id appId = '883308884863901717'; // default VRChat app id
bigIcon = 'vrchat';
activityType = ActivityType.Playing; activityType = ActivityType.Playing;
} }
if (details.length < 2) { if (details.length < 2) {
@@ -371,10 +382,10 @@ export const useDiscordPresenceSettingsStore = defineStore(
Discord.SetAssets( Discord.SetAssets(
details, // main text details, // main text
stateText, // secondary text stateText, // secondary text
stateUrl, // state url detailsUrl, // details url
bigIcon, // big icon bigIcon, // big icon
'Powered by VRCX', // big icon hover text poweredBy, // big icon hover text
statusImage, // small icon statusImage, // small icon
statusName, // small icon hover text statusName, // small icon hover text
+1 -1
View File
@@ -126,7 +126,7 @@ declare global {
SetAssets( SetAssets(
details: string, details: string,
state: string, state: string,
stateUrl: string, detailsUrl: string,
bigIcon: string, bigIcon: string,
bigIconText: string, bigIconText: string,
smallIcon: string, smallIcon: string,