Initial Commit
This commit is contained in:
commit
47981d31e5
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
/packages/
|
||||||
|
riderModule.iml
|
||||||
|
/_ReSharper.Caches/
|
13
.idea/.idea.GlaxOSC/.idea/.gitignore
vendored
Normal file
13
.idea/.idea.GlaxOSC/.idea/.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/contentModel.xml
|
||||||
|
/.idea.GlaxOSC.iml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
4
.idea/.idea.GlaxOSC/.idea/encodings.xml
Normal file
4
.idea/.idea.GlaxOSC/.idea/encodings.xml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
8
.idea/.idea.GlaxOSC/.idea/indexLayout.xml
Normal file
8
.idea/.idea.GlaxOSC/.idea/indexLayout.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
6
.idea/.idea.GlaxOSC/.idea/vcs.xml
Normal file
6
.idea/.idea.GlaxOSC/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
16
GlaxOSC.sln
Normal file
16
GlaxOSC.sln
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GlaxOSC", "GlaxOSC\GlaxOSC.csproj", "{58612EF7-9968-4FD9-9236-7108692D3E6E}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{58612EF7-9968-4FD9-9236-7108692D3E6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{58612EF7-9968-4FD9-9236-7108692D3E6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{58612EF7-9968-4FD9-9236-7108692D3E6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{58612EF7-9968-4FD9-9236-7108692D3E6E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
11
GlaxOSC/AvatarConfig.cs
Normal file
11
GlaxOSC/AvatarConfig.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
// ReSharper disable CollectionNeverUpdated.Global
|
||||||
|
#pragma warning disable CS0169 // Field is never used
|
||||||
|
|
||||||
|
namespace GlaxOSC;
|
||||||
|
|
||||||
|
public struct AvatarConfig
|
||||||
|
{
|
||||||
|
public string id, name;
|
||||||
|
public List<Parameter> parameters;
|
||||||
|
}
|
14
GlaxOSC/GlaxOSC.csproj
Normal file
14
GlaxOSC/GlaxOSC.csproj
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BuildSoft.OscCore" Version="1.2.1.1" />
|
||||||
|
<PackageReference Include="VRChat.OSCQuery" Version="0.0.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
133
GlaxOSC/OSC.cs
Normal file
133
GlaxOSC/OSC.cs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
11
GlaxOSC/Parameter.cs
Normal file
11
GlaxOSC/Parameter.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
|
||||||
|
#pragma warning disable CS0169 // Field is never used
|
||||||
|
|
||||||
|
namespace GlaxOSC;
|
||||||
|
|
||||||
|
public struct Parameter
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
public ParameterValue input, output;
|
||||||
|
}
|
9
GlaxOSC/ParameterValue.cs
Normal file
9
GlaxOSC/ParameterValue.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
#pragma warning disable CS0169 // Field is never used
|
||||||
|
|
||||||
|
namespace GlaxOSC;
|
||||||
|
|
||||||
|
public struct ParameterValue
|
||||||
|
{
|
||||||
|
private string address, type;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user