Skip to content

Commit 5576ceb

Browse files
authored
Merge pull request #290 from benaclejames/refactor/osc-handling-refactor
refactor: osc handling
2 parents 642744d + d0c2733 commit 5576ceb

File tree

8 files changed

+165
-181
lines changed

8 files changed

+165
-181
lines changed

VRCFaceTracking.Core/Models/OscTarget.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using CommunityToolkit.Mvvm.ComponentModel;
1+
using System.ComponentModel.DataAnnotations;
2+
using CommunityToolkit.Mvvm.ComponentModel;
23
using VRCFaceTracking.Core.Contracts;
34
using VRCFaceTracking.Core.Contracts.Services;
45
using VRCFaceTracking.Core.Validation;
@@ -9,19 +10,19 @@ public partial class OscTarget : ObservableValidator, IOscTarget
910
{
1011
[ObservableProperty] private bool _isConnected;
1112

12-
[ObservableProperty] [property: SavedSetting("OSCInPort", 9001)]
13-
13+
[ObservableProperty]
14+
[Range(1, 25535)]
15+
[property: SavedSetting("OSCInPort", 9001)]
1416
private int _inPort;
1517

16-
[ObservableProperty] [property: SavedSetting("OSCOutPort", 9000)]
17-
18+
[ObservableProperty]
19+
[Range(1, 25535)]
20+
[property: SavedSetting("OSCOutPort", 9000)]
1821
private int _outPort;
1922

2023
[ObservableProperty]
21-
[NotifyPropertyChangedFor(nameof(InPort))] [NotifyPropertyChangedFor(nameof(OutPort))]
22-
23-
[property: SavedSetting("OSCAddress", "127.0.0.1")]
2424
[ValidIpAddress]
25+
[property: SavedSetting("OSCAddress", "127.0.0.1")]
2526
private string _destinationAddress;
2627

2728
public OscTarget(ILocalSettingsService localSettingsService)

VRCFaceTracking.Core/OSC/Query/HttpHandler.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,15 @@
44

55
namespace VRCFaceTracking.Core.OSC.Query.mDNS;
66

7-
public class HttpHandler : IDisposable
7+
public class HttpHandler(IOscTarget oscTarget, ILogger<HttpHandler> logger) : IDisposable
88
{
99
private readonly HttpListener _listener = new();
1010
private IAsyncResult _contextListenerResult;
11-
private readonly IOscTarget _oscTarget;
12-
private readonly ILogger<HttpHandler> _logger;
1311
private string _appName = "VRCFT";
1412
private int _oscPort = 9001;
1513

1614
public Action OnHostInfoQueried = () => { };
1715

18-
public HttpHandler(IOscTarget oscTarget, ILogger<HttpHandler> logger)
19-
{
20-
_oscTarget = oscTarget;
21-
_logger = logger;
22-
}
23-
2416
public void BindTo(string uri, int oscPort)
2517
{
2618
_oscPort = oscPort;
@@ -50,20 +42,20 @@ private async void HttpListenerLoop(IAsyncResult result)
5042
var hostInfo = new OscQueryHostInfo
5143
{
5244
name = _appName,
53-
oscIP = _oscTarget.DestinationAddress,
45+
oscIP = oscTarget.DestinationAddress,
5446
oscPort = _oscPort
5547
};
5648
respStr = hostInfo.ToString();
5749
OnHostInfoQueried();
58-
_logger.LogDebug($"Responding to oscquery host info request with {respStr}");
50+
logger.LogDebug($"Responding to oscquery host info request with {respStr}");
5951
}
6052
else
6153
{
6254
if (context.Request.Url != null && context.Request.Url.LocalPath != "/")
6355
{
6456
return; // Not properly implementing oscquery protocol because I'm unemployed and not being paid to
6557
}
66-
58+
6759
var rootNode = new OscQueryRoot();
6860
rootNode.AddNode(new OscQueryNode("/avatar/change", AccessValues.WriteOnly, "s"));
6961

@@ -86,4 +78,4 @@ public void Dispose()
8678
{
8779
_listener.Stop();
8880
}
89-
}
81+
}

VRCFaceTracking.Core/OscQueryConfigParser.cs

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,24 @@
77

88
namespace VRCFaceTracking.Core;
99

10-
public class OscQueryConfigParser
10+
public class OscQueryConfigParser(
11+
ILogger<OscQueryConfigParser> parserLogger,
12+
AvatarConfigParser configParser,
13+
MulticastDnsService multicastDnsService)
1114
{
12-
private readonly ILogger<OscQueryConfigParser> _logger;
13-
private readonly AvatarConfigParser _configParser;
14-
private readonly MulticastDnsService _multicastDnsService;
15-
16-
public OscQueryConfigParser(
17-
ILogger<OscQueryConfigParser> parserLogger,
18-
AvatarConfigParser configParser,
19-
MulticastDnsService multicastDnsService
20-
)
21-
{
22-
_logger = parserLogger;
23-
_configParser = configParser;
24-
_multicastDnsService = multicastDnsService;
25-
}
26-
2715
private readonly HttpClient _httpClient = new();
2816

2917
public async Task<(IAvatarInfo avatarInfo, List<Parameter> relevantParameters)?> ParseAvatar(string avatarId = null)
3018
{
3119
try
3220
{
33-
if (_multicastDnsService.VrchatClientEndpoint == null)
21+
if (multicastDnsService.VrchatClientEndpoint == null)
3422
{
3523
return null;
3624
}
37-
25+
3826
// Request on the endpoint + /avatar/parameters
39-
var httpEndpoint = "http://" + _multicastDnsService.VrchatClientEndpoint + "/avatar";
27+
var httpEndpoint = "http://" + multicastDnsService.VrchatClientEndpoint + "/avatar";
4028

4129
// Get the response
4230
var response = await _httpClient.GetAsync(httpEndpoint);
@@ -47,7 +35,7 @@ MulticastDnsService multicastDnsService
4735

4836
var avatarConfig =
4937
JsonConvert.DeserializeObject<OscQueryNode>(await response.Content.ReadAsStringAsync());
50-
_logger.LogDebug(avatarConfig.ToString());
38+
parserLogger.LogDebug(avatarConfig.ToString());
5139
var avatarInfo = new OscQueryAvatarInfo(avatarConfig);
5240

5341
// Reset all parameters
@@ -56,28 +44,28 @@ MulticastDnsService multicastDnsService
5644
{
5745
paramList.AddRange(parameter.ResetParam(avatarInfo.Parameters));
5846
}
59-
47+
6048
// God help me why is this something I need to do to get the avatar name
6149
// this impl is really disappointing vrc
62-
var configFileInfo = await _configParser.ParseAvatar(avatarInfo.Id);
63-
_logger.LogInformation($"Attempting to resolve avatar config file for {avatarInfo.Id}");
50+
var configFileInfo = await configParser.ParseAvatar(avatarInfo.Id);
51+
parserLogger.LogInformation($"Attempting to resolve avatar config file for {avatarInfo.Id}");
6452
if (!string.IsNullOrEmpty(configFileInfo?.avatarInfo.Name))
6553
{
6654
avatarInfo.Name = configFileInfo.Value.avatarInfo.Name;
67-
_logger.LogInformation($"Successfully found config containing avatar name {avatarInfo.Name}");
55+
parserLogger.LogInformation($"Successfully found config containing avatar name {avatarInfo.Name}");
6856
}
6957
else
7058
{
71-
_logger.LogWarning("Odd. Our attempt to find the legacy osc config json for this avatar failed.");
59+
parserLogger.LogWarning("Odd. Our attempt to find the legacy osc config json for this avatar failed.");
7260
}
7361

7462
return (avatarInfo, paramList);
7563
}
7664
catch (Exception e)
7765
{
78-
_logger.LogError(e.Message);
79-
SentrySdk.CaptureException(e, scope => scope.SetExtra("endpoint", _multicastDnsService.VrchatClientEndpoint));
66+
parserLogger.LogError(e.Message);
67+
SentrySdk.CaptureException(e, scope => scope.SetExtra("endpoint", multicastDnsService.VrchatClientEndpoint));
8068
return null;
8169
}
8270
}
83-
}
71+
}

