From a1788da52a32e1cd330d241060fbfcfa68d8ebde Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sat, 19 Nov 2022 11:44:01 +0900 Subject: [PATCH 1/7] Project/Solution files maintenance --- src/vrcosclib/vrcosclib.csproj | 1 + vrcosclib.sln | 4 +--- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vrcosclib/vrcosclib.csproj b/src/vrcosclib/vrcosclib.csproj index 838de66..6c29ce4 100644 --- a/src/vrcosclib/vrcosclib.csproj +++ b/src/vrcosclib/vrcosclib.csproj @@ -15,6 +15,7 @@ MIT A OSC library for VRChat https://github.com/ChanyaVRC/VRCOscLib + https://github.com/ChanyaVRC/VRCOscLib diff --git a/vrcosclib.sln b/vrcosclib.sln index 9e322b4..de88bd7 100644 --- a/vrcosclib.sln +++ b/vrcosclib.sln @@ -29,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarParameterSender", "Sa {3D5D91B8-5663-43AB-BD91-FD80502BDEF0} = {3D5D91B8-5663-43AB-BD91-FD80502BDEF0} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatboxSender", "Sample\ChatboxSender\ChatboxSender.csproj", "{1BF37DAA-23F4-4321-B709-6D1EAD45D10E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatboxSender", "Sample\ChatboxSender\ChatboxSender.csproj", "{1BF37DAA-23F4-4321-B709-6D1EAD45D10E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -98,13 +98,11 @@ Global {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|For Test.ActiveCfg = Debug|Any CPU - {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|For Test.Build.0 = Debug|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|Full Build.ActiveCfg = Debug|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Debug|Full Build.Build.0 = Debug|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Any CPU.Build.0 = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|For Test.ActiveCfg = Release|Any CPU - {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|For Test.Build.0 = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Full Build.ActiveCfg = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Full Build.Build.0 = Release|Any CPU EndGlobalSection From effd8ab1f5cda5aa758f38e5dd4446e52021edb7 Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 13:16:01 +0900 Subject: [PATCH 2/7] Fixed `OscUtility.Initialize` method. --- .../Avatar/OscAvatarParametorContainer.cs | 3 +- .../Avatar/Utility/OscAvatarUtility.cs | 23 ++++- .../Utility/OscConnectionSettings.cs | 96 +++++++++++++++++++ src/vrcosclib/Utility/OscParameter.cs | 20 +++- .../Utility/OscUtility.Connection.cs | 91 ++++-------------- src/vrcosclib/Utility/OscUtility.cs | 15 ++- 6 files changed, 168 insertions(+), 80 deletions(-) create mode 100644 src/vrcosclib/Utility/OscConnectionSettings.cs diff --git a/src/vrcosclib/Avatar/OscAvatarParametorContainer.cs b/src/vrcosclib/Avatar/OscAvatarParametorContainer.cs index 5ba3ae5..8471c85 100644 --- a/src/vrcosclib/Avatar/OscAvatarParametorContainer.cs +++ b/src/vrcosclib/Avatar/OscAvatarParametorContainer.cs @@ -15,6 +15,7 @@ public class OscAvatarParametorContainer : IReadOnlyDictionary /// internal static void Initialize() { + } /// @@ -22,7 +23,7 @@ internal static void Initialize() /// static OscAvatarParametorContainer() { - OscParameter.Initialize(); + Initialize(); } #endregion diff --git a/src/vrcosclib/Avatar/Utility/OscAvatarUtility.cs b/src/vrcosclib/Avatar/Utility/OscAvatarUtility.cs index 9c2cae5..ee95d49 100644 --- a/src/vrcosclib/Avatar/Utility/OscAvatarUtility.cs +++ b/src/vrcosclib/Avatar/Utility/OscAvatarUtility.cs @@ -46,14 +46,31 @@ internal static void RegisterAvaterConfig(OscAvatarConfig avatarConfig) public static event OscValueChangedEventHandler? AvatarChanged; - internal static void Initialize() + public static void Initialize() { + OscParameter.Initialize(); + var parameters = OscParameter.Parameters; + + OscParameterChangedEventHandler readAvatarIdFromApp = ReadAvatarIdFromApp; + parameters.RemoveValueChangedEventByAddress(OscConst.AvatarIdAddress, readAvatarIdFromApp); + parameters.AddValueChangedEventByAddress(OscConst.AvatarIdAddress, readAvatarIdFromApp); } static OscAvatarUtility() { - var parameters = OscParameter.Parameters; - parameters.AddValueChangedEventByAddress(OscConst.AvatarIdAddress, ReadAvatarIdFromApp); + try + { + Initialize(); + } + catch (Exception ex) + { + if (OscConnectionSettings._utilityInitialized) + { + throw; + } + OscUtility._initializationExceptions.Add(ex); + return; + } } private static void ReadAvatarIdFromApp(IReadOnlyOscParameterCollection sender, ValueChangedEventArgs e) diff --git a/src/vrcosclib/Utility/OscConnectionSettings.cs b/src/vrcosclib/Utility/OscConnectionSettings.cs new file mode 100644 index 0000000..6eabd71 --- /dev/null +++ b/src/vrcosclib/Utility/OscConnectionSettings.cs @@ -0,0 +1,96 @@ +using BuildSoft.OscCore; + +namespace BuildSoft.VRChat.Osc; + +public static class OscConnectionSettings +{ + internal static bool _utilityInitialized = false; + + private static OscServer? _server; + private static OscClient? _client; + + internal static OscServer Server => _server ??= new OscServer(_receivePort); + internal static OscClient Client => _client ??= new OscClient(_vrcIPAddress, _sendPort); + + private static int _receivePort = 9001; + public static int ReceivePort + { + get => _receivePort; + set + { + if (value > 65535 || value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + if (_receivePort == value) + { + return; + } + _receivePort = value; + if (!_utilityInitialized) + { + return; + } + if (_server == null) + { + return; + } + + _server.Dispose(); + _server = new OscServer(value); + + if (_monitorCallbacks == null) + { + return; + } + for (int i = 0; i < _monitorCallbacks.Count; i++) + { + _server.AddMonitorCallback(_monitorCallbacks[i]); + } + } + } + + private static int _sendPort = 9000; + public static int SendPort + { + get => _sendPort; + set + { + if (value > 65535 || value < 0) + { + throw new ArgumentOutOfRangeException(nameof(value)); + } + if (_sendPort == value) + { + return; + } + + _sendPort = value; + if (_client != null) + { + _client.Dispose(); + _client = null; + } + } + } + + private static string _vrcIPAddress = "127.0.0.1"; + + public static string VrcIPAddress + { + get => _vrcIPAddress; + set + { + // throw Exception if `value` can't be parsed. + _vrcIPAddress = System.Net.IPAddress.Parse(value).ToString(); + + if (_client != null) + { + _client.Dispose(); + _client = null; + } + } + } + private static List? _monitorCallbacks; + internal static List MonitorCallbacks => _monitorCallbacks ??= new List(); +} diff --git a/src/vrcosclib/Utility/OscParameter.cs b/src/vrcosclib/Utility/OscParameter.cs index 459374b..2784ef6 100644 --- a/src/vrcosclib/Utility/OscParameter.cs +++ b/src/vrcosclib/Utility/OscParameter.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.Net.Sockets; using System.Text; +using BuildSoft.OscCore; using ParamChangedHandler = BuildSoft.VRChat.Osc.OscParameterChangedEventHandler; namespace BuildSoft.VRChat.Osc; @@ -17,11 +19,25 @@ public static event ParamChangedHandler ValueChanged static OscParameter() { - OscUtility.RegisterMonitorCallback(ReceiveMessage); + try + { + Initialize(); + } + catch (Exception ex) + { + if (OscConnectionSettings._utilityInitialized) + { + throw; + } + OscUtility._initializationExceptions.Add(ex); + return; + } } internal static void Initialize() { - + MonitorCallback callback = ReceiveMessage; + OscUtility.UnregisterMonitorCallback(callback); + OscUtility.RegisterMonitorCallback(callback); } } diff --git a/src/vrcosclib/Utility/OscUtility.Connection.cs b/src/vrcosclib/Utility/OscUtility.Connection.cs index 9ac23b1..94df899 100644 --- a/src/vrcosclib/Utility/OscUtility.Connection.cs +++ b/src/vrcosclib/Utility/OscUtility.Connection.cs @@ -4,95 +4,42 @@ namespace BuildSoft.VRChat.Osc; public static partial class OscUtility { - private static OscServer? _server; - private static OscClient? _client; + internal static OscServer Server => OscConnectionSettings.Server; + internal static OscClient Client => OscConnectionSettings.Client; - internal static OscServer Server => _server ??= new OscServer(_receivePort); - internal static OscClient Client => _client ??= new OscClient(_vrcIPAddress, _sendPort); - private static int _receivePort = 9001; + [Obsolete($"Use '{nameof(OscConnectionSettings)}.{nameof(OscConnectionSettings.ReceivePort)}'")] public static int ReceivePort { - get => _receivePort; - set - { - if (value > 65535 || value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - if (_receivePort == value) - { - return; - } - _receivePort = value; - - if (_server == null) - { - return; - } - - _server.Dispose(); - _server = new OscServer(value); - - if (_monitorCallbacks == null) - { - return; - } - for (int i = 0; i < _monitorCallbacks.Count; i++) - { - _server.AddMonitorCallback(_monitorCallbacks[i]); - } - } + get => OscConnectionSettings.ReceivePort; + set => OscConnectionSettings.ReceivePort = value; } - private static int _sendPort = 9000; + [Obsolete($"Use '{nameof(OscConnectionSettings)}.{nameof(OscConnectionSettings.SendPort)}'")] public static int SendPort { - get => _sendPort; - set - { - if (value > 65535 || value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - if (_sendPort == value) - { - return; - } - - _sendPort = value; - if (_client != null) - { - _client.Dispose(); - _client = null; - } - } + get => OscConnectionSettings.SendPort; + set => OscConnectionSettings.SendPort = value; } - private static string _vrcIPAddress = "127.0.0.1"; - + [Obsolete($"Use '{nameof(OscConnectionSettings)}.{nameof(OscConnectionSettings.VrcIPAddress)}'")] public static string VrcIPAddress { - get => _vrcIPAddress; - set - { - // throw Exception if `value` can't be parsed. - _vrcIPAddress = System.Net.IPAddress.Parse(value).ToString(); - - if (_client != null) - { - _client.Dispose(); - _client = null; - } - } + get => OscConnectionSettings.VrcIPAddress; + set => OscConnectionSettings.VrcIPAddress = value; } public static void RegisterMonitorCallback(MonitorCallback callback) { - var callbacks = _monitorCallbacks ??= new(); - callbacks.Add(callback); + var callbacks = OscConnectionSettings.MonitorCallbacks; Server.AddMonitorCallback(callback); + callbacks.Add(callback); + } + public static void UnregisterMonitorCallback(MonitorCallback callback) + { + var callbacks = OscConnectionSettings.MonitorCallbacks; + Server.RemoveMonitorCallback(callback); + callbacks.Remove(callback); } - private static List? _monitorCallbacks; } diff --git a/src/vrcosclib/Utility/OscUtility.cs b/src/vrcosclib/Utility/OscUtility.cs index ce23c9b..52ec9f0 100644 --- a/src/vrcosclib/Utility/OscUtility.cs +++ b/src/vrcosclib/Utility/OscUtility.cs @@ -9,14 +9,25 @@ public static partial class OscUtility public static readonly string VRChatAppDataPath = Path.Combine(UserProfile, @"AppData", "LocalLow", "VRChat", "VRChat"); public static readonly string VRChatOscPath = Path.Combine(VRChatAppDataPath, @"Osc"); + internal static readonly List _initializationExceptions = new(); + public static bool IsFailedAutoInitialization => _initializationExceptions.Count > 0; + public static void Initialize() { - + OscAvatarUtility.Initialize(); } static OscUtility() { - OscAvatarUtility.Initialize(); + try + { + Initialize(); + } + catch (Exception ex) + { + _initializationExceptions.Add(ex); + } + OscConnectionSettings._utilityInitialized = true; } internal static object? ReadValue(this OscMessageValues value, int index) From cad0b250f504b80f40a9e06bf3128bb65f344f61 Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 13:30:00 +0900 Subject: [PATCH 3/7] Add test for `effd8ab`. --- .../OscInitializeTest.cs | 46 ++++ ...vrcosclib.OscUtilityInitialize.Test.csproj | 25 ++ .../Utility/OscConnectionSettingsTests.cs | 251 ++++++++++++++++++ src/vrcosclib.Test/vrcosclib.Test.csproj | 4 + vrcosclib.sln | 18 ++ 5 files changed, 344 insertions(+) create mode 100644 src/vrcosclib.OscUtilityInitialize.Test/OscInitializeTest.cs create mode 100644 src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj create mode 100644 src/vrcosclib.Test/Utility/OscConnectionSettingsTests.cs diff --git a/src/vrcosclib.OscUtilityInitialize.Test/OscInitializeTest.cs b/src/vrcosclib.OscUtilityInitialize.Test/OscInitializeTest.cs new file mode 100644 index 0000000..2a09da7 --- /dev/null +++ b/src/vrcosclib.OscUtilityInitialize.Test/OscInitializeTest.cs @@ -0,0 +1,46 @@ +using System.Net; +using System.Net.Sockets; +using BuildSoft.OscCore; +using NUnit.Framework; + +namespace BuildSoft.VRChat.Osc.Test; + +[TestOf(typeof(OscUtility))] +public class OscInitializeTest +{ + OscServer? _oscServer; + [SetUp] + public void Setup() + { + _oscServer = new(9001); + } + + [TearDown] + public void TearDown() + { + _oscServer?.Dispose(); + } + + [OneTimeSetUp] + public void OneTimeSetUp() + { + + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + + } + + [Test] + public void InitializeTest() + { + Assert.IsTrue(OscUtility.IsFailedAutoInitialization); + Assert.Throws(() => OscUtility.Initialize()); + + OscConnectionSettings.ReceivePort = 9003; + Assert.DoesNotThrow(() => OscUtility.Initialize()); + Assert.IsTrue(OscUtility.IsFailedAutoInitialization); + } +} diff --git a/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj b/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj new file mode 100644 index 0000000..ef63892 --- /dev/null +++ b/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj @@ -0,0 +1,25 @@ + + + + net6.0;net461 + net6.0 + enable + BuildSoft.VRChat.Osc.Test + 10.0 + enable + false + true + + + + + + + + + + + + + + diff --git a/src/vrcosclib.Test/Utility/OscConnectionSettingsTests.cs b/src/vrcosclib.Test/Utility/OscConnectionSettingsTests.cs new file mode 100644 index 0000000..6a97b04 --- /dev/null +++ b/src/vrcosclib.Test/Utility/OscConnectionSettingsTests.cs @@ -0,0 +1,251 @@ +using System.Net; +using System.Net.Sockets; +using BuildSoft.OscCore; +using NUnit.Framework; + +using static BuildSoft.VRChat.Osc.Test.TestUtility; + +namespace BuildSoft.VRChat.Osc.Test; + +[TestOf(typeof(OscConnectionSettings))] +public class OscConnectionSettingsTests +{ + [SetUp] + public void Setup() + { + StashOscDirectory(); + } + + [TearDown] + public void TearDown() + { + RestoreOscDirectory(); + } + + [OneTimeSetUp] + public void OneTimeSetUp() + { + OscParameter.Parameters.Clear(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + + } + + [Test] + public async Task TestAroundGetCurrentAvatarConfigPath() + { + Assert.IsNull(OscUtility.GetCurrentOscAvatarConfigPath()); + Assert.ThrowsAsync(async () => await OscUtility.WaitAndGetCurrentOscAvatarConfigPathAsync(CanceledToken)); + + const string TestAvatarId = "avtr_test_avatar_id"; + + using (var client = new OscClient("127.0.0.1", OscConnectionSettings.ReceivePort)) + { + client.Send(OscConst.AvatarIdAddress, TestAvatarId); + await LoopWhile(() => Avatar.OscAvatarUtility.CurrentAvatar.Id == null, LatencyTimeout); + } + + Assert.Throws(() => OscUtility.GetCurrentOscAvatarConfigPath()); + Assert.ThrowsAsync(async () => await OscUtility.WaitAndGetCurrentOscAvatarConfigPathAsync()); + + var testAvatarDirectory = Path.Combine(OscUtility.VRChatOscPath, @"usr_test_user_id", "Avatars"); + var path = CreateConfigFileForTest(TestAvatarId, "TestAvatar", testAvatarDirectory, true); + + var configPath = OscUtility.GetCurrentOscAvatarConfigPath(); + var configPathAsync = await OscUtility.WaitAndGetCurrentOscAvatarConfigPathAsync(); + + Assert.AreEqual(path, configPath); + Assert.AreEqual(path, configPathAsync); + } + + [Test] + public void TestGetAvatarConfigPath() + { + const string TestAvatarId = "avtr_test_avatar_id"; + Assert.Throws(() => OscUtility.GetOscAvatarConfigPath(TestAvatarId)); + + var testAvatarDirectory = Path.Combine(OscUtility.VRChatOscPath, @"usr_test_user_id", "Avatars"); + Assert.Throws(() => OscUtility.GetOscAvatarConfigPath(TestAvatarId)); + + var path = CreateConfigFileForTest(TestAvatarId, "TestAvatar", testAvatarDirectory, true); + Assert.AreEqual(path, OscUtility.GetOscAvatarConfigPath(TestAvatarId)); + } + + [Test] + public void TestGetCurrentAvatarConfigPathes() + { + CollectionAssert.IsEmpty(OscUtility.GetOscAvatarConfigPathes()); + + var testAvatarDirectory = Path.Combine(OscUtility.VRChatOscPath, @"usr_test_user_id", "Avatars"); + Directory.CreateDirectory(testAvatarDirectory); + + CollectionAssert.IsEmpty(OscUtility.GetOscAvatarConfigPathes()); + + var path1 = CreateConfigFileForTest("avtr_test_avatar_id1", "TestAvatar", testAvatarDirectory, true); + CollectionAssert.AreEqual(new[] { path1 }, OscUtility.GetOscAvatarConfigPathes()); + var path2 = CreateConfigFileForTest("avtr_test_avatar_id2", "TestAvatar", testAvatarDirectory, true); + CollectionAssert.AreEqual(new[] { path1, path2 }, OscUtility.GetOscAvatarConfigPathes().Sort()); + } + + [TestCase(0)] + [TestCase(8001)] + [TestCase(65535)] + public void TestSendPort(int port) + { + int oldPort = OscConnectionSettings.SendPort; + var oldClient = OscConnectionSettings.Client; + + OscConnectionSettings.SendPort = port; + Assert.AreEqual(port, OscConnectionSettings.SendPort); + Assert.AreEqual(port, OscConnectionSettings.Client.Destination.Port); + Assert.AreNotSame(oldClient, OscConnectionSettings.Client); + + OscConnectionSettings.SendPort = oldPort; + } + + [TestCase(-1)] + [TestCase(65536)] + public void TestSendPortOutOfRange(int port) + { + int oldPort = OscConnectionSettings.SendPort; + + Assert.Throws(() => OscConnectionSettings.SendPort = port); + Assert.AreEqual(oldPort, OscConnectionSettings.SendPort); + } + + [TestCase(0)] + [TestCase(8001)] + [TestCase(65535)] + public void TestReceivePort(int port) + { + OscConnectionSettings.ReceivePort = port; + Assert.AreEqual(port, OscConnectionSettings.ReceivePort); + Assert.AreEqual(port, OscConnectionSettings.Server.Port); + } + + [TestCase(-1)] + [TestCase(65536)] + public void TestReceivePortOutOfRange(int port) + { + int oldPort = OscConnectionSettings.ReceivePort; + Assert.Throws(() => OscConnectionSettings.ReceivePort = port); + Assert.AreEqual(oldPort, OscConnectionSettings.ReceivePort); + Assert.AreEqual(oldPort, OscConnectionSettings.Server.Port); + } + + [TestCase("127.0.0.1")] + [TestCase("192.168.1.1")] + [TestCase("8.8.8.8")] + public void TestVrcIPAddress(string ipAddress) + { + string oldAddress = OscConnectionSettings.VrcIPAddress; + + OscConnectionSettings.VrcIPAddress = ipAddress; + Assert.AreEqual(ipAddress, OscConnectionSettings.VrcIPAddress); + Assert.AreEqual(ipAddress, OscConnectionSettings.Client.Destination.Address.ToString()); + + OscConnectionSettings.VrcIPAddress = oldAddress; + } + + [TestCase("example.com")] + [TestCase("ipaddress")] + public void TestVrcIPAddressInvalidFormat(string ipAddress) + { + string oldAddress = OscConnectionSettings.VrcIPAddress; + + Assert.Throws(() => OscConnectionSettings.VrcIPAddress = ipAddress); + Assert.AreEqual(oldAddress, OscConnectionSettings.VrcIPAddress); + } + + [Test] + public void TestClientIPAddressNull() + { + string oldAddress = OscConnectionSettings.VrcIPAddress; + + Assert.Throws(() => OscConnectionSettings.VrcIPAddress = null!); + Assert.AreEqual(oldAddress, OscConnectionSettings.VrcIPAddress); + } + + [Test] + public async Task TestRegisterMonitorCallback() + { + int value = 0; + OscUtility.RegisterMonitorCallback((_, _) => value++); + + int oldPort = OscConnectionSettings.ReceivePort; + + OscConnectionSettings.ReceivePort = 12345; + using (var client = new OscClient("127.0.0.1", 12345)) + { + client.Send("/value/send", 1); + await LoopWhile(() => value == 0, LatencyTimeout); + Assert.AreEqual(1, value); + + client.Send("/value/send", 1); + await LoopWhile(() => value == 1, LatencyTimeout); + Assert.AreEqual(2, value); + } + + OscConnectionSettings.ReceivePort = 54321; + using (var client = new OscClient("127.0.0.1", 54321)) + { + client.Send("/value/send", 1); + await LoopWhile(() => value == 2, LatencyTimeout); + Assert.AreEqual(3, value); + } + + OscConnectionSettings.ReceivePort = oldPort; + } + + [Test] + public async Task TestSendPortWithSending() + { + int oldPort = OscConnectionSettings.SendPort; + + OscConnectionSettings.SendPort = 12345; + using (var client = new UdpClient(12345)) + { + OscParameter.SendValue("/value/send", 1); + var result = await client.ReceiveAsync().WaitAsync(LatencyTimeout); + Assert.AreEqual(OscConnectionSettings.VrcIPAddress, result.RemoteEndPoint.Address.ToString()); + } + + OscConnectionSettings.SendPort = 54321; + using (var client = new UdpClient(54321)) + { + OscParameter.SendValue("/value/send", 1); + var result = await client.ReceiveAsync().WaitAsync(LatencyTimeout); + Assert.AreEqual(OscConnectionSettings.VrcIPAddress, result.RemoteEndPoint.Address.ToString()); + } + + OscConnectionSettings.SendPort = oldPort; + } + + [Test] + public async Task TestVrcIPAddressWithSending() + { + string oldAddress = OscConnectionSettings.VrcIPAddress; + + using (var client = new UdpClient(new IPEndPoint( + IPAddress.Parse("127.0.0.1"), + OscConnectionSettings.SendPort))) + { + OscParameter.SendValue("/value/send", 1); + await client.ReceiveAsync().WaitAsync(LatencyTimeout); + } + + using (var client = new UdpClient(new IPEndPoint( + IPAddress.Parse("127.0.0.2"), + OscConnectionSettings.SendPort))) + { + OscConnectionSettings.VrcIPAddress = "127.0.0.2"; //127.0.0.1 to 127.0.0.2 + OscParameter.SendValue("/value/send", 2); + await client.ReceiveAsync().WaitAsync(LatencyTimeout); + } + + OscConnectionSettings.VrcIPAddress = oldAddress; + } +} diff --git a/src/vrcosclib.Test/vrcosclib.Test.csproj b/src/vrcosclib.Test/vrcosclib.Test.csproj index 217f59f..568acc7 100644 --- a/src/vrcosclib.Test/vrcosclib.Test.csproj +++ b/src/vrcosclib.Test/vrcosclib.Test.csproj @@ -11,6 +11,10 @@ true + + CS0618 + + diff --git a/vrcosclib.sln b/vrcosclib.sln index de88bd7..843015f 100644 --- a/vrcosclib.sln +++ b/vrcosclib.sln @@ -31,6 +31,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvatarParameterSender", "Sa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatboxSender", "Sample\ChatboxSender\ChatboxSender.csproj", "{1BF37DAA-23F4-4321-B709-6D1EAD45D10E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{D20C17F1-E359-46B5-95D9-BBB5B87969EB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "vrcosclib.OscUtilityInitialize.Test", "src\vrcosclib.OscUtilityInitialize.Test\vrcosclib.OscUtilityInitialize.Test.csproj", "{E61A1022-422F-4711-99CB-895227018E39}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,15 +109,29 @@ Global {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|For Test.ActiveCfg = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Full Build.ActiveCfg = Release|Any CPU {1BF37DAA-23F4-4321-B709-6D1EAD45D10E}.Release|Full Build.Build.0 = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|For Test.ActiveCfg = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|For Test.Build.0 = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|Full Build.ActiveCfg = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Debug|Full Build.Build.0 = Debug|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|Any CPU.Build.0 = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|For Test.ActiveCfg = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|For Test.Build.0 = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|Full Build.ActiveCfg = Release|Any CPU + {E61A1022-422F-4711-99CB-895227018E39}.Release|Full Build.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution + {905C0E65-4B84-40C9-9C6C-1A454BCB2D2A} = {D20C17F1-E359-46B5-95D9-BBB5B87969EB} {5729DE08-F01E-4091-8031-3FAE649F7627} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5} {B214E3A8-C61B-4921-94A6-73CAAB358A14} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5} {E3589FA0-A1ED-4986-B51F-7D8270A4656E} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5} {1BF37DAA-23F4-4321-B709-6D1EAD45D10E} = {E2335844-9FA5-4574-B2CF-50E8C4C172C5} + {E61A1022-422F-4711-99CB-895227018E39} = {D20C17F1-E359-46B5-95D9-BBB5B87969EB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7ADDCAD0-5084-420D-96B4-54FD8F6A2FA5} From 8585ef104bad5973b8ede70af59f3a1d549a4800 Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 14:23:32 +0900 Subject: [PATCH 4/7] Improved performance of `OscAvatarConfig.CreateAll()`. --- .../Avatar/OscAvatarConfigPerformanceTests.cs | 68 +++++++++++++++++++ src/vrcosclib/Avatar/OscAvatarConfig.cs | 3 +- .../Utility/OscUtility.AvatarConfig.cs | 12 ++-- 3 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs diff --git a/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs b/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs new file mode 100644 index 0000000..26c48c4 --- /dev/null +++ b/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs @@ -0,0 +1,68 @@ +using BuildSoft.VRChat.Osc.Test; +using NUnit.Framework; + +namespace BuildSoft.VRChat.Osc.Avatar.Test; + +[TestOf(typeof(OscAvatarConfig))] +public class OscAvatarConfigPerformanceTests +{ + private static IEnumerable ConfigJsonsForPerformanceTest + { + get + { + List parameters = new(); + + for (int j = 0; j < 10000; j++) + { + parameters.Clear(); + + for (int k = 0; k < 100; k++) + { + parameters.Add(new OscAvatarParameterJson($"param{k}", OscType.Float)); + } + yield return new OscAvatarConfigJson($"avtr_{j}", $"name{j}", parameters.ToArray()); + } + } + } + + + [SetUp] + public void Setup() + { + + } + + [TearDown] + public void TearDown() + { + + } + + [OneTimeSetUp] + public void OneTimeSetup() + { + OscUtility.Initialize(); + + TestUtility.StashOscDirectory(); + + var directory = Path.Combine(OscUtility.VRChatOscPath, "Avatars"); + foreach (var config in ConfigJsonsForPerformanceTest) + { + TestUtility.CreateConfigFileForTest(config, directory); + } + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + TestUtility.RestoreOscDirectory(); + } + + + [Timeout(4000)] + [Test] + public void CreateAll_PerformanceTest() + { + _ = OscAvatarConfig.CreateAll(); + } +} diff --git a/src/vrcosclib/Avatar/OscAvatarConfig.cs b/src/vrcosclib/Avatar/OscAvatarConfig.cs index a6f87f1..eb8b24c 100644 --- a/src/vrcosclib/Avatar/OscAvatarConfig.cs +++ b/src/vrcosclib/Avatar/OscAvatarConfig.cs @@ -54,7 +54,8 @@ public OscAvatarConfig(string id, string name, IEnumerable p public static OscAvatarConfig[] CreateAll() => - OscUtility.GetOscAvatarConfigPathes() + OscUtility.EnumerateOscAvatarConfigPathes() + .AsParallel() .Select(GetAvatarConfig) .Where(config => config != null).ToArray()!; diff --git a/src/vrcosclib/Utility/OscUtility.AvatarConfig.cs b/src/vrcosclib/Utility/OscUtility.AvatarConfig.cs index a8ed199..ffbea69 100644 --- a/src/vrcosclib/Utility/OscUtility.AvatarConfig.cs +++ b/src/vrcosclib/Utility/OscUtility.AvatarConfig.cs @@ -38,21 +38,25 @@ public static string GetOscAvatarConfigPath(string avatarId) } public static ImmutableArray GetOscAvatarConfigPathes() + { + return EnumerateOscAvatarConfigPathes().ToImmutableArray(); + } + + internal static IEnumerable EnumerateOscAvatarConfigPathes() { if (!Directory.Exists(VRChatOscPath)) { - return ImmutableArray.Empty; + return Array.Empty(); } try { return Directory.EnumerateDirectories(VRChatOscPath, "Avatars", SearchOption.AllDirectories) - .SelectMany(s => Directory.EnumerateFiles(s, "*.json")) - .ToImmutableArray(); + .SelectMany(s => Directory.EnumerateFiles(s, "*.json")); } catch (DirectoryNotFoundException) { - return ImmutableArray.Empty; + return Array.Empty(); } } } From 223b28fc137b74ce3ec9583bc98d01749ddc1a78 Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 14:24:48 +0900 Subject: [PATCH 5/7] Updated the test package version. --- .../vrcosclib.OscUtilityInitialize.Test.csproj | 4 ++-- src/vrcosclib.Test/vrcosclib.Test.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj b/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj index ef63892..e8a86de 100644 --- a/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj +++ b/src/vrcosclib.OscUtilityInitialize.Test/vrcosclib.OscUtilityInitialize.Test.csproj @@ -13,8 +13,8 @@ - - + + diff --git a/src/vrcosclib.Test/vrcosclib.Test.csproj b/src/vrcosclib.Test/vrcosclib.Test.csproj index 568acc7..f824928 100644 --- a/src/vrcosclib.Test/vrcosclib.Test.csproj +++ b/src/vrcosclib.Test/vrcosclib.Test.csproj @@ -17,8 +17,8 @@ - - + + From 823efab2d2c7b9d96e1507afac2976358eabac2e Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 14:25:20 +0900 Subject: [PATCH 6/7] Release v1.4.0 --- src/vrcosclib/vrcosclib.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vrcosclib/vrcosclib.csproj b/src/vrcosclib/vrcosclib.csproj index 6c29ce4..207e009 100644 --- a/src/vrcosclib/vrcosclib.csproj +++ b/src/vrcosclib/vrcosclib.csproj @@ -10,7 +10,7 @@ ..\..\bin VRCOscLib - 1.3.3 + 1.4.0 ChanyaKushima MIT A OSC library for VRChat From ee21b8f1dda62517888191609edd6c45faf70ac1 Mon Sep 17 00:00:00 2001 From: ChanyaKushima <63221603+ChanyaVRC@users.noreply.github.com> Date: Sun, 20 Nov 2022 14:33:57 +0900 Subject: [PATCH 7/7] Aligned timeout with CI/CD. --- src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs b/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs index 26c48c4..1edda15 100644 --- a/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs +++ b/src/vrcosclib.Test/Avatar/OscAvatarConfigPerformanceTests.cs @@ -59,7 +59,7 @@ public void OneTimeTearDown() } - [Timeout(4000)] + [Timeout(8000)] [Test] public void CreateAll_PerformanceTest() {