Skip to content

Commit 85276f6

Browse files
committed
Merge branch 'dev' into main
2 parents f85d981 + 7a4d266 commit 85276f6

File tree

22 files changed

+643
-167
lines changed

22 files changed

+643
-167
lines changed

VRCOSC.Desktop/VRCOSC.Desktop.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
<ApplicationIcon>game.ico</ApplicationIcon>
77
<ApplicationManifest>app.manifest</ApplicationManifest>
88
<Version>0.0.0</Version>
9-
<FileVersion>2023.928.1</FileVersion>
9+
<FileVersion>2023.1007.0</FileVersion>
1010
<Title>VRCOSC</Title>
1111
<Authors>VolcanicArts</Authors>
1212
<Company>VolcanicArts</Company>
1313
<Nullable>enable</Nullable>
14-
<AssemblyVersion>2023.928.1</AssemblyVersion>
14+
<AssemblyVersion>2023.1007.0</AssemblyVersion>
1515
</PropertyGroup>
1616
<ItemGroup Label="Project References">
1717
<ProjectReference Include="..\VRCOSC.Game\VRCOSC.Game.csproj" />

VRCOSC.Game/App/AppManager.cs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public partial class AppManager : Component
3838

3939
private static readonly TimeSpan openvr_check_interval = TimeSpan.FromSeconds(1);
4040
private static readonly TimeSpan vrchat_check_interval = TimeSpan.FromSeconds(5);
41+
private static readonly TimeSpan oscjson_check_interval = TimeSpan.FromSeconds(5);
4142

4243
private readonly Queue<VRChatOscMessage> oscMessageQueue = new();
4344
private ScheduledDelegate? runningModulesDelegate;
@@ -144,6 +145,8 @@ private void initialiseDelayedTasks()
144145
{
145146
Scheduler.AddDelayed(checkForOpenVR, openvr_check_interval.TotalMilliseconds, true);
146147
Scheduler.AddDelayed(checkForVRChat, vrchat_check_interval.TotalMilliseconds, true);
148+
Scheduler.AddDelayed(checkForOscjson, oscjson_check_interval.TotalMilliseconds, true);
149+
checkForOscjson();
147150
}
148151

149152
#endregion
@@ -189,10 +192,7 @@ private void processControlParameters(VRChatOscMessage message)
189192

190193
private void scheduleModuleEnabledParameters()
191194
{
192-
runningModulesDelegate = Scheduler.AddDelayed(() =>
193-
{
194-
ModuleManager.Modules.ForEach(module => sendModuleRunningState(module, ModuleManager.IsModuleRunning(module)));
195-
}, TimeSpan.FromSeconds(1).TotalMilliseconds, true);
195+
runningModulesDelegate = Scheduler.AddDelayed(() => ModuleManager.Modules.ForEach(module => sendModuleRunningState(module, ModuleManager.IsModuleRunning(module))), TimeSpan.FromSeconds(1).TotalMilliseconds, true);
196196
}
197197

198198
private void cancelRunningModulesDelegate()
@@ -314,10 +314,23 @@ private void checkForOpenVR() => Task.Run(() =>
314314

315315
private void checkForVRChat()
316316
{
317-
if (!configManager.Get<bool>(VRCOSCSetting.AutoStartStop) || !VRChat.HasOpenStateChanged()) return;
317+
var newOpenState = VRChat.HasOpenStateChanged();
318+
319+
if (!configManager.Get<bool>(VRCOSCSetting.AutoStartStop) || !newOpenState) return;
320+
321+
if (VRChat.ClientOpen && State.Value == AppManagerState.Stopped) Start();
322+
if (!VRChat.ClientOpen && State.Value == AppManagerState.Started) Stop();
323+
}
324+
325+
private async void checkForOscjson()
326+
{
327+
if (!VRChat.IsClientOpen())
328+
{
329+
OSCClient.Reset();
330+
return;
331+
}
318332

319-
if (VRChat.IsClientOpen && State.Value == AppManagerState.Stopped) Start();
320-
if (!VRChat.IsClientOpen && State.Value == AppManagerState.Started) Stop();
333+
await OSCClient.CheckForVRChatOSCQuery();
321334
}
322335

323336
#endregion

VRCOSC.Game/App/VRChat.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace VRCOSC.Game.App;
1111

1212
public class VRChat
1313
{
14-
public bool IsClientOpen;
14+
public bool ClientOpen;
1515

1616
public Player Player = null!;
1717
public AvatarConfig? AvatarConfig;
@@ -37,12 +37,14 @@ public void HandleAvatarChange(VRChatOscMessage message)
3737
}
3838
}
3939

