[ci skip] Add braces around single-statement bodies

This commit is contained in:
Oliver Booth 2022-11-27 17:51:18 +00:00
parent 1bf050eca0
commit 693e90bff1
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
36 changed files with 445 additions and 70 deletions

View File

@ -14,8 +14,14 @@ public sealed class Application
{ {
Name = name ?? throw new ArgumentNullException(nameof(name)); Name = name ?? throw new ArgumentNullException(nameof(name));
if (string.IsNullOrWhiteSpace(version)) Version = null; if (string.IsNullOrWhiteSpace(version))
else Version = version; {
Version = null;
}
else
{
Version = version;
}
} }
/// <summary> /// <summary>

View File

@ -116,10 +116,25 @@ public readonly struct ColorF : IEquatable<ColorF>
/// </exception> /// </exception>
public static ColorF FromArgb(float a, float r, float g, float b) public static ColorF FromArgb(float a, float r, float g, float b)
{ {
if (a is < 0 or > 1) throw ThrowHelper.ZeroThroughOneException(nameof(a)); if (a is < 0 or > 1)
if (r is < 0 or > 1) throw ThrowHelper.ZeroThroughOneException(nameof(r)); {
if (g is < 0 or > 1) throw ThrowHelper.ZeroThroughOneException(nameof(g)); throw ThrowHelper.ZeroThroughOneException(nameof(a));
if (b is < 0 or > 1) throw ThrowHelper.ZeroThroughOneException(nameof(b)); }
if (r is < 0 or > 1)
{
throw ThrowHelper.ZeroThroughOneException(nameof(r));
}
if (g is < 0 or > 1)
{
throw ThrowHelper.ZeroThroughOneException(nameof(g));
}
if (b is < 0 or > 1)
{
throw ThrowHelper.ZeroThroughOneException(nameof(b));
}
return new ColorF(a, r, g, b); return new ColorF(a, r, g, b);
} }

View File

@ -1,4 +1,4 @@
using System.Numerics; using System.Numerics;
using VpSharp.Extensions; using VpSharp.Extensions;
using VpSharp.Internal; using VpSharp.Internal;
using VpSharp.Internal.NativeAttributes; using VpSharp.Internal.NativeAttributes;
@ -98,8 +98,16 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
/// </returns> /// </returns>
public bool Equals(VirtualParadiseAvatar? other) public bool Equals(VirtualParadiseAvatar? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Session == other.Session && User.Equals(other.User); return Session == other.Session && User.Equals(other.User);
} }
@ -130,7 +138,9 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
{ {
// ReSharper disable once InconsistentlySynchronizedField // ReSharper disable once InconsistentlySynchronizedField
if (this == _client.CurrentAvatar) if (this == _client.CurrentAvatar)
{
return Task.FromException(ThrowHelper.CannotUseSelfException()); return Task.FromException(ThrowHelper.CannotUseSelfException());
}
clickPoint ??= Location.Position; clickPoint ??= Location.Position;
(double x, double y, double z) = clickPoint.Value; (double x, double y, double z) = clickPoint.Value;
@ -145,8 +155,10 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
var reason = (ReasonCode)vp_avatar_click(handle, Session); var reason = (ReasonCode)vp_avatar_click(handle, Session);
if (reason == ReasonCode.NotInWorld) if (reason == ReasonCode.NotInWorld)
{
return Task.FromException(ThrowHelper.NotInWorldException()); return Task.FromException(ThrowHelper.NotInWorldException());
} }
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -164,7 +176,9 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
// ReSharper disable once InconsistentlySynchronizedField // ReSharper disable once InconsistentlySynchronizedField
if (this == _client.CurrentAvatar) if (this == _client.CurrentAvatar)
{
return Task.FromException(ThrowHelper.CannotUseSelfException()); return Task.FromException(ThrowHelper.CannotUseSelfException());
}
lock (_client.Lock) lock (_client.Lock)
{ {
@ -250,9 +264,11 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
var reason = (ReasonCode)vp_state_change(handle); var reason = (ReasonCode)vp_state_change(handle);
if (reason == ReasonCode.NotInWorld) if (reason == ReasonCode.NotInWorld)
{
ThrowHelper.ThrowNotInWorldException(); ThrowHelper.ThrowNotInWorldException();
} }
} }
}
else else
{ {
lock (_client.Lock) lock (_client.Lock)
@ -262,9 +278,11 @@ public sealed class VirtualParadiseAvatar : IEquatable<VirtualParadiseAvatar>
var reason = (ReasonCode)vp_teleport_avatar(handle, Session, world, x, y, z, yaw, pitch); var reason = (ReasonCode)vp_teleport_avatar(handle, Session, world, x, y, z, yaw, pitch);
if (reason == ReasonCode.NotInWorld) if (reason == ReasonCode.NotInWorld)
{
ThrowHelper.ThrowNotInWorldException(); ThrowHelper.ThrowNotInWorldException();
} }
} }
}
Location = new Location(new VirtualParadiseWorld(_client, world), position, rotation); Location = new Location(new VirtualParadiseWorld(_client, world), position, rotation);
// ReSharper restore InconsistentlySynchronizedField // ReSharper restore InconsistentlySynchronizedField

View File

