diff --git a/Dotnet/AppApi/OVRToolkit.cs b/Dotnet/AppApi/OVRToolkit.cs new file mode 100644 index 000000000..1fa34ab67 --- /dev/null +++ b/Dotnet/AppApi/OVRToolkit.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.Net.WebSockets; +using System.Threading.Tasks; +using Websocket.Client; + +namespace VRCX +{ + public partial class AppApi + { + private static readonly Uri _ovrtWebsocketUri = new("ws://127.0.0.1:11450/api"); + private IWebsocketClient _ovrtWebsocketClient; + private object _ovrtLock = new(); + + public void Initialize() + { + lock (_ovrtLock) + { + if (_ovrtWebsocketClient != null) + return; + + var dotnetWebsocketClientFactory = new Func(() => + { + var client = new ClientWebSocket + { + Options = + { + KeepAliveInterval = TimeSpan.FromSeconds(5), + } + }; + client.Options.SetRequestHeader("user-agent", Program.Version); + return client; + }); + + _ovrtWebsocketClient = new WebsocketClient(_ovrtWebsocketUri, dotnetWebsocketClientFactory) + { + Name = "OVRToolkit Websocket", + // Swap ReconnectTimeout when Ping is implemented + // ReconnectTimeout = TimeSpan.FromSeconds(20), + ReconnectTimeout = null, + ErrorReconnectTimeout = TimeSpan.FromSeconds(30), + }; + + _ovrtWebsocketClient.ReconnectionHappened.Subscribe(info => + { + logger.ConditionalDebug("[OVRToolkit Websocket] Reconnection happened, type: {0}", info?.Type.ToString()); + }); + _ovrtWebsocketClient.DisconnectionHappened.Subscribe(info => + { + logger.ConditionalDebug("[OVRToolkit Websocket] Disconnection happened, type: {0}", info?.Type.ToString()); + }); + _ovrtWebsocketClient.MessageReceived.Subscribe(msg => + { + logger.ConditionalDebug("[OVRToolkit Websocket] Message received: {0}", msg.Text); + }); + + _ovrtWebsocketClient.Start().Wait(); + + // Uncomment when Ping is implemented + // Task.Run(PingLoop); + } + } + + /// + /// Displays an OVRToolkit notification with the specified title and body. + /// HUD ntoficcation - Visible in the lower part of the HMD view and moves with the head. + /// + /// Whether or not to display a HUD notification. + /// Whether or not to display a Wrist notification. + /// The title of the notification. + /// The content of the notification. + /// [CURRENTLY UNUSED]The timeout of the notification. + /// [CURRENTLY UNUSED]The image of the notification. + public void OVRTNotification(bool hudNotification, bool wristNotification, string title, string body, int timeout, string image = "") + { + List messages = []; + + // Uncomment when Image notification is implemented + /* + if(!string.IsNullOrWhiteSpace(image) && File.Exists(image)) + { + image = Convert.ToBase64String(File.ReadAllBytes(image)); + } + else + { + image = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAAsTAAALEwEAmpwYAAAHaGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDUgNzkuMTYzNDk5LCAyMDE4LzA4LzEzLTE2OjQwOjIyICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMS0wNC0wOFQxNjozMzoxMCsxMjowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHhtcE1NOkRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1NTE2MWIyMi1hYzgxLTY3NDYtODAyYi1kODIzYWFmN2RjYjciIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3ZjJjNTA2ZS02YTVhLWRhNGEtOTg5Mi02NDZiMzQ0MGQxZTgiPiA8cGhvdG9zaG9wOkRvY3VtZW50QW5jZXN0b3JzPiA8cmRmOkJhZz4gPHJkZjpsaT5hZG9iZTpkb2NpZDpwaG90b3Nob3A6NmJmOGE5MTgtY2QzZS03OTRjLTk3NzktMzM0YjYwZWJiNTYyPC9yZGY6bGk+IDwvcmRmOkJhZz4gPC9waG90b3Nob3A6RG9jdW1lbnRBbmNlc3RvcnM+IDx4bXBNTTpIaXN0b3J5PiA8cmRmOlNlcT4gPHJkZjpsaSBzdEV2dDphY3Rpb249ImNyZWF0ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6N2YyYzUwNmUtNmE1YS1kYTRhLTk4OTItNjQ2YjM0NDBkMWU4IiBzdEV2dDp3aGVuPSIyMDIxLTA0LTA4VDE0OjU3OjAxKzEyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ0MgMjAxOSAoV2luZG93cykiLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOmJhM2ZjODI3LTM0ZjQtYjU0OC05ZGFiLTZhMTZlZmQzZjAxMSIgc3RFdnQ6d2hlbj0iMjAyMS0wNC0wOFQxNTowMTozMSsxMjowMCIgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDxyZGY6bGkgc3RFdnQ6YWN0aW9uPSJzYXZlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo2YTY5MmQzYi03ZTJkLTNiNGUtYTMzZC1hN2MwOTNlOGU0OTkiIHN0RXZ0OndoZW49IjIwMjEtMDQtMDhUMTY6MzM6MTArMTI6MDAiIHN0RXZ0OnNv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"; + } + */ + + if (hudNotification) + { + messages.Add(new OVRTMessage + { + messageType = "SendNotification", + json = System.Text.Json.JsonSerializer.Serialize(new OVRTNotificationMessage + { + title = title, + body = body + }) + }); + } + + if (wristNotification) + { + messages.Add(new OVRTMessage + { + messageType = "SendWristNotification", + json = System.Text.Json.JsonSerializer.Serialize(new OVRTNotificationMessage + { + body = title + " - " + body + }) + }); + } + + if (messages.Count > 0) + { + if (_ovrtWebsocketClient == null) + Initialize(); + + if (_ovrtWebsocketClient.IsRunning) + { + foreach (var message in messages) + { + _ovrtWebsocketClient.Send(System.Text.Json.JsonSerializer.Serialize(message)); + } + } + } + + } + + private async Task PingLoop() + { + var pingMessage = System.Text.Json.JsonSerializer.Serialize(new OVRTMessage + { + messageType = "Ping", + json = string.Empty, + }); + + while (true) + { + await Task.Delay(5000); + + if (_ovrtWebsocketClient?.IsRunning != true) + continue; + + _ovrtWebsocketClient?.Send(pingMessage); + } + } + + private struct OVRTMessage + { + public string messageType { get; set; } + public string json { get; set; } + } + + private struct OVRTNotificationMessage + { + public string title { get; set; } + public string body { get; set; } + } + } +} \ No newline at end of file diff --git a/Dotnet/AppApi/RegistryPlayerPrefs.cs b/Dotnet/AppApi/RegistryPlayerPrefs.cs index e0e11a793..7d421284d 100644 --- a/Dotnet/AppApi/RegistryPlayerPrefs.cs +++ b/Dotnet/AppApi/RegistryPlayerPrefs.cs @@ -282,7 +282,7 @@ public void OpenVrcRegJsonFileDialog() var thread = new Thread(() => { - using (var openFileDialog = new OpenFileDialog()) + using (var openFileDialog = new System.Windows.Forms.OpenFileDialog()) { openFileDialog.DefaultExt = ".json"; openFileDialog.Filter = "JSON Files (*.json)|*.json"; diff --git a/VRCX.csproj b/VRCX.csproj index 3f8097254..34f13db0d 100644 --- a/VRCX.csproj +++ b/VRCX.csproj @@ -130,6 +130,7 @@ + diff --git a/html/src/app.js b/html/src/app.js index d38bc0c3e..ef53161e7 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -6369,6 +6369,8 @@ speechSynthesis.getVoices(); playDesktopToast = true; } var playXSNotification = this.xsNotifications; + var playOvrtHudNotifications = this.ovrtHudNotifications; + var playOvrtWristNotifications = this.ovrtWristNotifications; var playOverlayNotification = false; if ( this.overlayNotifications && @@ -6399,7 +6401,13 @@ speechSynthesis.getVoices(); if (playNotificationTTS) { this.playNotyTTS(noty, message); } - if (playDesktopToast || playXSNotification || playOverlayNotification) { + if (playDesktopToast || playXSNotification || playOvrtHudNotifications || playOvrtWristNotifications || playOverlayNotification) { + // Currently images are not supported on OVRT, I have future-proofed the code for when they are. + // Remove this when OVRT supports images and uncomment the two if statements below. + if (playOvrtHudNotifications || playOvrtWristNotifications) { + this.displayOvrtNotification(playOvrtHudNotifications, playOvrtWristNotifications, noty, message, ''); + } + if (this.imageNotifications) { this.notySaveImage(noty).then((image) => { if (playXSNotification) { @@ -6411,6 +6419,9 @@ speechSynthesis.getVoices(); if (playOverlayNotification) { this.displayOverlayNotification(noty, message, image); } + //if (playOvrtHudNotifications || playOvrtWristNotifications) { + // this.displayOvrtNotification(playOvrtHudNotifications, playOvrtWristNotifications, noty, message, image); + //} }); } else { if (playXSNotification) { @@ -6422,6 +6433,9 @@ speechSynthesis.getVoices(); if (playOverlayNotification) { this.displayOverlayNotification(noty, message, ''); } + //if (playOvrtHudNotifications || playOvrtWristNotifications) { + // this.displayOvrtNotification(playOvrtHudNotifications, playOvrtWristNotifications, noty, message, ''); + //} } } }; @@ -6964,6 +6978,319 @@ speechSynthesis.getVoices(); } }; + $app.methods.displayOvrtNotification = function (playOvrtHudNotifications, playOvrtWristNotifications, noty, message, image) { + var timeout = Math.floor(parseInt(this.notificationTimeout, 10) / 1000); + switch (noty.type) { + case 'OnPlayerJoined': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has joined`, + timeout, + image + ); + break; + case 'OnPlayerLeft': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has left`, + timeout, + image + ); + break; + case 'OnPlayerJoining': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} is joining`, + timeout, + image + ); + break; + case 'GPS': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} is in ${this.displayLocation( + noty.location, + noty.worldName, + noty.groupName + )}`, + timeout, + image + ); + break; + case 'Online': + var locationName = ''; + if (noty.worldName) { + locationName = ` to ${this.displayLocation( + noty.location, + noty.worldName, + noty.groupName + )}`; + } + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has logged in${locationName}`, + timeout, + image + ); + break; + case 'Offline': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has logged out`, + timeout, + image + ); + break; + case 'Status': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} status is now ${noty.status} ${noty.statusDescription}`, + timeout, + image + ); + break; + case 'invite': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.senderUsername + } has invited you to ${this.displayLocation( + noty.details.worldId, + noty.details.worldName + )}${message}`, + timeout, + image + ); + break; + case 'requestInvite': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.senderUsername} has requested an invite${message}`, + timeout, + image + ); + break; + case 'inviteResponse': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.senderUsername} has responded to your invite${message}`, + timeout, + image + ); + break; + case 'requestInviteResponse': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.senderUsername} has responded to your invite request${message}`, + timeout, + image + ); + break; + case 'friendRequest': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.senderUsername} has sent you a friend request`, + timeout, + image + ); + break; + case 'Friend': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} is now your friend`, + timeout, + image + ); + break; + case 'Unfriend': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} is no longer your friend`, + timeout, + image + ); + break; + case 'TrustLevel': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} trust level is now ${noty.trustLevel}`, + timeout, + image + ); + break; + case 'DisplayName': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.previousDisplayName} changed their name to ${noty.displayName}`, + timeout, + image + ); + break; + case 'group.announcement': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'group.informative': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'group.invite': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'group.joinRequest': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'group.queueReady': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'instance.closed': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'PortalSpawn': + if (noty.displayName) { + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName + } has spawned a portal to ${this.displayLocation( + noty.instanceId, + noty.worldName, + noty.groupName + )}`, + timeout, + image + ); + } else { + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + 'User has spawned a portal', + timeout, + image + ); + } + break; + case 'AvatarChange': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} changed into avatar ${noty.name}`, + timeout, + image + ); + break; + case 'ChatBoxMessage': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} said ${noty.text}`, + timeout, + image + ); + break; + case 'Event': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.data, timeout, image); + break; + case 'External': + AppApi.OVRTNotification(playOvrtHudNotifications, playOvrtWristNotifications, 'VRCX', noty.message, timeout, image); + break; + case 'VideoPlay': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `Now playing: ${noty.notyName}`, + timeout, + image + ); + break; + case 'BlockedOnPlayerJoined': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `Blocked user ${noty.displayName} has joined`, + timeout, + image + ); + break; + case 'BlockedOnPlayerLeft': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `Blocked user ${noty.displayName} has left`, + timeout, + image + ); + break; + case 'MutedOnPlayerJoined': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `Muted user ${noty.displayName} has joined`, + timeout, + image + ); + break; + case 'MutedOnPlayerLeft': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `Muted user ${noty.displayName} has left`, + timeout, + image + ); + break; + case 'Blocked': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has blocked you`, + timeout, + image + ); + break; + case 'Unblocked': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has unblocked you`, + timeout, + image + ); + break; + case 'Muted': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has muted you`, + timeout, + image + ); + break; + case 'Unmuted': + AppApi.OVRTNotification( + playOvrtHudNotifications, playOvrtWristNotifications, + 'VRCX', + `${noty.displayName} has unmuted you`, + timeout, + image + ); + break; + } + }; + $app.methods.displayDesktopToast = function (noty, message, image) { switch (noty.type) { case 'OnPlayerJoined': @@ -14451,6 +14778,14 @@ speechSynthesis.getVoices(); 'VRCX_xsNotifications', true ); + $app.data.ovrtHudNotifications = await configRepository.getBool( + 'VRCX_ovrtHudNotifications', + true + ); + $app.data.ovrtWristNotifications = await configRepository.getBool( + 'VRCX_ovrtWristNotifications', + false + ); $app.data.imageNotifications = await configRepository.getBool( 'VRCX_imageNotifications', true @@ -14643,6 +14978,14 @@ speechSynthesis.getVoices(); 'VRCX_xsNotifications', this.xsNotifications ); + await configRepository.setBool( + 'VRCX_ovrtHudNotifications', + this.ovrtHudNotifications + ); + await configRepository.setBool( + 'VRCX_ovrtWristNotifications', + this.ovrtWristNotifications + ); await configRepository.setBool( 'VRCX_imageNotifications', this.imageNotifications diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index 6246a4f12..f2c3ac420 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -314,6 +314,8 @@ "overlay_notifications": "Overlay Notifications", "notification_position": "Notification Position", "xsoverlay_notifications": "XSOverlay Notifications", + "ovrtoolkit_hud_notifications": "OVR Toolkit HUD Notifications", + "ovrtoolkit_wrist_notifications": "OVR Toolkit Wrist Notifications", "user_images": "User Images (slower)", "notification_timeout": "Notification Timeout" }, diff --git a/html/src/mixins/tabs/settings.pug b/html/src/mixins/tabs/settings.pug index 217237cf3..ee323d81a 100644 --- a/html/src/mixins/tabs/settings.pug +++ b/html/src/mixins/tabs/settings.pug @@ -257,6 +257,12 @@ mixin settingsTab() div.options-container-item span.name {{ $t('view.settings.notifications.notifications.steamvr_notifications.xsoverlay_notifications') }} el-switch(v-model="xsNotifications" @change="saveOpenVROption") + div.options-container-item + span.name {{ $t('view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_hud_notifications') }} + el-switch(v-model="ovrtHudNotifications" @change="saveOpenVROption") + div.options-container-item + span.name {{ $t('view.settings.notifications.notifications.steamvr_notifications.ovrtoolkit_wrist_notifications') }} + el-switch(v-model="ovrtWristNotifications" @change="saveOpenVROption") div.options-container-item span.name {{ $t('view.settings.notifications.notifications.steamvr_notifications.user_images') }} el-switch(v-model="imageNotifications" @change="saveOpenVROption")