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