@ -65,7 +65,9 @@ public class VirtualParadiseModelObject : VirtualParadiseObject
protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject) protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject)
{ {
if (virtualParadiseObject is not VirtualParadiseModelObject model) if (virtualParadiseObject is not VirtualParadiseModelObject model)
{
return; return;
}
Action = model.Action; Action = model.Action;
Description = model.Description; Description = model.Description;

View File

@ -107,9 +107,20 @@ public sealed class VirtualParadiseModelObjectBuilder : VirtualParadiseObjectBui
{ {
IntPtr handle = Client.NativeInstanceHandle; IntPtr handle = Client.NativeInstanceHandle;
if (Action is { } action) vp_string_set(handle, StringAttribute.ObjectAction, action); if (Action is { } action)
if (Description is { } description) vp_string_set(handle, StringAttribute.ObjectDescription, description); {
if (Model is { } model) vp_string_set(handle, StringAttribute.ObjectModel, model); vp_string_set(handle, StringAttribute.ObjectAction, action);
}
if (Description is { } description)
{
vp_string_set(handle, StringAttribute.ObjectDescription, description);
}
if (Model is { } model)
{
vp_string_set(handle, StringAttribute.ObjectModel, model);
}
if (Position is { } position) if (Position is { } position)
{ {
@ -147,7 +158,9 @@ public sealed class VirtualParadiseModelObjectBuilder : VirtualParadiseObjectBui
if (ModificationTimestamp is { } modificationTimestamp) if (ModificationTimestamp is { } modificationTimestamp)
{ {
if (Mode != ObjectBuilderMode.Load) if (Mode != ObjectBuilderMode.Load)
{
throw new InvalidOperationException("Modification timestamp can only be assigned during an object load."); throw new InvalidOperationException("Modification timestamp can only be assigned during an object load.");
}
vp_int_set(handle, IntegerAttribute.ObjectTime, (int) modificationTimestamp.ToUnixTimeSeconds()); vp_int_set(handle, IntegerAttribute.ObjectTime, (int) modificationTimestamp.ToUnixTimeSeconds());
} }
@ -155,7 +168,9 @@ public sealed class VirtualParadiseModelObjectBuilder : VirtualParadiseObjectBui
if (Owner is { } owner) if (Owner is { } owner)
{ {
if (Mode != ObjectBuilderMode.Load) if (Mode != ObjectBuilderMode.Load)
{
throw new InvalidOperationException("Owner can only be assigned during an object load."); throw new InvalidOperationException("Owner can only be assigned during an object load.");
}
vp_int_set(handle, IntegerAttribute.ObjectUserId, owner.Id); vp_int_set(handle, IntegerAttribute.ObjectUserId, owner.Id);
} }

View File

@ -77,13 +77,21 @@ public abstract class VirtualParadiseObject : IEquatable<VirtualParadiseObject>
ValueTask SendBegin() ValueTask SendBegin()
{ {
lock (Client.Lock) vp_object_bump_begin(Client.NativeInstanceHandle, Id, session); lock (Client.Lock)
{
vp_object_bump_begin(Client.NativeInstanceHandle, Id, session);
}
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
ValueTask SendEnd() ValueTask SendEnd()
{ {
lock (Client.Lock) vp_object_bump_end(Client.NativeInstanceHandle, Id, session); lock (Client.Lock)
{
vp_object_bump_end(Client.NativeInstanceHandle, Id, session);
}
return ValueTask.CompletedTask; return ValueTask.CompletedTask;
} }
@ -115,7 +123,9 @@ public abstract class VirtualParadiseObject : IEquatable<VirtualParadiseObject>
public Task ClickAsync(Vector3d? position = null, VirtualParadiseAvatar? target = null) public Task ClickAsync(Vector3d? position = null, VirtualParadiseAvatar? target = null)
{ {
if (target == Client.CurrentAvatar) if (target == Client.CurrentAvatar)
{
ThrowHelper.ThrowCannotUseSelfException(); ThrowHelper.ThrowCannotUseSelfException();
}
lock (Client.Lock) lock (Client.Lock)
{ {
@ -159,17 +169,37 @@ public abstract class VirtualParadiseObject : IEquatable<VirtualParadiseObject>
/// <returns><see langword="true" /> if the two objects are equal; otherwise, <see langword="false" />.</returns> /// <returns><see langword="true" /> if the two objects are equal; otherwise, <see langword="false" />.</returns>
public bool Equals(VirtualParadiseObject? other) public bool Equals(VirtualParadiseObject? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Location.World.Equals(other.Location.World) && Id == other.Id; return Location.World.Equals(other.Location.World) && Id == other.Id;
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(null, obj))
if (ReferenceEquals(this, obj)) return true; {
if (obj.GetType() != GetType()) return false; return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((VirtualParadiseObject) obj); return Equals((VirtualParadiseObject) obj);
} }

View File

@ -209,7 +209,9 @@ public sealed class VirtualParadiseParticleEmitterObject : VirtualParadiseObject
protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject) protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject)
{ {
if (virtualParadiseObject is not VirtualParadiseParticleEmitterObject emitter) if (virtualParadiseObject is not VirtualParadiseParticleEmitterObject emitter)
{
return; return;
}
const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
PropertyInfo[] properties = typeof(VirtualParadiseParticleEmitterObject).GetProperties(bindingFlags); PropertyInfo[] properties = typeof(VirtualParadiseParticleEmitterObject).GetProperties(bindingFlags);
@ -217,7 +219,9 @@ public sealed class VirtualParadiseParticleEmitterObject : VirtualParadiseObject
foreach (PropertyInfo property in properties) foreach (PropertyInfo property in properties)
{ {
if (property.GetCustomAttribute<SerializationKeyAttribute>() is null) if (property.GetCustomAttribute<SerializationKeyAttribute>() is null)
{
continue; continue;
}
property.SetValue(this, property.GetValue(emitter)); property.SetValue(this, property.GetValue(emitter));
} }
@ -235,7 +239,9 @@ public sealed class VirtualParadiseParticleEmitterObject : VirtualParadiseObject
{ {
var serializationKeyAttribute = property.GetCustomAttribute<SerializationKeyAttribute>(); var serializationKeyAttribute = property.GetCustomAttribute<SerializationKeyAttribute>();
if (serializationKeyAttribute is null) if (serializationKeyAttribute is null)
{
continue; continue;
}
keymap.Add(serializationKeyAttribute.Key, property); keymap.Add(serializationKeyAttribute.Key, property);
@ -244,9 +250,11 @@ public sealed class VirtualParadiseParticleEmitterObject : VirtualParadiseObject
{ {
Type converterType = converterAttribute.ConverterType; Type converterType = converterAttribute.ConverterType;
if (Activator.CreateInstance(converterType) is ValueConverter converter) if (Activator.CreateInstance(converterType) is ValueConverter converter)
{
converterMap.Add(serializationKeyAttribute.Key, converter); converterMap.Add(serializationKeyAttribute.Key, converter);
} }
} }
}
#pragma warning restore 612 #pragma warning restore 612
Span<char> text = stackalloc char[data.Length]; Span<char> text = stackalloc char[data.Length];

View File

@ -26,7 +26,9 @@ public sealed class VirtualParadisePathObject : VirtualParadiseObject
protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject) protected internal override void ExtractFromOther(VirtualParadiseObject virtualParadiseObject)
{ {
if (virtualParadiseObject is not VirtualParadisePathObject path) if (virtualParadiseObject is not VirtualParadisePathObject path)
{
return; return;
}
Path = (VirtualParadisePath)path.Path.Clone(); Path = (VirtualParadisePath)path.Path.Clone();
} }
@ -55,7 +57,9 @@ public sealed class VirtualParadisePathObject : VirtualParadiseObject
buffer.Clear(); buffer.Clear();
if (version != 1) if (version != 1)
{
throw new NotSupportedException($"Unsupported path version {version}"); throw new NotSupportedException($"Unsupported path version {version}");
}
// path name // path name
var name = string.Empty; var name = string.Empty;

View File

@ -93,8 +93,16 @@ public sealed class VirtualParadiseUser : IEquatable<VirtualParadiseUser>
/// </returns> /// </returns>
public bool Equals(VirtualParadiseUser? other) public bool Equals(VirtualParadiseUser? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Id == other.Id; return Id == other.Id;
} }
@ -214,8 +222,10 @@ public sealed class VirtualParadiseUser : IEquatable<VirtualParadiseUser>
location = new Location(world, position, rotation); location = new Location(world, position, rotation);
if (!suppressTeleport) if (!suppressTeleport)
{
await avatar.TeleportAsync(location.Value); await avatar.TeleportAsync(location.Value);
} }
}
JoinResponse response = reason switch JoinResponse response = reason switch
{ {

View File

@ -89,8 +89,16 @@ public sealed class VirtualParadiseWorld : IEquatable<VirtualParadiseWorld>
/// </returns> /// </returns>
public bool Equals(VirtualParadiseWorld? other) public bool Equals(VirtualParadiseWorld? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return IsNowhere == other.IsNowhere && Name == other.Name; return IsNowhere == other.IsNowhere && Name == other.Name;
} }

View File

@ -70,11 +70,15 @@ internal static class SpanExtensions
{ {
buffer.Append(current); buffer.Append(current);
if (index < value.Length - 1) if (index < value.Length - 1)
{
continue; continue;
} }
}
if (current != ' ') if (current != ' ')
{
continue; continue;
}
ReadOnlySpan<byte> span = buffer.AsSpan(); ReadOnlySpan<byte> span = buffer.AsSpan();
var floatValue = span.ToSingle(); var floatValue = span.ToSingle();
@ -111,11 +115,15 @@ internal static class SpanExtensions
{ {
buffer.Append(current); buffer.Append(current);
if (index < value.Length - 1) if (index < value.Length - 1)
{
continue; continue;
} }
}
if (current != ' ') if (current != ' ')
{
continue; continue;
}
ReadOnlySpan<byte> span = buffer.AsSpan(); ReadOnlySpan<byte> span = buffer.AsSpan();
var floatValue = span.ToSingle(); var floatValue = span.ToSingle();
@ -155,11 +163,15 @@ internal static class SpanExtensions
{ {
buffer.Append(current); buffer.Append(current);
if (index < value.Length - 1) if (index < value.Length - 1)
{
continue; continue;
} }
}
if (current != ' ') if (current != ' ')
{
continue; continue;
}
ReadOnlySpan<byte> span = buffer.AsSpan(); ReadOnlySpan<byte> span = buffer.AsSpan();
var floatValue = span.ToDouble(); var floatValue = span.ToDouble();
@ -200,11 +212,15 @@ internal static class SpanExtensions
{ {
buffer.Append(current); buffer.Append(current);
if (index < value.Length - 1) if (index < value.Length - 1)
{
continue; continue;
} }
}
if (current != ' ') if (current != ' ')
{
continue; continue;
}
ReadOnlySpan<byte> span = buffer.AsSpan(); ReadOnlySpan<byte> span = buffer.AsSpan();
var floatValue = span.ToSingle(); var floatValue = span.ToSingle();

View File

@ -17,13 +17,19 @@ internal sealed class ValueConverterAttribute : Attribute
public ValueConverterAttribute(Type converterType, params object?[]? args) public ValueConverterAttribute(Type converterType, params object?[]? args)
{ {
if (converterType is null) if (converterType is null)
{
throw new ArgumentNullException(nameof(converterType)); throw new ArgumentNullException(nameof(converterType));
}
if (converterType.IsAbstract) if (converterType.IsAbstract)
{
throw new ArgumentException("Cannot use abstract converter."); throw new ArgumentException("Cannot use abstract converter.");
}
if (!converterType.IsSubclassOf(typeof(ValueConverter))) if (!converterType.IsSubclassOf(typeof(ValueConverter)))
{
throw new ArgumentException($"Converter does not inherit {typeof(ValueConverter)}"); throw new ArgumentException($"Converter does not inherit {typeof(ValueConverter)}");
}
ConverterType = converterType; ConverterType = converterType;
Args = args; Args = args;

View File

@ -60,7 +60,11 @@ internal class Connection
GCHandle handle = GCHandle.FromIntPtr(ptr); GCHandle handle = GCHandle.FromIntPtr(ptr);
var connection = handle.Target as Connection; var connection = handle.Target as Connection;
string host = Marshal.PtrToStringAnsi(hostPtr); string host = Marshal.PtrToStringAnsi(hostPtr);
if (connection is not null) return connection.Connect(host, port); if (connection is not null)
{
return connection.Connect(host, port);
}
return 0; return 0;
} }
@ -83,7 +87,10 @@ internal class Connection
private void HandleTimeout() private void HandleTimeout()
{ {
if (_timer != null) Notify(NetworkNotification.Timeout, 0); if (_timer != null)
{
Notify(NetworkNotification.Timeout, 0);
}
} }
private static void HandleTimeout(object state) private static void HandleTimeout(object state)
@ -96,13 +103,18 @@ internal class Connection
lock (_lockObject) lock (_lockObject)
{ {
if (_vpConnection != IntPtr.Zero) if (_vpConnection != IntPtr.Zero)
{
Native.vp_net_notify(_vpConnection, (int) notification, rc); Native.vp_net_notify(_vpConnection, (int) notification, rc);
} }
} }
}
public int Receive(IntPtr data, uint length) public int Receive(IntPtr data, uint length)
{ {
if (_readyBuffers.Count == 0) return (int) NetworkReturnCode.WouldBlock; if (_readyBuffers.Count == 0)
{
return (int) NetworkReturnCode.WouldBlock;
}
var spaceLeft = (int) length; var spaceLeft = (int) length;
IntPtr destination = data; IntPtr destination = data;
@ -134,7 +146,11 @@ internal class Connection
private static void ReceiveCallback(IAsyncResult ar) private static void ReceiveCallback(IAsyncResult ar)
{ {
if (ar.AsyncState is not Connection connection) return; if (ar.AsyncState is not Connection connection)
{
return;
}
int bytesRead; int bytesRead;
try try
@ -176,14 +192,18 @@ internal class Connection
} }
} }
else else
{
connection.Notify(NetworkNotification.Disconnect, 0); connection.Notify(NetworkNotification.Disconnect, 0);
} }
} }
}
public static int ReceiveNative(IntPtr ptr, IntPtr data, uint length) public static int ReceiveNative(IntPtr ptr, IntPtr data, uint length)
{ {
if (GCHandle.FromIntPtr(ptr).Target is Connection connection) if (GCHandle.FromIntPtr(ptr).Target is Connection connection)
{
return connection.Receive(data, length); return connection.Receive(data, length);
}
return 0; return 0;
} }
@ -205,7 +225,9 @@ internal class Connection
public static int SendNative(IntPtr ptr, IntPtr data, uint length) public static int SendNative(IntPtr ptr, IntPtr data, uint length)
{ {
if (GCHandle.FromIntPtr(ptr).Target is Connection connection) if (GCHandle.FromIntPtr(ptr).Target is Connection connection)
{
return connection.Send(data, length); return connection.Send(data, length);
}
return 0; return 0;
} }
@ -213,9 +235,13 @@ internal class Connection
public int Timeout(int seconds) public int Timeout(int seconds)
{ {
if (seconds < 0) if (seconds < 0)
{
_timer = null; _timer = null;
}
else else
{
_timer = new Timer(HandleTimeout, this, seconds * 1000, global::System.Threading.Timeout.Infinite); _timer = new Timer(HandleTimeout, this, seconds * 1000, global::System.Threading.Timeout.Infinite);
}
return 0; return 0;
} }
@ -223,7 +249,9 @@ internal class Connection
public static int TimeoutNative(IntPtr ptr, int seconds) public static int TimeoutNative(IntPtr ptr, int seconds)
{ {
if (GCHandle.FromIntPtr(ptr).Target is Connection connection) if (GCHandle.FromIntPtr(ptr).Target is Connection connection)
{
return connection.Timeout(seconds); return connection.Timeout(seconds);
}
return 0; return 0;
} }

View File

@ -11,9 +11,14 @@ internal static class ObjectReferenceCounter
int ret; int ret;
Rwl.EnterWriteLock(); Rwl.EnterWriteLock();
if (s_reference < int.MaxValue) if (s_reference < int.MaxValue)
{
ret = s_reference++; ret = s_reference++;
}
else else
{
ret = s_reference = int.MinValue; ret = s_reference = int.MinValue;
}
Rwl.ExitWriteLock(); Rwl.ExitWriteLock();
return ret; return ret;
} }

