diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index 85e157a4..82dfb624 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -147,25 +147,6 @@ namespace VRCX VRCXVR.Instance.Restart(); } - /// - /// Returns an array of arrays containing information about the connected VR devices. - /// Each sub-array contains the type of device and its current state - /// - /// An array of arrays containing information about the connected VR devices. - public string[][] GetVRDevices() - { - return VRCXVR.Instance.GetDevices(); - } - - /// - /// Returns the current CPU usage as a percentage. - /// - /// The current CPU usage as a percentage. - public float CpuUsage() - { - return CpuMonitor.Instance.CpuUsage; - } - /// /// Retrieves an image from the VRChat API and caches it for future use. The function will return the cached image if it already exists. /// @@ -352,19 +333,6 @@ namespace VRCX WinformThemer.DoFunny(); } - /// - /// Returns the number of milliseconds that the system has been running. - /// - /// The number of milliseconds that the system has been running. - public double GetUptime() - { - using (var uptime = new PerformanceCounter("System", "System Up Time")) - { - uptime.NextValue(); - return TimeSpan.FromSeconds(uptime.NextValue()).TotalMilliseconds; - } - } - /// /// Returns a color value derived from the given user ID. /// This is, essentially, and is used for, random colors. diff --git a/Dotnet/AppApi/AppApiVr.cs b/Dotnet/AppApi/AppApiVr.cs new file mode 100644 index 00000000..82adf4c8 --- /dev/null +++ b/Dotnet/AppApi/AppApiVr.cs @@ -0,0 +1,62 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using CefSharp; + +namespace VRCX +{ + public class AppApiVr + { + public static readonly AppApiVr Instance; + + static AppApiVr() + { + Instance = new AppApiVr(); + } + + public void VrInit() + { + if (MainForm.Instance?.Browser != null && !MainForm.Instance.Browser.IsLoading && MainForm.Instance.Browser.CanExecuteJavascriptInMainFrame) + MainForm.Instance.Browser.ExecuteScriptAsync("$app.vrInit", ""); + } + + /// + /// Returns the current CPU usage as a percentage. + /// + /// The current CPU usage as a percentage. + public float CpuUsage() + { + return CpuMonitor.Instance.CpuUsage; + } + + /// + /// Returns an array of arrays containing information about the connected VR devices. + /// Each sub-array contains the type of device and its current state + /// + /// An array of arrays containing information about the connected VR devices. + public string[][] GetVRDevices() + { + return VRCXVR.Instance.GetDevices(); + } + + /// + /// Returns the number of milliseconds that the system has been running. + /// + /// The number of milliseconds that the system has been running. + public double GetUptime() + { + using var uptime = new PerformanceCounter("System", "System Up Time"); + uptime.NextValue(); + return TimeSpan.FromSeconds(uptime.NextValue()).TotalMilliseconds; + } + + /// + /// Returns the current language of the operating system. + /// + /// The current language of the operating system. + public string CurrentCulture() + { + return CultureInfo.CurrentCulture.ToString(); + } + } +} \ No newline at end of file diff --git a/Dotnet/AppApi/GameHandler.cs b/Dotnet/AppApi/GameHandler.cs index 2cad61bb..75c72c3a 100644 --- a/Dotnet/AppApi/GameHandler.cs +++ b/Dotnet/AppApi/GameHandler.cs @@ -58,51 +58,52 @@ namespace VRCX /// Starts the VRChat game process with the specified command-line arguments. /// /// The command-line arguments to pass to the VRChat game. - public void StartGame(string arguments) + public bool StartGame(string arguments) { // try stream first try { - using (var key = Registry.ClassesRoot.OpenSubKey(@"steam\shell\open\command")) + using var key = Registry.ClassesRoot.OpenSubKey(@"steam\shell\open\command"); + // "C:\Program Files (x86)\Steam\steam.exe" -- "%1" + var match = Regex.Match(key.GetValue(string.Empty) as string, "^\"(.+?)\\\\steam.exe\""); + if (match.Success) { - // "C:\Program Files (x86)\Steam\steam.exe" -- "%1" - var match = Regex.Match(key.GetValue(string.Empty) as string, "^\"(.+?)\\\\steam.exe\""); - if (match.Success) - { - var path = match.Groups[1].Value; - // var _arguments = Uri.EscapeDataString(arguments); - Process.Start(new ProcessStartInfo + var path = match.Groups[1].Value; + // var _arguments = Uri.EscapeDataString(arguments); + Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = $"{path}\\steam.exe", UseShellExecute = false, Arguments = $"-applaunch 438100 {arguments}" - }).Close(); - return; - } + }) + ?.Close(); + return true; } } catch { + logger.Warn("Failed to start VRChat from Steam"); } // fallback try { - using (var key = Registry.ClassesRoot.OpenSubKey(@"VRChat\shell\open\command")) + using var key = Registry.ClassesRoot.OpenSubKey(@"VRChat\shell\open\command"); + // "C:\Program Files (x86)\Steam\steamapps\common\VRChat\launch.exe" "%1" %* + var match = Regex.Match(key.GetValue(string.Empty) as string, "(?!\")(.+?\\\\VRChat.*)(!?\\\\launch.exe\")"); + if (match.Success) { - // "C:\Program Files (x86)\Steam\steamapps\common\VRChat\launch.exe" "%1" %* - var match = Regex.Match(key.GetValue(string.Empty) as string, "(?!\")(.+?\\\\VRChat.*)(!?\\\\launch.exe\")"); - if (match.Success) - { - var path = match.Groups[1].Value; - StartGameFromPath(path, arguments); - } + var path = match.Groups[1].Value; + return StartGameFromPath(path, arguments); } } catch { + logger.Warn("Failed to start VRChat from registry"); } + + return false; } /// @@ -116,7 +117,7 @@ namespace VRCX if (!path.EndsWith(".exe")) path = Path.Combine(path, "launch.exe"); - if (!File.Exists(path)) + if (!path.EndsWith("launch.exe") || !File.Exists(path)) return false; Process.Start(new ProcessStartInfo diff --git a/Dotnet/Util.cs b/Dotnet/JavascriptBindings.cs similarity index 64% rename from Dotnet/Util.cs rename to Dotnet/JavascriptBindings.cs index 73f1f60d..9105a654 100644 --- a/Dotnet/Util.cs +++ b/Dotnet/JavascriptBindings.cs @@ -1,11 +1,10 @@ -using System; using CefSharp; namespace VRCX { - public static class Util + public static class JavascriptBindings { - public static void ApplyJavascriptBindings(IJavascriptObjectRepository repository) + public static void ApplyAppJavascriptBindings(IJavascriptObjectRepository repository) { repository.NameConverter = null; repository.Register("AppApi", AppApi.Instance); @@ -17,5 +16,11 @@ namespace VRCX repository.Register("Discord", Discord.Instance); repository.Register("AssetBundleCacher", AssetBundleCacher.Instance); } + + public static void ApplyVrJavascriptBindings(IJavascriptObjectRepository repository) + { + repository.NameConverter = null; + repository.Register("AppApiVr", AppApiVr.Instance); + } } } diff --git a/Dotnet/MainForm.cs b/Dotnet/MainForm.cs index 94d6dde8..bed4bb06 100644 --- a/Dotnet/MainForm.cs +++ b/Dotnet/MainForm.cs @@ -63,7 +63,7 @@ namespace VRCX Browser.ShowDevTools(); }; - Util.ApplyJavascriptBindings(Browser.JavascriptObjectRepository); + JavascriptBindings.ApplyAppJavascriptBindings(Browser.JavascriptObjectRepository); Browser.ConsoleMessage += (_, args) => { jslogger.Debug(args.Message + " (" + args.Source + ":" + args.Line + ")"); diff --git a/Dotnet/Overlay/OffScreenBrowser.cs b/Dotnet/Overlay/OffScreenBrowser.cs index 48c2f871..e2c2f42d 100644 --- a/Dotnet/Overlay/OffScreenBrowser.cs +++ b/Dotnet/Overlay/OffScreenBrowser.cs @@ -37,7 +37,7 @@ namespace VRCX Size = new System.Drawing.Size(width, height); RenderHandler = this; - Util.ApplyJavascriptBindings(JavascriptObjectRepository); + JavascriptBindings.ApplyVrJavascriptBindings(JavascriptObjectRepository); } public new void Dispose() diff --git a/Dotnet/Overlay/VRForm.cs b/Dotnet/Overlay/VRForm.cs index 21bdcc3f..fe1f74ab 100644 --- a/Dotnet/Overlay/VRForm.cs +++ b/Dotnet/Overlay/VRForm.cs @@ -45,8 +45,8 @@ namespace VRCX Dock = DockStyle.Fill }; - Util.ApplyJavascriptBindings(_browser1.JavascriptObjectRepository); - Util.ApplyJavascriptBindings(_browser2.JavascriptObjectRepository); + JavascriptBindings.ApplyVrJavascriptBindings(_browser1.JavascriptObjectRepository); + JavascriptBindings.ApplyVrJavascriptBindings(_browser2.JavascriptObjectRepository); panel1.Controls.Add(_browser1); panel2.Controls.Add(_browser2); diff --git a/Dotnet/PWI/WorldDBManager.cs b/Dotnet/PWI/WorldDBManager.cs index 15c2c0b5..68354428 100644 --- a/Dotnet/PWI/WorldDBManager.cs +++ b/Dotnet/PWI/WorldDBManager.cs @@ -13,26 +13,41 @@ namespace VRCX { public class WorldDBManager { - public static WorldDBManager Instance; + public static readonly WorldDBManager Instance; private readonly HttpListener listener; private readonly WorldDatabase worldDB; private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); + private const string WorldDBServerUrl = "http://127.0.0.1:22500/"; private string lastError = null; private bool debugWorld = false; - public WorldDBManager(string url) + static WorldDBManager() + { + Instance = new WorldDBManager(); + } + + public WorldDBManager() { - Instance = this; // http://localhost:22500 listener = new HttpListener(); - listener.Prefixes.Add(url); + listener.Prefixes.Add(WorldDBServerUrl); worldDB = new WorldDatabase(Path.Combine(Program.AppDataDirectory, "VRCX-WorldData.db")); - } - public async Task Start() + public void Init() + { + if (VRCXStorage.Instance.Get("VRCX_DisableWorldDatabase") == "true") + { + logger.Info("World database is disabled. Not starting."); + return; + } + + Task.Run(Start); + } + + private async Task Start() { // typing this in vr gonna kms try @@ -223,9 +238,13 @@ namespace VRCX } var worldOverride = request.QueryString["world"]; + if (worldOverride == "global") + { + TryInitializeWorld(worldOverride, out connectionKey); + } if (worldOverride != null && worldId != worldOverride) { - var allowed = worldDB.GetWorldAllowExternalRead(worldOverride); + var allowed = worldDB.GetWorldAllowExternalRead(worldOverride) || worldOverride == "global"; if (!allowed) { return ConstructSuccessResponse(null, connectionKey); @@ -262,9 +281,13 @@ namespace VRCX } var worldOverride = request.QueryString["world"]; + if (worldOverride == "global") + { + TryInitializeWorld(worldOverride, out connectionKey); + } if (worldOverride != null && worldId != worldOverride) { - var allowed = worldDB.GetWorldAllowExternalRead(worldOverride); + var allowed = worldDB.GetWorldAllowExternalRead(worldOverride) || worldOverride == "global"; if (!allowed) { return ConstructSuccessResponse(null, connectionKey); @@ -319,9 +342,13 @@ namespace VRCX } var worldOverride = request.QueryString["world"]; + if (worldOverride == "global") + { + TryInitializeWorld(worldOverride, out connectionKey); + } if (worldOverride != null && worldId != worldOverride) { - var allowed = worldDB.GetWorldAllowExternalRead(worldOverride); + var allowed = worldDB.GetWorldAllowExternalRead(worldOverride) || worldOverride == "global"; if (!allowed) { return ConstructSuccessResponse(null, connectionKey); @@ -526,8 +553,6 @@ namespace VRCX return; } - - // Make sure the connection key is a valid GUID. No point in doing anything else if it's not. if (!debugWorld && !Guid.TryParse(request.ConnectionKey, out Guid _)) { @@ -539,6 +564,12 @@ namespace VRCX // Get the world ID from the connection key string worldId = worldDB.GetWorldByConnectionKey(request.ConnectionKey); + + // Global override + if (request.ConnectionKey == "global") + { + worldId = "global"; + } // World ID is null, which means the connection key is invalid (or someone just deleted a world from the DB while VRCX was running lol). if (worldId == null) diff --git a/Dotnet/Program.cs b/Dotnet/Program.cs index 755cb8d0..8c00ce3b 100644 --- a/Dotnet/Program.cs +++ b/Dotnet/Program.cs @@ -135,16 +135,14 @@ namespace VRCX Application.SetCompatibleTextRenderingDefault(false); logger.Info("{0} Starting...", Version); - - // I'll re-do this whole function eventually I swear - var worldDBServer = new WorldDBManager("http://127.0.0.1:22500/"); - Task.Run(worldDBServer.Start); + ProcessMonitor.Instance.Init(); SQLiteLegacy.Instance.Init(); VRCXStorage.Load(); CpuMonitor.Instance.Init(); Discord.Instance.Init(); + WorldDBManager.Instance.Init(); WebApi.Instance.Init(); LogWatcher.Instance.Init(); AutoAppLaunchManager.Instance.Init(); @@ -161,7 +159,7 @@ namespace VRCX AutoAppLaunchManager.Instance.Exit(); LogWatcher.Instance.Exit(); WebApi.Instance.Exit(); - worldDBServer.Stop(); + WorldDBManager.Instance.Stop(); Discord.Instance.Exit(); CpuMonitor.Instance.Exit(); diff --git a/VRCX.csproj b/VRCX.csproj index df73160d..c2e9eddd 100644 --- a/VRCX.csproj +++ b/VRCX.csproj @@ -117,12 +117,12 @@ - - + + - + diff --git a/html/.eslintrc.json b/html/.eslintrc.json index 426b210f..c8279f6a 100644 --- a/html/.eslintrc.json +++ b/html/.eslintrc.json @@ -29,6 +29,7 @@ "LogWatcher": "readonly", "Discord": "readonly", "AppApi": "readonly", + "AppApiVr": "readonly", "SharedVariable": "readonly", "WebApi": "readonly", "AssetBundleCacher": "readonly" diff --git a/html/src/app.js b/html/src/app.js index b4ee7592..ecffdd3c 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -643,8 +643,8 @@ speechSynthesis.getVoices(); options.N > 0 ? options.N > options.params.offset : options.N < 0 - ? args.json.length - : options.params.n === args.json.length) + ? args.json.length + : options.params.n === args.json.length) ) { this.bulk(options); } else if ('done' in options) { @@ -4684,7 +4684,7 @@ speechSynthesis.getVoices(); } this.errorNoty = new Noty({ type: 'error', - text: `WebSocket Error: ${err}` + text: escapeTag(`WebSocket Error: ${err}`) }).show(); return; } @@ -14687,14 +14687,10 @@ speechSynthesis.getVoices(); 'VRCX_StartAtWindowsStartup', false ); - $app.data.isStartAsMinimizedState = false; - $app.data.isCloseToTray = false; - VRCXStorage.Get('VRCX_StartAsMinimizedState').then((result) => { - $app.isStartAsMinimizedState = result === 'true'; - }); - VRCXStorage.Get('VRCX_CloseToTray').then((result) => { - $app.isCloseToTray = result === 'true'; - }); + $app.data.isStartAsMinimizedState = + (await VRCXStorage.Get('VRCX_StartAsMinimizedState')) === 'true'; + $app.data.isCloseToTray = + (await VRCXStorage.Get('VRCX_CloseToTray')) === 'true'; if (await configRepository.getBool('VRCX_CloseToTray')) { // move back to JSON $app.data.isCloseToTray = @@ -14702,6 +14698,8 @@ speechSynthesis.getVoices(); VRCXStorage.Set('VRCX_CloseToTray', $app.data.isCloseToTray.toString()); await configRepository.remove('VRCX_CloseToTray'); } + $app.data.disableWorldDatabase = + (await VRCXStorage.Get('VRCX_DisableWorldDatabase')) === 'true'; $app.methods.saveVRCXWindowOption = async function () { await configRepository.setBool( 'VRCX_StartAtWindowsStartup', @@ -14712,6 +14710,10 @@ speechSynthesis.getVoices(); this.isStartAsMinimizedState.toString() ); VRCXStorage.Set('VRCX_CloseToTray', this.isCloseToTray.toString()); + VRCXStorage.Set( + 'VRCX_DisableWorldDatabase', + this.disableWorldDatabase.toString() + ); AppApi.SetStartup(this.isStartAtWindowsStartup); }; $app.data.photonEventOverlay = await configRepository.getBool( @@ -16662,6 +16664,16 @@ speechSynthesis.getVoices(); D.isShowAvatar = true; } }); + } else { + database + .getUserStats(D.ref, inCurrentWorld) + .then((ref1) => { + if (ref1.userId === D.id) { + D.lastSeen = ref1.created_at; + D.joinCount = ref1.joinCount; + D.timeSpent = ref1.timeSpent; + } + }); } API.getRepresentedGroup({ userId }).then((args1) => { D.representedGroup = args1.json; @@ -19541,14 +19553,25 @@ speechSynthesis.getVoices(); $app.launchOptionsDialog.visible = false; }); - $app.methods.updateLaunchOptions = async function () { + $app.methods.updateLaunchOptions = function () { var D = this.launchOptionsDialog; - D.visible = false; D.launchArguments = String(D.launchArguments) .replace(/\s+/g, ' ') .trim(); - await configRepository.setString('launchArguments', D.launchArguments); - await configRepository.setString( + configRepository.setString('launchArguments', D.launchArguments); + if ( + D.vrcLaunchPathOverride && + D.vrcLaunchPathOverride.endsWith('.exe') && + !D.vrcLaunchPathOverride.endsWith('launch.exe') + ) { + this.$message({ + message: + 'Invalid path, you must enter VRChat folder or launch.exe', + type: 'error' + }); + return; + } + configRepository.setString( 'vrcLaunchPathOverride', D.vrcLaunchPathOverride ); @@ -19556,6 +19579,7 @@ speechSynthesis.getVoices(); message: 'Updated launch options', type: 'success' }); + D.visible = false; }; $app.methods.showLaunchOptions = function () { @@ -20101,10 +20125,19 @@ speechSynthesis.getVoices(); } }); } else { - AppApi.StartGame(args.join(' ')); - this.$message({ - message: 'VRChat launched', - type: 'success' + AppApi.StartGame(args.join(' ')).then((result) => { + if (!result) { + this.$message({ + message: + 'Failed to find VRChat, set a custom path in launch options', + type: 'error' + }); + } else { + this.$message({ + message: 'VRChat launched', + type: 'success' + }); + } }); } console.log('Launch Game', args.join(' '), desktopMode); diff --git a/html/src/index.pug b/html/src/index.pug index df242651..91f9ea13 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -407,6 +407,14 @@ html i.el-icon-warning span.extra(v-if="userDialog.timeSpent === 0") - span.extra(v-else) {{ userDialog.timeSpent | timeToText }} + template(v-else) + .x-friend-item(@click="showPreviousInstancesUserDialog(userDialog.ref)") + .detail + span.name {{ $t('dialog.user.info.play_time') }} + el-tooltip(v-if="!hideTooltips" placement="top" style="margin-left:5px" :content="$t('dialog.user.info.accuracy_notice')") + i.el-icon-warning + span.extra(v-if="userDialog.timeSpent === 0") - + span.extra(v-else) {{ userDialog.timeSpent | timeToText }} .x-friend-item(style="cursor:default") el-tooltip(placement="top") template(#content) @@ -1486,7 +1494,7 @@ html el-button(type="primary" size="small" @click="createGroupInstance()" :disabled="!newInstanceDialog.groupId") {{ $t('dialog.new_instance.create_instance') }} //- dialog: launch options - el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchOptionsDialog" :visible.sync="launchOptionsDialog.visible" :title="$t('dialog.launch_options.header')" width="500px") + el-dialog.x-dialog(:before-close="beforeDialogClose" @mousedown.native="dialogMouseDown" @mouseup.native="dialogMouseUp" ref="launchOptionsDialog" :visible.sync="launchOptionsDialog.visible" :title="$t('dialog.launch_options.header')" width="600px") div(style="font-size:12px") | {{ $t('dialog.launch_options.description') }} #[br] | {{ $t('dialog.launch_options.example') }} #[el-tag(size="mini") --fps=144] diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index d1cf403a..cf321fd4 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -403,6 +403,10 @@ "header": "Automatically Manage Cache When Closing VRChat", "description": "Auto delete old versions from cache" }, + "disable_local_world_persistence": { + "header": "Disable Local World Persistence", + "description": "Disable localhost webserver (requires restart)" + }, "remote_database": { "header": "Remote Avatar Database", "enable": "Enable", @@ -582,6 +586,7 @@ "last_seen": "Last Seen", "join_count": "Join Count", "time_together": "Time Together", + "play_time": "Play Time", "online_for": "Online For", "offline_for": "Offline For", "last_activity": "Last Activity", @@ -592,7 +597,7 @@ "avatar_cloning": "Avatar Cloning", "avatar_cloning_allow": "Allowed", "avatar_cloning_deny": "Deny", - "home_location": "Home Location", + "home_location": "Home World", "id": "User ID", "id_tooltip": "Copy to clipboard", "copy_id": "Copy ID", diff --git a/html/src/mixins/tabs/settings.pug b/html/src/mixins/tabs/settings.pug index 1dee6990..8eed4827 100644 --- a/html/src/mixins/tabs/settings.pug +++ b/html/src/mixins/tabs/settings.pug @@ -415,6 +415,11 @@ mixin settingsTab() div.options-container-item span.name(style="min-width:300px") {{ $t('view.settings.advanced.advanced.auto_cache_management.description') }} el-switch(v-model="autoSweepVRChatCache" @change="saveOpenVROption") + //- Advanced | Disable local world database + span.sub-header {{ $t('view.settings.advanced.advanced.disable_local_world_persistence.header') }} + div.options-container-item + span.name(style="min-width:300px") {{ $t('view.settings.advanced.advanced.disable_local_world_persistence.description') }} + el-switch(v-model="disableWorldDatabase" @change="saveVRCXWindowOption") //- Advanced | Remote Avatar Database div.options-container span.header {{ $t('view.settings.advanced.advanced.remote_database.header') }} diff --git a/html/src/vr.js b/html/src/vr.js index 9885da01..7cd615e3 100644 --- a/html/src/vr.js +++ b/html/src/vr.js @@ -20,7 +20,7 @@ Vue.component('marquee-text', MarqueeText); (async function () { var $app = null; - await CefSharp.BindObjectAsync('AppApi'); + await CefSharp.BindObjectAsync('AppApiVr'); Noty.overrideDefaults({ animation: { @@ -50,6 +50,18 @@ Vue.component('marquee-text', MarqueeText); String(s).replace(/["&'<>]/gu, (c) => `&#${c.charCodeAt(0)};`); Vue.filter('escapeTag', escapeTag); + var escapeTagRecursive = (obj) => { + if (typeof obj === 'string') { + return escapeTag(obj); + } + if (typeof obj === 'object') { + for (var key in obj) { + obj[key] = escapeTagRecursive(obj[key]); + } + } + return obj; + }; + var commaNumber = (n) => String(Number(n) || 0).replace(/(\d)(?=(\d{3})+(?!\d))/gu, '$1,'); Vue.filter('commaNumber', commaNumber); @@ -216,10 +228,7 @@ Vue.component('marquee-text', MarqueeText); watch: {}, el: '#x-app', mounted() { - workerTimers.setTimeout( - () => AppApi.ExecuteAppFunction('vrInit', ''), - 1000 - ); + workerTimers.setTimeout(() => AppApiVr.VrInit(), 1000); if (this.appType === '1') { this.updateStatsLoop(); } @@ -410,7 +419,7 @@ Vue.component('marquee-text', MarqueeText); .replace(',', ''); if (!this.config.hideCpuUsageFromFeed) { - var cpuUsage = await AppApi.CpuUsage(); + var cpuUsage = await AppApiVr.CpuUsage(); this.cpuUsage = cpuUsage.toFixed(0); } if (this.lastLocation.date !== 0) { @@ -429,7 +438,7 @@ Vue.component('marquee-text', MarqueeText); } if (!this.config.hideDevicesFromFeed) { - AppApi.GetVRDevices().then((devices) => { + AppApiVr.GetVRDevices().then((devices) => { var deviceList = []; var baseStations = 0; devices.forEach((device) => { @@ -480,7 +489,7 @@ Vue.component('marquee-text', MarqueeText); this.devices = []; } if (this.config.pcUptimeOnFeed) { - AppApi.GetUptime().then((uptime) => { + AppApiVr.GetUptime().then((uptime) => { this.pcUptime = timeToText(uptime); }); } else { @@ -494,7 +503,12 @@ Vue.component('marquee-text', MarqueeText); $app.methods.playNoty = function (json) { var { noty, message, image } = JSON.parse(json); - var message = escapeTag(message); + if (typeof noty === 'undefined') { + console.error('noty is undefined'); + return; + } + var noty = escapeTagRecursive(noty); + var message = escapeTag(message) || ''; var text = ''; var img = ''; if (image) { @@ -515,8 +529,8 @@ Vue.component('marquee-text', MarqueeText); noty.displayName } is in ${this.displayLocation( noty.location, - escapeTag(noty.worldName), - escapeTag(noty.groupName) + noty.worldName, + noty.groupName )}`; break; case 'Online': @@ -524,8 +538,8 @@ Vue.component('marquee-text', MarqueeText); if (noty.worldName) { locationName = ` to ${this.displayLocation( noty.location, - escapeTag(noty.worldName), - escapeTag(noty.groupName) + noty.worldName, + noty.groupName )}`; } text = `${noty.displayName} has logged in${locationName}`; @@ -534,16 +548,14 @@ Vue.component('marquee-text', MarqueeText); text = `${noty.displayName} has logged out`; break; case 'Status': - text = `${noty.displayName} status is now ${ - noty.status - } ${escapeTag(noty.statusDescription)}`; + text = `${noty.displayName} status is now ${noty.status} ${noty.statusDescription}`; break; case 'invite': text = `${ noty.senderUsername } has invited you to ${this.displayLocation( noty.details.worldId, - escapeTag(noty.details.worldName) + noty.details.worldName )}${message}`; break; case 'requestInvite': @@ -571,19 +583,19 @@ Vue.component('marquee-text', MarqueeText); text = `${noty.previousDisplayName} changed their name to ${noty.displayName}`; break; case 'group.announcement': - text = escapeTag(noty.message); + text = noty.message; break; case 'group.informative': - text = escapeTag(noty.message); + text = noty.message; break; case 'group.invite': - text = escapeTag(noty.message); + text = noty.message; break; case 'group.joinRequest': - text = escapeTag(noty.message); + text = noty.message; break; case 'group.queueReady': - text = escapeTag(noty.message); + text = noty.message; break; case 'PortalSpawn': if (noty.displayName) { @@ -591,33 +603,27 @@ Vue.component('marquee-text', MarqueeText); noty.displayName } has spawned a portal to ${this.displayLocation( noty.instanceId, - escapeTag(noty.worldName), - escapeTag(noty.groupName) + noty.worldName, + noty.groupName )}`; } else { text = 'User has spawned a portal'; } break; case 'AvatarChange': - text = `${ - noty.displayName - } changed into avatar ${escapeTag(noty.name)}`; + text = `${noty.displayName} changed into avatar ${noty.name}`; break; case 'ChatBoxMessage': - text = `${noty.displayName} said ${escapeTag( - noty.text - )}`; + text = `${noty.displayName} said ${noty.text}`; break; case 'Event': - text = escapeTag(noty.data); + text = noty.data; break; case 'External': - text = escapeTag(noty.message); + text = noty.message; break; case 'VideoPlay': - text = `Now playing: ${escapeTag( - noty.notyName - )}`; + text = `Now playing: ${noty.notyName}`; break; case 'BlockedOnPlayerJoined': text = `Blocked user ${noty.displayName} has joined`; @@ -766,10 +772,10 @@ Vue.component('marquee-text', MarqueeText); this.hudTimeout = JSON.parse(json); }; - $app.data.currentCulture = await AppApi.CurrentCulture(); + $app.data.currentCulture = await AppApiVr.CurrentCulture(); $app.methods.setDatetimeFormat = async function () { - this.currentCulture = await AppApi.CurrentCulture(); + this.currentCulture = await AppApiVr.CurrentCulture(); var formatDate = function (date) { if (!date) { return '';