Keep local version of files
This commit is contained in:
@@ -1,86 +1,86 @@
|
||||
// accurate interval from Mirror II.
|
||||
// for sync / send intervals where it matters.
|
||||
// does not(!) do catch-up.
|
||||
//
|
||||
// first, let's understand the problem.
|
||||
// say we need an interval of 10 Hz, so every 100ms in Update we do:
|
||||
// if (Time.time >= lastTime + interval)
|
||||
// {
|
||||
// lastTime = Time.time;
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// this seems fine, but actually Time.time will always be a few ms beyond
|
||||
// the interval. but since lastTime is reset to Time.time, the remainder
|
||||
// is always ignored away.
|
||||
// with fixed tickRate servers (say 30 Hz), the remainder is significant!
|
||||
//
|
||||
// in practice if we have a 30 Hz tickRate server with a 30 Hz sendRate,
|
||||
// the above way to measure the interval would result in a 18-19 Hz sendRate!
|
||||
// => this is not just a little off. this is _way_ off, by almost half.
|
||||
// => displaying actual + target tick/send rate will show this very easily.
|
||||
//
|
||||
// we need an accurate way to measure intervals for where it matters.
|
||||
// and it needs to be testable to guarantee results.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class AccurateInterval
|
||||
{
|
||||
// static func instead of storing interval + lastTime struct.
|
||||
// + don't need to initialize struct ctor with interval in Awake
|
||||
// + allows for interval changes at runtime too
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Elapsed(double time, double interval, ref double lastTime)
|
||||
{
|
||||
// enough time elapsed?
|
||||
if (time < lastTime + interval)
|
||||
return false;
|
||||
|
||||
// naive implementation:
|
||||
//lastTime = time;
|
||||
|
||||
// accurate but doesn't handle heavy load situations:
|
||||
//lastTime += interval;
|
||||
|
||||
// heavy load edge case:
|
||||
// * interval is 100ms
|
||||
// * server is under heavy load, Updates slow down to 1/s
|
||||
// * Elapsed(1.000) returns true.
|
||||
// technically 10 intervals have elapsed.
|
||||
// * server recovers to normal, Updates are every 10ms again
|
||||
// * Elapsed(1.010) should return false again until 1.100.
|
||||
//
|
||||
// increasing lastTime by interval would require 10 more calls
|
||||
// to ever catch up again:
|
||||
// lastTime += interval
|
||||
//
|
||||
// as result, the next 10 calls to Elapsed would return true.
|
||||
// Elapsed(1.001) => true
|
||||
// Elapsed(1.002) => true
|
||||
// Elapsed(1.003) => true
|
||||
// ...
|
||||
// even though technically the delta was not >= interval.
|
||||
//
|
||||
// this would keep the server under heavy load, and it may never
|
||||
// catch-up. this is not ideal for large virtual worlds.
|
||||
//
|
||||
// instead, we want to skip multiples of 'interval' and only
|
||||
// keep the remainder.
|
||||
//
|
||||
// see also: AccurateIntervalTests.Slowdown()
|
||||
|
||||
// easy to understand:
|
||||
//double elapsed = time - lastTime;
|
||||
//double remainder = elapsed % interval;
|
||||
//lastTime = time - remainder;
|
||||
|
||||
// easier: set to rounded multiples of interval (fholm).
|
||||
// long to match double time.
|
||||
long multiplier = (long)(time / interval);
|
||||
lastTime = multiplier * interval;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// accurate interval from Mirror II.
|
||||
// for sync / send intervals where it matters.
|
||||
// does not(!) do catch-up.
|
||||
//
|
||||
// first, let's understand the problem.
|
||||
// say we need an interval of 10 Hz, so every 100ms in Update we do:
|
||||
// if (Time.time >= lastTime + interval)
|
||||
// {
|
||||
// lastTime = Time.time;
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// this seems fine, but actually Time.time will always be a few ms beyond
|
||||
// the interval. but since lastTime is reset to Time.time, the remainder
|
||||
// is always ignored away.
|
||||
// with fixed tickRate servers (say 30 Hz), the remainder is significant!
|
||||
//
|
||||
// in practice if we have a 30 Hz tickRate server with a 30 Hz sendRate,
|
||||
// the above way to measure the interval would result in a 18-19 Hz sendRate!
|
||||
// => this is not just a little off. this is _way_ off, by almost half.
|
||||
// => displaying actual + target tick/send rate will show this very easily.
|
||||
//
|
||||
// we need an accurate way to measure intervals for where it matters.
|
||||
// and it needs to be testable to guarantee results.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class AccurateInterval
|
||||
{
|
||||
// static func instead of storing interval + lastTime struct.
|
||||
// + don't need to initialize struct ctor with interval in Awake
|
||||
// + allows for interval changes at runtime too
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Elapsed(double time, double interval, ref double lastTime)
|
||||
{
|
||||
// enough time elapsed?
|
||||
if (time < lastTime + interval)
|
||||
return false;
|
||||
|
||||
// naive implementation:
|
||||
//lastTime = time;
|
||||
|
||||
// accurate but doesn't handle heavy load situations:
|
||||
//lastTime += interval;
|
||||
|
||||
// heavy load edge case:
|
||||
// * interval is 100ms
|
||||
// * server is under heavy load, Updates slow down to 1/s
|
||||
// * Elapsed(1.000) returns true.
|
||||
// technically 10 intervals have elapsed.
|
||||
// * server recovers to normal, Updates are every 10ms again
|
||||
// * Elapsed(1.010) should return false again until 1.100.
|
||||
//
|
||||
// increasing lastTime by interval would require 10 more calls
|
||||
// to ever catch up again:
|
||||
// lastTime += interval
|
||||
//
|
||||
// as result, the next 10 calls to Elapsed would return true.
|
||||
// Elapsed(1.001) => true
|
||||
// Elapsed(1.002) => true
|
||||
// Elapsed(1.003) => true
|
||||
// ...
|
||||
// even though technically the delta was not >= interval.
|
||||
//
|
||||
// this would keep the server under heavy load, and it may never
|
||||
// catch-up. this is not ideal for large virtual worlds.
|
||||
//
|
||||
// instead, we want to skip multiples of 'interval' and only
|
||||
// keep the remainder.
|
||||
//
|
||||
// see also: AccurateIntervalTests.Slowdown()
|
||||
|
||||
// easy to understand:
|
||||
//double elapsed = time - lastTime;
|
||||
//double remainder = elapsed % interval;
|
||||
//lastTime = time - remainder;
|
||||
|
||||
// easier: set to rounded multiples of interval (fholm).
|
||||
// long to match double time.
|
||||
long multiplier = (long)(time / interval);
|
||||
lastTime = multiplier * interval;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c1b18064e25046f28b88db65a4012ec1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/AccurateInterval.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: c1b18064e25046f28b88db65a4012ec1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/AccurateInterval.cs
|
||||
uploadId: 736421
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5c28963f9c4b97e418252a55500fb91e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Compression.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 5c28963f9c4b97e418252a55500fb91e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Compression.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
// manual delta compression for some types.
|
||||
// varint(b-a)
|
||||
// Mirror can't use Mirror II's bit-tree delta compression.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class DeltaCompression
|
||||
{
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
// parameter order: (last, current) makes most sense (Q3 does this too).
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, long last, long current) =>
|
||||
Compression.CompressVarInt(writer, current - last);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long Decompress(NetworkReader reader, long last) =>
|
||||
last + Compression.DecompressVarInt(reader);
|
||||
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, Vector3Long last, Vector3Long current)
|
||||
{
|
||||
Compress(writer, last.x, current.x);
|
||||
Compress(writer, last.y, current.y);
|
||||
Compress(writer, last.z, current.z);
|
||||
}
|
||||
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, Vector4Long last, Vector4Long current)
|
||||
{
|
||||
Compress(writer, last.x, current.x);
|
||||
Compress(writer, last.y, current.y);
|
||||
Compress(writer, last.z, current.z);
|
||||
Compress(writer, last.w, current.w);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3Long Decompress(NetworkReader reader, Vector3Long last)
|
||||
{
|
||||
long x = Decompress(reader, last.x);
|
||||
long y = Decompress(reader, last.y);
|
||||
long z = Decompress(reader, last.z);
|
||||
return new Vector3Long(x, y, z);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long Decompress(NetworkReader reader, Vector4Long last)
|
||||
{
|
||||
long x = Decompress(reader, last.x);
|
||||
long y = Decompress(reader, last.y);
|
||||
long z = Decompress(reader, last.z);
|
||||
long w = Decompress(reader, last.w);
|
||||
return new Vector4Long(x, y, z, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
// manual delta compression for some types.
|
||||
// varint(b-a)
|
||||
// Mirror can't use Mirror II's bit-tree delta compression.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class DeltaCompression
|
||||
{
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
// parameter order: (last, current) makes most sense (Q3 does this too).
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, long last, long current) =>
|
||||
Compression.CompressVarInt(writer, current - last);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static long Decompress(NetworkReader reader, long last) =>
|
||||
last + Compression.DecompressVarInt(reader);
|
||||
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, Vector3Long last, Vector3Long current)
|
||||
{
|
||||
Compress(writer, last.x, current.x);
|
||||
Compress(writer, last.y, current.y);
|
||||
Compress(writer, last.z, current.z);
|
||||
}
|
||||
|
||||
// delta (usually small), then zigzag varint to support +- changes
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Compress(NetworkWriter writer, Vector4Long last, Vector4Long current)
|
||||
{
|
||||
Compress(writer, last.x, current.x);
|
||||
Compress(writer, last.y, current.y);
|
||||
Compress(writer, last.z, current.z);
|
||||
Compress(writer, last.w, current.w);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector3Long Decompress(NetworkReader reader, Vector3Long last)
|
||||
{
|
||||
long x = Decompress(reader, last.x);
|
||||
long y = Decompress(reader, last.y);
|
||||
long z = Decompress(reader, last.z);
|
||||
return new Vector3Long(x, y, z);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long Decompress(NetworkReader reader, Vector4Long last)
|
||||
{
|
||||
long x = Decompress(reader, last.x);
|
||||
long y = Decompress(reader, last.y);
|
||||
long z = Decompress(reader, last.z);
|
||||
long w = Decompress(reader, last.w);
|
||||
return new Vector4Long(x, y, z, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b8f3fffcb4754c15bc5ed4c33e2497b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/DeltaCompression.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 6b8f3fffcb4754c15bc5ed4c33e2497b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/DeltaCompression.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
// N-day EMA implementation from Mirror with a few changes (struct etc.)
|
||||
// it calculates an exponential moving average roughly equivalent to the last n observations
|
||||
// https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
using System;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct ExponentialMovingAverage
|
||||
{
|
||||
readonly double alpha;
|
||||
bool initialized;
|
||||
|
||||
public double Value;
|
||||
public double Variance;
|
||||
public double StandardDeviation; // absolute value, see test
|
||||
|
||||
public ExponentialMovingAverage(int n)
|
||||
{
|
||||
// standard N-day EMA alpha calculation
|
||||
alpha = 2.0 / (n + 1);
|
||||
initialized = false;
|
||||
Value = 0;
|
||||
Variance = 0;
|
||||
StandardDeviation = 0;
|
||||
}
|
||||
|
||||
public void Add(double newValue)
|
||||
{
|
||||
// simple algorithm for EMA described here:
|
||||
// https://en.wikipedia.org/wiki/Moving_average#Exponentially_weighted_moving_variance_and_standard_deviation
|
||||
if (initialized)
|
||||
{
|
||||
double delta = newValue - Value;
|
||||
Value += alpha * delta;
|
||||
Variance = (1 - alpha) * (Variance + alpha * delta * delta);
|
||||
StandardDeviation = Math.Sqrt(Variance);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = newValue;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
initialized = false;
|
||||
Value = 0;
|
||||
Variance = 0;
|
||||
StandardDeviation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// N-day EMA implementation from Mirror with a few changes (struct etc.)
|
||||
// it calculates an exponential moving average roughly equivalent to the last n observations
|
||||
// https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
using System;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct ExponentialMovingAverage
|
||||
{
|
||||
readonly double alpha;
|
||||
bool initialized;
|
||||
|
||||
public double Value;
|
||||
public double Variance;
|
||||
public double StandardDeviation; // absolute value, see test
|
||||
|
||||
public ExponentialMovingAverage(int n)
|
||||
{
|
||||
// standard N-day EMA alpha calculation
|
||||
alpha = 2.0 / (n + 1);
|
||||
initialized = false;
|
||||
Value = 0;
|
||||
Variance = 0;
|
||||
StandardDeviation = 0;
|
||||
}
|
||||
|
||||
public void Add(double newValue)
|
||||
{
|
||||
// simple algorithm for EMA described here:
|
||||
// https://en.wikipedia.org/wiki/Moving_average#Exponentially_weighted_moving_variance_and_standard_deviation
|
||||
if (initialized)
|
||||
{
|
||||
double delta = newValue - Value;
|
||||
Value += alpha * delta;
|
||||
Variance = (1 - alpha) * (Variance + alpha * delta * delta);
|
||||
StandardDeviation = Math.Sqrt(Variance);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value = newValue;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
initialized = false;
|
||||
Value = 0;
|
||||
Variance = 0;
|
||||
StandardDeviation = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05e858cbaa54b4ce4a48c8c7f50c1914
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/ExponentialMovingAverage.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 05e858cbaa54b4ce4a48c8c7f50c1914
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/ExponentialMovingAverage.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,145 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static string ToHexString(this ArraySegment<byte> segment) =>
|
||||
BitConverter.ToString(segment.Array, segment.Offset, segment.Count);
|
||||
|
||||
// string.GetHashCode is not guaranteed to be the same on all
|
||||
// machines, but we need one that is the same on all machines.
|
||||
// Uses fnv1a as hash function for more uniform distribution http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
// Tests: https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
|
||||
// NOTE: Do not call this from hot path because it's slow O(N) for long method names.
|
||||
// - As of 2012-02-16 There are 2 design-time callers (weaver) and 1 runtime caller that caches.
|
||||
public static int GetStableHashCode(this string text)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint hash = 0x811c9dc5;
|
||||
uint prime = 0x1000193;
|
||||
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
byte value = (byte)text[i];
|
||||
hash = hash ^ value;
|
||||
hash *= prime;
|
||||
}
|
||||
|
||||
//UnityEngine.Debug.Log($"Created stable hash {(ushort)hash} for {text}");
|
||||
return (int)hash;
|
||||
}
|
||||
}
|
||||
|
||||
// smaller version of our GetStableHashCode.
|
||||
// careful, this significantly increases chance of collisions.
|
||||
public static ushort GetStableHashCode16(this string text)
|
||||
{
|
||||
// deterministic hash
|
||||
int hash = GetStableHashCode(text);
|
||||
|
||||
// Gets the 32bit fnv1a hash
|
||||
// To get it down to 16bit but still reduce hash collisions we cant just cast it to ushort
|
||||
// Instead we take the highest 16bits of the 32bit hash and fold them with xor into the lower 16bits
|
||||
// This will create a more uniform 16bit hash, the method is described in:
|
||||
// http://www.isthe.com/chongo/tech/comp/fnv/ in section "Changing the FNV hash size - xor-folding"
|
||||
return (ushort)((hash >> 16) ^ hash);
|
||||
}
|
||||
|
||||
// previously in DotnetCompatibility.cs
|
||||
// leftover from the UNET days. supposedly for windows store?
|
||||
public static string GetMethodName(this Delegate func)
|
||||
{
|
||||
#if NETFX_CORE
|
||||
return func.GetMethodInfo().Name;
|
||||
#else
|
||||
return func.Method.Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
// helper function to copy to List<T>
|
||||
// C# only provides CopyTo(T[])
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo<T>(this IEnumerable<T> source, List<T> destination)
|
||||
{
|
||||
// foreach allocates. use AddRange.
|
||||
destination.AddRange(source);
|
||||
}
|
||||
|
||||
#if !UNITY_2021_OR_NEWER
|
||||
// Unity 2020 and earlier don't have Queue.TryDequeue which we need for batching.
|
||||
public static bool TryDequeue<T>(this Queue<T> source, out T element)
|
||||
{
|
||||
if (source.Count > 0)
|
||||
{
|
||||
element = source.Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
element = default;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_2021_OR_NEWER
|
||||
// Unity 2020 and earlier don't have ConcurrentQueue.Clear which we need for ThreadedTransport.
|
||||
public static void Clear<T>(this ConcurrentQueue<T> source)
|
||||
{
|
||||
// while count > 0 risks deadlock if other thread write at the same time.
|
||||
// our safest solution is a best-effort approach to clear 'Count' once.
|
||||
int count = source.Count; // get it only once
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
source.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_2021_3_OR_NEWER
|
||||
// Some patch versions of Unity 2021.3 and earlier don't have transform.GetPositionAndRotation which we use for performance in some places
|
||||
public static void GetPositionAndRotation(this Transform transform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
position = transform.position;
|
||||
rotation = transform.rotation;
|
||||
}
|
||||
|
||||
public static void SetPositionAndRotation(this Transform transform, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
transform.position = position;
|
||||
transform.rotation = rotation;
|
||||
}
|
||||
|
||||
public static void GetLocalPositionAndRotation(this Transform transform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
position = transform.localPosition;
|
||||
rotation = transform.localRotation;
|
||||
}
|
||||
|
||||
public static void SetLocalPositionAndRotation(this Transform transform, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
transform.localPosition = position;
|
||||
transform.localRotation = rotation;
|
||||
}
|
||||
#endif
|
||||
|
||||
// IPEndPoint address only to pretty string.
|
||||
// useful for to get a connection's address for IP bans etc.
|
||||
public static string PrettyAddress(this IPEndPoint endPoint)
|
||||
{
|
||||
if (endPoint == null) return "";
|
||||
|
||||
// Map to IPv4 if "IsIPv4MappedToIPv6" for readability
|
||||
// "::ffff:127.0.0.1" -> "127.0.0.1"
|
||||
return
|
||||
endPoint.Address.IsIPv4MappedToIPv6
|
||||
? endPoint.Address.MapToIPv4().ToString()
|
||||
: endPoint.Address.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
public static string ToHexString(this ArraySegment<byte> segment) =>
|
||||
BitConverter.ToString(segment.Array, segment.Offset, segment.Count);
|
||||
|
||||
// string.GetHashCode is not guaranteed to be the same on all
|
||||
// machines, but we need one that is the same on all machines.
|
||||
// Uses fnv1a as hash function for more uniform distribution http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
// Tests: https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
|
||||
// NOTE: Do not call this from hot path because it's slow O(N) for long method names.
|
||||
// - As of 2012-02-16 There are 2 design-time callers (weaver) and 1 runtime caller that caches.
|
||||
public static int GetStableHashCode(this string text)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
uint hash = 0x811c9dc5;
|
||||
uint prime = 0x1000193;
|
||||
|
||||
for (int i = 0; i < text.Length; ++i)
|
||||
{
|
||||
byte value = (byte)text[i];
|
||||
hash = hash ^ value;
|
||||
hash *= prime;
|
||||
}
|
||||
|
||||
//UnityEngine.Debug.Log($"Created stable hash {(ushort)hash} for {text}");
|
||||
return (int)hash;
|
||||
}
|
||||
}
|
||||
|
||||
// smaller version of our GetStableHashCode.
|
||||
// careful, this significantly increases chance of collisions.
|
||||
public static ushort GetStableHashCode16(this string text)
|
||||
{
|
||||
// deterministic hash
|
||||
int hash = GetStableHashCode(text);
|
||||
|
||||
// Gets the 32bit fnv1a hash
|
||||
// To get it down to 16bit but still reduce hash collisions we cant just cast it to ushort
|
||||
// Instead we take the highest 16bits of the 32bit hash and fold them with xor into the lower 16bits
|
||||
// This will create a more uniform 16bit hash, the method is described in:
|
||||
// http://www.isthe.com/chongo/tech/comp/fnv/ in section "Changing the FNV hash size - xor-folding"
|
||||
return (ushort)((hash >> 16) ^ hash);
|
||||
}
|
||||
|
||||
// previously in DotnetCompatibility.cs
|
||||
// leftover from the UNET days. supposedly for windows store?
|
||||
public static string GetMethodName(this Delegate func)
|
||||
{
|
||||
#if NETFX_CORE
|
||||
return func.GetMethodInfo().Name;
|
||||
#else
|
||||
return func.Method.Name;
|
||||
#endif
|
||||
}
|
||||
|
||||
// helper function to copy to List<T>
|
||||
// C# only provides CopyTo(T[])
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void CopyTo<T>(this IEnumerable<T> source, List<T> destination)
|
||||
{
|
||||
// foreach allocates. use AddRange.
|
||||
destination.AddRange(source);
|
||||
}
|
||||
|
||||
#if !UNITY_2021_OR_NEWER
|
||||
// Unity 2020 and earlier don't have Queue.TryDequeue which we need for batching.
|
||||
public static bool TryDequeue<T>(this Queue<T> source, out T element)
|
||||
{
|
||||
if (source.Count > 0)
|
||||
{
|
||||
element = source.Dequeue();
|
||||
return true;
|
||||
}
|
||||
|
||||
element = default;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_2021_OR_NEWER
|
||||
// Unity 2020 and earlier don't have ConcurrentQueue.Clear which we need for ThreadedTransport.
|
||||
public static void Clear<T>(this ConcurrentQueue<T> source)
|
||||
{
|
||||
// while count > 0 risks deadlock if other thread write at the same time.
|
||||
// our safest solution is a best-effort approach to clear 'Count' once.
|
||||
int count = source.Count; // get it only once
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
source.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !UNITY_2021_3_OR_NEWER
|
||||
// Some patch versions of Unity 2021.3 and earlier don't have transform.GetPositionAndRotation which we use for performance in some places
|
||||
public static void GetPositionAndRotation(this Transform transform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
position = transform.position;
|
||||
rotation = transform.rotation;
|
||||
}
|
||||
|
||||
public static void SetPositionAndRotation(this Transform transform, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
transform.position = position;
|
||||
transform.rotation = rotation;
|
||||
}
|
||||
|
||||
public static void GetLocalPositionAndRotation(this Transform transform, out Vector3 position, out Quaternion rotation)
|
||||
{
|
||||
position = transform.localPosition;
|
||||
rotation = transform.localRotation;
|
||||
}
|
||||
|
||||
public static void SetLocalPositionAndRotation(this Transform transform, Vector3 position, Quaternion rotation)
|
||||
{
|
||||
transform.localPosition = position;
|
||||
transform.localRotation = rotation;
|
||||
}
|
||||
#endif
|
||||
|
||||
// IPEndPoint address only to pretty string.
|
||||
// useful for to get a connection's address for IP bans etc.
|
||||
public static string PrettyAddress(this IPEndPoint endPoint)
|
||||
{
|
||||
if (endPoint == null) return "";
|
||||
|
||||
// Map to IPv4 if "IsIPv4MappedToIPv6" for readability
|
||||
// "::ffff:127.0.0.1" -> "127.0.0.1"
|
||||
return
|
||||
endPoint.Address.IsIPv4MappedToIPv6
|
||||
? endPoint.Address.MapToIPv4().ToString()
|
||||
: endPoint.Address.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: decf32fd053744d18f35712b7a6f5116
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Extensions.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: decf32fd053744d18f35712b7a6f5116
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Extensions.cs
|
||||
uploadId: 736421
|
||||
|
||||
+773
-773
File diff suppressed because it is too large
Load Diff
@@ -1,10 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70b5947c49174f3c90121edd21ea6b15
|
||||
timeCreated: 1729783714
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Half.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 70b5947c49174f3c90121edd21ea6b15
|
||||
timeCreated: 1729783714
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Half.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
// 'double' precision variants for some of Unity's Mathf functions.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class Mathd
|
||||
{
|
||||
// Unity 2020 doesn't have Math.Clamp yet.
|
||||
/// <summary>Clamps value between 0 and 1 and returns value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp(double value, double min, double max)
|
||||
{
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>Clamps value between 0 and 1 and returns value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp01(double value) => Clamp(value, 0, 1);
|
||||
|
||||
/// <summary>Calculates the linear parameter t that produces the interpolant value within the range [a, b].</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double InverseLerp(double a, double b, double value) =>
|
||||
a != b ? Clamp01((value - a) / (b - a)) : 0;
|
||||
|
||||
/// <summary>Linearly interpolates between a and b by t with no limit to t.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double LerpUnclamped(double a, double b, double t) =>
|
||||
a + (b - a) * t;
|
||||
}
|
||||
}
|
||||
// 'double' precision variants for some of Unity's Mathf functions.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class Mathd
|
||||
{
|
||||
// Unity 2020 doesn't have Math.Clamp yet.
|
||||
/// <summary>Clamps value between 0 and 1 and returns value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp(double value, double min, double max)
|
||||
{
|
||||
if (value < min) return min;
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>Clamps value between 0 and 1 and returns value.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double Clamp01(double value) => Clamp(value, 0, 1);
|
||||
|
||||
/// <summary>Calculates the linear parameter t that produces the interpolant value within the range [a, b].</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double InverseLerp(double a, double b, double value) =>
|
||||
a != b ? Clamp01((value - a) / (b - a)) : 0;
|
||||
|
||||
/// <summary>Linearly interpolates between a and b by t with no limit to t.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double LerpUnclamped(double a, double b, double t) =>
|
||||
a + (b - a) * t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f74084b91c74df2839b426c4a381373
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Mathd.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 5f74084b91c74df2839b426c4a381373
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Mathd.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
// Pool to avoid allocations (from libuv2k)
|
||||
// API consistent with Microsoft's ObjectPool<T>.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public class Pool<T>
|
||||
{
|
||||
// Mirror is single threaded, no need for concurrent collections.
|
||||
// stack increases the chance that a reused writer remains in cache.
|
||||
readonly Stack<T> objects = new Stack<T>();
|
||||
|
||||
// some types might need additional parameters in their constructor, so
|
||||
// we use a Func<T> generator
|
||||
readonly Func<T> objectGenerator;
|
||||
|
||||
public Pool(Func<T> objectGenerator, int initialCapacity)
|
||||
{
|
||||
this.objectGenerator = objectGenerator;
|
||||
|
||||
// allocate an initial pool so we have fewer (if any)
|
||||
// allocations in the first few frames (or seconds).
|
||||
for (int i = 0; i < initialCapacity; ++i)
|
||||
objects.Push(objectGenerator());
|
||||
}
|
||||
|
||||
// take an element from the pool, or create a new one if empty
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Get() => objects.Count > 0 ? objects.Pop() : objectGenerator();
|
||||
|
||||
// return an element to the pool
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Return(T item)
|
||||
{
|
||||
// make sure we can't accidentally insert null values into the pool.
|
||||
// debugging this would be hard since it would only show on get().
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
|
||||
objects.Push(item);
|
||||
}
|
||||
|
||||
// count to see how many objects are in the pool. useful for tests.
|
||||
public int Count => objects.Count;
|
||||
}
|
||||
}
|
||||
// Pool to avoid allocations (from libuv2k)
|
||||
// API consistent with Microsoft's ObjectPool<T>.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public class Pool<T>
|
||||
{
|
||||
// Mirror is single threaded, no need for concurrent collections.
|
||||
// stack increases the chance that a reused writer remains in cache.
|
||||
readonly Stack<T> objects = new Stack<T>();
|
||||
|
||||
// some types might need additional parameters in their constructor, so
|
||||
// we use a Func<T> generator
|
||||
readonly Func<T> objectGenerator;
|
||||
|
||||
public Pool(Func<T> objectGenerator, int initialCapacity)
|
||||
{
|
||||
this.objectGenerator = objectGenerator;
|
||||
|
||||
// allocate an initial pool so we have fewer (if any)
|
||||
// allocations in the first few frames (or seconds).
|
||||
for (int i = 0; i < initialCapacity; ++i)
|
||||
objects.Push(objectGenerator());
|
||||
}
|
||||
|
||||
// take an element from the pool, or create a new one if empty
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Get() => objects.Count > 0 ? objects.Pop() : objectGenerator();
|
||||
|
||||
// return an element to the pool
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Return(T item)
|
||||
{
|
||||
// make sure we can't accidentally insert null values into the pool.
|
||||
// debugging this would be hard since it would only show on get().
|
||||
if (item == null)
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
|
||||
objects.Push(item);
|
||||
}
|
||||
|
||||
// count to see how many objects are in the pool. useful for tests.
|
||||
public int Count => objects.Count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 845bb05fa349344c3811022f4f15dfbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Pool.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 845bb05fa349344c3811022f4f15dfbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Pool.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da033671de7d49e0838223a997c56bf1
|
||||
timeCreated: 1667486850
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Readme.txt
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: da033671de7d49e0838223a997c56bf1
|
||||
timeCreated: 1667486850
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Readme.txt
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
// TimeSample from Mirror II.
|
||||
// simple profiling sample, averaged for display in statistics.
|
||||
// usable in builds without unitiy profiler overhead etc.
|
||||
//
|
||||
// .average may safely be called from main thread while Begin/End is in another.
|
||||
// i.e. worker threads, transport, etc.
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct TimeSample
|
||||
{
|
||||
// UnityEngine.Time isn't thread safe. use stopwatch instead.
|
||||
readonly Stopwatch watch;
|
||||
|
||||
// remember when Begin was called
|
||||
double beginTime;
|
||||
|
||||
// keep accumulating times over the given interval.
|
||||
// (not readonly. we modify its contents.)
|
||||
ExponentialMovingAverage ema;
|
||||
|
||||
// average in seconds.
|
||||
// code often runs in sub-millisecond time. float is more precise.
|
||||
//
|
||||
// set with Interlocked for thread safety.
|
||||
// can be read from main thread while sampling happens in other thread.
|
||||
public double average; // THREAD SAFE
|
||||
|
||||
// average over N begin/end captures
|
||||
public TimeSample(int n)
|
||||
{
|
||||
watch = new Stopwatch();
|
||||
watch.Start();
|
||||
ema = new ExponentialMovingAverage(n);
|
||||
beginTime = 0;
|
||||
average = 0;
|
||||
}
|
||||
|
||||
// begin is called before the code to be sampled
|
||||
public void Begin()
|
||||
{
|
||||
// remember when Begin was called.
|
||||
// keep StopWatch running so we can average over the given interval.
|
||||
beginTime = watch.Elapsed.TotalSeconds;
|
||||
// Debug.Log($"Begin @ {beginTime:F4}");
|
||||
}
|
||||
|
||||
// end is called after the code to be sampled
|
||||
public void End()
|
||||
{
|
||||
// add duration in seconds to accumulated durations
|
||||
double elapsed = watch.Elapsed.TotalSeconds - beginTime;
|
||||
ema.Add(elapsed);
|
||||
|
||||
// expose new average thread safely
|
||||
Interlocked.Exchange(ref average, ema.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TimeSample from Mirror II.
|
||||
// simple profiling sample, averaged for display in statistics.
|
||||
// usable in builds without unitiy profiler overhead etc.
|
||||
//
|
||||
// .average may safely be called from main thread while Begin/End is in another.
|
||||
// i.e. worker threads, transport, etc.
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct TimeSample
|
||||
{
|
||||
// UnityEngine.Time isn't thread safe. use stopwatch instead.
|
||||
readonly Stopwatch watch;
|
||||
|
||||
// remember when Begin was called
|
||||
double beginTime;
|
||||
|
||||
// keep accumulating times over the given interval.
|
||||
// (not readonly. we modify its contents.)
|
||||
ExponentialMovingAverage ema;
|
||||
|
||||
// average in seconds.
|
||||
// code often runs in sub-millisecond time. float is more precise.
|
||||
//
|
||||
// set with Interlocked for thread safety.
|
||||
// can be read from main thread while sampling happens in other thread.
|
||||
public double average; // THREAD SAFE
|
||||
|
||||
// average over N begin/end captures
|
||||
public TimeSample(int n)
|
||||
{
|
||||
watch = new Stopwatch();
|
||||
watch.Start();
|
||||
ema = new ExponentialMovingAverage(n);
|
||||
beginTime = 0;
|
||||
average = 0;
|
||||
}
|
||||
|
||||
// begin is called before the code to be sampled
|
||||
public void Begin()
|
||||
{
|
||||
// remember when Begin was called.
|
||||
// keep StopWatch running so we can average over the given interval.
|
||||
beginTime = watch.Elapsed.TotalSeconds;
|
||||
// Debug.Log($"Begin @ {beginTime:F4}");
|
||||
}
|
||||
|
||||
// end is called after the code to be sampled
|
||||
public void End()
|
||||
{
|
||||
// add duration in seconds to accumulated durations
|
||||
double elapsed = watch.Elapsed.TotalSeconds - beginTime;
|
||||
ema.Add(elapsed);
|
||||
|
||||
// expose new average thread safely
|
||||
Interlocked.Exchange(ref average, ema.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26c32f6429554546a88d800c846c74ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/TimeSample.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 26c32f6429554546a88d800c846c74ed
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/TimeSample.cs
|
||||
uploadId: 736421
|
||||
|
||||
+222
-222
@@ -1,222 +1,222 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
// Handles network messages on client and server
|
||||
public delegate void NetworkMessageDelegate(NetworkConnection conn, NetworkReader reader, int channelId);
|
||||
|
||||
// Handles requests to spawn objects on the client
|
||||
public delegate GameObject SpawnDelegate(Vector3 position, uint assetId);
|
||||
|
||||
public delegate GameObject SpawnHandlerDelegate(SpawnMessage msg);
|
||||
|
||||
// Handles requests to unspawn objects on the client
|
||||
public delegate void UnSpawnDelegate(GameObject spawned);
|
||||
|
||||
// channels are const ints instead of an enum so people can add their own
|
||||
// channels (can't extend an enum otherwise).
|
||||
//
|
||||
// note that Mirror is slowly moving towards quake style networking which
|
||||
// will only require reliable for handshake, and unreliable for the rest.
|
||||
// so eventually we can change this to an Enum and transports shouldn't
|
||||
// add custom channels anymore.
|
||||
public static class Channels
|
||||
{
|
||||
public const int Reliable = 0; // ordered
|
||||
public const int Unreliable = 1; // unordered
|
||||
}
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
// detect headless / dedicated server mode
|
||||
// SystemInfo.graphicsDeviceType is never null in the editor.
|
||||
// UNITY_SERVER works in builds for all Unity versions 2019 LTS and later.
|
||||
// For Unity 2019 / 2020, there is no way to detect Server Build checkbox
|
||||
// state in Build Settings, so they never auto-start headless server / client.
|
||||
// UNITY_SERVER works in the editor in Unity 2021 LTS and later
|
||||
// because that's when Dedicated Server platform was added.
|
||||
// It is intentional for editor play mode to auto-start headless server / client
|
||||
// when Dedicated Server platform is selected in the editor so that editor
|
||||
// acts like a headless build to every extent possible for testing / debugging.
|
||||
public static bool IsHeadless() =>
|
||||
#if UNITY_SERVER
|
||||
true;
|
||||
#else
|
||||
SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null;
|
||||
#endif
|
||||
|
||||
// detect WebGL mode
|
||||
public const bool IsWebGL =
|
||||
#if UNITY_WEBGL
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
// detect Debug mode
|
||||
public const bool IsDebug =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
public static uint GetTrueRandomUInt()
|
||||
{
|
||||
// use Crypto RNG to avoid having time based duplicates
|
||||
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
byte[] bytes = new byte[4];
|
||||
rng.GetBytes(bytes);
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPrefab(GameObject obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.PrefabUtility.IsPartOfPrefabAsset(obj);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// simplified IsSceneObject check from Mirror II
|
||||
public static bool IsSceneObject(NetworkIdentity identity)
|
||||
{
|
||||
// original UNET / Mirror still had the IsPersistent check.
|
||||
// it never fires though. even for Prefabs dragged to the Scene.
|
||||
// (see Scene Objects example scene.)
|
||||
// #if UNITY_EDITOR
|
||||
// if (UnityEditor.EditorUtility.IsPersistent(identity.gameObject))
|
||||
// return false;
|
||||
// #endif
|
||||
|
||||
return identity.gameObject.hideFlags != HideFlags.NotEditable &&
|
||||
identity.gameObject.hideFlags != HideFlags.HideAndDontSave &&
|
||||
identity.sceneId != 0;
|
||||
}
|
||||
|
||||
public static bool IsSceneObjectWithPrefabParent(GameObject gameObject, out GameObject prefab)
|
||||
{
|
||||
prefab = null;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabInstance(gameObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
|
||||
#endif
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Failed to find prefab parent for scene object [name:{gameObject.name}]");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// is a 2D point in screen? (from ummorpg)
|
||||
// (if width = 1024, then indices from 0..1023 are valid (=1024 indices)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPointInScreen(Vector2 point) =>
|
||||
0 <= point.x && point.x < Screen.width &&
|
||||
0 <= point.y && point.y < Screen.height;
|
||||
|
||||
// pretty print bytes as KB/MB/GB/etc. from DOTSNET
|
||||
// long to support > 2GB
|
||||
// divides by floats to return "2.5MB" etc.
|
||||
public static string PrettyBytes(long bytes)
|
||||
{
|
||||
// bytes
|
||||
if (bytes < 1024)
|
||||
return $"{bytes} B";
|
||||
// kilobytes
|
||||
else if (bytes < 1024L * 1024L)
|
||||
return $"{(bytes / 1024f):F2} KB";
|
||||
// megabytes
|
||||
else if (bytes < 1024 * 1024L * 1024L)
|
||||
return $"{(bytes / (1024f * 1024f)):F2} MB";
|
||||
// gigabytes
|
||||
return $"{(bytes / (1024f * 1024f * 1024f)):F2} GB";
|
||||
}
|
||||
|
||||
// pretty print seconds as hours:minutes:seconds(.milliseconds/100)s.
|
||||
// double for long running servers.
|
||||
public static string PrettySeconds(double seconds)
|
||||
{
|
||||
TimeSpan t = TimeSpan.FromSeconds(seconds);
|
||||
string res = "";
|
||||
if (t.Days > 0) res += $"{t.Days}d";
|
||||
if (t.Hours > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Hours}h";
|
||||
if (t.Minutes > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Minutes}m";
|
||||
// 0.5s, 1.5s etc. if any milliseconds. 1s, 2s etc. if any seconds
|
||||
if (t.Milliseconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}.{(t.Milliseconds / 100)}s";
|
||||
else if (t.Seconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}s";
|
||||
// if the string is still empty because the value was '0', then at least
|
||||
// return the seconds instead of returning an empty string
|
||||
return res != "" ? res : "0s";
|
||||
}
|
||||
|
||||
// universal .spawned function
|
||||
public static NetworkIdentity GetSpawnedInServerOrClient(uint netId)
|
||||
{
|
||||
// server / host mode: use the one from server.
|
||||
// host mode has access to all spawned.
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
NetworkServer.spawned.TryGetValue(netId, out NetworkIdentity entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
// client
|
||||
if (NetworkClient.active)
|
||||
{
|
||||
NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// keep a GUI window in screen.
|
||||
// for example. if it's at x=1000 and screen is resized to w=500,
|
||||
// it won't get lost in the invisible area etc.
|
||||
public static Rect KeepInScreen(Rect rect)
|
||||
{
|
||||
// ensure min
|
||||
rect.x = Math.Max(rect.x, 0);
|
||||
rect.y = Math.Max(rect.y, 0);
|
||||
|
||||
// ensure max
|
||||
rect.x = Math.Min(rect.x, Screen.width - rect.width);
|
||||
rect.y = Math.Min(rect.y, Screen.width - rect.height);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
// create local connections pair and connect them
|
||||
public static void CreateLocalConnections(
|
||||
out LocalConnectionToClient connectionToClient,
|
||||
out LocalConnectionToServer connectionToServer)
|
||||
{
|
||||
connectionToServer = new LocalConnectionToServer();
|
||||
connectionToClient = new LocalConnectionToClient();
|
||||
connectionToServer.connectionToClient = connectionToClient;
|
||||
connectionToClient.connectionToServer = connectionToServer;
|
||||
}
|
||||
|
||||
public static bool IsSceneActive(string scene)
|
||||
{
|
||||
Scene activeScene = SceneManager.GetActiveScene();
|
||||
return activeScene.path == scene ||
|
||||
activeScene.name == scene;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
// Handles network messages on client and server
|
||||
public delegate void NetworkMessageDelegate(NetworkConnection conn, NetworkReader reader, int channelId);
|
||||
|
||||
// Handles requests to spawn objects on the client
|
||||
public delegate GameObject SpawnDelegate(Vector3 position, uint assetId);
|
||||
|
||||
public delegate GameObject SpawnHandlerDelegate(SpawnMessage msg);
|
||||
|
||||
// Handles requests to unspawn objects on the client
|
||||
public delegate void UnSpawnDelegate(GameObject spawned);
|
||||
|
||||
// channels are const ints instead of an enum so people can add their own
|
||||
// channels (can't extend an enum otherwise).
|
||||
//
|
||||
// note that Mirror is slowly moving towards quake style networking which
|
||||
// will only require reliable for handshake, and unreliable for the rest.
|
||||
// so eventually we can change this to an Enum and transports shouldn't
|
||||
// add custom channels anymore.
|
||||
public static class Channels
|
||||
{
|
||||
public const int Reliable = 0; // ordered
|
||||
public const int Unreliable = 1; // unordered
|
||||
}
|
||||
|
||||
public static class Utils
|
||||
{
|
||||
// detect headless / dedicated server mode
|
||||
// SystemInfo.graphicsDeviceType is never null in the editor.
|
||||
// UNITY_SERVER works in builds for all Unity versions 2019 LTS and later.
|
||||
// For Unity 2019 / 2020, there is no way to detect Server Build checkbox
|
||||
// state in Build Settings, so they never auto-start headless server / client.
|
||||
// UNITY_SERVER works in the editor in Unity 2021 LTS and later
|
||||
// because that's when Dedicated Server platform was added.
|
||||
// It is intentional for editor play mode to auto-start headless server / client
|
||||
// when Dedicated Server platform is selected in the editor so that editor
|
||||
// acts like a headless build to every extent possible for testing / debugging.
|
||||
public static bool IsHeadless() =>
|
||||
#if UNITY_SERVER
|
||||
true;
|
||||
#else
|
||||
SystemInfo.graphicsDeviceType == GraphicsDeviceType.Null;
|
||||
#endif
|
||||
|
||||
// detect WebGL mode
|
||||
public const bool IsWebGL =
|
||||
#if UNITY_WEBGL
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
// detect Debug mode
|
||||
public const bool IsDebug =
|
||||
#if DEBUG
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
public static uint GetTrueRandomUInt()
|
||||
{
|
||||
// use Crypto RNG to avoid having time based duplicates
|
||||
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
|
||||
{
|
||||
byte[] bytes = new byte[4];
|
||||
rng.GetBytes(bytes);
|
||||
return BitConverter.ToUInt32(bytes, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPrefab(GameObject obj)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
return UnityEditor.PrefabUtility.IsPartOfPrefabAsset(obj);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// simplified IsSceneObject check from Mirror II
|
||||
public static bool IsSceneObject(NetworkIdentity identity)
|
||||
{
|
||||
// original UNET / Mirror still had the IsPersistent check.
|
||||
// it never fires though. even for Prefabs dragged to the Scene.
|
||||
// (see Scene Objects example scene.)
|
||||
// #if UNITY_EDITOR
|
||||
// if (UnityEditor.EditorUtility.IsPersistent(identity.gameObject))
|
||||
// return false;
|
||||
// #endif
|
||||
|
||||
return identity.gameObject.hideFlags != HideFlags.NotEditable &&
|
||||
identity.gameObject.hideFlags != HideFlags.HideAndDontSave &&
|
||||
identity.sceneId != 0;
|
||||
}
|
||||
|
||||
public static bool IsSceneObjectWithPrefabParent(GameObject gameObject, out GameObject prefab)
|
||||
{
|
||||
prefab = null;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.PrefabUtility.IsPartOfPrefabInstance(gameObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
|
||||
#endif
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError($"Failed to find prefab parent for scene object [name:{gameObject.name}]");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// is a 2D point in screen? (from ummorpg)
|
||||
// (if width = 1024, then indices from 0..1023 are valid (=1024 indices)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsPointInScreen(Vector2 point) =>
|
||||
0 <= point.x && point.x < Screen.width &&
|
||||
0 <= point.y && point.y < Screen.height;
|
||||
|
||||
// pretty print bytes as KB/MB/GB/etc. from DOTSNET
|
||||
// long to support > 2GB
|
||||
// divides by floats to return "2.5MB" etc.
|
||||
public static string PrettyBytes(long bytes)
|
||||
{
|
||||
// bytes
|
||||
if (bytes < 1024)
|
||||
return $"{bytes} B";
|
||||
// kilobytes
|
||||
else if (bytes < 1024L * 1024L)
|
||||
return $"{(bytes / 1024f):F2} KB";
|
||||
// megabytes
|
||||
else if (bytes < 1024 * 1024L * 1024L)
|
||||
return $"{(bytes / (1024f * 1024f)):F2} MB";
|
||||
// gigabytes
|
||||
return $"{(bytes / (1024f * 1024f * 1024f)):F2} GB";
|
||||
}
|
||||
|
||||
// pretty print seconds as hours:minutes:seconds(.milliseconds/100)s.
|
||||
// double for long running servers.
|
||||
public static string PrettySeconds(double seconds)
|
||||
{
|
||||
TimeSpan t = TimeSpan.FromSeconds(seconds);
|
||||
string res = "";
|
||||
if (t.Days > 0) res += $"{t.Days}d";
|
||||
if (t.Hours > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Hours}h";
|
||||
if (t.Minutes > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Minutes}m";
|
||||
// 0.5s, 1.5s etc. if any milliseconds. 1s, 2s etc. if any seconds
|
||||
if (t.Milliseconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}.{(t.Milliseconds / 100)}s";
|
||||
else if (t.Seconds > 0) res += $"{(res.Length > 0 ? " " : "")}{t.Seconds}s";
|
||||
// if the string is still empty because the value was '0', then at least
|
||||
// return the seconds instead of returning an empty string
|
||||
return res != "" ? res : "0s";
|
||||
}
|
||||
|
||||
// universal .spawned function
|
||||
public static NetworkIdentity GetSpawnedInServerOrClient(uint netId)
|
||||
{
|
||||
// server / host mode: use the one from server.
|
||||
// host mode has access to all spawned.
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
NetworkServer.spawned.TryGetValue(netId, out NetworkIdentity entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
// client
|
||||
if (NetworkClient.active)
|
||||
{
|
||||
NetworkClient.spawned.TryGetValue(netId, out NetworkIdentity entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// keep a GUI window in screen.
|
||||
// for example. if it's at x=1000 and screen is resized to w=500,
|
||||
// it won't get lost in the invisible area etc.
|
||||
public static Rect KeepInScreen(Rect rect)
|
||||
{
|
||||
// ensure min
|
||||
rect.x = Math.Max(rect.x, 0);
|
||||
rect.y = Math.Max(rect.y, 0);
|
||||
|
||||
// ensure max
|
||||
rect.x = Math.Min(rect.x, Screen.width - rect.width);
|
||||
rect.y = Math.Min(rect.y, Screen.width - rect.height);
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
// create local connections pair and connect them
|
||||
public static void CreateLocalConnections(
|
||||
out LocalConnectionToClient connectionToClient,
|
||||
out LocalConnectionToServer connectionToServer)
|
||||
{
|
||||
connectionToServer = new LocalConnectionToServer();
|
||||
connectionToClient = new LocalConnectionToClient();
|
||||
connectionToServer.connectionToClient = connectionToClient;
|
||||
connectionToClient.connectionToServer = connectionToServer;
|
||||
}
|
||||
|
||||
public static bool IsSceneActive(string scene)
|
||||
{
|
||||
Scene activeScene = SceneManager.GetActiveScene();
|
||||
return activeScene.path == scene ||
|
||||
activeScene.name == scene;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b530ce39098b54374a29ad308c8e4554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Utils.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: b530ce39098b54374a29ad308c8e4554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Utils.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,125 +1,125 @@
|
||||
#pragma warning disable CS0659 // 'Vector3Long' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||
#pragma warning disable CS0661 // 'Vector3Long' defines operator == or operator != but does not override Object.GetHashCode()
|
||||
|
||||
// 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
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
default: 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(Vector4)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object other) =>
|
||||
other is Vector3Long 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
|
||||
}
|
||||
}
|
||||
#pragma warning disable CS0659 // 'Vector3Long' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||
#pragma warning disable CS0661 // 'Vector3Long' defines operator == or operator != but does not override Object.GetHashCode()
|
||||
|
||||
// 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
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
default: 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(Vector4)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object other) =>
|
||||
other is Vector3Long 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18efa4e349254185ad257401dd24628b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Vector3Long.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 18efa4e349254185ad257401dd24628b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 7453abfe9e8b2c04a8a47eb536fe21eb, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Vector3Long.cs
|
||||
uploadId: 736421
|
||||
|
||||
@@ -1,126 +1,126 @@
|
||||
#pragma warning disable CS0659 // 'Vector4Long' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||
#pragma warning disable CS0661 // 'Vector4Long' defines operator == or operator != but does not override Object.GetHashCode()
|
||||
|
||||
// Vector4Long by mischa (based on game engine project)
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct Vector4Long
|
||||
{
|
||||
public long x;
|
||||
public long y;
|
||||
public long z;
|
||||
public long w;
|
||||
|
||||
public static readonly Vector4Long zero = new Vector4Long(0, 0, 0, 0);
|
||||
public static readonly Vector4Long one = new Vector4Long(1, 1, 1, 1);
|
||||
|
||||
// constructor /////////////////////////////////////////////////////////
|
||||
public Vector4Long(long x, long y, long z, long w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
// operators ///////////////////////////////////////////////////////////
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator +(Vector4Long a, Vector4Long b) =>
|
||||
new Vector4Long(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator -(Vector4Long a, Vector4Long b) =>
|
||||
new Vector4Long(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator -(Vector4Long v) =>
|
||||
new Vector4Long(-v.x, -v.y, -v.z, -v.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator *(Vector4Long a, long n) =>
|
||||
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator *(long n, Vector4Long a) =>
|
||||
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||
|
||||
// == returns true if approximately equal (with epsilon).
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector4Long a, Vector4Long b) =>
|
||||
a.x == b.x &&
|
||||
a.y == b.y &&
|
||||
a.z == b.z &&
|
||||
a.w == b.w;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Vector4Long a, Vector4Long b) => !(a == b);
|
||||
|
||||
// NO IMPLICIT System.Numerics.Vector4Long 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
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
case 3: return w;
|
||||
default: throw new IndexOutOfRangeException($"Vector4Long[{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;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default: throw new IndexOutOfRangeException($"Vector4Long[{index}] out of range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instance functions //////////////////////////////////////////////////
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override string ToString() => $"({x} {y} {z} {w})";
|
||||
|
||||
// equality ////////////////////////////////////////////////////////////
|
||||
// implement Equals & HashCode explicitly for performance.
|
||||
// calling .Equals (instead of "==") checks for exact equality.
|
||||
// (API compatibility)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(Vector4Long other) =>
|
||||
x == other.x && y == other.y && z == other.z && w == other.w;
|
||||
|
||||
// Equals(object) can reuse Equals(Vector4)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object other) =>
|
||||
other is Vector4Long 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, w);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#pragma warning disable CS0659 // 'Vector4Long' overrides Object.Equals(object o) but does not override Object.GetHashCode()
|
||||
#pragma warning disable CS0661 // 'Vector4Long' defines operator == or operator != but does not override Object.GetHashCode()
|
||||
|
||||
// Vector4Long by mischa (based on game engine project)
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public struct Vector4Long
|
||||
{
|
||||
public long x;
|
||||
public long y;
|
||||
public long z;
|
||||
public long w;
|
||||
|
||||
public static readonly Vector4Long zero = new Vector4Long(0, 0, 0, 0);
|
||||
public static readonly Vector4Long one = new Vector4Long(1, 1, 1, 1);
|
||||
|
||||
// constructor /////////////////////////////////////////////////////////
|
||||
public Vector4Long(long x, long y, long z, long w)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.w = w;
|
||||
}
|
||||
|
||||
// operators ///////////////////////////////////////////////////////////
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator +(Vector4Long a, Vector4Long b) =>
|
||||
new Vector4Long(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator -(Vector4Long a, Vector4Long b) =>
|
||||
new Vector4Long(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator -(Vector4Long v) =>
|
||||
new Vector4Long(-v.x, -v.y, -v.z, -v.w);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator *(Vector4Long a, long n) =>
|
||||
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Vector4Long operator *(long n, Vector4Long a) =>
|
||||
new Vector4Long(a.x * n, a.y * n, a.z * n, a.w * n);
|
||||
|
||||
// == returns true if approximately equal (with epsilon).
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Vector4Long a, Vector4Long b) =>
|
||||
a.x == b.x &&
|
||||
a.y == b.y &&
|
||||
a.z == b.z &&
|
||||
a.w == b.w;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Vector4Long a, Vector4Long b) => !(a == b);
|
||||
|
||||
// NO IMPLICIT System.Numerics.Vector4Long 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
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: return x;
|
||||
case 1: return y;
|
||||
case 2: return z;
|
||||
case 3: return w;
|
||||
default: throw new IndexOutOfRangeException($"Vector4Long[{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;
|
||||
case 3:
|
||||
w = value;
|
||||
break;
|
||||
default: throw new IndexOutOfRangeException($"Vector4Long[{index}] out of range.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instance functions //////////////////////////////////////////////////
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override string ToString() => $"({x} {y} {z} {w})";
|
||||
|
||||
// equality ////////////////////////////////////////////////////////////
|
||||
// implement Equals & HashCode explicitly for performance.
|
||||
// calling .Equals (instead of "==") checks for exact equality.
|
||||
// (API compatibility)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(Vector4Long other) =>
|
||||
x == other.x && y == other.y && z == other.z && w == other.w;
|
||||
|
||||
// Equals(object) can reuse Equals(Vector4)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object other) =>
|
||||
other is Vector4Long 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, w);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f69bf6b6d73476ab3f31dd635bb6497
|
||||
timeCreated: 1726777293
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Vector4Long.cs
|
||||
uploadId: 736421
|
||||
fileFormatVersion: 2
|
||||
guid: 9f69bf6b6d73476ab3f31dd635bb6497
|
||||
timeCreated: 1726777293
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 129321
|
||||
packageName: Mirror
|
||||
packageVersion: 96.0.1
|
||||
assetPath: Assets/Mirror/Core/Tools/Vector4Long.cs
|
||||
uploadId: 736421
|
||||
|
||||
Reference in New Issue
Block a user