diff --git a/VpSharp/src/Entities/VirtualParadiseAvatar.cs b/VpSharp/src/Entities/VirtualParadiseAvatar.cs index 6d11085..d7c2518 100644 --- a/VpSharp/src/Entities/VirtualParadiseAvatar.cs +++ b/VpSharp/src/Entities/VirtualParadiseAvatar.cs @@ -1,6 +1,5 @@ using System.Drawing; using System.Numerics; -using VpSharp.Extensions; using VpSharp.Internal; using VpSharp.Internal.NativeAttributes; using X10D.Numerics; @@ -314,7 +313,7 @@ public sealed class VirtualParadiseAvatar : IEquatable public Task TeleportAsync(VirtualParadiseWorld world, Vector3d position) { ArgumentNullException.ThrowIfNull(world); - return TeleportAsync(world.Name, position, Quaternion.Identity); + return TeleportAsync(world.Name, position, Rotation.None); } /// @@ -324,7 +323,7 @@ public sealed class VirtualParadiseAvatar : IEquatable /// The position to which this avatar should be teleported. /// The rotation to which this avatar should be teleported. /// is . - public Task TeleportAsync(VirtualParadiseWorld world, Vector3d position, Quaternion rotation) + public Task TeleportAsync(VirtualParadiseWorld world, Vector3d position, Rotation rotation) { ArgumentNullException.ThrowIfNull(world); return TeleportAsync(world.Name, position, rotation); @@ -337,7 +336,7 @@ public sealed class VirtualParadiseAvatar : IEquatable /// The position to which this avatar should be teleported. public Task TeleportAsync(string world, Vector3d position) { - return TeleportAsync(world, position, Quaternion.Identity); + return TeleportAsync(world, position, Rotation.None); } /// @@ -346,7 +345,7 @@ public sealed class VirtualParadiseAvatar : IEquatable /// The name of the world to which this avatar should be teleported. /// The position to which this avatar should be teleported. /// The rotation to which this avatar should be teleported. - public async Task TeleportAsync(string world, Vector3d position, Quaternion rotation) + public async Task TeleportAsync(string world, Vector3d position, Rotation rotation) { ArgumentNullException.ThrowIfNull(world); #if NET7_0_OR_GREATER @@ -385,7 +384,7 @@ public sealed class VirtualParadiseAvatar : IEquatable lock (_client.Lock) { (double x, double y, double z) = position; - (double pitch, double yaw, double _) = rotation.ToEulerAngles(); + (double pitch, double yaw, double _) = rotation; _ = vp_double_set(handle, FloatAttribute.MyX, x); _ = vp_double_set(handle, FloatAttribute.MyY, y); @@ -405,9 +404,9 @@ public sealed class VirtualParadiseAvatar : IEquatable lock (_client.Lock) { (float x, float y, float z) = (Vector3)position; - (float pitch, float yaw, float _) = (Vector3)rotation.ToEulerAngles(); + (double pitch, double yaw, double _) = rotation; - 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, (float)yaw, (float)pitch); if (reason == ReasonCode.NotInWorld) { ThrowHelper.ThrowNotInWorldException(); @@ -433,7 +432,7 @@ public sealed class VirtualParadiseAvatar : IEquatable /// /// The position to which this avatar should be teleported. /// The rotation to which this avatar should be teleported - public Task TeleportAsync(Vector3d position, Quaternion rotation) + public Task TeleportAsync(Vector3d position, Rotation rotation) { return TeleportAsync(Location with {Position = position, Rotation = rotation}); } diff --git a/VpSharp/src/Entities/VirtualParadiseObject.cs b/VpSharp/src/Entities/VirtualParadiseObject.cs index 486e507..baa0c8b 100644 --- a/VpSharp/src/Entities/VirtualParadiseObject.cs +++ b/VpSharp/src/Entities/VirtualParadiseObject.cs @@ -241,7 +241,7 @@ public abstract class VirtualParadiseObject : IEquatable Location location = Location; Vector3d position = location.Position; - Quaternion rotation = location.Rotation; + Rotation rotation = location.Rotation; if (builder.Position.HasValue) { diff --git a/VpSharp/src/Entities/VirtualParadiseObjectBuilder.cs b/VpSharp/src/Entities/VirtualParadiseObjectBuilder.cs index 47ce02b..34873de 100644 --- a/VpSharp/src/Entities/VirtualParadiseObjectBuilder.cs +++ b/VpSharp/src/Entities/VirtualParadiseObjectBuilder.cs @@ -1,7 +1,4 @@ -using System.Numerics; -using VpSharp.Extensions; -using VpSharp.Internal; -using X10D.Math; +using VpSharp.Internal; using static VpSharp.Internal.NativeAttributes.DataAttribute; using static VpSharp.Internal.NativeAttributes.FloatAttribute; using static VpSharp.Internal.NativeAttributes.IntegerAttribute; @@ -57,7 +54,7 @@ public abstract class VirtualParadiseObjectBuilder /// Gets or sets the rotation of the object. /// /// The rotation of the object, or to leave unchanged. - public Optional Rotation { get; set; } + public Optional Rotation { get; set; } internal Optional> Data { get; set; } @@ -157,27 +154,12 @@ public abstract class VirtualParadiseObjectBuilder if (!Rotation.HasValue && Mode == ObjectBuilderMode.Create) { - Rotation = Quaternion.Identity; + Rotation = VpSharp.Rotation.None; } if (Rotation.HasValue) { - (double x, double y, double z) = Vector3d.Zero; - double angle = double.PositiveInfinity; - if (Rotation.Value != Quaternion.Identity) - { - Rotation.Value.ToAxisAngle(out Vector3d axis, out angle); - (x, y, z) = axis; - angle = angle.RadiansToDegrees(); - } - - if (double.IsPositiveInfinity(angle)) - { - x = x.RadiansToDegrees(); - y = y.RadiansToDegrees(); - z = z.RadiansToDegrees(); - } - + (double x, double y, double z, double angle) = Rotation.Value; _ = vp_double_set(handle, ObjectRotationX, x); _ = vp_double_set(handle, ObjectRotationY, y); _ = vp_double_set(handle, ObjectRotationZ, z); @@ -185,16 +167,10 @@ public abstract class VirtualParadiseObjectBuilder } else { - TargetObject.Location.Rotation.ToAxisAngle(out Vector3d axis, out double angle); - if (Vector3d.IsNan(axis)) - { - axis = Vector3d.Zero; - angle = double.PositiveInfinity; - } - - _ = vp_double_set(handle, ObjectRotationX, axis.X); - _ = vp_double_set(handle, ObjectRotationY, axis.Y); - _ = vp_double_set(handle, ObjectRotationZ, axis.Z); + (double x, double y, double z, double angle) = TargetObject.Location.Rotation; + _ = vp_double_set(handle, ObjectRotationX, x); + _ = vp_double_set(handle, ObjectRotationY, y); + _ = vp_double_set(handle, ObjectRotationZ, z); _ = vp_double_set(handle, ObjectRotationAngle, angle); } } diff --git a/VpSharp/src/Entities/VirtualParadiseUser.cs b/VpSharp/src/Entities/VirtualParadiseUser.cs index 0aadcb9..3dd8723 100644 --- a/VpSharp/src/Entities/VirtualParadiseUser.cs +++ b/VpSharp/src/Entities/VirtualParadiseUser.cs @@ -1,6 +1,4 @@ -using System.Numerics; using VpSharp.Exceptions; -using VpSharp.Extensions; using VpSharp.Internal; using VpSharp.Internal.NativeAttributes; using static VpSharp.Internal.NativeMethods; @@ -144,7 +142,7 @@ public sealed class VirtualParadiseUser : IEquatable string world = location.Value.World.Name; (double x, double y, double z) = location.Value.Position; - (double pitch, double yaw, _) = location.Value.Rotation.ToEulerAngles(); + (double pitch, double yaw, _) = location.Value.Rotation; _ = vp_int_set(_client.NativeInstanceHandle, IntegerAttribute.ReferenceNumber, reference); _ = vp_invite(_client.NativeInstanceHandle, Id, world, x, y, z, (float)yaw, (float)pitch); @@ -215,7 +213,7 @@ public sealed class VirtualParadiseUser : IEquatable } var position = new Vector3d(x, y, z); - var rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); + var rotation = Rotation.CreateFromTiltYawRoll(pitch, yaw, 0); VirtualParadiseWorld world = (await _client.GetWorldAsync(worldName).ConfigureAwait(false))!; location = new Location(world, position, rotation); diff --git a/VpSharp/src/Extensions/QuaternionExtensions.cs b/VpSharp/src/Extensions/QuaternionExtensions.cs deleted file mode 100644 index 6731d39..0000000 --- a/VpSharp/src/Extensions/QuaternionExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Numerics; - -namespace VpSharp.Extensions; - -/// -/// Extension methods for . -/// -public static class QuaternionExtensions -{ - /// - /// Converts this quaternion to a containing an Euler representation of the rotation. - /// - /// The quaternion to convert. - /// The Euler representation of . - /// - public static Vector3d ToEulerAngles(this Quaternion value) - { - value = Quaternion.Normalize(value); - double x = Math.Atan2(2 * (value.X * value.W - value.Y * value.Z), 1 - 2 * (value.X * value.X + value.Z * value.Z)); - double y = Math.Asin(2 * (value.X * value.Z + value.Y * value.W)); - double z = Math.Atan2(2 * (value.Z * value.W - value.X * value.Y), 1 - 2 * (value.Y * value.Y + value.Z * value.Z)); - return new Vector3d(x, y, z) * (180 / Math.PI); - } - - /// - /// Converts this quaternion to an axis/angle pair. - /// - /// The quaternion to convert. - /// The axis value. - /// The angle value. -#pragma warning disable CA1021 - public static void ToAxisAngle(this Quaternion value, out Vector3d axis, out double angle) -#pragma warning restore CA1021 - { - angle = 2 * Math.Acos(value.W); - axis = Vector3d.Normalize(new Vector3d(value.X, value.Y, value.Z)); - } -} diff --git a/VpSharp/src/JoinRequest.cs b/VpSharp/src/JoinRequest.cs index 0a06c69..7e78ebb 100644 --- a/VpSharp/src/JoinRequest.cs +++ b/VpSharp/src/JoinRequest.cs @@ -1,6 +1,5 @@ using VpSharp.Entities; using VpSharp.Exceptions; -using VpSharp.Extensions; using VpSharp.Internal; namespace VpSharp; @@ -76,7 +75,7 @@ public sealed class JoinRequest : IEquatable location ??= _client.CurrentAvatar.Location; string worldName = location.Value.World.Name; (double x, double y, double z) = location.Value.Position; - (double pitch, double yaw, double _) = location.Value.Rotation.ToEulerAngles(); + (double pitch, double yaw, double _) = location.Value.Rotation; lock (_client.Lock) { diff --git a/VpSharp/src/Location.cs b/VpSharp/src/Location.cs index bd9dc9c..5598c18 100644 --- a/VpSharp/src/Location.cs +++ b/VpSharp/src/Location.cs @@ -1,7 +1,4 @@ -using System.Numerics; -using VpSharp.Entities; -using VpSharp.Extensions; -using X10D.Math; +using VpSharp.Entities; namespace VpSharp; @@ -29,7 +26,7 @@ public readonly struct Location : IEquatable { World = world ?? throw new ArgumentNullException(nameof(world)); Position = new Vector3d(coordinates.X, coordinates.Y, coordinates.Z); - Rotation = Quaternion.CreateFromYawPitchRoll((float)coordinates.Yaw.DegreesToRadians(), 0, 0); + Rotation = Rotation.CreateFromTiltYawRoll(0, coordinates.Yaw, 0); } /// @@ -39,7 +36,7 @@ public readonly struct Location : IEquatable /// The position. /// The rotation. /// is . - public Location(VirtualParadiseWorld world, Vector3d position = default, Quaternion rotation = default) + public Location(VirtualParadiseWorld world, Vector3d position = default, Rotation rotation = default) { World = world ?? throw new ArgumentNullException(nameof(world)); Position = position; @@ -64,7 +61,7 @@ public readonly struct Location : IEquatable /// Gets the rotation represented by this location. /// /// The rotation. - public Quaternion Rotation { get; init; } + public Rotation Rotation { get; init; } /// /// Gets the world represented by this location. @@ -131,8 +128,7 @@ public readonly struct Location : IEquatable public Coordinates ToCoordinates() { (double x, double y, double z) = Position; - (_, double yaw, _) = Rotation.ToEulerAngles(); - return new Coordinates(World?.Name, x, y, z, yaw); + return new Coordinates(World?.Name, x, y, z, Rotation.Yaw); } /// diff --git a/VpSharp/src/Rotation.cs b/VpSharp/src/Rotation.cs new file mode 100644 index 0000000..6aca32d --- /dev/null +++ b/VpSharp/src/Rotation.cs @@ -0,0 +1,172 @@ +using System.Numerics; +using X10D.Math; +using X10D.Numerics; + +namespace VpSharp; + +/// +/// Represents a rotation. +/// +public readonly struct Rotation : IEquatable +{ + /// + /// Represents no rotation. + /// + public static readonly Rotation None = new(0, 0, 0, double.PositiveInfinity); + + /// + /// Initializes a new instance of the struct. + /// + /// The tilt. + /// The yaw. + /// The roll. + /// The angle. + public Rotation(double tilt, double yaw, double roll, double angle) + { + Angle = angle; + Roll = roll; + Tilt = tilt; + Yaw = yaw; + } + + /// + /// Gets or initializes the angle component of this rotation. + /// + /// The angle component. + public double Angle { get; init; } + + /// + /// Gets or initializes the roll component of this rotation. + /// + /// The roll component. + /// This value is the rotation on the Z axis. + public double Roll { get; init; } + + /// + /// Gets or initializes the tilt component of this rotation. + /// + /// The tilt component. + /// This value is the rotation on the X axis. + public double Tilt { get; init; } + + /// + /// Gets or initializes the yaw component of this rotation. + /// + /// The yaw component. + /// This value is the rotation on the Y axis. + public double Yaw { get; init; } + + /// + /// Returns a value indicating whether the two given rotations are equal. + /// + /// The first rotation to compare. + /// The second rotation to compare. + /// if the two rotations are equal; otherwise, . + public static bool operator ==(Rotation left, Rotation right) + { + return left.Equals(right); + } + + /// + /// Returns a value indicating whether the two given rotations are not equal. + /// + /// The first rotation to compare. + /// The second rotation to compare. + /// if the two rotations are not equal; otherwise, . + public static bool operator !=(Rotation left, Rotation right) + { + return !left.Equals(right); + } + + /// + /// Creates a from the specified axis and angle, as represented in degrees. + /// + /// The axis value. + /// The angle value. + /// A new instance of . + public static Rotation CreateFromAxisAngle(Vector3d axis, double angle) + { + return new Rotation(axis.X, axis.Y, axis.Z, angle.DegreesToRadians()); + } + + /// + /// Creates a from the specified quaternion. + /// + /// The quaternion. + /// A new instance of . + public static Rotation CreateFromQuaternion(Quaternion quaternion) + { + (Vector3d axis, double angle) = quaternion.ToAxisAngle(); + return new Rotation(axis.X, axis.Y, axis.Z, angle); + } + + /// + /// Creates a from the specified Euler rotation vector, as represented in degrees. + /// + /// The Euler rotation vector. + /// A new instance of . + public static Rotation CreateFromTiltYawRoll(Vector3d vector) + { + return new Rotation(vector.X, vector.Y, vector.Z, double.PositiveInfinity); + } + + /// + /// Creates a from the specified tilt, yaw, and roll, as represented in degrees. + /// + /// The tilt. + /// The yaw. + /// The roll. + /// A new instance of . + public static Rotation CreateFromTiltYawRoll(double tilt, double yaw, double roll) + { + return new Rotation(tilt, yaw, roll, double.PositiveInfinity); + } + + /// + /// Deconstructs this rotation. + /// + /// When this method returns, contains the component value. + /// When this method returns, contains the component value. + /// When this method returns, contains the component value. + public void Deconstruct(out double tilt, out double yaw, out double roll) + { + Deconstruct(out tilt, out yaw, out roll, out _); + } + + /// + /// Deconstructs this rotation. + /// + /// When this method returns, contains the component value. + /// When this method returns, contains the component value. + /// When this method returns, contains the component value. + /// When this method returns, contains the component value. + public void Deconstruct(out double tilt, out double yaw, out double roll, out double angle) + { + tilt = Tilt; + yaw = Yaw; + roll = Roll; + angle = Angle; + } + + /// + /// Returns a value indicating whether this rotation and another rotation are equal. + /// + /// The rotation to compare with this instance. + /// if the two rotations are equal; otherwise, . + public bool Equals(Rotation other) + { + return Angle.Equals(other.Angle) && Roll.Equals(other.Roll) && Tilt.Equals(other.Tilt) && Yaw.Equals(other.Yaw); + } + + /// + public override bool Equals(object? obj) + { + return obj is Rotation other && Equals(other); + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(Angle, Roll, Tilt, Yaw); + } +} diff --git a/VpSharp/src/VirtualParadiseClient.Avatars.cs b/VpSharp/src/VirtualParadiseClient.Avatars.cs index 8686785..8853347 100644 --- a/VpSharp/src/VirtualParadiseClient.Avatars.cs +++ b/VpSharp/src/VirtualParadiseClient.Avatars.cs @@ -1,5 +1,4 @@ using System.Collections.Concurrent; -using System.Numerics; using VpSharp.Entities; using VpSharp.Internal.NativeAttributes; using static VpSharp.Internal.NativeMethods; @@ -52,7 +51,7 @@ public sealed partial class VirtualParadiseClient var yaw = (float)vp_double(sender, FloatAttribute.AvatarYaw); var position = new Vector3d(x, y, z); - var rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); + var rotation = Rotation.CreateFromTiltYawRoll(pitch, yaw, 0); string applicationName = vp_string(sender, StringAttribute.AvatarApplicationName); string applicationVersion = vp_string(sender, StringAttribute.AvatarApplicationVersion); diff --git a/VpSharp/src/VirtualParadiseClient.NativeEvents.cs b/VpSharp/src/VirtualParadiseClient.NativeEvents.cs index a6f1e16..efc77c2 100644 --- a/VpSharp/src/VirtualParadiseClient.NativeEvents.cs +++ b/VpSharp/src/VirtualParadiseClient.NativeEvents.cs @@ -1,6 +1,5 @@ using System.Collections.Concurrent; using System.Drawing; -using System.Numerics; using System.Threading.Channels; using VpSharp.ClientExtensions; using VpSharp.Entities; @@ -103,7 +102,7 @@ public sealed partial class VirtualParadiseClient int session; int type; Vector3d position; - Quaternion rotation; + Rotation rotation; lock (Lock) { @@ -117,7 +116,7 @@ public sealed partial class VirtualParadiseClient var pitch = (float)vp_double(sender, FloatAttribute.AvatarPitch); var yaw = (float)vp_double(sender, FloatAttribute.AvatarYaw); - rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); + rotation = Rotation.CreateFromTiltYawRoll(pitch, yaw, 0); } VirtualParadiseAvatar? avatar = GetAvatar(session); @@ -436,7 +435,7 @@ public sealed partial class VirtualParadiseClient int session; string worldName; Vector3d position; - Quaternion rotation; + Rotation rotation; lock (Lock) { @@ -449,7 +448,7 @@ public sealed partial class VirtualParadiseClient float yaw = vp_float(sender, FloatAttribute.TeleportYaw); float pitch = vp_float(sender, FloatAttribute.TeleportPitch); - rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); + rotation = Rotation.CreateFromTiltYawRoll(pitch, yaw, 0); worldName = vp_string(sender, StringAttribute.TeleportWorld); } @@ -546,7 +545,7 @@ public sealed partial class VirtualParadiseClient private async void OnInviteNativeEvent(nint sender) { Vector3d position; - Quaternion rotation; + Rotation rotation; int requestId; int userId; string worldName; @@ -566,7 +565,7 @@ public sealed partial class VirtualParadiseClient var pitch = (float)vp_double(sender, FloatAttribute.InvitePitch); position = new Vector3d(x, y, z); - rotation = Quaternion.CreateFromYawPitchRoll(yaw, pitch, 0); + rotation = Rotation.CreateFromTiltYawRoll(pitch, yaw, 0); worldName = vp_string(sender, StringAttribute.InviteWorld); } diff --git a/VpSharp/src/VirtualParadiseClient.Objects.cs b/VpSharp/src/VirtualParadiseClient.Objects.cs index bb590d0..5828751 100644 --- a/VpSharp/src/VirtualParadiseClient.Objects.cs +++ b/VpSharp/src/VirtualParadiseClient.Objects.cs @@ -1,11 +1,9 @@ using System.Collections.Concurrent; -using System.Numerics; using System.Threading.Channels; using VpSharp.Entities; using VpSharp.Exceptions; using VpSharp.Internal; using VpSharp.Internal.NativeAttributes; -using X10D.Math; using static VpSharp.Internal.NativeMethods; namespace VpSharp; @@ -179,7 +177,7 @@ public sealed partial class VirtualParadiseClient int id; int time; int owner; - Quaternion rotation; + Rotation rotation; Vector3d position; lock (Lock) @@ -200,15 +198,12 @@ public sealed partial class VirtualParadiseClient if (double.IsPositiveInfinity(angle)) { - rotX = rotX.DegreesToRadians(); - rotY = rotY.DegreesToRadians(); - rotZ = rotZ.DegreesToRadians(); - rotation = Quaternion.CreateFromYawPitchRoll(rotY, rotX, rotZ); + rotation = Rotation.CreateFromTiltYawRoll(rotX, rotY, rotZ); } else { - var axis = new Vector3(rotX, rotY, rotZ); - rotation = Quaternion.CreateFromAxisAngle(axis, angle); + var axis = new Vector3d(rotX, rotY, rotZ); + rotation = Rotation.CreateFromAxisAngle(axis, angle); } time = vp_int(sender, IntegerAttribute.ObjectTime); diff --git a/VpSharp/src/VirtualParadiseClient.cs b/VpSharp/src/VirtualParadiseClient.cs index 5f65671..6ffa290 100644 --- a/VpSharp/src/VirtualParadiseClient.cs +++ b/VpSharp/src/VirtualParadiseClient.cs @@ -2,7 +2,6 @@ using System.Collections.Concurrent; using System.Drawing; using System.Net; using System.Net.Sockets; -using System.Numerics; using System.Security.Authentication; using System.Threading.Channels; using VpSharp.Entities; @@ -218,7 +217,7 @@ public sealed partial class VirtualParadiseClient : IDisposable public async Task EnterAsync(string worldName, Vector3d position) { await EnterAsync(worldName).ConfigureAwait(false); - await CurrentAvatar!.TeleportAsync(position, Quaternion.Identity).ConfigureAwait(false); + await CurrentAvatar!.TeleportAsync(position, Rotation.None).ConfigureAwait(false); return CurrentWorld!; } @@ -235,7 +234,7 @@ public sealed partial class VirtualParadiseClient : IDisposable /// Connection to the universe server was lost, or connecting to the world failed. /// The specified world was not found. /// Connection to the world server timed out. - public async Task EnterAsync(string worldName, Vector3d position, Quaternion rotation) + public async Task EnterAsync(string worldName, Vector3d position, Rotation rotation) { await EnterAsync(worldName).ConfigureAwait(false); await CurrentAvatar!.TeleportAsync(position, rotation).ConfigureAwait(false); @@ -257,7 +256,7 @@ public sealed partial class VirtualParadiseClient : IDisposable public async Task EnterAsync(VirtualParadiseWorld world, Vector3d position) { await EnterAsync(world).ConfigureAwait(false); - await CurrentAvatar!.TeleportAsync(position, Quaternion.Identity).ConfigureAwait(false); + await CurrentAvatar!.TeleportAsync(position, Rotation.None).ConfigureAwait(false); } /// @@ -273,7 +272,7 @@ public sealed partial class VirtualParadiseClient : IDisposable /// Connection to the universe server was lost, or connecting to the world failed. /// The specified world was not found. /// Connection to the world server timed out. - public async Task EnterAsync(VirtualParadiseWorld world, Vector3d position, Quaternion rotation) + public async Task EnterAsync(VirtualParadiseWorld world, Vector3d position, Rotation rotation) { await EnterAsync(world).ConfigureAwait(false); await CurrentAvatar!.TeleportAsync(position, rotation).ConfigureAwait(false); @@ -401,7 +400,7 @@ public sealed partial class VirtualParadiseClient : IDisposable { Application = _configuration.Application!, Name = $"[{_configuration.BotName}]", - Location = new Location(world, Vector3d.Zero, Quaternion.Identity), + Location = new Location(world, Vector3d.Zero, Rotation.None), User = CurrentUser! };