diff --git a/Assets/Mirror/Components/NetworkTransformBase.cs b/Assets/Mirror/Components/NetworkTransformBase.cs index c3a542b95..efdbf1142 100644 --- a/Assets/Mirror/Components/NetworkTransformBase.cs +++ b/Assets/Mirror/Components/NetworkTransformBase.cs @@ -77,14 +77,14 @@ static void SerializeIntoWriter(NetworkWriter writer, Vector3 position, Quaterni else if (compressRotation == Compression.Much) { // write 3 byte. scaling [0,360] to [0,255] - writer.Write(Utils.ScaleFloatToByte(euler.x, 0, 360, byte.MinValue, byte.MaxValue)); - writer.Write(Utils.ScaleFloatToByte(euler.y, 0, 360, byte.MinValue, byte.MaxValue)); - writer.Write(Utils.ScaleFloatToByte(euler.z, 0, 360, byte.MinValue, byte.MaxValue)); + writer.Write(FloatBytePacker.ScaleFloatToByte(euler.x, 0, 360, byte.MinValue, byte.MaxValue)); + writer.Write(FloatBytePacker.ScaleFloatToByte(euler.y, 0, 360, byte.MinValue, byte.MaxValue)); + writer.Write(FloatBytePacker.ScaleFloatToByte(euler.z, 0, 360, byte.MinValue, byte.MaxValue)); } else if (compressRotation == Compression.Lots) { // write 2 byte, 5 bits for each float - writer.Write(Utils.PackThreeFloatsIntoUShort(euler.x, euler.y, euler.z, 0, 360)); + writer.Write(FloatBytePacker.PackThreeFloatsIntoUShort(euler.x, euler.y, euler.z, 0, 360)); } } @@ -128,15 +128,15 @@ void DeserializeFromReader(NetworkReader reader) else if (compressRotation == Compression.Much) { // read 3 byte. scaling [0,255] to [0,360] - float x = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); - float y = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); - float z = Utils.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); + float x = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); + float y = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); + float z = FloatBytePacker.ScaleByteToFloat(reader.ReadByte(), byte.MinValue, byte.MaxValue, 0, 360); temp.rotation = Quaternion.Euler(x, y, z); } else if (compressRotation == Compression.Lots) { // read 2 byte, 5 bits per float - float[] xyz = Utils.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360); + float[] xyz = FloatBytePacker.UnpackUShortIntoThreeFloats(reader.ReadUInt16(), 0, 360); temp.rotation = Quaternion.Euler(xyz[0], xyz[1], xyz[2]); } diff --git a/Assets/Mirror/Runtime/FloatBytePacker.cs b/Assets/Mirror/Runtime/FloatBytePacker.cs new file mode 100644 index 000000000..13311948e --- /dev/null +++ b/Assets/Mirror/Runtime/FloatBytePacker.cs @@ -0,0 +1,58 @@ +namespace Mirror +{ + public static class FloatBytePacker + { + // ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 0 + // ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue) => 127 + // ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue) => 191 + // ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 255 + public static byte ScaleFloatToByte(float value, float minValue, float maxValue, byte minTarget, byte maxTarget) + { + // note: C# byte - byte => int, hence so many casts + int targetRange = maxTarget - minTarget; // max byte - min byte only fits into something bigger + float valueRange = maxValue - minValue; + float valueRelative = value - minValue; + return (byte)(minTarget + (byte)(valueRelative/valueRange * (float)targetRange)); + } + + // ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1) => -1 + // ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1) => -0.003921569 + // ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1) => 0.4980392 + // ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1) => 1 + public static float ScaleByteToFloat(byte value, byte minValue, byte maxValue, float minTarget, float maxTarget) + { + // note: C# byte - byte => int, hence so many casts + float targetRange = maxTarget - minTarget; + byte valueRange = (byte)(maxValue - minValue); + byte valueRelative = (byte)(value - minValue); + return minTarget + ((float)valueRelative/(float)valueRange * targetRange); + } + + // eulerAngles have 3 floats, putting them into 2 bytes of [x,y],[z,0] + // would be a waste. instead we compress into 5 bits each => 15 bits. + // so a ushort. + public static ushort PackThreeFloatsIntoUShort(float u, float v, float w, float minValue, float maxValue) + { + // 5 bits max value = 1+2+4+8+16 = 31 = 0x1F + byte lower = ScaleFloatToByte(u, minValue, maxValue, 0x00, 0x1F); + byte middle = ScaleFloatToByte(v, minValue, maxValue, 0x00, 0x1F); + byte upper = ScaleFloatToByte(w, minValue, maxValue, 0x00, 0x1F); + ushort combined = (ushort)(upper << 10 | middle << 5 | lower); + return combined; + } + + // see PackThreeFloatsIntoUShort for explanation + public static float[] UnpackUShortIntoThreeFloats(ushort combined, float minTarget, float maxTarget) + { + byte lower = (byte)(combined & 0x1F); + byte middle = (byte)((combined >> 5) & 0x1F); + byte upper = (byte)(combined >> 10); // nothing on the left, no & needed + + // note: we have to use 4 bits per float, so between 0x00 and 0x0F + float u = ScaleByteToFloat(lower, 0x00, 0x1F, minTarget, maxTarget); + float v = ScaleByteToFloat(middle, 0x00, 0x1F, minTarget, maxTarget); + float w = ScaleByteToFloat(upper, 0x00, 0x1F, minTarget, maxTarget); + return new float[]{u, v, w}; + } + } +} \ No newline at end of file diff --git a/Assets/Mirror/Runtime/FloatBytePacker.cs.meta b/Assets/Mirror/Runtime/FloatBytePacker.cs.meta new file mode 100644 index 000000000..92145fe5c --- /dev/null +++ b/Assets/Mirror/Runtime/FloatBytePacker.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: afd3cca6a786d4208b1d0f7f2b168901 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Mirror/Runtime/NetworkManager.cs b/Assets/Mirror/Runtime/NetworkManager.cs index 02af23fbc..184c16f10 100644 --- a/Assets/Mirror/Runtime/NetworkManager.cs +++ b/Assets/Mirror/Runtime/NetworkManager.cs @@ -85,13 +85,19 @@ public virtual void Awake() InitializeSingleton(); // headless mode? then start the server - if (Utils.IsHeadless() && startOnHeadless) + if (IsHeadless() && startOnHeadless) { Application.targetFrameRate = 60; StartServer(); } } + // headless mode detection + public static bool IsHeadless() + { + return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null; + } + void InitializeSingleton() { if (singleton != null && singleton == this) diff --git a/Assets/Mirror/Runtime/UNetwork.cs b/Assets/Mirror/Runtime/UNetwork.cs index 58633a5c0..16e199a3b 100644 --- a/Assets/Mirror/Runtime/UNetwork.cs +++ b/Assets/Mirror/Runtime/UNetwork.cs @@ -133,66 +133,4 @@ public static bool UnpackMessage(NetworkReader messageReader, out ushort msgType return true; } } - - public static class Utils - { - // ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 0 - // ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue) => 127 - // ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue) => 191 - // ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue) => 255 - public static byte ScaleFloatToByte(float value, float minValue, float maxValue, byte minTarget, byte maxTarget) - { - // note: C# byte - byte => int, hence so many casts - int targetRange = maxTarget - minTarget; // max byte - min byte only fits into something bigger - float valueRange = maxValue - minValue; - float valueRelative = value - minValue; - return (byte)(minTarget + (byte)(valueRelative/valueRange * (float)targetRange)); - } - - // ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1) => -1 - // ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1) => -0.003921569 - // ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1) => 0.4980392 - // ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1) => 1 - public static float ScaleByteToFloat(byte value, byte minValue, byte maxValue, float minTarget, float maxTarget) - { - // note: C# byte - byte => int, hence so many casts - float targetRange = maxTarget - minTarget; - byte valueRange = (byte)(maxValue - minValue); - byte valueRelative = (byte)(value - minValue); - return minTarget + ((float)valueRelative/(float)valueRange * targetRange); - } - - // eulerAngles have 3 floats, putting them into 2 bytes of [x,y],[z,0] - // would be a waste. instead we compress into 5 bits each => 15 bits. - // so a ushort. - public static ushort PackThreeFloatsIntoUShort(float u, float v, float w, float minValue, float maxValue) - { - // 5 bits max value = 1+2+4+8+16 = 31 = 0x1F - byte lower = ScaleFloatToByte(u, minValue, maxValue, 0x00, 0x1F); - byte middle = ScaleFloatToByte(v, minValue, maxValue, 0x00, 0x1F); - byte upper = ScaleFloatToByte(w, minValue, maxValue, 0x00, 0x1F); - ushort combined = (ushort)(upper << 10 | middle << 5 | lower); - return combined; - } - - // see PackThreeFloatsIntoUShort for explanation - public static float[] UnpackUShortIntoThreeFloats(ushort combined, float minTarget, float maxTarget) - { - byte lower = (byte)(combined & 0x1F); - byte middle = (byte)((combined >> 5) & 0x1F); - byte upper = (byte)(combined >> 10); // nothing on the left, no & needed - - // note: we have to use 4 bits per float, so between 0x00 and 0x0F - float u = ScaleByteToFloat(lower, 0x00, 0x1F, minTarget, maxTarget); - float v = ScaleByteToFloat(middle, 0x00, 0x1F, minTarget, maxTarget); - float w = ScaleByteToFloat(upper, 0x00, 0x1F, minTarget, maxTarget); - return new float[]{u, v, w}; - } - - // headless mode detection - public static bool IsHeadless() - { - return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null; - } - } } diff --git a/Assets/Mirror/Tests/FloatBytePackerTest.cs b/Assets/Mirror/Tests/FloatBytePackerTest.cs new file mode 100644 index 000000000..2fd53d6ca --- /dev/null +++ b/Assets/Mirror/Tests/FloatBytePackerTest.cs @@ -0,0 +1,25 @@ +using NUnit.Framework; +namespace Mirror +{ + [TestFixture] + public class FloatBytePackerTest + { + [Test] + public void TestScaleFloatToByte() + { + Assert.That(FloatBytePacker.ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(0)); + Assert.That(FloatBytePacker.ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(127)); + Assert.That(FloatBytePacker.ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(191)); + Assert.That(FloatBytePacker.ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(255)); + } + + [Test] + public void ScaleByteToFloat() + { + Assert.That(FloatBytePacker.ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f)); + Assert.That(FloatBytePacker.ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-0.003921569f).Within(0.0001f)); + Assert.That(FloatBytePacker.ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(0.4980392f).Within(0.0001f)); + Assert.That(FloatBytePacker.ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f)); + } + } +} \ No newline at end of file diff --git a/Assets/Mirror/Tests/UtilsTest.cs.meta b/Assets/Mirror/Tests/FloatBytePackerTest.cs.meta similarity index 100% rename from Assets/Mirror/Tests/UtilsTest.cs.meta rename to Assets/Mirror/Tests/FloatBytePackerTest.cs.meta diff --git a/Assets/Mirror/Tests/UtilsTest.cs b/Assets/Mirror/Tests/UtilsTest.cs deleted file mode 100644 index 37bdd40ee..000000000 --- a/Assets/Mirror/Tests/UtilsTest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using NUnit.Framework; -namespace Mirror -{ - [TestFixture] - public class UtilsTest - { - [Test] - public void TestScaleFloatToByte() - { - Assert.That(Utils.ScaleFloatToByte( -1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(0)); - Assert.That(Utils.ScaleFloatToByte( 0f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(127)); - Assert.That(Utils.ScaleFloatToByte(0.5f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(191)); - Assert.That(Utils.ScaleFloatToByte( 1f, -1f, 1f, byte.MinValue, byte.MaxValue), Is.EqualTo(255)); - } - - [Test] - public void ScaleByteToFloat() - { - Assert.That(Utils.ScaleByteToFloat( 0, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-1).Within(0.0001f)); - Assert.That(Utils.ScaleByteToFloat(127, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(-0.003921569f).Within(0.0001f)); - Assert.That(Utils.ScaleByteToFloat(191, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(0.4980392f).Within(0.0001f)); - Assert.That(Utils.ScaleByteToFloat(255, byte.MinValue, byte.MaxValue, -1, 1), Is.EqualTo(1).Within(0.0001f)); - } - } -} \ No newline at end of file