diff --git a/VRCOSC.Desktop/VRCOSC.Desktop.csproj b/VRCOSC.Desktop/VRCOSC.Desktop.csproj
index 9a61b70f..b126d80d 100644
--- a/VRCOSC.Desktop/VRCOSC.Desktop.csproj
+++ b/VRCOSC.Desktop/VRCOSC.Desktop.csproj
@@ -6,12 +6,12 @@
game.ico
app.manifest
0.0.0
- 2023.1022.0
+ 2023.1220.0
VRCOSC
VolcanicArts
VolcanicArts
enable
- 2023.1022.0
+ 2023.1220.0
diff --git a/VRCOSC.Game/ChatBox/Clips/Clip.cs b/VRCOSC.Game/ChatBox/Clips/Clip.cs
index a2ed30f9..ba9a4391 100644
--- a/VRCOSC.Game/ChatBox/Clips/Clip.cs
+++ b/VRCOSC.Game/ChatBox/Clips/Clip.cs
@@ -214,8 +214,6 @@ private void removeInvalidStates(List localStates)
var currentStates = AssociatedModules.Where(moduleName => appManager.ModuleManager.GetModule(moduleName)!.Enabled.Value && chatBoxManager.StateValues.ContainsKey(moduleName) && chatBoxManager.StateValues[moduleName] is not null).Select(moduleName => chatBoxManager.StateValues[moduleName]).ToList();
currentStates.Sort();
- if (!currentStates.Any()) return;
-
foreach (var clipState in localStates.ToImmutableList())
{
var clipStateStates = clipState.StateNames;
diff --git a/VRCOSC.Game/OSC/VRChat/VRChatOscClient.cs b/VRCOSC.Game/OSC/VRChat/VRChatOscClient.cs
index 2eeddef2..b7786ef0 100644
--- a/VRCOSC.Game/OSC/VRChat/VRChatOscClient.cs
+++ b/VRCOSC.Game/OSC/VRChat/VRChatOscClient.cs
@@ -6,6 +6,7 @@
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
+using osu.Framework.IO.Network;
using osu.Framework.Logging;
using VRC.OSCQuery;
using VRCOSC.Game.OSC.Client;
@@ -72,7 +73,8 @@ public void Reset()
{
if (QueryPort is null) return null;
- var url = $"http://127.0.0.1:{QueryPort}/avatar/parameters/{parameterName}";
+ var parameterNameEncoded = UrlEncoding.UrlEncode(parameterName);
+ var url = $"http://127.0.0.1:{QueryPort}/avatar/parameters/{parameterNameEncoded}";
var response = await client.GetAsync(new Uri(url));
var content = await response.Content.ReadAsStringAsync();
diff --git a/VRCOSC.Game/OpenVR/OVRHelper.cs b/VRCOSC.Game/OpenVR/OVRHelper.cs
index cf20ec09..6461d679 100644
--- a/VRCOSC.Game/OpenVR/OVRHelper.cs
+++ b/VRCOSC.Game/OpenVR/OVRHelper.cs
@@ -1,4 +1,4 @@
-// Copyright (c) VolcanicArts. Licensed under the GPL-3.0 License.
+// Copyright (c) VolcanicArts. Licensed under the GPL-3.0 License.
// See the LICENSE file in the repository root for full license text.
using System;
@@ -108,6 +108,8 @@ internal static IEnumerable GetIndexesForTrackedDeviceClass(ETrackedDevice
internal static bool GetBoolTrackedDeviceProperty(uint index, ETrackedDeviceProperty property)
{
+ if (index == Valve.VR.OpenVR.k_unTrackedDeviceIndexInvalid) return default;
+
var error = new ETrackedPropertyError();
var value = Valve.VR.OpenVR.System.GetBoolTrackedDeviceProperty(index, property, ref error);
@@ -119,6 +121,8 @@ internal static bool GetBoolTrackedDeviceProperty(uint index, ETrackedDeviceProp
internal static int GetInt32TrackedDeviceProperty(uint index, ETrackedDeviceProperty property)
{
+ if (index == Valve.VR.OpenVR.k_unTrackedDeviceIndexInvalid) return default;
+
var error = new ETrackedPropertyError();
var value = Valve.VR.OpenVR.System.GetInt32TrackedDeviceProperty(index, property, ref error);
@@ -130,6 +134,8 @@ internal static int GetInt32TrackedDeviceProperty(uint index, ETrackedDeviceProp
internal static float GetFloatTrackedDeviceProperty(uint index, ETrackedDeviceProperty property)
{
+ if (index == Valve.VR.OpenVR.k_unTrackedDeviceIndexInvalid) return default;
+
var error = new ETrackedPropertyError();
var value = Valve.VR.OpenVR.System.GetFloatTrackedDeviceProperty(index, property, ref error);
@@ -143,6 +149,8 @@ internal static float GetFloatTrackedDeviceProperty(uint index, ETrackedDevicePr
internal static string GetStringTrackedDeviceProperty(uint index, ETrackedDeviceProperty property)
{
+ if (index == Valve.VR.OpenVR.k_unTrackedDeviceIndexInvalid) return string.Empty;
+
var error = new ETrackedPropertyError();
sb.Clear();
Valve.VR.OpenVR.System.GetStringTrackedDeviceProperty(index, property, sb, Valve.VR.OpenVR.k_unMaxPropertyStringSize, ref error);
diff --git a/VRCOSC.Game/Providers/Hardware/IComponent.cs b/VRCOSC.Game/Providers/Hardware/Components.cs
similarity index 66%
rename from VRCOSC.Game/Providers/Hardware/IComponent.cs
rename to VRCOSC.Game/Providers/Hardware/Components.cs
index cfa196ea..aec13f67 100644
--- a/VRCOSC.Game/Providers/Hardware/IComponent.cs
+++ b/VRCOSC.Game/Providers/Hardware/Components.cs
@@ -3,29 +3,20 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using LibreHardwareMonitor.Hardware;
namespace VRCOSC.Game.Providers.Hardware;
-public class SensorInfoList
-{
- public readonly List InfoList = new();
-
- public SensorInfoList(SensorType type, params string[] names)
- {
- foreach (var name in names) InfoList.Add(new SensorInfo(type, name));
- }
-}
+public record SensorPair(SensorType Type, string Name);
public class SensorInfo
{
- public readonly string Name;
- public readonly SensorType Type;
+ public readonly List Pairs = new();
- public SensorInfo(SensorType type, string name)
+ public SensorInfo(SensorType type, params string[] names)
{
- Type = type;
- Name = name;
+ foreach (var name in names) Pairs.Add(new SensorPair(type, name));
}
}
@@ -33,22 +24,13 @@ public abstract class HardwareComponent
{
protected virtual SensorInfo LoadInfo => throw new NotImplementedException();
- public readonly int Id;
-
public float Usage { get; private set; }
- protected HardwareComponent(int id)
- {
- Id = id;
- }
-
- protected bool GetIntValue(ISensor sensor, SensorInfoList infoList, out int value)
+ protected static bool GetIntValue(ISensor sensor, SensorInfo info, out int value)
{
- foreach (var info in infoList.InfoList)
+ if (!GetFloatValue(sensor, info, out var floatValue))
{
- if (!GetIntValue(sensor, info, out var intValue)) continue;
-
- value = intValue;
+ value = (int)MathF.Round(floatValue);
return true;
}
@@ -56,27 +38,15 @@ protected bool GetIntValue(ISensor sensor, SensorInfoList infoList, out int valu
return false;
}
- protected bool GetIntValue(ISensor sensor, SensorInfo info, out int value)
+ protected static bool GetFloatValue(ISensor sensor, SensorInfo info, out float value)
{
- if (GetFloatValue(sensor, info, out var valueFloat))
- {
- value = (int)Math.Round(valueFloat);
- return true;
- }
-
- value = 0;
- return false;
- }
-
- protected bool GetFloatValue(ISensor sensor, SensorInfo info, out float value)
- {
- if (sensor.Name == info.Name && sensor.SensorType == info.Type)
+ if (info.Pairs.Any(pair => sensor.SensorType == pair.Type && sensor.Name == pair.Name))
{
value = sensor.Value ?? 0f;
return true;
}
- value = 0f;
+ value = 0;
return false;
}
@@ -90,16 +60,11 @@ public abstract class CPU : HardwareComponent
{
protected override SensorInfo LoadInfo => new(SensorType.Load, "CPU Total");
protected virtual SensorInfo PowerInfo => throw new NotImplementedException();
- protected virtual SensorInfoList TemperatureInfo => throw new NotImplementedException();
+ protected virtual SensorInfo TemperatureInfo => throw new NotImplementedException();
public int Power { get; private set; }
public int Temperature { get; private set; }
- protected CPU(int id)
- : base(id)
- {
- }
-
public override void Update(ISensor sensor)
{
base.Update(sensor);
@@ -111,24 +76,14 @@ public override void Update(ISensor sensor)
public class IntelCPU : CPU
{
protected override SensorInfo PowerInfo => new(SensorType.Power, "CPU Package");
- protected override SensorInfoList TemperatureInfo => new(SensorType.Temperature, "CPU Package");
-
- public IntelCPU(int id)
- : base(id)
- {
- }
+ protected override SensorInfo TemperatureInfo => new(SensorType.Temperature, "CPU Package");
}
// ReSharper disable once InconsistentNaming
public class AMDCPU : CPU
{
protected override SensorInfo PowerInfo => new(SensorType.Power, "Package");
- protected override SensorInfoList TemperatureInfo => new(SensorType.Temperature, "Core (Tdie)", "Core (Tctl/Tdie)", "CPU Cores");
-
- public AMDCPU(int id)
- : base(id)
- {
- }
+ protected override SensorInfo TemperatureInfo => new(SensorType.Temperature, "Core (Tdie)", "Core (Tctl/Tdie)", "CPU Cores");
}
public class GPU : HardwareComponent
@@ -137,7 +92,7 @@ public class GPU : HardwareComponent
private readonly SensorInfo powerInfo = new(SensorType.Power, "GPU Package");
private readonly SensorInfo temperatureInfo = new(SensorType.Temperature, "GPU Core");
private readonly SensorInfo memoryFreeInfo = new(SensorType.SmallData, "GPU Memory Free");
- private readonly SensorInfo memoryUsedINfo = new(SensorType.SmallData, "GPU Memory Used");
+ private readonly SensorInfo memoryUsedInfo = new(SensorType.SmallData, "GPU Memory Used", "D3D Dedicated Memory Used");
private readonly SensorInfo memoryTotalInfo = new(SensorType.SmallData, "GPU Memory Total");
public int Power { get; private set; }
@@ -147,18 +102,13 @@ public class GPU : HardwareComponent
public float MemoryTotal { get; private set; }
public float MemoryUsage => MemoryUsed / MemoryTotal;
- public GPU(int id)
- : base(id)
- {
- }
-
public override void Update(ISensor sensor)
{
base.Update(sensor);
if (GetIntValue(sensor, powerInfo, out var powerValue)) Power = powerValue;
if (GetIntValue(sensor, temperatureInfo, out var temperatureValue)) Temperature = temperatureValue;
if (GetFloatValue(sensor, memoryFreeInfo, out var memoryFreeValue)) MemoryFree = memoryFreeValue;
- if (GetFloatValue(sensor, memoryUsedINfo, out var memoryUsedValue)) MemoryUsed = memoryUsedValue;
+ if (GetFloatValue(sensor, memoryUsedInfo, out var memoryUsedValue)) MemoryUsed = memoryUsedValue;
if (GetFloatValue(sensor, memoryTotalInfo, out var memoryTotalValue)) MemoryTotal = memoryTotalValue;
}
}
@@ -173,11 +123,6 @@ public class RAM : HardwareComponent
public float Available { get; private set; }
public float Total => Used + Available;
- public RAM()
- : base(0)
- {
- }
-
public override void Update(ISensor sensor)
{
base.Update(sensor);
diff --git a/VRCOSC.Game/Providers/Hardware/HardwareStatsProvider.cs b/VRCOSC.Game/Providers/Hardware/HardwareStatsProvider.cs
index 72fb66b0..62cfc12c 100644
--- a/VRCOSC.Game/Providers/Hardware/HardwareStatsProvider.cs
+++ b/VRCOSC.Game/Providers/Hardware/HardwareStatsProvider.cs
@@ -3,11 +3,13 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using LibreHardwareMonitor.Hardware;
using osu.Framework.Extensions.IEnumerableExtensions;
+// ReSharper disable InconsistentNaming
+
namespace VRCOSC.Game.Providers.Hardware;
public sealed class HardwareStatsProvider
@@ -19,13 +21,14 @@ public sealed class HardwareStatsProvider
IsMemoryEnabled = true
};
- public bool CanAcceptQueries { get; private set; }
+ private readonly Regex hardwareIDRegex = new(".+/([0-9])");
+ private readonly Regex sensorIDRegex = new(".+/([0-9])/.+");
- private readonly List components = new();
+ public bool CanAcceptQueries { get; private set; }
- public CPU? GetCPU(int id) => components.Where(component => component.GetType().IsSubclassOf(typeof(CPU))).Select(component => (CPU)component).SingleOrDefault(cpu => cpu.Id == id);
- public GPU? GetGPU(int id) => components.Where(component => component.GetType() == typeof(GPU)).Select(component => (GPU)component).SingleOrDefault(gpu => gpu.Id == id);
- public RAM? GetRam() => components.Where(component => component.GetType() == typeof(RAM)).Select(component => (RAM)component).SingleOrDefault();
+ public readonly Dictionary CPUs = new();
+ public readonly Dictionary GPUs = new();
+ public RAM? RAM;
public void Init() => Task.Run(() =>
{
@@ -36,7 +39,10 @@ public void Init() => Task.Run(() =>
public void Shutdown() => Task.Run(() =>
{
CanAcceptQueries = false;
- components.Clear();
+ CPUs.Clear();
+ GPUs.Clear();
+ RAM = null;
+
computer.Close();
});
@@ -46,7 +52,33 @@ public async Task Update() => await Task.Run(() =>
{
updateHardware(hardware);
auditHardware(hardware);
- hardware.Sensors.ForEach(sensor => components.ForEach(component => component.Update(sensor)));
+ hardware.Sensors.ForEach(sensor =>
+ {
+ var identifier = sensor.Identifier.ToString()!;
+
+ if (identifier.Contains("ram", StringComparison.InvariantCultureIgnoreCase))
+ {
+ RAM!.Update(sensor);
+ return;
+ }
+
+ var sensorIdMatch = sensorIDRegex.Match(identifier);
+ if (!sensorIdMatch.Success) return;
+
+ var sensorId = int.Parse(sensorIdMatch.Groups[1].Value);
+
+ if (identifier.Contains("cpu", StringComparison.InvariantCultureIgnoreCase))
+ {
+ CPUs[sensorId].Update(sensor);
+ return;
+ }
+
+ if (identifier.Contains("gpu", StringComparison.InvariantCultureIgnoreCase))
+ {
+ GPUs[sensorId].Update(sensor);
+ return;
+ }
+ });
});
});
@@ -58,51 +90,38 @@ private void updateHardware(IHardware hardware)
private void auditHardware(IHardware hardware)
{
- var address = hardware.Identifier.ToString();
- var index = 0;
+ var identifier = hardware.Identifier.ToString()!;
- try
+ if (identifier.Contains("ram", StringComparison.InvariantCultureIgnoreCase))
{
- index = int.Parse(address.Split('/').Last());
+ RAM ??= new RAM();
+ return;
}
- catch (FormatException) { }
- if (address.Contains("cpu", StringComparison.InvariantCultureIgnoreCase))
- {
- var cpu = GetCPU(index);
+ var hardwareIDMatch = hardwareIDRegex.Match(identifier);
+ if (!hardwareIDMatch.Success) return;
- if (cpu is null)
- {
- if (address.Contains("intel", StringComparison.InvariantCultureIgnoreCase))
- {
- components.Add(new IntelCPU(index));
- }
+ var hardwareID = int.Parse(hardwareIDMatch.Groups[1].Value);
- if (address.Contains("amd", StringComparison.InvariantCultureIgnoreCase))
- {
- components.Add(new AMDCPU(index));
- }
- }
- }
-
- if (address.Contains("gpu", StringComparison.InvariantCultureIgnoreCase))
+ if (identifier.Contains("cpu", StringComparison.InvariantCultureIgnoreCase))
{
- var gpu = GetGPU(index);
+ if (identifier.Contains("intel", StringComparison.InvariantCultureIgnoreCase))
+ {
+ CPUs.TryAdd(hardwareID, new IntelCPU());
+ return;
+ }
- if (gpu is null)
+ if (identifier.Contains("amd", StringComparison.InvariantCultureIgnoreCase))
{
- components.Add(new GPU(index));
+ CPUs.TryAdd(hardwareID, new AMDCPU());
+ return;
}
}
- if (address.Contains("ram", StringComparison.InvariantCultureIgnoreCase))
+ if (identifier.Contains("gpu", StringComparison.InvariantCultureIgnoreCase))
{
- var ram = GetRam();
-
- if (ram is null)
- {
- components.Add(new RAM());
- }
+ GPUs.TryAdd(hardwareID, new GPU());
+ return;
}
}
}
diff --git a/VRCOSC.Game/VRCOSC.Game.csproj b/VRCOSC.Game/VRCOSC.Game.csproj
index a1ae79f2..eae41c1a 100644
--- a/VRCOSC.Game/VRCOSC.Game.csproj
+++ b/VRCOSC.Game/VRCOSC.Game.csproj
@@ -19,10 +19,10 @@
-
-
+
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -30,7 +30,7 @@
-
+
diff --git a/VRCOSC.Modules/HardwareStats/HardwareStatsModule.cs b/VRCOSC.Modules/HardwareStats/HardwareStatsModule.cs
index d361a0e3..025f3716 100644
--- a/VRCOSC.Modules/HardwareStats/HardwareStatsModule.cs
+++ b/VRCOSC.Modules/HardwareStats/HardwareStatsModule.cs
@@ -73,25 +73,23 @@ private async void updateParameters()
await hardwareStatsProvider.Update();
- var cpu = hardwareStatsProvider.GetCPU(GetSetting(HardwareStatsSetting.SelectedCPU));
- var gpu = hardwareStatsProvider.GetGPU(GetSetting(HardwareStatsSetting.SelectedGPU));
- var ram = hardwareStatsProvider.GetRam();
-
- if (cpu is null)
+ if (!hardwareStatsProvider.CPUs.TryGetValue(GetSetting(HardwareStatsSetting.SelectedCPU), out var cpu))
{
- Log("Warning. Could not connect to CPU");
+ Log($"CPU of id {GetSetting(HardwareStatsSetting.SelectedCPU)} isn't available. If you have multiple, try changing the index");
return;
}
- if (gpu is null)
+ if (!hardwareStatsProvider.GPUs.TryGetValue(GetSetting(HardwareStatsSetting.SelectedGPU), out var gpu))
{
- Log("Warning. Could not connect to GPU");
+ Log($"GPU of id {GetSetting(HardwareStatsSetting.SelectedGPU)} isn't available. If you have multiple, try changing the index");
return;
}
+ var ram = hardwareStatsProvider.RAM;
+
if (ram is null)
{
- Log("Warning. Could not connect to RAM");
+ Log("Could not connect to RAM. This is impossible, so well done!");
return;
}
diff --git a/VRCOSC.Modules/OpenVR/OpenVRStatisticsModule.cs b/VRCOSC.Modules/OpenVR/OpenVRStatisticsModule.cs
index 3fed16f9..cd910562 100644
--- a/VRCOSC.Modules/OpenVR/OpenVRStatisticsModule.cs
+++ b/VRCOSC.Modules/OpenVR/OpenVRStatisticsModule.cs
@@ -37,8 +37,11 @@ protected override void CreateAttributes()
}
CreateVariable(OpenVrVariable.FPS, "FPS", "fps");
+ CreateVariable(OpenVrVariable.HMDCharging, "HMD Charging", "hmdcharging");
CreateVariable(OpenVrVariable.HMDBattery, "HMD Battery (%)", "hmdbattery");
+ CreateVariable(OpenVrVariable.LeftControllerCharging, "Left Controller Charging", "leftcontrollercharging");
CreateVariable(OpenVrVariable.LeftControllerBattery, "Left Controller Battery (%)", "leftcontrollerbattery");
+ CreateVariable(OpenVrVariable.RightControllerCharging, "Right Controller Charging", "rightcontrollercharging");
CreateVariable(OpenVrVariable.RightControllerBattery, "Right Controller Battery (%)", "rightcontrollerbattery");
CreateVariable(OpenVrVariable.AverageTrackerBattery, "Average Tracker Battery (%)", "averagetrackerbattery");
@@ -71,16 +74,22 @@ private void updateVariablesAndParameters()
}
SetVariableValue(OpenVrVariable.FPS, OVRClient.System.FPS.ToString("##0"));
+ SetVariableValue(OpenVrVariable.HMDCharging, OVRClient.HMD.IsCharging ? "Charging" : "Uncharging");
SetVariableValue(OpenVrVariable.HMDBattery, ((int)(OVRClient.HMD.BatteryPercentage * 100)).ToString("##0"));
+ SetVariableValue(OpenVrVariable.LeftControllerCharging, OVRClient.LeftController.IsCharging ? "Charging" : "Uncharging");
SetVariableValue(OpenVrVariable.LeftControllerBattery, ((int)(OVRClient.LeftController.BatteryPercentage * 100)).ToString("##0"));
+ SetVariableValue(OpenVrVariable.RightControllerCharging, OVRClient.RightController.IsCharging ? "Charging" : "Uncharging");
SetVariableValue(OpenVrVariable.RightControllerBattery, ((int)(OVRClient.RightController.BatteryPercentage * 100)).ToString("##0"));
SetVariableValue(OpenVrVariable.AverageTrackerBattery, ((int)(trackerBatteryAverage * 100)).ToString("##0"));
}
else
{
SetVariableValue(OpenVrVariable.FPS, "0");
+ SetVariableValue(OpenVrVariable.HMDCharging, "Unknown");
SetVariableValue(OpenVrVariable.HMDBattery, "0");
+ SetVariableValue(OpenVrVariable.LeftControllerCharging, "Unknown");
SetVariableValue(OpenVrVariable.LeftControllerBattery, "0");
+ SetVariableValue(OpenVrVariable.RightControllerCharging, "Unknown");
SetVariableValue(OpenVrVariable.RightControllerBattery, "0");
SetVariableValue(OpenVrVariable.AverageTrackerBattery, "0");
@@ -220,6 +229,9 @@ private enum OpenVrVariable
HMDBattery,
LeftControllerBattery,
RightControllerBattery,
- AverageTrackerBattery
+ AverageTrackerBattery,
+ HMDCharging,
+ LeftControllerCharging,
+ RightControllerCharging
}
}
diff --git a/VRCOSC.Modules/Timer/TimerModule.cs b/VRCOSC.Modules/Timer/TimerModule.cs
index c25ca26c..3ae0f123 100644
--- a/VRCOSC.Modules/Timer/TimerModule.cs
+++ b/VRCOSC.Modules/Timer/TimerModule.cs
@@ -20,6 +20,11 @@ protected override void CreateAttributes()
CreateState(TimerState.Default, "Default", GetVariableFormat(TimerVariable.Time));
}
+ protected override void OnModuleStart()
+ {
+ ChangeStateTo(TimerState.Default);
+ }
+
[ModuleUpdate(ModuleUpdateMode.ChatBox)]
private void onChatBoxUpdate()
{
diff --git a/VRCOSC.Modules/VoiceRecognition/VoiceRecognitionModule.cs b/VRCOSC.Modules/VoiceRecognition/VoiceRecognitionModule.cs
index dd9dc3a3..f89aa929 100644
--- a/VRCOSC.Modules/VoiceRecognition/VoiceRecognitionModule.cs
+++ b/VRCOSC.Modules/VoiceRecognition/VoiceRecognitionModule.cs
@@ -53,7 +53,10 @@ protected override void OnModuleStop()
[ModuleUpdate(ModuleUpdateMode.Custom, false, 5000)]
private void onModuleUpdate()
{
- speechToTextProvider?.Update();
+ if (speechToTextProvider is null) return;
+
+ speechToTextProvider.AnalysisEnabled = enabled;
+ speechToTextProvider.Update();
}
protected override void OnRegisteredParameterReceived(AvatarParameter avatarParameter)
diff --git a/VRCOSC.Modules/Weather/WeatherModule.cs b/VRCOSC.Modules/Weather/WeatherModule.cs
index 2d088dbe..c3063692 100644
--- a/VRCOSC.Modules/Weather/WeatherModule.cs
+++ b/VRCOSC.Modules/Weather/WeatherModule.cs
@@ -56,7 +56,7 @@ private async void updateParameters()
return;
}
- var weather = await weatherProvider.RetrieveFor(GetSetting(WeatherSetting.Postcode));
+ var weather = await weatherProvider.RetrieveFor(GetSetting(WeatherSetting.Postcode), DateTime.Now);
if (weather is null)
{
diff --git a/VRCOSC.Modules/Weather/WeatherProvider.cs b/VRCOSC.Modules/Weather/WeatherProvider.cs
index 315fac0d..00bf5fb9 100644
--- a/VRCOSC.Modules/Weather/WeatherProvider.cs
+++ b/VRCOSC.Modules/Weather/WeatherProvider.cs
@@ -2,6 +2,7 @@
// See the LICENSE file in the repository root for full license text.
using Newtonsoft.Json;
+using osu.Framework.Logging;
namespace VRCOSC.Modules.Weather;
@@ -13,7 +14,7 @@ public class WeatherProvider
private const string condition_url = "https://www.weatherapi.com/docs/weather_conditions.json";
- private static readonly TimeSpan api_call_delta = TimeSpan.FromMinutes(20);
+ private static readonly TimeSpan cache_expire_time = TimeSpan.FromMinutes(20);
private readonly HttpClient httpClient = new();
private readonly string apiKey;
@@ -29,52 +30,105 @@ public WeatherProvider(string apiKey)
this.apiKey = apiKey;
}
- public async Task RetrieveFor(string location)
+ public async Task RetrieveFor(string location, DateTime locationDateTime)
{
- if (lastUpdate + api_call_delta > DateTimeOffset.Now && location == lastLocation) return weather;
+ try
+ {
+ if (lastUpdate + cache_expire_time > DateTimeOffset.Now && location == lastLocation) return weather;
+
+ lastUpdate = DateTimeOffset.Now;
+ lastLocation = location;
+
+ var currentUrl = string.Format(current_url_format, apiKey, location);
+ var currentResponseData = await httpClient.GetAsync(new Uri(currentUrl));
+
+ if (!currentResponseData.IsSuccessStatusCode)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not retrieve {nameof(currentResponseData)}");
+ return weather;
+ }
+
+ var currentResponseString = await currentResponseData.Content.ReadAsStringAsync();
+ var currentResponse = JsonConvert.DeserializeObject(currentResponseString)?.Current;
+
+ if (currentResponse is null)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not parse {nameof(currentResponse)}");
+ return weather;
+ }
+
+ var astronomyUrl = string.Format(astronomy_url_format, apiKey, location, DateTime.Now.ToString("yyyy-MM-dd"));
+ var astronomyResponseData = await httpClient.GetAsync(new Uri(astronomyUrl));
+
+ if (!astronomyResponseData.IsSuccessStatusCode)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not retrieve {nameof(astronomyResponseData)}");
+ return weather;
+ }
+
+ var astronomyResponseString = await astronomyResponseData.Content.ReadAsStringAsync();
+ var astronomyResponse = JsonConvert.DeserializeObject(astronomyResponseString)?.Astronomy.Astro;
+
+ if (astronomyResponse is null)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not parse {nameof(astronomyResponse)}");
+ return weather;
+ }
+
+ currentResponse.ConditionString = "Unknown";
+
+ if (!DateTime.TryParse(astronomyResponse.Sunrise, out var sunriseParsed)) return weather;
+ if (!DateTime.TryParse(astronomyResponse.Sunset, out var sunsetParsed)) return weather;
+
+ await retrieveConditions();
+
+ if (conditions is not null)
+ {
+ var conditionResponse = conditions[currentResponse.Condition.Code];
+ currentResponse.ConditionString = locationDateTime >= sunriseParsed && locationDateTime < sunsetParsed ? conditionResponse.Day : conditionResponse.Night;
+ }
+
+ weather = currentResponse;
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $"{nameof(WeatherProvider)} experienced an issue");
+ weather = null;
+ }
- lastUpdate = DateTimeOffset.Now;
- lastLocation = location;
-
- var currentUrl = string.Format(current_url_format, apiKey, location);
- var currentResponseData = await httpClient.GetAsync(new Uri(currentUrl));
- var currentResponseString = await currentResponseData.Content.ReadAsStringAsync();
- var currentResponse = JsonConvert.DeserializeObject(currentResponseString)?.Current;
-
- if (currentResponse is null) return null;
-
- var astronomyUrl = string.Format(astronomy_url_format, apiKey, location, DateTime.Now.ToString("yyyy-MM-dd"));
- var astronomyResponseData = await httpClient.GetAsync(new Uri(astronomyUrl));
- var astronomyResponseString = await astronomyResponseData.Content.ReadAsStringAsync();
- var astronomyResponse = JsonConvert.DeserializeObject(astronomyResponseString)?.Astronomy.Astro;
-
- if (astronomyResponse is null) return null;
- if (!DateTime.TryParse(astronomyResponse.Sunrise, out var sunriseParsed)) return null;
- if (!DateTime.TryParse(astronomyResponse.Sunset, out var sunsetParsed)) return null;
-
- if (conditions is null) await retrieveConditions();
-
- var conditionResponse = conditions?[currentResponse.Condition.Code];
- var dateTimeNow = DateTime.Now;
-
- if (dateTimeNow >= sunriseParsed && dateTimeNow < sunsetParsed)
- currentResponse.ConditionString = conditionResponse?.Day ?? string.Empty;
- else
- currentResponse.ConditionString = conditionResponse?.Night ?? string.Empty;
-
- weather = currentResponse;
return weather;
}
private async Task retrieveConditions()
{
- var conditionResponseData = await httpClient.GetAsync(condition_url);
- var conditionResponseString = await conditionResponseData.Content.ReadAsStringAsync();
- var conditionData = JsonConvert.DeserializeObject>(conditionResponseString);
-
- if (conditionData is null) return;
-
- conditions = new Dictionary();
- conditionData.ForEach(condition => conditions.Add(condition.Code, condition));
+ try
+ {
+ if (conditions is not null) return;
+
+ var conditionResponseData = await httpClient.GetAsync(condition_url);
+
+ if (!conditionResponseData.IsSuccessStatusCode)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not retrieve {nameof(conditionResponseData)}");
+ return;
+ }
+
+ var conditionResponseString = await conditionResponseData.Content.ReadAsStringAsync();
+ var conditionResponse = JsonConvert.DeserializeObject>(conditionResponseString);
+
+ if (conditionResponse is null)
+ {
+ Logger.Log($"{nameof(WeatherProvider)} could not parse {nameof(conditionResponse)}");
+ return;
+ }
+
+ conditions = new Dictionary();
+ conditionResponse.ForEach(condition => conditions.Add(condition.Code, condition));
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, $"{nameof(WeatherProvider)} experienced an issue");
+ conditions = null;
+ }
}
}