View File

@ -13,6 +13,8 @@ internal sealed class UriConverter : ValueConverter<Uri>
public override void Serialize(TextWriter writer, Uri value) public override void Serialize(TextWriter writer, Uri value)
{ {
if (value is not null) if (value is not null)
{
writer.Write(value.ToString()); writer.Write(value.ToString());
} }
} }
}

View File

@ -18,10 +18,14 @@ internal sealed class Vector2Converter : ValueConverter<Vector2>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 2 && readChar != -1) if (spaceCount < 2 && readChar != -1)
{
continue; continue;
}
result = builder.AsSpan().ToVector2(); result = builder.AsSpan().ToVector2();
break; break;

View File

@ -18,10 +18,14 @@ internal sealed class Vector3Converter : ValueConverter<Vector3>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 3 && readChar != -1) if (spaceCount < 3 && readChar != -1)
{
continue; continue;
}
result = builder.AsSpan().ToVector3(); result = builder.AsSpan().ToVector3();
break; break;

View File

@ -17,10 +17,14 @@ internal sealed class Vector3ToColorConverter : ValueConverter<ColorF>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 3 && readChar != -1) if (spaceCount < 3 && readChar != -1)
{
continue; continue;
}
(float x, float y, float z) = builder.AsSpan().ToVector3(); (float x, float y, float z) = builder.AsSpan().ToVector3();
result = ColorF.FromArgb(x, y, z); result = ColorF.FromArgb(x, y, z);

