1
0
Glax-VRC-OSC/GlaxOSC/OSC.cs
2024-05-13 17:57:10 +02:00

133 lines
5.2 KiB
C#

using System.Net;
using System.Text.Json;
using BuildSoft.OscCore;
using Microsoft.Extensions.Logging;
using VRC.OSCQuery;
namespace GlaxOSC;
public class OSC : IDisposable
{
private readonly OSCQueryService _oscQuery;
private readonly OscServer _oscServer;
public readonly int OSCPort;
public readonly int TCPPort;
public readonly OscClient Client;
private readonly ILogger? _logger;
private bool _keepRunning = true;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly Thread _listenThread;
private const int UpdateInterval = 10;
internal readonly Dictionary<string, object?> ParameterValues = new();
public OSC(string serviceName, Dictionary<string, Type> endpoints, string ipSend = "127.0.0.1", int portSend = 9000, int? portReceive = null, ILogger? logger = null)
{
this._logger = logger;
this.Client = new OscClient(ipSend, portSend);
this.OSCPort = portReceive ?? Extensions.GetAvailableUdpPort();
this.TCPPort = Extensions.GetAvailableTcpPort();
this._logger?.LogInformation("Setting up OSCQuery");
this._oscQuery = new OSCQueryServiceBuilder()
.WithServiceName(serviceName)
.WithUdpPort(OSCPort)
.WithTcpPort(TCPPort)
.WithDefaults()
.Build();
this._oscServer = new OscServer(OSCPort);
this.AddEndpoint("/avatar/change", typeof(string));
foreach (KeyValuePair<string, Type> endpoint in endpoints)
this.AddEndpoint(endpoint.Key, endpoint.Value);
this._oscServer.Start();
this._logger?.LogInformation($"TCP: {this._oscQuery.TcpPort} UDP/OSC: {portReceive}");
this._listenThread = new(Listen);
this._listenThread.Start();
}
private void Listen()
{
while (_keepRunning)
{
this._oscServer.Update();
Thread.Sleep(UpdateInterval);
}
}
public AvatarConfig GetAvatarConfig()
{
this._logger?.LogInformation("Waiting for Avatar-Config. Try to \"Reset Avatar.\"");
while (ParameterValues["/avatar/change"] is null)
Thread.Sleep(10);
string oscPath = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}Low\\VRChat\\VRChat\\OSC\\";
DirectoryInfo di = new (oscPath);
string[] userPaths = di.GetDirectories().Select(dir => Path.Combine(oscPath, dir.Name, "\\Avatars\\"))
.ToArray();
string userDir = userPaths.First(path =>
{
DirectoryInfo avatarDirInfo = new(path);
if (avatarDirInfo.GetFiles().Any(file => file.Name.Contains((string)ParameterValues["/avatar/change"]!)))
return true;
return false;
});
string configPath = Path.Combine(userDir, $"{(string)ParameterValues["/avatar/change"]!}.json");
string configJson = File.ReadAllText(configPath);
return JsonSerializer.Deserialize<AvatarConfig>(configJson);
}
public event OnParameterChangeEventHandler? OnParameterChangeEvent;
public delegate void OnParameterChangeEventHandler(string endpoint, object? oldValue, object? newValue);
internal void AddEndpoint(string endpoint, Type type)
{
this._logger?.LogDebug($"Adding endpoint {endpoint}");
Dictionary<Type, string> oscTypeLookup = new Dictionary<Type, string>()
{
{typeof(int), "i"},
{typeof(uint), "u"},
{typeof(long), "h"},
{typeof(float), "f"},
{typeof(double), "d"},
{typeof(string), "s"},
{typeof(char), "c"},
{typeof(Array), "[,]"},
{typeof(byte[]), "b"},
{typeof(bool), "T"},
};
this._oscQuery.AddEndpoint(endpoint, oscTypeLookup[type], Attributes.AccessValues.WriteOnly);
this.ParameterValues.Add(endpoint, null);
this._oscServer.TryAddMethod(endpoint, values => HandleParameterChange(endpoint, type, values));
}
private void HandleParameterChange(string endpoint, Type type, OscMessageValues values)
{
object? oldValue = ParameterValues[endpoint];
if (type == typeof(int))
ParameterValues[endpoint] = values.ReadIntElement(0);
else if (type == typeof(long))
ParameterValues[endpoint] = values.ReadInt64Element(0);
else if (type == typeof(float))
ParameterValues[endpoint] = values.ReadFloatElement(0);
else if (type == typeof(double))
ParameterValues[endpoint] = values.ReadFloat64Element(0);
else if (type == typeof(string))
ParameterValues[endpoint] = values.ReadStringElement(0);
else if (type == typeof(char))
ParameterValues[endpoint] = values.ReadAsciiCharElement(0);
else if (type == typeof(bool))
ParameterValues[endpoint] = values.ReadBooleanElement(0);
OnParameterChangeEvent?.Invoke(endpoint, oldValue, ParameterValues[endpoint]);
this._logger?.LogDebug($"{endpoint} {oldValue} -> {ParameterValues[endpoint]}");
}
public void Dispose()
{
this._logger?.LogDebug("Disposing");
_keepRunning = false;
_oscQuery.Dispose();
_oscServer.Dispose();
}
}