diff --git a/Assets/Mirror/Core/Vector3Long.cs b/Assets/Mirror/Core/Vector3Long.cs new file mode 100644 index 000000000..945649594 --- /dev/null +++ b/Assets/Mirror/Core/Vector3Long.cs @@ -0,0 +1,114 @@ +// Vector3Long by mischa (based on game engine project) +using System; +using System.Runtime.CompilerServices; + +namespace Mirror +{ + public struct Vector3Long + { + public long x; + public long y; + public long z; + + public static readonly Vector3Long zero = new Vector3Long(0, 0, 0); + public static readonly Vector3Long one = new Vector3Long(1, 1, 1); + public static readonly Vector3Long forward = new Vector3Long(0, 0, 1); + public static readonly Vector3Long back = new Vector3Long(0, 0, -1); + public static readonly Vector3Long left = new Vector3Long(-1, 0, 0); + public static readonly Vector3Long right = new Vector3Long(1, 0, 0); + public static readonly Vector3Long up = new Vector3Long(0, 1, 0); + public static readonly Vector3Long down = new Vector3Long(0, -1, 0); + + // constructor ///////////////////////////////////////////////////////// + public Vector3Long(long x, long y, long z) + { + this.x = x; + this.y = y; + this.z = z; + } + + // operators /////////////////////////////////////////////////////////// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Long operator +(Vector3Long a, Vector3Long b) => + new Vector3Long(a.x + b.x, a.y + b.y, a.z + b.z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Long operator -(Vector3Long a, Vector3Long b) => + new Vector3Long(a.x - b.x, a.y - b.y, a.z - b.z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Long operator -(Vector3Long v) => + new Vector3Long(-v.x, -v.y, -v.z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Long operator *(Vector3Long a, long n) => + new Vector3Long(a.x * n, a.y * n, a.z * n); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Long operator *(long n, Vector3Long a) => + new Vector3Long(a.x * n, a.y * n, a.z * n); + + // == returns true if approximately equal (with epsilon). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Vector3Long a, Vector3Long b) => + a.x == b.x && + a.y == b.y && + a.z == b.z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Vector3Long a, Vector3Long b) => !(a == b); + + // NO IMPLICIT System.Numerics.Vector3Long conversion because double<->float + // would silently lose precision in large worlds. + + // [i] component index. useful for iterating all components etc. + public long this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => index switch { + 0 => x, + 1 => y, + 2 => z, + _ => throw new IndexOutOfRangeException($"Vector3Long[{index}] out of range.") + }; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { + switch (index) + { + case 0: + x = value; + break; + case 1: + y = value; + break; + case 2: + z = value; + break; + default: throw new IndexOutOfRangeException($"Vector3Long[{index}] out of range."); + } + } + } + + // instance functions ////////////////////////////////////////////////// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override string ToString() => $"({x} {y} {z})"; + + // equality //////////////////////////////////////////////////////////// + // implement Equals & HashCode explicitly for performance. + // calling .Equals (instead of "==") checks for exact equality. + // (API compatibility) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Vector3Long other) => + x == other.x && y == other.y && z == other.z; + + // Equals(object) can reuse Equals(Vector3) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object other) => + other is Vector3Long vector4 && Equals(vector4); + + // default generated by rider + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(x, y, z); + } +} diff --git a/Assets/Mirror/Core/Vector3Long.cs.meta b/Assets/Mirror/Core/Vector3Long.cs.meta new file mode 100644 index 000000000..2239765f8 --- /dev/null +++ b/Assets/Mirror/Core/Vector3Long.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 18efa4e349254185ad257401dd24628b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Tests/Editor/Vector3LongTests.cs b/Assets/Mirror/Tests/Editor/Vector3LongTests.cs new file mode 100644 index 000000000..74b32a6ec --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Vector3LongTests.cs @@ -0,0 +1,146 @@ +using System; +using NUnit.Framework; + +namespace Mirror.Tests +{ + public class Vector3LongTests + { + [Test] + public void Constructor() + { + Vector3Long v = new Vector3Long(); + Assert.That(v.x, Is.EqualTo(0)); + Assert.That(v.y, Is.EqualTo(0)); + Assert.That(v.z, Is.EqualTo(0)); + } + + [Test] + public void ConstructorXYZ() + { + Vector3Long v = new Vector3Long(1, 2, 3); + Assert.That(v.x, Is.EqualTo(1)); + Assert.That(v.y, Is.EqualTo(2)); + Assert.That(v.z, Is.EqualTo(3)); + } + + [Test] + public void OperatorAdd() + { + Vector3Long a = new Vector3Long(1, 2, 3); + Vector3Long b = new Vector3Long(2, 3, 4); + Assert.That(a + b, Is.EqualTo(new Vector3Long(3, 5, 7))); + } + + [Test] + public void OperatorSubtract() + { + Vector3Long a = new Vector3Long(1, 2, 3); + Vector3Long b = new Vector3Long(-2, -3, -4); + Assert.That(a - b, Is.EqualTo(new Vector3Long(3, 5, 7))); + } + + [Test] + public void OperatorInverse() + { + Vector3Long v = new Vector3Long(1, 2, 3); + Assert.That(-v, Is.EqualTo(new Vector3Long(-1, -2, -3))); + } + + [Test] + public void OperatorMultiply() + { + Vector3Long a = new Vector3Long(1, 2, 3); + // a * n, n * a are two different operators. test both. + Assert.That(a * 2, Is.EqualTo(new Vector3Long(2, 4, 6))); + Assert.That(2 * a, Is.EqualTo(new Vector3Long(2, 4, 6))); + } + + [Test] + public void OperatorEquals() + { + // two vectors which are approximately the same + Vector3Long a = new Vector3Long(1, 2, 3); + Vector3Long b = new Vector3Long(1, 2, 3); + Assert.That(a == b, Is.True); + + // two vectors which are definitely not the same + Assert.That(a == Vector3Long.one, Is.False); + } + + [Test] + public void OperatorNotEquals() + { + // two vectors which are approximately the same + Vector3Long a = new Vector3Long(1, 2, 3); + Vector3Long b = new Vector3Long(1, 2, 3); + Assert.That(a != b, Is.False); + + // two vectors which are definitely not the same + Assert.That(a != Vector3Long.one, Is.True); + } + + [Test] + public void OperatorIndexer() + { + Vector3Long a = new Vector3Long(1, 2, 3); + + // get + Assert.That(a[0], Is.EqualTo(1)); + Assert.That(a[1], Is.EqualTo(2)); + Assert.That(a[2], Is.EqualTo(3)); + Assert.Throws(() => + { + double _ = a[-1]; + }); + Assert.Throws(() => + { + double _ = a[3]; + }); + + // set + a[0] = -1; + a[1] = -2; + a[2] = -3; + Assert.Throws(() => { a[-1] = 0; }); + Assert.Throws(() => { a[3] = 0; }); + Assert.That(a, Is.EqualTo(new Vector3Long(-1, -2, -3))); + } + + [Test] + public void ToStringTest() + { + // should be rounded to :F2 + Vector3Long v = new Vector3Long(-10, 0, 42); + Assert.That(v.ToString(), Is.EqualTo("(-10 0 42)")); + } + + [Test] + public void EqualsVector3Long() + { + Assert.That(Vector3Long.one.Equals(Vector3Long.one), Is.True); + Assert.That(Vector3Long.one.Equals(Vector3Long.zero), Is.False); + } + + [Test] + public void EqualsObject() + { + Assert.That(Vector3Long.one.Equals((object)42), Is.False); + Assert.That(Vector3Long.one.Equals((object)Vector3Long.one), Is.True); + Assert.That(Vector3Long.one.Equals((object)Vector3Long.zero), Is.False); + } + + [Test] + public void GetHashCodeTest() + { + // shouldn't be 0 + Assert.That(Vector3Long.zero.GetHashCode(), !Is.EqualTo(0)); + Assert.That(Vector3Long.one.GetHashCode(), !Is.EqualTo(0)); + + // should be same for same vector + Assert.That(Vector3Long.zero.GetHashCode(), Is.EqualTo(Vector3Long.zero.GetHashCode())); + + // should be different for different vectors + Assert.That(Vector3Long.zero.GetHashCode(), !Is.EqualTo(Vector3Long.one.GetHashCode())); + } + } +} diff --git a/Assets/Mirror/Tests/Editor/Vector3LongTests.cs.meta b/Assets/Mirror/Tests/Editor/Vector3LongTests.cs.meta new file mode 100644 index 000000000..58d2f2f3d --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Vector3LongTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 622df7adb3c345b3bd1283b0801ecfae +timeCreated: 1666897728 \ No newline at end of file