40+
public bool IsClientOpen() => Process.GetProcessesByName("vrchat").Any();
41+
4042
public bool HasOpenStateChanged()
4143
{
4244
var clientNewOpenState = Process.GetProcessesByName("vrchat").Any();
43-
if (clientNewOpenState == IsClientOpen) return false;
45+
if (clientNewOpenState == ClientOpen) return false;
4446

45-
IsClientOpen = clientNewOpenState;
47+
ClientOpen = clientNewOpenState;
4648
return true;
4749
}
4850
}

VRCOSC.Game/Extensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ public static class AssemblyExtensions
8686

8787
public static class StringExtensions
8888
{
89+
public const char ZERO_WIDTH = '\u200B';
90+
8991
public static string Truncate(this string value, int maxChars) => value.Length <= maxChars ? value : value[..maxChars] + "...";
92+
public static string EscapeNewLine(this string s) => s.Replace("/n", $"/{ZERO_WIDTH}n");
9093
public static string TrimEnd(this string s, string trimmer) => string.IsNullOrEmpty(s) || string.IsNullOrEmpty(trimmer) || !s.EndsWith(trimmer, StringComparison.OrdinalIgnoreCase) ? s : s[..^trimmer.Length];
9194
}
9295

VRCOSC.Game/Modules/Module.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Linq;
88
using System.Reflection;
99
using System.Text.RegularExpressions;
10+
using System.Threading.Tasks;
1011
using osu.Framework.Bindables;
1112
using osu.Framework.Extensions.EnumExtensions;
1213
using osu.Framework.Extensions.IEnumerableExtensions;
@@ -406,6 +407,18 @@ protected T GetSetting<T>(Enum lookup)
406407

407408
#region Parameters
408409

410+
/// <summary>
411+
/// Lets you pass a parameter name to attempt to find the current value using OSCQuery
412+
/// </summary>
413+
/// <remarks>Returns null if no parameter is found or OSCQuery hasn't initialised</remarks>
414+
protected async Task<object?> FindParameterValue(string parameterName) => await oscClient.FindParameterValue(parameterName);
415+
416+
/// <summary>
417+
/// Lets you pass a parameter name to attempt to find the type using OSCQuery
418+
/// </summary>
419+
/// <remarks>Returns null if no parameter is found or OSCQuery hasn't initialised</remarks>
420+
protected async Task<TypeCode?> FindParameterType(string parameterName) => await oscClient.FindParameterType(parameterName);
421+
409422
/// <summary>
410423
/// Allows for sending a parameter that hasn't been registered. Only use this when absolutely necessary
411424
/// </summary>

VRCOSC.Game/OSC/VRChat/VRChatOscClient.cs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

44
using System;
55
using System.Linq;
6+
using System.Net.Http;
7+
using System.Threading.Tasks;
8+
using Newtonsoft.Json;
9+
using osu.Framework.Logging;
10+
using VRC.OSCQuery;
611
using VRCOSC.Game.OSC.Client;
12+
using Zeroconf;
713

814
namespace VRCOSC.Game.OSC.VRChat;
915

@@ -12,12 +18,12 @@ public class VRChatOscClient : OscClient
1218
public Action<VRChatOscMessage>? OnParameterSent;
1319
public Action<VRChatOscMessage>? OnParameterReceived;
1420

