145 lines
5.9 KiB
C#
145 lines
5.9 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, IEnumerable<OSCEndpoint> 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 (OSCEndpoint endpoint in endpoints)
|
|
this.AddEndpoint(endpoint);
|
|
this._oscServer.Start();
|
|
|
|
this._logger?.LogInformation($"TCP: {this._oscQuery.TcpPort} UDP/OSC: {portReceive}");
|
|
|
|
this._listenThread = new(Listen);
|
|
this._listenThread.Start();
|
|
}
|
|
|
|
public OSC(string serviceName, Dictionary<string, Type> endpoints, string ipSend = "127.0.0.1", int portSend = 9000,
|
|
int? portReceive = null, ILogger? logger = null)
|
|
: this(serviceName, endpoints.Select(endpoint => new OSCEndpoint(endpoint.Key, endpoint.Value, null)), ipSend,
|
|
portSend, portReceive, logger)
|
|
{
|
|
|
|
}
|
|
|
|
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(OSCEndpoint oscEndpoint) =>
|
|
AddEndpoint(oscEndpoint.Endpoint, oscEndpoint.Type, oscEndpoint.Callback);
|
|
|
|
internal void AddEndpoint(string endpoint, Type type, Action<string, object?, object?>? callback = null)
|
|
{
|
|
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, callback));
|
|
}
|
|
|
|
private void HandleParameterChange(string endpoint, Type type, OscMessageValues values, Action<string, object?, object?>? callback)
|
|
{
|
|
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]);
|
|
callback?.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();
|
|
}
|
|
} |