View File

@ -17,10 +17,14 @@ internal sealed class Vector3dConverter : ValueConverter<Vector3d>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 3 && readChar != -1) if (spaceCount < 3 && readChar != -1)
{
continue; continue;
}
result = builder.AsSpan().ToVector3d(); result = builder.AsSpan().ToVector3d();
break; break;

View File

@ -17,10 +17,14 @@ internal sealed class Vector4ToColorConverter : ValueConverter<ColorF>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 4 && readChar != -1) if (spaceCount < 4 && readChar != -1)
{
continue; continue;
}
(float x, float y, float z, float w) = builder.AsSpan().ToVector4(); (float x, float y, float z, float w) = builder.AsSpan().ToVector4();
result = ColorF.FromArgb(w, x, y, z); result = ColorF.FromArgb(w, x, y, z);

View File

@ -18,10 +18,14 @@ internal sealed class Vector4ToVector3Converter : ValueConverter<Vector3>
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
if (spaceCount < 3 && readChar != -1) if (spaceCount < 3 && readChar != -1)
{
continue; continue;
}
result = builder.AsSpan().ToVector3(); result = builder.AsSpan().ToVector3();
break; break;

View File

@ -24,14 +24,20 @@ internal sealed class VectorToNthComponentConverter : ValueConverter<float>
int readChar = reader.Read(); int readChar = reader.Read();
if (readChar == -1) if (readChar == -1)
{
break; break;
}
var currentChar = (char) readChar; var currentChar = (char) readChar;
if (currentChar == ' ') if (currentChar == ' ')
{
spaceCount++; spaceCount++;
}
else if (spaceCount == _componentNumber - 1) else if (spaceCount == _componentNumber - 1)
{
builder.Append(currentChar); builder.Append(currentChar);
} }
}
result = builder.AsSpan().ToSingle(); result = builder.AsSpan().ToSingle();
} }

View File

@ -15,13 +15,17 @@ internal static class WorldSettingsConverter
{ {
var attribute = property.GetCustomAttribute<SerializationKeyAttribute>(); var attribute = property.GetCustomAttribute<SerializationKeyAttribute>();
if (attribute is null) if (attribute is null)
{
continue; continue;
}
object? propertyValue = property.GetValue(settings); object? propertyValue = property.GetValue(settings);
Type propertyType = property.PropertyType; Type propertyType = property.PropertyType;
if (propertyValue is null) if (propertyValue is null)
{
continue; continue;
}
var result = propertyValue.ToString(); var result = propertyValue.ToString();
@ -33,9 +37,13 @@ internal static class WorldSettingsConverter
ValueConverter? converter; ValueConverter? converter;
if (converterAttribute.UseArgs) if (converterAttribute.UseArgs)
{
converter = Activator.CreateInstance(converterType, converterAttribute.Args) as ValueConverter; converter = Activator.CreateInstance(converterType, converterAttribute.Args) as ValueConverter;
}
else else
{
converter = Activator.CreateInstance(converterType) as ValueConverter; converter = Activator.CreateInstance(converterType) as ValueConverter;
}
if (converter is not null) if (converter is not null)
{ {
@ -48,12 +56,16 @@ internal static class WorldSettingsConverter
else else
{ {
if (propertyType == typeof(bool) || propertyType == typeof(bool?)) if (propertyType == typeof(bool) || propertyType == typeof(bool?))
{
result = (bool)propertyValue ? "1" : "0"; result = (bool)propertyValue ? "1" : "0";
} }
}
if (result is not null) if (result is not null)
{
dictionary.Add(attribute.Key, result); dictionary.Add(attribute.Key, result);
} }
}
return dictionary; return dictionary;
} }
@ -67,7 +79,9 @@ internal static class WorldSettingsConverter
{ {
var defaultValueAttribute = property.GetCustomAttribute<DefaultValueAttribute>(); var defaultValueAttribute = property.GetCustomAttribute<DefaultValueAttribute>();
if (defaultValueAttribute is null) if (defaultValueAttribute is null)
{
continue; continue;
}
property.SetValue(settings, defaultValueAttribute.Value); property.SetValue(settings, defaultValueAttribute.Value);
} }
@ -79,7 +93,9 @@ internal static class WorldSettingsConverter
StringComparison.OrdinalIgnoreCase)); StringComparison.OrdinalIgnoreCase));
if (property is null) if (property is null)
{
continue; continue;
}
using var reader = new StringReader(value); using var reader = new StringReader(value);
object propertyValue = value; object propertyValue = value;
@ -95,16 +111,26 @@ internal static class WorldSettingsConverter
Type propertyType = property.PropertyType; Type propertyType = property.PropertyType;
if (propertyType == typeof(bool)) if (propertyType == typeof(bool))
{
propertyValue = value == "1" || (bool.TryParse(value, out bool result) && result); propertyValue = value == "1" || (bool.TryParse(value, out bool result) && result);
}
else if (propertyType == typeof(int)) else if (propertyType == typeof(int))
{
propertyValue = int.TryParse(value, out int result) ? result : 0; propertyValue = int.TryParse(value, out int result) ? result : 0;
}
else if (propertyType == typeof(float)) else if (propertyType == typeof(float))
{
propertyValue = float.TryParse(value, out float result) ? result : 0.0f; propertyValue = float.TryParse(value, out float result) ? result : 0.0f;
}
else if (propertyType == typeof(double)) else if (propertyType == typeof(double))
{
propertyValue = double.TryParse(value, out double result) ? result : 0.0; propertyValue = double.TryParse(value, out double result) ? result : 0.0;
}
else if (propertyType.IsEnum && int.TryParse(value, out int result)) else if (propertyType.IsEnum && int.TryParse(value, out int result))
{
propertyValue = Convert.ChangeType(result, propertyType); propertyValue = Convert.ChangeType(result, propertyType);
} }
}
// ReSharper disable ConditionIsAlwaysTrueOrFalse // ReSharper disable ConditionIsAlwaysTrueOrFalse
#pragma warning disable 612 #pragma warning disable 612
@ -112,9 +138,13 @@ internal static class WorldSettingsConverter
{ {
ValueConverter? converter; ValueConverter? converter;
if (converterAttribute.UseArgs) if (converterAttribute.UseArgs)
{
converter = Activator.CreateInstance(converterType, converterAttribute.Args) as ValueConverter; converter = Activator.CreateInstance(converterType, converterAttribute.Args) as ValueConverter;
}
else else
{
converter = Activator.CreateInstance(converterType) as ValueConverter; converter = Activator.CreateInstance(converterType) as ValueConverter;
}
converter?.Deserialize(reader, out propertyValue); converter?.Deserialize(reader, out propertyValue);
} }