21+
private readonly HttpClient client = new();
22+
public int? QueryPort { get; private set; }
23+
1524
public VRChatOscClient()
1625
{
17-
OnMessageSent += message =>
18-
{
19-
OnParameterSent?.Invoke(new VRChatOscMessage(message));
20-
};
26+
OnMessageSent += message => { OnParameterSent?.Invoke(new VRChatOscMessage(message)); };
2127

2228
OnMessageReceived += message =>
2329
{
@@ -27,4 +33,88 @@ public VRChatOscClient()
2733
OnParameterReceived?.Invoke(data);
2834
};
2935
}
36+
37+
public async Task CheckForVRChatOSCQuery()
38+
{
39+
if (QueryPort is not null) return;
40+
41+
var hosts = await ZeroconfResolver.ResolveAsync("_oscjson._tcp.local.");
42+
var host = hosts.FirstOrDefault();
43+
44+
if (host is null)
45+
{
46+
Logger.Log("No OscJson host found");
47+
QueryPort = null;
48+
return;
49+
}
50+
51+
if (!host.Services.Any(s => s.Value.ServiceName.Contains("VRChat-Client")))
52+
{
53+
Logger.Log("No VRChat-Client found");
54+
QueryPort = null;
55+
return;
56+
}
57+
58+
var service = host.Services.Single(s => s.Value.ServiceName.Contains("VRChat-Client"));
59+
60+
QueryPort = service.Value.Port;
61+
Logger.Log($"Successfully found OscJson port: {QueryPort}");
62+
}
63+
64+
public void Reset()
65+
{
66+
QueryPort = null;
67+
}
68+
69+
public async Task<object?> FindParameterValue(string parameterName)
70+
{
71+
if (QueryPort is null) return null;
72+
73+
var url = $"http://127.0.0.1:{QueryPort}/avatar/parameters/{parameterName}";
74+
75+
var response = await client.GetAsync(new Uri(url));
76+
var content = await response.Content.ReadAsStringAsync();
77+
var node = JsonConvert.DeserializeObject<OSCQueryNode>(content);
78+
79+
if (node is null)
80+
{
81+
Logger.Log("Could not decode node");
82+
return null;
83+
}
84+
85+
return node.OscType switch
86+
{
87+
"f" => Convert.ToSingle(node.Value[0]),
88+
"i" => Convert.ToInt32(node.Value[0]),
89+
"T" => Convert.ToBoolean(node.Value[0]),
90+
"F" => Convert.ToBoolean(node.Value[0]),
91+
_ => throw new InvalidOperationException("Unknown type")
92+
};
93+
}
94+
95+
public async Task<TypeCode?> FindParameterType(string parameterName)
96+
{
97+
if (QueryPort is null) return null;
98+
99+
var url = $"http://127.0.0.1:{QueryPort}/avatar/parameters/{parameterName}";
100+
101+
var response = await client.GetAsync(new Uri(url));
102+
var content = await response.Content.ReadAsStringAsync();
103+
var node = JsonConvert.DeserializeObject<OSCQueryNode>(content);
104+
105+
if (node is null)
106+
{
107+
Logger.Log("Could not decode node");
108+
return null;
109+
}
110+
111+
return node.OscType switch
112+
{
113+
"f" => TypeCode.Single,
114+
"i" => TypeCode.Int32,
115+
"T" => TypeCode.Boolean,
116+
"F" => TypeCode.Boolean,
117+
_ => throw new InvalidOperationException("Unknown type")
118+
};
119+
}
30120
}

VRCOSC.Game/Providers/Media/MediaState.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ public enum MediaPlaybackStatus
4040

4141
public class MediaTimelineProperties
4242
{
43-
public TimeSpan Start { get; internal set; }
44-
public TimeSpan End { get; internal set; }
45-
public TimeSpan Position { get; internal set; }
43+
public TimeSpan Start { get; internal set; } = TimeSpan.Zero;
44+
public TimeSpan End { get; internal set; } = TimeSpan.FromSeconds(1);
45+
public TimeSpan Position { get; internal set; } = TimeSpan.Zero;
4646

4747
public float PositionPercentage => Position.Ticks / (float)End.Ticks;
4848
}

VRCOSC.Game/VRCOSC.Game.csproj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<Nullable>enable</Nullable>
55
<LangVersion>11</LangVersion>
66
<PackageId>VolcanicArts.VRCOSC.SDK</PackageId>
7-
<Version>2023.928.0</Version>
7+
<Version>2023.1007.0</Version>
88
<Title>VRCOSC SDK</Title>
99
<Authors>VolcanicArts</Authors>
1010
<Description>SDK for creating custom modules with VRCOSC</Description>
@@ -19,16 +19,18 @@
1919
</ItemGroup>
2020
<ItemGroup>
2121
<PackageReference Include="LibreHardwareMonitorLib" Version="0.9.2" />
22-
<PackageReference Include="Octokit" Version="8.0.0" />
23-
<PackageReference Include="ppy.osu.Framework" Version="2023.904.0" />
22+
<PackageReference Include="Octokit" Version="8.0.1" />
23+
<PackageReference Include="ppy.osu.Framework" Version="2023.1006.0" />
2424
<PackageReference Include="PInvoke.User32" Version="0.7.124" />
2525
<PackageReference Include="PolySharp" Version="1.13.2">
2626
<PrivateAssets>all</PrivateAssets>
2727
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2828
</PackageReference>
29+
<PackageReference Include="VRChat.OSCQuery" Version="0.0.7" />
2930
<PackageReference Include="WebSocket4Net" Version="0.15.2" />
3031
<PackageReference Include="NAudio.Wasapi" Version="22.0.0" />
3132
<PackageReference Include="Vosk" Version="0.3.38" />
3233
<PackageReference Include="VolcanicArts.Libs.OpenVR" Version="1.26.7" />
34+
<PackageReference Include="Zeroconf" Version="3.6.11" />
3335
</ItemGroup>
3436
</Project>

0 commit comments

Comments
 (0)