From c36feeb0fe57f0f1d72a354d36c3c1b6a40a00b7 Mon Sep 17 00:00:00 2001 From: miwarnec Date: Wed, 6 Nov 2024 12:03:17 +0100 Subject: [PATCH 1/2] Vector3Half & Tests --- Assets/Mirror/Core/Tools/Vector3Half.cs | 125 +++++++++++++++ Assets/Mirror/Core/Tools/Vector3Half.cs.meta | 3 + .../Tests/Editor/Tools/Vector3HalfTests.cs | 150 ++++++++++++++++++ .../Editor/Tools/Vector3HalfTests.cs.meta | 3 + 4 files changed, 281 insertions(+) create mode 100644 Assets/Mirror/Core/Tools/Vector3Half.cs create mode 100644 Assets/Mirror/Core/Tools/Vector3Half.cs.meta create mode 100644 Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs create mode 100644 Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs.meta diff --git a/Assets/Mirror/Core/Tools/Vector3Half.cs b/Assets/Mirror/Core/Tools/Vector3Half.cs new file mode 100644 index 000000000..88b658128 --- /dev/null +++ b/Assets/Mirror/Core/Tools/Vector3Half.cs @@ -0,0 +1,125 @@ +#pragma warning disable CS0659 // 'Vector3Half' overrides Object.Equals(object o) but does not override Object.GetHashCode() +#pragma warning disable CS0661 // 'Vector3Half' defines operator == or operator != but does not override Object.GetHashCode() + +// Vector3Half by mischa (based on game engine project) +using System; +using System.Runtime.CompilerServices; + +namespace Mirror +{ + public struct Vector3Half + { + public Half x; + public Half y; + public Half z; + + public static readonly Vector3Half zero = new Vector3Half((Half)0, (Half)0, (Half)0); + public static readonly Vector3Half one = new Vector3Half((Half)1, (Half)1, (Half)1); + public static readonly Vector3Half forward = new Vector3Half((Half)0, (Half)0, (Half)1); + public static readonly Vector3Half back = new Vector3Half((Half)0, (Half)0, (Half)(-1)); + public static readonly Vector3Half left = new Vector3Half((Half)(-1), (Half)0, (Half)0); + public static readonly Vector3Half right = new Vector3Half((Half)1, (Half)0, (Half)0); + public static readonly Vector3Half up = new Vector3Half((Half)0, (Half)1, (Half)0); + public static readonly Vector3Half down = new Vector3Half((Half)0, (Half)(-1), (Half)0); + + // constructor ///////////////////////////////////////////////////////// + public Vector3Half(Half x, Half y, Half z) + { + this.x = x; + this.y = y; + this.z = z; + } + + // operators /////////////////////////////////////////////////////////// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Half operator +(Vector3Half a, Vector3Half b) => + new Vector3Half(a.x + b.x, a.y + b.y, a.z + b.z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Half operator -(Vector3Half a, Vector3Half b) => + new Vector3Half(a.x - b.x, a.y - b.y, a.z - b.z); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3Half operator -(Vector3Half v) => + new Vector3Half(-v.x, -v.y, -v.z); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Vector3Half operator *(Vector3Half a, long n) => + // new Vector3Half(a.x * n, a.y * n, a.z * n); + + // [MethodImpl(MethodImplOptions.AggressiveInlining)] + // public static Vector3Half operator *(long n, Vector3Half a) => + // new Vector3Half(a.x * n, a.y * n, a.z * n); + + // == returns true if approximately equal (with epsilon). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Vector3Half a, Vector3Half b) => + a.x == b.x && + a.y == b.y && + a.z == b.z; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Vector3Half a, Vector3Half b) => !(a == b); + + // NO IMPLICIT System.Numerics.Vector3Half conversion because double<->float + // would silently lose precision in large worlds. + + // [i] component index. useful for iterating all components etc. + public Half this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + switch (index) + { + case 0: return x; + case 1: return y; + case 2: return z; + default: throw new IndexOutOfRangeException($"Vector3Half[{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($"Vector3Half[{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(Vector3Half other) => + x == other.x && y == other.y && z == other.z; + + // Equals(object) can reuse Equals(Vector4) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override bool Equals(object other) => + other is Vector3Half vector4 && Equals(vector4); + +#if UNITY_2021_3_OR_NEWER + // Unity 2019/2020 don't have HashCode.Combine yet. + // this is only to avoid reflection. without defining, it works too. + // default generated by rider + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() => HashCode.Combine(x, y, z); +#endif + } +} diff --git a/Assets/Mirror/Core/Tools/Vector3Half.cs.meta b/Assets/Mirror/Core/Tools/Vector3Half.cs.meta new file mode 100644 index 000000000..5f025e09e --- /dev/null +++ b/Assets/Mirror/Core/Tools/Vector3Half.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b70bf28681a14c65a37793a87c8df33e +timeCreated: 1730890316 \ No newline at end of file diff --git a/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs b/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs new file mode 100644 index 000000000..300f46d82 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs @@ -0,0 +1,150 @@ +using System; +using NUnit.Framework; + +namespace Mirror.Tests.Tools +{ + public class Vector3HalfTests + { + [Test] + public void Constructor() + { + Vector3Half v = new Vector3Half(); + Assert.That(v.x, Is.EqualTo((Half)0)); + Assert.That(v.y, Is.EqualTo((Half)0)); + Assert.That(v.z, Is.EqualTo((Half)0)); + } + + [Test] + public void ConstructorXYZ() + { + Vector3Half v = new Vector3Half((Half)1, (Half)2, (Half)3); + Assert.That(v.x, Is.EqualTo((Half)1)); + Assert.That(v.y, Is.EqualTo((Half)2)); + Assert.That(v.z, Is.EqualTo((Half)3)); + } + + [Test] + public void OperatorAdd() + { + Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + Vector3Half b = new Vector3Half((Half)2, (Half)3, (Half)4); + Assert.That(a + b, Is.EqualTo(new Vector3Half((Half)3, (Half)5, (Half)7))); + } + + [Test] + public void OperatorSubtract() + { + Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + Vector3Half b = new Vector3Half((Half)(-2), (Half)(-3), (Half)(-4)); + Assert.That(a - b, Is.EqualTo(new Vector3Half((Half)3, (Half)5, (Half)7))); + } + + [Test] + public void OperatorInverse() + { + Vector3Half v = new Vector3Half((Half)1, (Half)2, (Half)3); + Assert.That(-v, Is.EqualTo(new Vector3Half((Half)(-1), (Half)(-2), (Half)(-3)))); + } + + // [Test] + // public void OperatorMultiply() + // { + // Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + // // a * n, n * a are two different operators. test both. + // Assert.That(a * (Half)2, Is.EqualTo(new Vector3Half((Half)2, (Half)4, (Half)6))); + // Assert.That((Half)2 * a, Is.EqualTo(new Vector3Half((Half)2, (Half)4, (Half)6))); + // } + +#if UNITY_2021_3_OR_NEWER + [Test] + public void OperatorEquals() + { + // two vectors which are approximately the same + Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + Vector3Half b = new Vector3Half((Half)1, (Half)2, (Half)3); + Assert.That(a == b, Is.True); + + // two vectors which are definitely not the same + Assert.That(a == Vector3Half.one, Is.False); + } + + [Test] + public void OperatorNotEquals() + { + // two vectors which are approximately the same + Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + Vector3Half b = new Vector3Half((Half)1, (Half)2, (Half)3); + Assert.That(a != b, Is.False); + + // two vectors which are definitely not the same + Assert.That(a != Vector3Half.one, Is.True); + } +#endif + + [Test] + public void OperatorIndexer() + { + Vector3Half a = new Vector3Half((Half)1, (Half)2, (Half)3); + + // get + Assert.That(a[0], Is.EqualTo((Half)1)); + Assert.That(a[1], Is.EqualTo((Half)2)); + Assert.That(a[2], Is.EqualTo((Half)3)); + Assert.Throws(() => + { + Half _ = a[-1]; + }); + Assert.Throws(() => + { + Half _ = a[3]; + }); + + // set + a[0] = -1; + a[1] = -2; + a[2] = -3; + Assert.Throws(() => { a[-1] = (Half)0; }); + Assert.Throws(() => { a[3] = (Half)0; }); + Assert.That(a, Is.EqualTo(new Vector3Half((Half)(-1), (Half)(-2), (Half)(-3)))); + } + + [Test] + public void ToStringTest() + { + // should be rounded to :F2 + Vector3Half v = new Vector3Half((Half)(-10), (Half)0, (Half)42); + Assert.That(v.ToString(), Is.EqualTo("(-10 0 42)")); + } + +#if UNITY_2021_3_OR_NEWER + [Test] + public void EqualsVector3Half() + { + Assert.That(Vector3Half.one.Equals(Vector3Half.one), Is.True); + Assert.That(Vector3Half.one.Equals(Vector3Half.zero), Is.False); + } + + [Test] + public void EqualsObject() + { + Assert.That(Vector3Half.one.Equals((object)42), Is.False); + Assert.That(Vector3Half.one.Equals((object)Vector3Half.one), Is.True); + Assert.That(Vector3Half.one.Equals((object)Vector3Half.zero), Is.False); + } + + [Test] + public void GetHashCodeTest() + { + // shouldn't be 0 + Assert.That(Vector3Half.zero.GetHashCode(), !Is.EqualTo(0)); + Assert.That(Vector3Half.one.GetHashCode(), !Is.EqualTo(0)); + + // should be same for same vector + Assert.That(Vector3Half.zero.GetHashCode(), Is.EqualTo(Vector3Half.zero.GetHashCode())); + + // should be different for different vectors + Assert.That(Vector3Half.zero.GetHashCode(), !Is.EqualTo(Vector3Half.one.GetHashCode())); + } +#endif + } +} diff --git a/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs.meta b/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs.meta new file mode 100644 index 000000000..d70f401d2 --- /dev/null +++ b/Assets/Mirror/Tests/Editor/Tools/Vector3HalfTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bc5f32db47ab4ffb92433050cc1b6e9f +timeCreated: 1730890719 \ No newline at end of file From cb6a7812f50d43c7539b378607c0a360bfbf6b6d Mon Sep 17 00:00:00 2001 From: miwarnec Date: Wed, 6 Nov 2024 12:39:35 +0100 Subject: [PATCH 2/2] Vector3Half reader/writer --- Assets/Mirror/Core/NetworkReaderExtensions.cs | 7 +++++++ Assets/Mirror/Core/NetworkWriterExtensions.cs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/Assets/Mirror/Core/NetworkReaderExtensions.cs b/Assets/Mirror/Core/NetworkReaderExtensions.cs index dd366f667..254aa3ec9 100644 --- a/Assets/Mirror/Core/NetworkReaderExtensions.cs +++ b/Assets/Mirror/Core/NetworkReaderExtensions.cs @@ -63,6 +63,13 @@ public static class NetworkReaderExtensions public static decimal? ReadDecimalNullable(this NetworkReader reader) => reader.ReadBlittableNullable(); public static Half ReadHalf(this NetworkReader reader) => new Half(reader.ReadUShort()); + public static Vector3Half ReadVector3Half(this NetworkReader reader) + { + Half x = reader.ReadHalf(); + Half y = reader.ReadHalf(); + Half z = reader.ReadHalf(); + return new Vector3Half(x, y, z); + } /// if an invalid utf8 string is sent public static string ReadString(this NetworkReader reader) diff --git a/Assets/Mirror/Core/NetworkWriterExtensions.cs b/Assets/Mirror/Core/NetworkWriterExtensions.cs index 1f6588f77..e74b3e8c6 100644 --- a/Assets/Mirror/Core/NetworkWriterExtensions.cs +++ b/Assets/Mirror/Core/NetworkWriterExtensions.cs @@ -58,6 +58,12 @@ public static class NetworkWriterExtensions public static void WriteDecimalNullable(this NetworkWriter writer, decimal? value) => writer.WriteBlittableNullable(value); public static void WriteHalf(this NetworkWriter writer, Half value) => writer.WriteUShort(value._value); + public static void WriteVector3Half(this NetworkWriter writer, Vector3Half value) + { + writer.WriteHalf(value.x); + writer.WriteHalf(value.y); + writer.WriteHalf(value.z); + } public static void WriteString(this NetworkWriter writer, string value) {