View File

@ -46,8 +46,16 @@ public sealed class InviteRequest : IEquatable<InviteRequest>
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(InviteRequest? other) public bool Equals(InviteRequest? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return _requestId == other._requestId && _client.Equals(other._client); return _requestId == other._requestId && _client.Equals(other._client);
} }
@ -59,10 +67,15 @@ public sealed class InviteRequest : IEquatable<InviteRequest>
/// </param> /// </param>
public Task AcceptAsync(bool suppressTeleport = false) public Task AcceptAsync(bool suppressTeleport = false)
{ {
lock (_client.Lock) Native.vp_invite_accept(_client.NativeInstanceHandle, _requestId); lock (_client.Lock)
{
Native.vp_invite_accept(_client.NativeInstanceHandle, _requestId);
}
if (suppressTeleport) if (suppressTeleport)
{
return Task.CompletedTask; return Task.CompletedTask;
}
return _client.CurrentAvatar.TeleportAsync(Location); return _client.CurrentAvatar.TeleportAsync(Location);
} }
@ -72,7 +85,10 @@ public sealed class InviteRequest : IEquatable<InviteRequest>
/// </summary> /// </summary>
public Task DeclineAsync() public Task DeclineAsync()
{ {
lock (_client.Lock) Native.vp_invite_decline(_client.NativeInstanceHandle, _requestId); lock (_client.Lock)
{
Native.vp_invite_decline(_client.NativeInstanceHandle, _requestId);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -80,7 +96,11 @@ public sealed class InviteRequest : IEquatable<InviteRequest>
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
if (ReferenceEquals(this, obj)) return true; if (ReferenceEquals(this, obj))
{
return true;
}
return obj is InviteRequest other && Equals(other); return obj is InviteRequest other && Equals(other);
} }

View File

@ -45,7 +45,9 @@ public sealed class JoinRequest : IEquatable<JoinRequest>
public Task AcceptAsync(Location? location = null) public Task AcceptAsync(Location? location = null)
{ {
if (_client.CurrentAvatar is null) if (_client.CurrentAvatar is null)
{
ThrowHelper.ThrowNotInWorldException(); ThrowHelper.ThrowNotInWorldException();
}
location ??= _client.CurrentAvatar!.Location; location ??= _client.CurrentAvatar!.Location;
string worldName = location.Value.World.Name; string worldName = location.Value.World.Name;
@ -53,7 +55,9 @@ public sealed class JoinRequest : IEquatable<JoinRequest>
(double pitch, double yaw, double _) = location.Value.Rotation.ToEulerAngles(); (double pitch, double yaw, double _) = location.Value.Rotation.ToEulerAngles();
lock (_client.Lock) lock (_client.Lock)
{
Native.vp_join_accept(_client.NativeInstanceHandle, _requestId, worldName, x, y, z, (float) yaw, (float) pitch); Native.vp_join_accept(_client.NativeInstanceHandle, _requestId, worldName, x, y, z, (float) yaw, (float) pitch);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -63,7 +67,10 @@ public sealed class JoinRequest : IEquatable<JoinRequest>
/// </summary> /// </summary>
public Task DeclineAsync() public Task DeclineAsync()
{ {
lock (_client.Lock) Native.vp_join_decline(_client.NativeInstanceHandle, _requestId); lock (_client.Lock)
{
Native.vp_join_decline(_client.NativeInstanceHandle, _requestId);
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -72,15 +79,27 @@ public sealed class JoinRequest : IEquatable<JoinRequest>
/// <inheritdoc /> /// <inheritdoc />
public bool Equals(JoinRequest? other) public bool Equals(JoinRequest? other)
{ {
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other))
if (ReferenceEquals(this, other)) return true; {
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return _requestId == other._requestId && _client.Equals(other._client); return _requestId == other._requestId && _client.Equals(other._client);
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
if (ReferenceEquals(this, obj)) return true; if (ReferenceEquals(this, obj))
{
return true;
}
return obj is JoinRequest other && Equals(other); return obj is JoinRequest other && Equals(other);
} }

View File

@ -104,8 +104,15 @@ public abstract class FluentActionComponent
var builder = new StringBuilder(); var builder = new StringBuilder();
builder.Append($"texture {texture}"); builder.Append($"texture {texture}");
if (!string.IsNullOrWhiteSpace(mask)) builder.Append($" mask={mask}"); if (!string.IsNullOrWhiteSpace(mask))
if (!string.IsNullOrWhiteSpace(tag)) builder.Append($" tag={tag}"); {
builder.Append($" mask={mask}");
}
if (!string.IsNullOrWhiteSpace(tag))
{
builder.Append($" tag={tag}");
}
return new FluentActionCommand(Action, Command.Texture); return new FluentActionCommand(Action, Command.Texture);
} }

View File

@ -67,7 +67,9 @@ public struct Vector3d : IEquatable<Vector3d>, IFormattable
public Vector3d(ReadOnlySpan<double> values) public Vector3d(ReadOnlySpan<double> values)
{ {
if (values.Length < 3) if (values.Length < 3)
{
throw new IndexOutOfRangeException("The specified span has an insufficient number of elements."); throw new IndexOutOfRangeException("The specified span has an insufficient number of elements.");
}
this = Unsafe.ReadUnaligned<Vector3d>(ref Unsafe.As<double, byte>(ref MemoryMarshal.GetReference(values))); this = Unsafe.ReadUnaligned<Vector3d>(ref Unsafe.As<double, byte>(ref MemoryMarshal.GetReference(values)));
} }
@ -428,13 +430,19 @@ public struct Vector3d : IEquatable<Vector3d>, IFormattable
public readonly void CopyTo(double[] array, int index = 0) public readonly void CopyTo(double[] array, int index = 0)
{ {
if (array is null) if (array is null)
{
throw new ArgumentNullException(nameof(array)); throw new ArgumentNullException(nameof(array));
}
if (index < 0 || index >= array.Length) if (index < 0 || index >= array.Length)
{
throw new ArgumentOutOfRangeException(nameof(index), "Specified index was out of the bounds of the array."); throw new ArgumentOutOfRangeException(nameof(index), "Specified index was out of the bounds of the array.");
}
if (array.Length - index < 3) if (array.Length - index < 3)
{
throw new ArgumentException("The number of elements in source vector is greater than the destination array."); throw new ArgumentException("The number of elements in source vector is greater than the destination array.");
}
array[index] = X; array[index] = X;
array[index + 1] = Y; array[index + 1] = Y;

View File

@ -17,7 +17,9 @@ public sealed partial class VirtualParadiseClient
{ {
var reason = (ReasonCode) Native.vp_init(); var reason = (ReasonCode) Native.vp_init();
if (reason == ReasonCode.VersionMismatch) if (reason == ReasonCode.VersionMismatch)
{
throw new VersionMismatchException(); throw new VersionMismatchException();
}
_instanceHandle = GCHandle.Alloc(this); _instanceHandle = GCHandle.Alloc(this);
_netConfig.Context = GCHandle.ToIntPtr(_instanceHandle); _netConfig.Context = GCHandle.ToIntPtr(_instanceHandle);

View File

@ -41,7 +41,9 @@ public sealed partial class VirtualParadiseClient
private async void OnObjectGetNativeCallback(IntPtr sender, ReasonCode reason, int reference) private async void OnObjectGetNativeCallback(IntPtr sender, ReasonCode reason, int reference)
{ {
if (!_objectCompletionSources.TryGetValue(reference, out TaskCompletionSource<(ReasonCode, VirtualParadiseObject)>? taskCompletionSource)) if (!_objectCompletionSources.TryGetValue(reference, out TaskCompletionSource<(ReasonCode, VirtualParadiseObject)>? taskCompletionSource))
{
return; return;
}
VirtualParadiseObject virtualParadiseObject = reason == ReasonCode.Success ? await ExtractObjectAsync(sender) : null; VirtualParadiseObject virtualParadiseObject = reason == ReasonCode.Success ? await ExtractObjectAsync(sender) : null;
taskCompletionSource.SetResult((reason, virtualParadiseObject)); taskCompletionSource.SetResult((reason, virtualParadiseObject));

View File

@ -1,4 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Drawing; using System.Drawing;
using System.Numerics; using System.Numerics;
using System.Threading.Channels; using System.Threading.Channels;
@ -167,8 +167,10 @@ public sealed partial class VirtualParadiseClient
if (session == 0) if (session == 0)
{ {
if (_cellChannels.TryGetValue(cell, out Channel<VirtualParadiseObject>? channel)) if (_cellChannels.TryGetValue(cell, out Channel<VirtualParadiseObject>? channel))
{
await channel.Writer.WriteAsync(virtualParadiseObject); await channel.Writer.WriteAsync(virtualParadiseObject);
} }
}
else else
{ {
VirtualParadiseAvatar? avatar = GetAvatar(session); VirtualParadiseAvatar? avatar = GetAvatar(session);
@ -201,7 +203,9 @@ public sealed partial class VirtualParadiseClient
} }
if (virtualParadiseObject is not null) if (virtualParadiseObject is not null)
{
AddOrUpdateObject(virtualParadiseObject); AddOrUpdateObject(virtualParadiseObject);
}
var args = new ObjectChangedEventArgs(avatar, cachedObject, virtualParadiseObject); var args = new ObjectChangedEventArgs(avatar, cachedObject, virtualParadiseObject);
RaiseEvent(ObjectChanged, args); RaiseEvent(ObjectChanged, args);
@ -277,8 +281,10 @@ public sealed partial class VirtualParadiseClient
} }
if (_worldListChannel is not null) if (_worldListChannel is not null)
{
await _worldListChannel.Writer.WriteAsync(world); await _worldListChannel.Writer.WriteAsync(world);
} }
}
private void OnWorldSettingNativeEvent(IntPtr sender) private void OnWorldSettingNativeEvent(IntPtr sender)
{ {
@ -311,7 +317,10 @@ public sealed partial class VirtualParadiseClient
private void OnWorldDisconnectNativeEvent(IntPtr sender) private void OnWorldDisconnectNativeEvent(IntPtr sender)
{ {
DisconnectReason reason; DisconnectReason reason;
lock (Lock) reason = (DisconnectReason) vp_int(sender, IntegerAttribute.DisconnectErrorCode); lock (Lock)
{
reason = (DisconnectReason) vp_int(sender, IntegerAttribute.DisconnectErrorCode);
}
var args = new DisconnectedEventArgs(reason); var args = new DisconnectedEventArgs(reason);
RaiseEvent(WorldServerDisconnected, args); RaiseEvent(WorldServerDisconnected, args);
@ -320,7 +329,10 @@ public sealed partial class VirtualParadiseClient
private void OnUniverseDisconnectNativeEvent(IntPtr sender) private void OnUniverseDisconnectNativeEvent(IntPtr sender)
{ {
DisconnectReason reason; DisconnectReason reason;
lock (Lock) reason = (DisconnectReason) vp_int(sender, IntegerAttribute.DisconnectErrorCode); lock (Lock)
{
reason = (DisconnectReason) vp_int(sender, IntegerAttribute.DisconnectErrorCode);
}
var args = new DisconnectedEventArgs(reason); var args = new DisconnectedEventArgs(reason);
RaiseEvent(UniverseServerDisconnected, args); RaiseEvent(UniverseServerDisconnected, args);
@ -352,8 +364,10 @@ public sealed partial class VirtualParadiseClient
} }
if (_usersCompletionSources.TryGetValue(userId, out TaskCompletionSource<VirtualParadiseUser>? taskCompletionSource)) if (_usersCompletionSources.TryGetValue(userId, out TaskCompletionSource<VirtualParadiseUser>? taskCompletionSource))
{
taskCompletionSource.SetResult(user); taskCompletionSource.SetResult(user);
} }
}
private void OnQueryCellEndNativeEvent(IntPtr sender) private void OnQueryCellEndNativeEvent(IntPtr sender)
{ {
@ -368,8 +382,10 @@ public sealed partial class VirtualParadiseClient
} }
if (_cellChannels.TryRemove(cell, out Channel<VirtualParadiseObject>? channel)) if (_cellChannels.TryRemove(cell, out Channel<VirtualParadiseObject>? channel))
{
channel.Writer.TryComplete(); channel.Writer.TryComplete();
} }
}
private void OnAvatarClickNativeEvent(IntPtr sender) private void OnAvatarClickNativeEvent(IntPtr sender)
{ {
@ -456,7 +472,9 @@ public sealed partial class VirtualParadiseClient
} }
if (!Uri.IsWellFormedUriString(url, UriKind.Absolute)) if (!Uri.IsWellFormedUriString(url, UriKind.Absolute))
{
return; return;
}
VirtualParadiseAvatar? avatar = GetAvatar(session); VirtualParadiseAvatar? avatar = GetAvatar(session);
var uri = new Uri(url); var uri = new Uri(url);

View File

@ -24,7 +24,9 @@ public sealed partial class VirtualParadiseClient
public IAsyncEnumerable<VirtualParadiseObject> EnumerateObjectsAsync(Cell cell, int? revision = null) public IAsyncEnumerable<VirtualParadiseObject> EnumerateObjectsAsync(Cell cell, int? revision = null)
{ {
if (_cellChannels.TryGetValue(cell, out Channel<VirtualParadiseObject>? channel)) if (_cellChannels.TryGetValue(cell, out Channel<VirtualParadiseObject>? channel))
{
return channel.Reader.ReadAllAsync(); return channel.Reader.ReadAllAsync();
}
channel = Channel.CreateUnbounded<VirtualParadiseObject>(); channel = Channel.CreateUnbounded<VirtualParadiseObject>();
_cellChannels.TryAdd(cell, channel); _cellChannels.TryAdd(cell, channel);
@ -53,20 +55,27 @@ public sealed partial class VirtualParadiseClient
/// <returns>An enumerable of <see cref="VirtualParadiseObject" />.</returns> /// <returns>An enumerable of <see cref="VirtualParadiseObject" />.</returns>
public async IAsyncEnumerable<VirtualParadiseObject> EnumerateObjectsAsync(Cell center, int radius, int? revision = null) public async IAsyncEnumerable<VirtualParadiseObject> EnumerateObjectsAsync(Cell center, int radius, int? revision = null)
{ {
if (radius < 0) throw new ArgumentException("Range must be greater than or equal to 1."); if (radius < 0)
{
throw new ArgumentException("Range must be greater than or equal to 1.");
}
var cells = new HashSet<Cell>(); var cells = new HashSet<Cell>();
for (int x = center.X - radius; x < center.X + radius; x++) for (int x = center.X - radius; x < center.X + radius; x++)
for (int z = center.Z - radius; z < center.Z + radius; z++) for (int z = center.Z - radius; z < center.Z + radius; z++)
{
cells.Add(new Cell(x, z)); cells.Add(new Cell(x, z));
}
foreach (Cell cell in cells.OrderBy(c => Vector2.Distance(c, center))) foreach (Cell cell in cells.OrderBy(c => Vector2.Distance(c, center)))
{ {
await foreach (VirtualParadiseObject vpObject in EnumerateObjectsAsync(cell)) await foreach (VirtualParadiseObject vpObject in EnumerateObjectsAsync(cell))
{
yield return vpObject; yield return vpObject;
} }
} }
}
/// <summary> /// <summary>
/// Gets an object by its ID. /// Gets an object by its ID.
@ -82,7 +91,9 @@ public sealed partial class VirtualParadiseClient
public async ValueTask<VirtualParadiseObject> GetObjectAsync(int id) public async ValueTask<VirtualParadiseObject> GetObjectAsync(int id)
{ {
if (_objects.TryGetValue(id, out VirtualParadiseObject? virtualParadiseObject)) if (_objects.TryGetValue(id, out VirtualParadiseObject? virtualParadiseObject))
{
return virtualParadiseObject; return virtualParadiseObject;
}
ReasonCode reason; ReasonCode reason;
@ -97,15 +108,19 @@ public sealed partial class VirtualParadiseClient
vp_int_set(NativeInstanceHandle, IntegerAttribute.ReferenceNumber, id); vp_int_set(NativeInstanceHandle, IntegerAttribute.ReferenceNumber, id);
reason = (ReasonCode) vp_object_get(NativeInstanceHandle, id); reason = (ReasonCode) vp_object_get(NativeInstanceHandle, id);
if (reason != ReasonCode.Success) if (reason != ReasonCode.Success)
{
goto PreReturn; goto PreReturn;
} }
} }
}
(reason, virtualParadiseObject) = await taskCompletionSource.Task; (reason, virtualParadiseObject) = await taskCompletionSource.Task;
_objectCompletionSources.TryRemove(id, out _); _objectCompletionSources.TryRemove(id, out _);
if (virtualParadiseObject is not null) if (virtualParadiseObject is not null)
{
_objects.TryAdd(id, virtualParadiseObject); _objects.TryAdd(id, virtualParadiseObject);
}
PreReturn: PreReturn:
return reason switch return reason switch

View File

@ -19,15 +19,22 @@ public sealed partial class VirtualParadiseClient
public async Task<VirtualParadiseUser> GetUserAsync(int userId) public async Task<VirtualParadiseUser> GetUserAsync(int userId)
{ {
if (_users.TryGetValue(userId, out VirtualParadiseUser? user)) if (_users.TryGetValue(userId, out VirtualParadiseUser? user))
{
return user; return user;
}
if (_usersCompletionSources.TryGetValue(userId, out TaskCompletionSource<VirtualParadiseUser>? taskCompletionSource)) if (_usersCompletionSources.TryGetValue(userId, out TaskCompletionSource<VirtualParadiseUser>? taskCompletionSource))
{
return await taskCompletionSource.Task; return await taskCompletionSource.Task;
}
taskCompletionSource = new TaskCompletionSource<VirtualParadiseUser>(); taskCompletionSource = new TaskCompletionSource<VirtualParadiseUser>();
_usersCompletionSources.TryAdd(userId, taskCompletionSource); _usersCompletionSources.TryAdd(userId, taskCompletionSource);
lock (Lock) vp_user_attributes_by_id(NativeInstanceHandle, userId); lock (Lock)
{
vp_user_attributes_by_id(NativeInstanceHandle, userId);
}
user = await taskCompletionSource.Task; user = await taskCompletionSource.Task;
user = AddOrUpdateUser(user); user = AddOrUpdateUser(user);

View File

@ -1,12 +1,6 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Drawing;
using System.Numerics;
using System.Threading.Channels; using System.Threading.Channels;
using VpSharp.Entities; using VpSharp.Entities;
using VpSharp.Exceptions;
using VpSharp.Internal;
using VpSharp.Internal.NativeAttributes;
using VpSharp.Internal.ValueConverters;
namespace VpSharp; namespace VpSharp;
@ -30,13 +24,17 @@ public sealed partial class VirtualParadiseClient
public async Task<VirtualParadiseWorld?> GetWorldAsync(string name) public async Task<VirtualParadiseWorld?> GetWorldAsync(string name)
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException(ExceptionMessages.WorldNameCannotBeEmpty, nameof(name)); throw new ArgumentException(ExceptionMessages.WorldNameCannotBeEmpty, nameof(name));
}
await foreach (VirtualParadiseWorld world in EnumerateWorldsAsync()) await foreach (VirtualParadiseWorld world in EnumerateWorldsAsync())
{ {
if (string.Equals(world.Name, name)) if (string.Equals(world.Name, name))
{
return world; return world;
} }
}
return null; return null;
} }

View File

@ -1,4 +1,4 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Drawing; using System.Drawing;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
@ -92,8 +92,15 @@ public sealed partial class VirtualParadiseClient : IDisposable
/// </remarks> /// </remarks>
public async Task ConnectAsync(string? host = null, int port = -1) public async Task ConnectAsync(string? host = null, int port = -1)
{ {
if (string.IsNullOrWhiteSpace(host)) host = DefaultUniverseHost; if (string.IsNullOrWhiteSpace(host))
if (port < 1) port = DefaultUniversePort; {
host = DefaultUniverseHost;
}
if (port < 1)
{
port = DefaultUniversePort;
}
ReasonCode reason; ReasonCode reason;
@ -103,8 +110,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
reason = (ReasonCode)vp_connect_universe(NativeInstanceHandle, host, port); reason = (ReasonCode)vp_connect_universe(NativeInstanceHandle, host, port);
if (reason != ReasonCode.Success) if (reason != ReasonCode.Success)
{
goto NoSuccess; goto NoSuccess;
} }
}
reason = await _connectCompletionSource.Task; reason = await _connectCompletionSource.Task;
@ -130,7 +139,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
/// <exception cref="ArgumentException"><paramref name="remoteEP" /> is not a supported endpoint.</exception> /// <exception cref="ArgumentException"><paramref name="remoteEP" /> is not a supported endpoint.</exception>
public Task ConnectAsync(EndPoint remoteEP) public Task ConnectAsync(EndPoint remoteEP)
{ {
if (remoteEP is null) throw new ArgumentNullException(nameof(remoteEP)); if (remoteEP is null)
{
throw new ArgumentNullException(nameof(remoteEP));
}
string host; string host;
int port; int port;
@ -273,7 +285,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
if (CurrentWorld is not null) if (CurrentWorld is not null)
{ {
lock (Lock) vp_leave(NativeInstanceHandle); lock (Lock)
{
vp_leave(NativeInstanceHandle);
}
} }
ReasonCode reason; ReasonCode reason;
@ -321,7 +336,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
} }
int size; int size;
lock (Lock) size = vp_int(NativeInstanceHandle, IntegerAttribute.WorldSize); lock (Lock)
{
size = vp_int(NativeInstanceHandle, IntegerAttribute.WorldSize);
}
await _worldSettingsCompletionSource.Task; await _worldSettingsCompletionSource.Task;
@ -332,8 +350,15 @@ public sealed partial class VirtualParadiseClient : IDisposable
world = new VirtualParadiseWorld(this, worldName); world = new VirtualParadiseWorld(this, worldName);
} }
if (CurrentAvatar is not null) CurrentAvatar.Location = new Location(world); if (CurrentAvatar is not null)
lock (Lock) vp_state_change(NativeInstanceHandle); {
CurrentAvatar.Location = new Location(world);
}
lock (Lock)
{
vp_state_change(NativeInstanceHandle);
}
world.Size = new Size(size, size); world.Size = new Size(size, size);
@ -356,7 +381,9 @@ public sealed partial class VirtualParadiseClient : IDisposable
_ = Task.Run(async () => _ = Task.Run(async () =>
{ {
await foreach (VirtualParadiseObject virtualParadiseObject in EnumerateObjectsAsync(default, radius: size)) await foreach (VirtualParadiseObject virtualParadiseObject in EnumerateObjectsAsync(default, radius: size))
{
AddOrUpdateObject(virtualParadiseObject); AddOrUpdateObject(virtualParadiseObject);
}
}); });
} }
@ -375,7 +402,11 @@ public sealed partial class VirtualParadiseClient : IDisposable
public IAsyncEnumerable<VirtualParadiseWorld> EnumerateWorldsAsync() public IAsyncEnumerable<VirtualParadiseWorld> EnumerateWorldsAsync()
{ {
_worldListChannel = Channel.CreateUnbounded<VirtualParadiseWorld>(); _worldListChannel = Channel.CreateUnbounded<VirtualParadiseWorld>();
lock (Lock) vp_world_list(NativeInstanceHandle, 0); lock (Lock)
{
vp_world_list(NativeInstanceHandle, 0);
}
return _worldListChannel.Reader.ReadAllAsync(); return _worldListChannel.Reader.ReadAllAsync();
} }
@ -391,8 +422,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
{ {
var reason = (ReasonCode)vp_leave(NativeInstanceHandle); var reason = (ReasonCode)vp_leave(NativeInstanceHandle);
if (reason == ReasonCode.NotInWorld) if (reason == ReasonCode.NotInWorld)
{
return Task.FromException(ThrowHelper.NotInWorldException()); return Task.FromException(ThrowHelper.NotInWorldException());
} }
}
_avatars.Clear(); _avatars.Clear();
_objects.Clear(); _objects.Clear();
@ -438,7 +471,9 @@ public sealed partial class VirtualParadiseClient : IDisposable
_configuration.BotName = botName; _configuration.BotName = botName;
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(botName)) if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(botName))
{
throw new ArgumentException("Cannot login due to incomplete configuration."); throw new ArgumentException("Cannot login due to incomplete configuration.");
}
_loginCompletionSource = new TaskCompletionSource<ReasonCode>(); _loginCompletionSource = new TaskCompletionSource<ReasonCode>();
@ -456,8 +491,10 @@ public sealed partial class VirtualParadiseClient : IDisposable
reason = (ReasonCode)vp_login(NativeInstanceHandle, username, password, botName); reason = (ReasonCode)vp_login(NativeInstanceHandle, username, password, botName);
if (reason != ReasonCode.Success) if (reason != ReasonCode.Success)
{
goto NoSuccess; goto NoSuccess;
} }
}
reason = await _loginCompletionSource.Task; reason = await _loginCompletionSource.Task;
NoSuccess: NoSuccess:

View File

@ -19,10 +19,15 @@ public sealed class VirtualParadiseConfiguration
/// <param name="configuration">The configuration to copy.</param> /// <param name="configuration">The configuration to copy.</param>
public VirtualParadiseConfiguration(VirtualParadiseConfiguration configuration) public VirtualParadiseConfiguration(VirtualParadiseConfiguration configuration)
{ {
if (configuration is null) throw new ArgumentNullException(nameof(configuration)); if (configuration is null)
{
throw new ArgumentNullException(nameof(configuration));
}
if (configuration.Application is ({ } name, { } version)) if (configuration.Application is ({ } name, { } version))
{
Application = new Application(name, version); Application = new Application(name, version);
}
AutoQuery = configuration.AutoQuery; AutoQuery = configuration.AutoQuery;
BotName = new string(configuration.BotName); BotName = new string(configuration.BotName);

View File

@ -388,13 +388,17 @@ public sealed class WorldSettingsBuilder
foreach ((string key, string? value) in dictionary) foreach ((string key, string? value) in dictionary)
{ {
if (value is null) if (value is null)
{
continue; continue;
}
var reason = (ReasonCode)Native.vp_world_setting_set(_client.NativeInstanceHandle, key, value, session); var reason = (ReasonCode)Native.vp_world_setting_set(_client.NativeInstanceHandle, key, value, session);
if (reason == ReasonCode.NotAllowed) if (reason == ReasonCode.NotAllowed)
{
throw new UnauthorizedAccessException("Not allowed to modify world settings."); throw new UnauthorizedAccessException("Not allowed to modify world settings.");
} }
} }
} }
} }
}