VRCFaceTracking.Core/Services/OscQueryService.cs

Lines changed: 48 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -12,101 +12,74 @@
1212

1313
namespace VRCFaceTracking.Core.Services;
1414

15-
public partial class OscQueryService : ObservableObject
15+
public partial class OscQueryService(
16+
ILogger<OscQueryService> logger,
17+
OscQueryConfigParser oscQueryConfigParser,
18+
AvatarConfigParser avatarConfigParser,
19+
ParameterSenderService parameterSenderService,
20+
IOscTarget oscTarget,
21+
MulticastDnsService multicastDnsService,
22+
OscRecvService recvService,
23+
ILocalSettingsService settingsService,
24+
HttpHandler httpHandler)
25+
: ObservableObject
1626
{
17-
private const string k_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
18-
27+
1928
// Services
20-
private readonly ILogger _logger;
21-
private readonly OscQueryConfigParser _oscQueryConfigParser;
22-
private readonly MulticastDnsService _multicastDnsService;
23-
private readonly AvatarConfigParser _configParser;
24-
private readonly ParameterSenderService _parameterSenderService;
25-
private static readonly Random Random = new ();
26-
29+
private readonly ILogger _logger = logger;
30+
2731
[ObservableProperty] private IAvatarInfo _avatarInfo = new NullAvatarDef("Loading...", "Loading...");
2832
[ObservableProperty] private List<Parameter> _avatarParameters;
2933

30-
private readonly IOscTarget _oscTarget;
31-
3234
// Local vars
33-
private readonly OscRecvService _recvService;
34-
private readonly ILocalSettingsService _settingsService;
35-
private readonly HttpHandler _httpHandler;
36-
37-
public OscQueryService(
38-
ILogger<OscQueryService> logger,
39-
OscQueryConfigParser oscQueryConfigParser,
40-
AvatarConfigParser avatarConfigParser,
41-
ParameterSenderService parameterSenderService,
42-
IOscTarget oscTarget,
43-
MulticastDnsService multicastDnsService,
44-
OscRecvService recvService,
45-
ILocalSettingsService settingsService,
46-
HttpHandler httpHandler
47-
)
48-
{
49-
_oscQueryConfigParser = oscQueryConfigParser;
50-
_configParser = avatarConfigParser;
51-
_parameterSenderService = parameterSenderService;
52-
_logger = logger;
53-
_recvService = recvService;
54-
_recvService.OnMessageReceived = HandleNewMessage;
55-
_multicastDnsService = multicastDnsService;
56-
_oscTarget = oscTarget;
57-
_settingsService = settingsService;
58-
_httpHandler = httpHandler;
59-
}
6035

6136
public async Task InitializeAsync()
6237
{
6338
_logger.LogDebug("OSC Service Initializing");
64-
65-
await _settingsService.Load(_oscTarget);
66-
39+
40+
recvService.OnMessageReceived = HandleNewMessage;
41+
42+
await settingsService.Load(oscTarget);
43+
6744
(bool listenerSuccess, bool senderSuccess) result = (false, false);
6845

69-
if (string.IsNullOrWhiteSpace(_oscTarget.DestinationAddress))
46+
if (string.IsNullOrWhiteSpace(oscTarget.DestinationAddress))
7047
{
7148
return; // Return both false as we cant bind to anything without an address
7249
}
7350

74-
_multicastDnsService.OnVrcClientDiscovered += FirstClientDiscovered;
75-
76-
_multicastDnsService.SendQuery("_oscjson._tcp.local");
77-
51+
multicastDnsService.OnVrcClientDiscovered += FirstClientDiscovered;
52+
53+
multicastDnsService.SendQuery("_oscjson._tcp.local");
54+
7855
_logger.LogDebug("OSC Service Initialized with result {0}", result);
7956
await Task.CompletedTask;
8057
}
81-
58+
8259
private void FirstClientDiscovered()
8360
{
84-
_multicastDnsService.OnVrcClientDiscovered -= FirstClientDiscovered;
85-
86-
_logger.LogInformation("OSCQuery detected. Setting port negotiation to autopilot.");
87-
88-
var randomStr = new string(Enumerable.Repeat(k_chars, 6).Select(s => s[Random.Next(s.Length)]).ToArray());
89-
_httpHandler.OnHostInfoQueried += HandleNewAvatarWrapper;
90-
91-
var recvEndpoint = _recvService.UpdateTarget(new IPEndPoint(IPAddress.Parse(_oscTarget.DestinationAddress), 0));
61+
multicastDnsService.OnVrcClientDiscovered -= FirstClientDiscovered;
62+
63+
_logger.LogInformation($"OSCQuery detected at {multicastDnsService.VrchatClientEndpoint}. Setting port negotiation to autopilot.");
64+
65+
httpHandler.OnHostInfoQueried += HandleNewAvatarWrapper;
66+
67+
var recvEndpoint = recvService.UpdateTarget(new IPEndPoint(IPAddress.Parse(oscTarget.DestinationAddress), 0));
9268
if (recvEndpoint == null)
9369
{
9470
_logger.LogError("Very strange. We were unable to bind to a random port.");
95-
recvEndpoint = new IPEndPoint(IPAddress.Parse(_oscTarget.DestinationAddress), _oscTarget.InPort);
71+
recvEndpoint = new IPEndPoint(IPAddress.Parse(oscTarget.DestinationAddress), oscTarget.InPort);
9672
}
97-
98-
// TODO: Move this somewhere more appropriate
99-
var listener = new TcpListener(IPAddress.Any, 0);
100-
listener.Start();
101-
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
102-
listener.Stop();
103-
_httpHandler.SetAppName("VRCFT-" + randomStr);
104-
_httpHandler.BindTo($"http://127.0.0.1:{port}/", recvEndpoint.Port);
105-
73+
74+
var randomServiceSuffix = Utils.GetRandomChars(6);
75+
var httpPort = Utils.GetRandomFreePort();
76+
httpHandler.SetAppName("VRCFT-" + randomServiceSuffix);
77+
httpHandler.BindTo($"http://127.0.0.1:{httpPort}/", recvEndpoint.Port);
78+
10679
// Advertise our OSC JSON and OSC endpoints (OSC JSON to display the silly lil popup in-game)
107-
_multicastDnsService.Advertise("_oscjson._tcp", "VRCFT-"+randomStr, port, IPAddress.Loopback);
108-
_multicastDnsService.Advertise("_osc._udp", "VRCFT-"+randomStr, recvEndpoint.Port, IPAddress.Loopback);
109-
80+
multicastDnsService.Advertise("_oscjson._tcp", new AdvertisedService("VRCFT-"+randomServiceSuffix, httpPort, IPAddress.Loopback));
81+
multicastDnsService.Advertise("_osc._udp", new AdvertisedService("VRCFT-"+randomServiceSuffix, recvEndpoint.Port, IPAddress.Loopback));
82+
11083
HandleNewAvatar();
11184
}
11285

@@ -115,21 +88,21 @@ private async void HandleNewAvatar(string newId = null)
11588
var newAvatar = default((IAvatarInfo avatarInfo, List<Parameter> relevantParameters)?);
11689
if (newId == null)
11790
{
118-
_httpHandler.OnHostInfoQueried -= HandleNewAvatarWrapper;
119-
newAvatar = await _oscQueryConfigParser.ParseAvatar("");
91+
httpHandler.OnHostInfoQueried -= HandleNewAvatarWrapper;
92+
newAvatar = await oscQueryConfigParser.ParseAvatar("");
12093
}
12194
else
12295
{
12396
// handle normal osc
124-
newAvatar = await _configParser.ParseAvatar(newId);
97+
newAvatar = await avatarConfigParser.ParseAvatar(newId);
12598
}
12699
if (newAvatar.HasValue)
127100
{
128101
AvatarInfo = newAvatar.Value.avatarInfo;
129102
AvatarParameters = newAvatar.Value.relevantParameters;
130103
}
131104
}
132-
105+
133106
private void HandleNewAvatarWrapper()
134107
{
135108
HandleNewAvatar();
@@ -145,7 +118,7 @@ private void HandleNewMessage(OscMessage msg)
145118
case "/vrcft/settings/forceRelevant": // Endpoint for external tools to force vrcft to send all parameters
146119
if (msg.Value is bool relevancy)
147120
{
148-
_parameterSenderService.AllParametersRelevant = relevancy;
121+
parameterSenderService.AllParametersRelevant = relevancy;
149122
}
150123

151124
break;
@@ -192,4 +165,4 @@ partial void OnAvatarParametersChanged(List<Parameter> value)
192165
_logger.LogDebug($"Loaded deprecated parameters: {string.Join(", ", deprecatedParams.SelectMany(x => x.GetParamNames()).Select(y => y.paramName))}");
193166
}
194167
}
195-
}
168+
}

0 commit comments

Comments
 (0)