Merged TestNT2-WebGL

This commit is contained in:
MrGadget1024 2023-03-25 10:21:49 -04:00
commit 186190e2f8
67 changed files with 2592 additions and 6942 deletions

View File

@ -13,8 +13,8 @@ jobs:
unityVersion:
- 2019.4.40f1
- 2020.3.46f1
- 2021.3.20f1
- 2022.2.10f1
- 2021.3.21f1
- 2022.2.12f1
steps:
- name: Checkout repository

View File

@ -14,27 +14,6 @@ public class NetworkDiscovery : NetworkDiscoveryBase<ServerRequest, ServerRespon
{
#region Server
public long ServerId { get; private set; }
[Tooltip("Transport to be advertised during discovery")]
public Transport transport;
[Tooltip("Invoked when a server is found")]
public ServerFoundUnityEvent OnServerFound;
public override void Start()
{
ServerId = RandomLong();
// active transport gets initialized in awake
// so make sure we set it here in Start() (after awakes)
// Or just let the user assign it in the inspector
if (transport == null)
transport = Transport.active;
base.Start();
}
/// <summary>
/// Process the request from a client
/// </summary>
@ -68,9 +47,11 @@ protected override ServerResponse ProcessRequest(ServerRequest request, IPEndPoi
throw;
}
}
#endregion
#region Client
/// <summary>
/// Create a message that will be broadcasted on the network to discover servers
/// </summary>
@ -106,6 +87,7 @@ protected override void ProcessResponse(ServerResponse response, IPEndPoint endp
OnServerFound.Invoke(response);
}
#endregion
}
}

View File

@ -23,33 +23,46 @@ public abstract class NetworkDiscoveryBase<Request, Response> : MonoBehaviour
{
public static bool SupportedOnThisPlatform { get { return Application.platform != RuntimePlatform.WebGLPlayer; } }
// each game should have a random unique handshake, this way you can tell if this is the same game or not
[HideInInspector]
public long secretHandshake;
[SerializeField]
[Tooltip("If true, broadcasts a discovery request every ActiveDiscoveryInterval seconds")]
public bool enableActiveDiscovery = true;
// broadcast address needs to be configurable on iOS:
// https://github.com/vis2k/Mirror/pull/3255
[Tooltip("iOS may require LAN IP address here (e.g. 192.168.x.x), otherwise leave blank.")]
public string BroadcastAddress = "";
[SerializeField]
[Tooltip("The UDP port the server will listen for multi-cast messages")]
protected int serverBroadcastListenPort = 47777;
[SerializeField]
[Tooltip("If true, broadcasts a discovery request every ActiveDiscoveryInterval seconds")]
public bool enableActiveDiscovery = true;
[SerializeField]
[Tooltip("Time in seconds between multi-cast messages")]
[Range(1, 60)]
float ActiveDiscoveryInterval = 3;
// broadcast address needs to be configurable on iOS:
// https://github.com/vis2k/Mirror/pull/3255
public string BroadcastAddress = "";
[Tooltip("Transport to be advertised during discovery")]
public Transport transport;
[Tooltip("Invoked when a server is found")]
public ServerFoundUnityEvent OnServerFound;
// Each game should have a random unique handshake,
// this way you can tell if this is the same game or not
[HideInInspector]
public long secretHandshake;
public long ServerId { get; private set; }
protected UdpClient serverUdpClient;
protected UdpClient clientUdpClient;
#if UNITY_EDITOR
void OnValidate()
public virtual void OnValidate()
{
if (transport == null)
transport = GetComponent<Transport>();
if (secretHandshake == 0)
{
secretHandshake = RandomLong();
@ -58,24 +71,32 @@ void OnValidate()
}
#endif
public static long RandomLong()
{
int value1 = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
int value2 = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
return value1 + ((long)value2 << 32);
}
/// <summary>
/// virtual so that inheriting classes' Start() can call base.Start() too
/// </summary>
public virtual void Start()
{
ServerId = RandomLong();
// active transport gets initialized in Awake
// so make sure we set it here in Start() after Awake
// Or just let the user assign it in the inspector
if (transport == null)
transport = Transport.active;
// Server mode? then start advertising
#if UNITY_SERVER
AdvertiseServer();
#endif
}
public static long RandomLong()
{
int value1 = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
int value2 = UnityEngine.Random.Range(int.MinValue, int.MaxValue);
return value1 + ((long)value2 << 32);
}
// Ensure the ports are cleared no matter when Game/Unity UI exits
void OnApplicationQuit()
{
@ -166,9 +187,7 @@ public async Task ServerListenAsync()
// socket has been closed
break;
}
catch (Exception)
{
}
catch (Exception) {}
}
}
@ -247,6 +266,7 @@ protected virtual void ProcessClientRequest(Request request, IPEndPoint endpoint
AndroidJavaObject multicastLock;
bool hasMulticastLock;
#endif
void BeginMulticastLock()
{
#if UNITY_ANDROID

View File

@ -39,17 +39,22 @@ public override void OnSpawned(NetworkIdentity identity)
// Match ID could have been set in NetworkBehaviour::OnStartServer on this object.
// Since that's after OnCheckObserver is called it would be missed, so force Rebuild here.
RebuildMatchObservers(networkMatchId);
// Add the current match to dirtyMatches for Update to rebuild it.
dirtyMatches.Add(networkMatchId);
}
[ServerCallback]
public override void OnDestroyed(NetworkIdentity identity)
{
// Don't RebuildSceneObservers here - that will happen in Update.
// Multiple objects could be destroyed in same frame and we don't
// want to rebuild for each one...let Update do it once.
// We must add the current match to dirtyMatches for Update to rebuild it.
if (lastObjectMatch.TryGetValue(identity, out Guid currentMatch))
{
lastObjectMatch.Remove(identity);
if (currentMatch != Guid.Empty && matchObjects.TryGetValue(currentMatch, out HashSet<NetworkIdentity> objects) && objects.Remove(identity))
RebuildMatchObservers(currentMatch);
dirtyMatches.Add(currentMatch);
}
}

View File

@ -35,11 +35,15 @@ public override void OnSpawned(NetworkIdentity identity)
[ServerCallback]
public override void OnDestroyed(NetworkIdentity identity)
{
// Don't RebuildSceneObservers here - that will happen in Update.
// Multiple objects could be destroyed in same frame and we don't
// want to rebuild for each one...let Update do it once.
// We must add the current scene to dirtyScenes for Update to rebuild it.
if (lastObjectScene.TryGetValue(identity, out Scene currentScene))
{
lastObjectScene.Remove(identity);
if (sceneObjects.TryGetValue(currentScene, out HashSet<NetworkIdentity> objects) && objects.Remove(identity))
RebuildSceneObservers(currentScene);
dirtyScenes.Add(currentScene);
}
}

View File

@ -35,17 +35,22 @@ public override void OnSpawned(NetworkIdentity identity)
// Team ID could have been set in NetworkBehaviour::OnStartServer on this object.
// Since that's after OnCheckObserver is called it would be missed, so force Rebuild here.
RebuildTeamObservers(networkTeamId);
// Add the current team to dirtyTeams for Update to rebuild it.
dirtyTeams.Add(networkTeamId);
}
[ServerCallback]
public override void OnDestroyed(NetworkIdentity identity)
{
// Don't RebuildSceneObservers here - that will happen in Update.
// Multiple objects could be destroyed in same frame and we don't
// want to rebuild for each one...let Update do it once.
// We must add the current team to dirtyTeams for Update to rebuild it.
if (lastObjectTeam.TryGetValue(identity, out string currentTeam))
{
lastObjectTeam.Remove(identity);
if (!string.IsNullOrWhiteSpace(currentTeam) && teamObjects.TryGetValue(currentTeam, out HashSet<NetworkIdentity> objects) && objects.Remove(identity))
RebuildTeamObservers(currentTeam);
dirtyTeams.Add(currentTeam);
}
}

View File

@ -11,9 +11,18 @@ public class NetworkTransformReliable : NetworkTransformBase
[Header("Sync Only If Changed")]
[Tooltip("When true, changes are not sent unless greater than sensitivity values below.")]
public bool onlySyncOnChange = true;
float onlySyncOnChangeInterval => onlySyncOnChangeCorrectionMultiplier * sendIntervalMultiplier;
uint sendIntervalCounter = 0;
double lastSendIntervalTime = double.MinValue;
[Tooltip("If we only sync on change, then we need to correct old snapshots if more time than sendInterval * multiplier has elapsed.\n\nOtherwise the first move will always start interpolating from the last move sequence's time, which will make it stutter when starting every time.")]
public float onlySyncOnChangeCorrectionMultiplier = 2;
[Header("Send Interval Multiplier")]
[Tooltip("Check/Sync every multiple of Network Manager send interval (= 1 / NM Send Rate), instead of every send interval.")]
[Range(1, 120)]
public uint sendIntervalMultiplier = 3;
[Header("Rotation")]
[Tooltip("Sensitivity of changes needed before an updated state is sent over the network")]
public float rotationSensitivity = 0.01f;
@ -33,6 +42,10 @@ public class NetworkTransformReliable : NetworkTransformBase
[Range(0.00_01f, 1f)] // disallow 0 division. 1mm to 1m precision is enough range.
public float scalePrecision = 0.01f; // 1 cm
[Header("Snapshot Interpolation")]
[Tooltip("Add a small timeline offset to account for decoupled arrival of NetworkTime and NetworkTransform snapshots.\nfixes: https://github.com/MirrorNetworking/Mirror/issues/3427")]
public bool timelineOffset = false;
// delta compression needs to remember 'last' to compress against
protected Vector3Long lastSerializedPosition = Vector3Long.zero;
protected Vector3Long lastDeserializedPosition = Vector3Long.zero;
@ -43,8 +56,9 @@ public class NetworkTransformReliable : NetworkTransformBase
// Used to store last sent snapshots
protected TransformSnapshot last;
protected int lastClientCount = 1;
// update //////////////////////////////////////////////////////////////
// Update applies interpolation.
void Update()
{
// if server then always sync to others.
@ -54,18 +68,19 @@ void Update()
else if (isClient) UpdateClient();
}
// LateUpdate sets dirty.
// movement scripts may change positions in Update.
// use LateUpdate to ensure changes are detected in the same frame.
// otherwise this may run before user update, delaying detection until next frame.
// this would cause visible jitter.
void LateUpdate()
{
// set dirty to trigger OnSerialize. either always, or only if changed.
if (isServer || (IsClientWithAuthority && NetworkClient.ready)) // is NetworkClient.ready even needed?
// It has to be checked in LateUpdate() for onlySyncOnChange to avoid
// the possibility of Update() running first before the object's movement
// script's Update(), which then causes NT to send every alternate frame
// instead.
if (isServer || (IsClientWithAuthority && NetworkClient.ready))
{
if (!onlySyncOnChange || Changed(Construct()))
if (sendIntervalCounter == sendIntervalMultiplier && (!onlySyncOnChange || Changed(Construct())))
SetDirty();
CheckLastSendTime();
}
}
@ -122,6 +137,19 @@ protected virtual void UpdateClient()
TransformSnapshot computed = TransformSnapshot.Interpolate(from, to, t);
Apply(computed, to);
}
lastClientCount = clientSnapshots.Count;
}
}
protected virtual void CheckLastSendTime()
{
// timeAsDouble not available in older Unity versions.
if (AccurateInterval.Elapsed(NetworkTime.localTime, NetworkServer.sendInterval, ref lastSendIntervalTime))
{
if (sendIntervalCounter == sendIntervalMultiplier)
sendIntervalCounter = 0;
sendIntervalCounter++;
}
}
@ -169,6 +197,16 @@ public override void OnSerialize(NetworkWriter writer, bool initialState)
// initial
if (initialState)
{
// If there is a last serialized snapshot, we use it.
// This prevents the new client getting a snapshot that is different
// from what the older clients last got. If this happens, and on the next
// regular serialisation the delta compression will get wrong values.
// Notes:
// 1. Interestingly only the older clients have it wrong, because at the end
// of this function, last = snapshot which is the initial state's snapshot
// 2. Regular NTR gets by this bug because it sends every frame anyway so initialstate
// snapshot constructed would have been the same as the last anyway.
if (last.remoteTime > 0) snapshot = last;
if (syncPosition) writer.WriteVector3(snapshot.position);
if (syncRotation)
{
@ -205,9 +243,6 @@ public override void OnSerialize(NetworkWriter writer, bool initialState)
Compression.ScaleToLong(snapshot.scale, scalePrecision, out Vector3Long quantized);
DeltaCompression.Compress(writer, lastSerializedScale, quantized);
}
// int written = writer.Position - before;
// Debug.Log($"{name} compressed to {written} bytes");
}
// save serialized as 'last' for next delta compression
@ -291,14 +326,19 @@ protected virtual void OnClientToServerSync(Vector3? position, Quaternion? rotat
serverSnapshots,
connectionToClient.remoteTimeStamp,
NetworkTime.localTime, // arrival remote timestamp. NOT remote timeline.
NetworkServer.sendInterval, // Unity 2019 doesn't have timeAsDouble yet
NetworkServer.sendInterval * sendIntervalMultiplier, // Unity 2019 doesn't have timeAsDouble yet
target.localPosition,
target.localRotation,
target.localScale);
// Debug.Log($"{name}: corrected history on server to fix initial stutter after not sending for a while.");
}
AddSnapshot(serverSnapshots, connectionToClient.remoteTimeStamp + NetworkServer.sendInterval, position, rotation, scale);
// add a small timeline offset to account for decoupled arrival of
// NetworkTime and NetworkTransform snapshots.
// needs to be sendInterval. half sendInterval doesn't solve it.
// https://github.com/MirrorNetworking/Mirror/issues/3427
// remove this after LocalWorldState.
double offset = timelineOffset ? NetworkServer.sendInterval : 0;
AddSnapshot(serverSnapshots, connectionToClient.remoteTimeStamp + offset, position, rotation, scale);
}
// server broadcasts sync message to all clients
@ -309,20 +349,25 @@ protected virtual void OnServerToClientSync(Vector3? position, Quaternion? rotat
// 'only sync on change' needs a correction on every new move sequence.
if (onlySyncOnChange &&
NeedsCorrection(clientSnapshots, NetworkClient.connection.remoteTimeStamp, NetworkClient.sendInterval, onlySyncOnChangeCorrectionMultiplier))
NeedsCorrection(clientSnapshots, NetworkClient.connection.remoteTimeStamp, NetworkClient.sendInterval * sendIntervalMultiplier, onlySyncOnChangeInterval))
{
RewriteHistory(
clientSnapshots,
NetworkClient.connection.remoteTimeStamp, // arrival remote timestamp. NOT remote timeline.
NetworkTime.localTime, // Unity 2019 doesn't have timeAsDouble yet
NetworkClient.sendInterval,
NetworkClient.sendInterval * sendIntervalMultiplier,
target.localPosition,
target.localRotation,
target.localScale);
// Debug.Log($"{name}: corrected history on client to fix initial stutter after not sending for a while.");
}
AddSnapshot(clientSnapshots, NetworkClient.connection.remoteTimeStamp + NetworkClient.sendInterval, position, rotation, scale);
// add a small timeline offset to account for decoupled arrival of
// NetworkTime and NetworkTransform snapshots.
// needs to be sendInterval. half sendInterval doesn't solve it.
// https://github.com/MirrorNetworking/Mirror/issues/3427
// remove this after LocalWorldState.
double offset = timelineOffset ? NetworkServer.sendInterval : 0;
AddSnapshot(clientSnapshots, NetworkClient.connection.remoteTimeStamp + offset, position, rotation, scale);
}
// only sync on change /////////////////////////////////////////////////

View File

@ -9,11 +9,12 @@ namespace Mirror
[HelpURL("https://mirror-networking.gitbook.io/docs/guides/interest-management")]
public abstract class InterestManagementBase : MonoBehaviour
{
// Awake configures InterestManagementBase in NetworkServer/Client
// Configures InterestManagementBase in NetworkServer/Client
// Do NOT check for active server or client here.
// Awake must always set the static aoi references.
// make sure to call base.Awake when overwriting!
protected virtual void Awake()
// OnEnable must always set the static aoi references.
// make sure to call base.OnEnable when overwriting!
// Previously used Awake()
protected virtual void OnEnable()
{
if (NetworkServer.aoi == null)
{

View File

@ -36,9 +36,14 @@ public abstract class NetworkBehaviour : MonoBehaviour
/// <summary>sync interval for OnSerialize (in seconds)</summary>
// hidden because NetworkBehaviourInspector shows it only if has OnSerialize.
// [0,2] should be enough. anything >2s is too laggy anyway.
//
// NetworkServer & NetworkClient broadcast() are behind a sendInterval timer now.
// it makes sense to keep every component's syncInterval setting at '0' by default.
// otherwise, the overlapping timers could introduce unexpected latency.
// careful: default of '0.1' may
[Tooltip("Time in seconds until next change is synchronized to the client. '0' means send immediately if changed. '0.5' means only send changes every 500ms.\n(This is for state synchronization like SyncVars, SyncLists, OnSerialize. Not for Cmds, Rpcs, etc.)")]
[Range(0, 2)]
[HideInInspector] public float syncInterval = 0.1f;
[HideInInspector] public float syncInterval = 0;
internal double lastSyncTime;
/// <summary>True if this object is on the server and has been spawned.</summary>

View File

@ -1692,7 +1692,7 @@ public static void OnGUI()
// only if in world
if (!ready) return;
GUILayout.BeginArea(new Rect(10, 5, 500, 50));
GUILayout.BeginArea(new Rect(10, 5, 800, 50));
GUILayout.BeginHorizontal("Box");
GUILayout.Label("Snapshot Interp.:");
@ -1702,8 +1702,11 @@ public static void OnGUI()
else GUI.color = Color.white;
GUILayout.Box($"timeline: {localTimeline:F2}");
GUILayout.Box($"buffer: {snapshots.Count}");
GUILayout.Box($"DriftEMA: {NetworkClient.driftEma.Value:F2}");
GUILayout.Box($"DelTimeEMA: {NetworkClient.deliveryTimeEma.Value:F2}");
GUILayout.Box($"timescale: {localTimescale:F2}");
GUILayout.Box($"BTM: {snapshotSettings.bufferTimeMultiplier:F2}");
GUILayout.Box($"RTT: {NetworkTime.rtt * 1000:000}");
GUILayout.EndHorizontal();
GUILayout.EndArea();

View File

@ -43,7 +43,7 @@ public static partial class NetworkClient
// catchup / slowdown adjustments are applied to timescale,
// to be adjusted in every update instead of when receiving messages.
internal static double localTimescale = 1;
public static double localTimescale = 1;
// catchup /////////////////////////////////////////////////////////////

View File

@ -540,14 +540,6 @@ void FinishStartHost()
public void StopHost()
{
OnStopHost();
// calling OnTransportDisconnected was needed to fix
// https://github.com/vis2k/Mirror/issues/1515
// so that the host client receives a DisconnectMessage
// TODO reevaluate if this is still needed after all the disconnect
// fixes, and try to put this into LocalConnection.Disconnect!
NetworkServer.OnTransportDisconnected(NetworkConnection.LocalConnectionId);
StopClient();
StopServer();
}
@ -600,6 +592,12 @@ public void StopClient()
if (mode == NetworkManagerMode.Offline)
return;
// For Host client, call OnServerDisconnect before NetworkClient.Disconnect
// because we need NetworkServer.localConnection to not be null
// NetworkClient.Disconnect will set it null.
if (mode == NetworkManagerMode.Host)
OnServerDisconnect(NetworkServer.localConnection);
// ask client -> transport to disconnect.
// handle voluntary and involuntary disconnects in OnClientDisconnect.
//

View File

@ -129,14 +129,17 @@ public static ArraySegment<byte> ReadBytesAndSizeSegment(this NetworkReader read
public static Quaternion ReadQuaternion(this NetworkReader reader) => reader.ReadBlittable<Quaternion>();
public static Quaternion? ReadQuaternionNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Quaternion>();
public static Rect ReadRect(this NetworkReader reader) => reader.ReadBlittable<Rect>();
public static Rect? ReadRectNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Rect>();
// Rect is a struct with properties instead of fields
public static Rect ReadRect(this NetworkReader reader) => new Rect(reader.ReadVector2(), reader.ReadVector2());
public static Rect? ReadRectNullable(this NetworkReader reader) => reader.ReadBool() ? ReadRect(reader) : default(Rect?);
public static Plane ReadPlane(this NetworkReader reader) => reader.ReadBlittable<Plane>();
public static Plane? ReadPlaneNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Plane>();
// Plane is a struct with properties instead of fields
public static Plane ReadPlane(this NetworkReader reader) => new Plane(reader.ReadVector3(), reader.ReadFloat());
public static Plane? ReadPlaneNullable(this NetworkReader reader) => reader.ReadBool() ? ReadPlane(reader) : default(Plane?);
public static Ray ReadRay(this NetworkReader reader) => reader.ReadBlittable<Ray>();
public static Ray? ReadRayNullable(this NetworkReader reader) => reader.ReadBlittableNullable<Ray>();
// Ray is a struct with properties instead of fields
public static Ray ReadRay(this NetworkReader reader) => new Ray(reader.ReadVector3(), reader.ReadVector3());
public static Ray? ReadRayNullable(this NetworkReader reader) => reader.ReadBool() ? ReadRay(reader) : default(Ray?);
public static Matrix4x4 ReadMatrix4x4(this NetworkReader reader) => reader.ReadBlittable<Matrix4x4>();
public static Matrix4x4? ReadMatrix4x4Nullable(this NetworkReader reader) => reader.ReadBlittableNullable<Matrix4x4>();
@ -335,11 +338,7 @@ public static Sprite ReadSprite(this NetworkReader reader)
return Sprite.Create(texture, reader.ReadRect(), reader.ReadVector2());
}
public static DateTime ReadDateTime(this NetworkReader reader)
{
return DateTime.FromOADate(reader.ReadDouble());
}
public static DateTime ReadDateTime(this NetworkReader reader) => DateTime.FromOADate(reader.ReadDouble());
public static DateTime? ReadDateTimeNullable(this NetworkReader reader) => reader.ReadBool() ? ReadDateTime(reader) : default(DateTime?);
}
}

View File

@ -166,14 +166,44 @@ public static void WriteArraySegment<T>(this NetworkWriter writer, ArraySegment<
public static void WriteQuaternion(this NetworkWriter writer, Quaternion value) => writer.WriteBlittable(value);
public static void WriteQuaternionNullable(this NetworkWriter writer, Quaternion? value) => writer.WriteBlittableNullable(value);
public static void WriteRect(this NetworkWriter writer, Rect value) => writer.WriteBlittable(value);
public static void WriteRectNullable(this NetworkWriter writer, Rect? value) => writer.WriteBlittableNullable(value);
// Rect is a struct with properties instead of fields
public static void WriteRect(this NetworkWriter writer, Rect value)
{
writer.WriteVector2(value.position);
writer.WriteVector2(value.size);
}
public static void WriteRectNullable(this NetworkWriter writer, Rect? value)
{
writer.WriteBool(value.HasValue);
if (value.HasValue)
writer.WriteRect(value.Value);
}
public static void WritePlane(this NetworkWriter writer, Plane value) => writer.WriteBlittable(value);
public static void WritePlaneNullable(this NetworkWriter writer, Plane? value) => writer.WriteBlittableNullable(value);
// Plane is a struct with properties instead of fields
public static void WritePlane(this NetworkWriter writer, Plane value)
{
writer.WriteVector3(value.normal);
writer.WriteFloat(value.distance);
}
public static void WritePlaneNullable(this NetworkWriter writer, Plane? value)
{
writer.WriteBool(value.HasValue);
if (value.HasValue)
writer.WritePlane(value.Value);
}
public static void WriteRay(this NetworkWriter writer, Ray value) => writer.WriteBlittable(value);
public static void WriteRayNullable(this NetworkWriter writer, Ray? value) => writer.WriteBlittableNullable(value);
// Ray is a struct with properties instead of fields
public static void WriteRay(this NetworkWriter writer, Ray value)
{
writer.WriteVector3(value.origin);
writer.WriteVector3(value.direction);
}
public static void WriteRayNullable(this NetworkWriter writer, Ray? value)
{
writer.WriteBool(value.HasValue);
if (value.HasValue)
writer.WriteRay(value.Value);
}
public static void WriteMatrix4x4(this NetworkWriter writer, Matrix4x4 value) => writer.WriteBlittable(value);
public static void WriteMatrix4x4Nullable(this NetworkWriter writer, Matrix4x4? value) => writer.WriteBlittableNullable(value);

View File

@ -127,8 +127,8 @@ public static double TimelineClamp(
// we define a boundary of 'bufferTime' around the target time.
// this is where catchup / slowdown will happen.
// outside of the area, we clamp.
double lowerBound = targetTime - bufferTime;
double upperBound = targetTime + bufferTime;
double lowerBound = targetTime - bufferTime; // how far behind we can get
double upperBound = targetTime + bufferTime; // how far ahead we can get
return Mathd.Clamp(localTimeline, lowerBound, upperBound);
}

View File

@ -28,11 +28,11 @@ public class SnapshotInterpolationSettings
[Tooltip("Local timeline acceleration in % while catching up.")]
[Range(0, 1)]
public double catchupSpeed = 0.01f; // 1%
public double catchupSpeed = 0.02f; // see snap interp demo. 1% is too slow.
[Tooltip("Local timeline slowdown in % while slowing down.")]
[Range(0, 1)]
public double slowdownSpeed = 0.01f; // 1%
public double slowdownSpeed = 0.04f; // slow down a little faster so we don't encounter empty buffer (= jitter)
[Tooltip("Catchup/Slowdown is adjusted over n-second exponential moving average.")]
public int driftEmaDuration = 1; // shouldn't need to modify this, but expose it anyway

View File

@ -368,8 +368,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1
@ -438,9 +438,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 363a8867bb9c7b845a73233566df8c1e, type: 3}
m_Name:
m_EditorClassIdentifier:
speed: 1
fadeImage: {fileID: 1040404845}
fadeColor: {r: 0, g: 0, b: 0, a: 1}
stepRate: 2
--- !u!114 &1300359892
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -7,20 +7,39 @@ namespace Mirror.Examples.AdditiveLevels
public class FadeInOut : MonoBehaviour
{
// set these in the inspector
[Range(1, 100), Tooltip("Speed of fade in / out: lower is slower")]
public byte speed = 1;
[Tooltip("Reference to Image component on child panel")]
public Image fadeImage;
[Tooltip("Color to use during scene transition")]
public Color fadeColor = Color.black;
WaitForSeconds waitForSeconds;
[Range(1, 100), Tooltip("Rate of fade in / out: higher is faster")]
public byte stepRate = 2;
void Awake()
float step;
void OnValidate()
{
waitForSeconds = new WaitForSeconds(speed * 0.01f);
if (fadeImage == null)
fadeImage = GetComponentInChildren<Image>();
}
void Start()
{
// Convert user-friendly setting value to working value
step = stepRate * 0.001f;
}
/// <summary>
/// Calculates FadeIn / FadeOut time.
/// </summary>
/// <returns>Duration in seconds</returns>
public float GetDuration()
{
float frames = 1 / step;
float frameRate = Time.deltaTime;
float duration = frames * frameRate * 0.1f;
return duration;
}
public IEnumerator FadeIn()
@ -29,8 +48,8 @@ public IEnumerator FadeIn()
while (alpha < 1)
{
yield return waitForSeconds;
alpha += 0.01f;
yield return null;
alpha += step;
fadeColor.a = alpha;
fadeImage.color = fadeColor;
}
@ -42,8 +61,8 @@ public IEnumerator FadeOut()
while (alpha > 0)
{
yield return waitForSeconds;
alpha -= 0.01f;
yield return null;
alpha -= step;
fadeColor.a = alpha;
fadeImage.color = fadeColor;
}

View File

@ -25,9 +25,6 @@ public void OnLabelTextChanged(string _, string newValue)
label.text = labelText;
}
// This is approximately the fade time
WaitForSeconds waitForFade = new WaitForSeconds(2f);
public override void OnStartServer()
{
labelText = Path.GetFileNameWithoutExtension(destinationScene);
@ -68,7 +65,7 @@ IEnumerator SendPlayerToNewScene(GameObject player)
// Tell client to unload previous subscene. No custom handling for this.
conn.Send(new SceneMessage { sceneName = gameObject.scene.path, sceneOperation = SceneOperation.UnloadAdditive, customHandling = true });
yield return waitForFade;
yield return new WaitForSeconds(AdditiveLevelsNetworkManager.singleton.fadeInOut.GetDuration());
NetworkServer.RemovePlayerForConnection(conn, false);

View File

@ -1970,8 +1970,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -216,8 +216,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -354,8 +354,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -355,8 +355,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -3278,8 +3278,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -501,12 +501,11 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: c761308e733c51245b2e8bb4201f46dc, type: 3}
m_Name:
m_EditorClassIdentifier:
secretHandshake: 1558261479176021378
serverBroadcastListenPort: 47777
enableActiveDiscovery: 1
ActiveDiscoveryInterval: 3
BroadcastAddress:
transport: {fileID: 0}
serverBroadcastListenPort: 47777
ActiveDiscoveryInterval: 3
transport: {fileID: 1556883244}
OnServerFound:
m_PersistentCalls:
m_Calls:
@ -522,6 +521,7 @@ MonoBehaviour:
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
secretHandshake: 1558261479176021378
--- !u!1 &1611696151
GameObject:
m_ObjectHideFlags: 0

View File

@ -276,8 +276,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -1198,8 +1198,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -904,8 +904,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -990,8 +990,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -278,8 +278,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -52,6 +52,10 @@ public class ClientCube : MonoBehaviour
void Awake()
{
// show vsync reminder. too easy to forget.
Debug.Log("Reminder: Snapshot interpolation is smoothest & easiest to debug with Vsync off.");
defaultColor = render.sharedMaterial.color;
// initialize EMA with 'emaDuration' seconds worth of history.
@ -173,14 +177,37 @@ void OnGUI()
{
lowFpsMode = !lowFpsMode;
}
if (GUILayout.Button("Timeline 100ms behind"))
GUILayout.Label("|");
if (GUILayout.Button("Timeline 10s behind"))
{
localTimeline -= 10.0;
}
if (GUILayout.Button("Timeline 1s behind"))
{
localTimeline -= 1.0;
}
if (GUILayout.Button("Timeline 0.1s behind"))
{
localTimeline -= 0.1;
}
if (GUILayout.Button("Timeline 100ms ahead"))
GUILayout.Label("|");
if (GUILayout.Button("Timeline 0.1s ahead"))
{
localTimeline += 0.1;
}
if (GUILayout.Button("Timeline 1s ahead"))
{
localTimeline += 1.0;
}
if (GUILayout.Button("Timeline 10s ahead"))
{
localTimeline += 10.0;
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndArea();

View File

@ -175,8 +175,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -481,8 +481,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: Bot
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,14 +65,14 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: BotNinja
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,18 +65,18 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 0.3301887, g: 0.029592374, b: 0.029592374, a: 1}
- _Color: {r: 0.4716981, g: 0.006674961, b: 0.006674961, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []

View File

@ -9,6 +9,6 @@ PhysicMaterial:
m_Name: Ice
dynamicFriction: 0
staticFriction: 0
bounciness: 0.8
bounciness: 0
frictionCombine: 0
bounceCombine: 0

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: NPC
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,14 +65,14 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: NPCNinja
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,14 +65,14 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: Player
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,14 +65,14 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1

View File

@ -9,7 +9,9 @@ Material:
m_PrefabAsset: {fileID: 0}
m_Name: PlayerNinja
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_ValidKeywords:
- _GLOSSYREFLECTIONS_OFF
- _SPECULARHIGHLIGHTS_OFF
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
@ -63,14 +65,14 @@ Material:
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _Glossiness: 1
- _GlossyReflections: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularHighlights: 0
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,725 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1617743760159216337
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5138658761349128847}
m_Layer: 6
m_Name: PlayerUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5138658761349128847
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1617743760159216337}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 7957892404843774757}
- {fileID: 7871618011363428555}
- {fileID: 5401498466654688564}
- {fileID: 6998545687844345781}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &3217452122434572523
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7957892404843774757}
- component: {fileID: 4793196872081777735}
- component: {fileID: 7544400784984079118}
m_Layer: 6
m_Name: NameText
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7957892404843774757
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3217452122434572523}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5138658761349128847}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 1.7}
m_SizeDelta: {x: 2, y: 0.3}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!23 &4793196872081777735
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3217452122434572523}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &7544400784984079118
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3217452122434572523}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Player Name
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4280953563
m_fontColor: {r: 0.8584906, g: 0.16602883, b: 0.16602883, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 1
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 2
m_fontSizeBase: 2
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 1
m_HorizontalAlignment: 2
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 0
m_parseCtrlCharacters: 0
m_isOrthographic: 0
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
_SortingLayer: 0
_SortingLayerID: 0
_SortingOrder: 0
m_hasFontAssetChanged: 0
m_renderer: {fileID: 4793196872081777735}
m_maskType: 0
--- !u!1 &4941179751968018183
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6998545687844345781}
- component: {fileID: 1220920522994516749}
- component: {fileID: 4867227432556263592}
m_Layer: 6
m_Name: SnapIntText
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &6998545687844345781
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4941179751968018183}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5138658761349128847}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 2.25}
m_SizeDelta: {x: 3, y: 0.75}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!23 &1220920522994516749
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4941179751968018183}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &4867227432556263592
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4941179751968018183}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278222848
m_fontColor: {r: 0, g: 0, b: 0, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 1
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 2
m_fontSizeBase: 2
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 1
m_HorizontalAlignment: 1
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 0
m_parseCtrlCharacters: 0
m_isOrthographic: 0
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
_SortingLayer: 0
_SortingLayerID: 0
_SortingOrder: 0
m_hasFontAssetChanged: 0
m_renderer: {fileID: 1220920522994516749}
m_maskType: 0
--- !u!1 &5178696367814392844
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5401498466654688564}
- component: {fileID: 9218990128522613023}
- component: {fileID: 8616963137272446910}
m_Layer: 6
m_Name: ServerBufferText
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &5401498466654688564
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5178696367814392844}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5138658761349128847}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 1.2}
m_SizeDelta: {x: 1.2, y: 0.25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!23 &9218990128522613023
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5178696367814392844}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &8616963137272446910
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5178696367814392844}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 'S:'
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278222848
m_fontColor: {r: 0, g: 0.5019608, b: 0, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 1
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 2
m_fontSizeBase: 2
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 1
m_HorizontalAlignment: 1
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 0
m_parseCtrlCharacters: 0
m_isOrthographic: 0
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
_SortingLayer: 0
_SortingLayerID: 0
_SortingOrder: 0
m_hasFontAssetChanged: 0
m_renderer: {fileID: 9218990128522613023}
m_maskType: 0
--- !u!1 &7371602426383271830
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7871618011363428555}
- component: {fileID: 4977190152561775245}
- component: {fileID: 3051119147354379199}
m_Layer: 6
m_Name: ClientBufferText
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7871618011363428555
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7371602426383271830}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 5138658761349128847}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 1.4}
m_SizeDelta: {x: 1.2, y: 0.25}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!23 &4977190152561775245
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7371602426383271830}
m_Enabled: 1
m_CastShadows: 0
m_ReceiveShadows: 0
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!114 &3051119147354379199
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7371602426383271830}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 9541d86e2fd84c1d9990edf0852d74ab, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 'C:'
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4278222848
m_fontColor: {r: 0, g: 0.5019608, b: 0, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 1
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 2
m_fontSizeBase: 2
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 1
m_HorizontalAlignment: 1
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 0
m_parseCtrlCharacters: 0
m_isOrthographic: 0
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
_SortingLayer: 0
_SortingLayerID: 0
_SortingOrder: 0
m_hasFontAssetChanged: 0
m_renderer: {fileID: 4977190152561775245}
m_maskType: 0

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 046d8acb77037cb439b4a45198e2bdb5
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -446,8 +446,8 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1
@ -455,8 +455,8 @@ MonoBehaviour:
timeInterpolationGui: 1
playerNinjaPrefab: {fileID: 9193589764031967748, guid: 7903ea74806680c4f9f3a62da96765c2, type: 3}
botPrefab: {fileID: 629047657802428152, guid: 45e99b6415bfdc846bbbfc2d949d53f5, type: 3}
npcPrefab: {fileID: 9078678990242496461, guid: 41855c7cf63a3dc45ac8a93b4e2ecd54, type: 3}
botNinjaPrefab: {fileID: 2972877558187846166, guid: 186c06a2be59cc84db82de14fccac374, type: 3}
npcPrefab: {fileID: 9078678990242496461, guid: 41855c7cf63a3dc45ac8a93b4e2ecd54, type: 3}
npcNinjaPrefab: {fileID: 388172571495221481, guid: 4f67582aaba8f814cae98c38f4e9782d, type: 3}
--- !u!114 &821201136
MonoBehaviour:

View File

@ -212,7 +212,6 @@ GameObject:
- component: {fileID: 821201138}
- component: {fileID: 821201136}
- component: {fileID: 821201140}
- component: {fileID: 821201139}
- component: {fileID: 821201141}
- component: {fileID: 821201142}
m_Layer: 0
@ -239,7 +238,7 @@ MonoBehaviour:
handshakeMaxSize: 3000
noDelay: 1
sendTimeout: 5000
receiveTimeout: 20000
receiveTimeout: 60000
serverMaxMessagesPerTick: 10000
clientMaxMessagesPerTick: 2000
batchSend: 0
@ -280,17 +279,18 @@ MonoBehaviour:
bufferTimeMultiplier: 2
catchupNegativeThreshold: -1
catchupPositiveThreshold: 1
catchupSpeed: 0.009999999776482582
slowdownSpeed: 0.009999999776482582
catchupSpeed: 0.019999999552965164
slowdownSpeed: 0.03999999910593033
driftEmaDuration: 1
dynamicAdjustment: 1
dynamicAdjustmentTolerance: 1
deliveryTimeEmaDuration: 2
timeInterpolationGui: 1
playerNinjaPrefab: {fileID: 1428992836745641328, guid: 4b4e4d1a8090c7a45bab0cbc52ccfd76, type: 3}
botPrefab: {fileID: 297236141164807331, guid: daa43d6577eba4240807391d6c63ea8d, type: 3}
botPrefab: {fileID: 629047657802428152, guid: 45e99b6415bfdc846bbbfc2d949d53f5, type: 3}
npcPrefab: {fileID: 9078678990242496461, guid: 41855c7cf63a3dc45ac8a93b4e2ecd54, type: 3}
botNinjaPrefab: {fileID: 2972877558187846166, guid: 186c06a2be59cc84db82de14fccac374, type: 3}
npcPrefab: {fileID: 9078678990242496461, guid: 41855c7cf63a3dc45ac8a93b4e2ecd54, type: 3}
npcNinjaPrefab: {fileID: 388172571495221481, guid: 4f67582aaba8f814cae98c38f4e9782d, type: 3}
--- !u!114 &821201136
MonoBehaviour:
@ -343,22 +343,6 @@ MonoBehaviour:
useNinja: 1
isBot: 0
multiplier: 3
--- !u!114 &821201139
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 821201133}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: bc654f29862fc2643b948f772ebb9e68, type: 3}
m_Name:
m_EditorClassIdentifier:
color: {r: 1, g: 1, b: 1, a: 1}
padding: 50
width: 100
height: 25
--- !u!114 &821201140
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -288,7 +288,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!65 &397708575
BoxCollider:
m_ObjectHideFlags: 0
@ -359,14 +359,14 @@ Transform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 397708574}
m_LocalRotation: {x: -0.21643952, y: 0, z: 0, w: 0.97629607}
m_LocalPosition: {x: -2, y: 1.2, z: 8}
m_LocalScale: {x: 3, y: 0.1, z: 6}
m_LocalRotation: {x: -0.09229599, y: 0.7010574, z: 0.09229599, w: 0.7010574}
m_LocalPosition: {x: 3, y: 0.35, z: 25.65}
m_LocalScale: {x: 3, y: 0.1, z: 3}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: -25, y: 0, z: 0}
m_LocalEulerAnglesHint: {x: -15, y: 90, z: 0}
--- !u!54 &397708579
Rigidbody:
m_ObjectHideFlags: 0
@ -586,6 +586,120 @@ MonoBehaviour:
serializedVersion: 2
m_Bits: 4294967295
m_MaxRayIntersections: 0
--- !u!1 &2051442530
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2051442535}
- component: {fileID: 2051442534}
- component: {fileID: 2051442533}
- component: {fileID: 2051442532}
- component: {fileID: 2051442531}
m_Layer: 0
m_Name: Ramp
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!54 &2051442531
Rigidbody:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051442530}
serializedVersion: 2
m_Mass: 1
m_Drag: 0
m_AngularDrag: 0.05
m_UseGravity: 1
m_IsKinematic: 1
m_Interpolate: 0
m_Constraints: 0
m_CollisionDetection: 0
--- !u!65 &2051442532
BoxCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051442530}
m_Material: {fileID: 13400000, guid: c3ac683db06890b49a15dd5009dbd6e8, type: 2}
m_IsTrigger: 0
m_Enabled: 1
serializedVersion: 2
m_Size: {x: 1, y: 1, z: 1}
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &2051442533
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051442530}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &2051442534
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051442530}
m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
--- !u!4 &2051442535
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2051442530}
m_LocalRotation: {x: 0.09229593, y: 0.7010574, z: -0.09229593, w: 0.7010574}
m_LocalPosition: {x: 0, y: 0.35, z: -19.21}
m_LocalScale: {x: 3, y: 0.1, z: 3}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 6
m_LocalEulerAnglesHint: {x: 15, y: 90, z: 0}
--- !u!1 &2122508970
GameObject:
m_ObjectHideFlags: 0

View File

@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using System.Collections.Generic;
namespace TestNT
{
@ -38,12 +39,19 @@ void Update()
[Command]
void CmdSpawnNPC()
{
//GameObject npc = Instantiate(TestNTNetworkManager.singleton.npcPrefab);
//npc.GetComponent<PlayerName>().playerName = "NPC";
//npc.GetComponent<CharacterController>().enabled = true;
//npc.GetComponent<PlayerMove>().enabled = true;
//NetworkServer.Spawn(npc);
//NpcList.Add(npc);
StartCoroutine(SpawnNPC());
}
IEnumerator SpawnNPC()
{
GameObject npc = Instantiate(TestNTNetworkManager.singleton.npcPrefab);
npc.GetComponent<PlayerName>().playerName = "NPC";
npc.GetComponent<CharacterController>().enabled = true;
npc.GetComponent<PlayerMove>().enabled = true;
NetworkServer.Spawn(npc);
NpcList.Add(npc);
yield return new WaitForSeconds(.3f);
GameObject npcNinja = Instantiate(TestNTNetworkManager.singleton.npcNinjaPrefab);
npcNinja.GetComponent<PlayerName>().playerName = "NPC-Ninja";

View File

@ -6,20 +6,17 @@
public class NTRCustomSendInterval : NetworkTransformBase
{
uint sendIntervalCounter = 0;
double lastSendIntervalTime = double.MinValue;
float onlySyncOnChangeInterval => onlySyncOnChangeCorrectionMultiplier * sendIntervalMultiplier;
[Header("Sync Only If Changed")]
[Tooltip("When true, changes are not sent unless greater than sensitivity values below.")]
public bool onlySyncOnChange = true;
float onlySyncOnChangeInterval => onlySyncOnChangeCorrectionMultiplier * sendIntervalMultiplier;
[Tooltip("If we only sync on change, then we need to correct old snapshots if more time than sendInterval * multiplier has elapsed.\n\nOtherwise the first move will always start interpolating from the last move sequence's time, which will make it stutter when starting every time.")]
public float onlySyncOnChangeCorrectionMultiplier = 2;
// uint so non negative.
[Header("Send Interval Multiplier")]
[Tooltip("Send every multiple of Network Manager send interval (= 1 / NM Send Rate).")]
[Tooltip("Check/Sync every multiple of Network Manager send interval (= 1 / NM Send Rate), instead of every send interval.")]
[Range(1, 120)]
public uint sendIntervalMultiplier = 3;
[Header("Rotation")]
@ -47,6 +44,9 @@ public class NTRCustomSendInterval : NetworkTransformBase
public Vector3 velocity;
public Vector3 angVelocity;
uint sendIntervalCounter = 0;
double lastSendIntervalTime = double.MinValue;
// delta compression needs to remember 'last' to compress against
protected Vector3Long lastSerializedPosition = Vector3Long.zero;
protected Vector3Long lastDeserializedPosition = Vector3Long.zero;
@ -76,7 +76,7 @@ void LateUpdate()
// the possibility of Update() running first before the object's movement
// script's Update(), which then causes NT to send every alternate frame
// instead.
if (isServer || (IsClientWithAuthority && NetworkClient.ready)) // is NetworkClient.ready even needed?
if (isServer || (IsClientWithAuthority && NetworkClient.ready))
{
if (sendIntervalCounter == sendIntervalMultiplier && (!onlySyncOnChange || Changed(Construct())))
SetDirty();
@ -167,23 +167,23 @@ protected virtual void UpdateClient()
protected virtual void CheckLastSendTime()
{
// timeAsDouble not available in older Unity versions.
#if !UNITY_2020_3_OR_NEWER
if (AccurateInterval.Elapsed(NetworkTime.localTime, NetworkServer.sendInterval, ref lastSendIntervalTime))
{
if (sendIntervalCounter == sendIntervalMultiplier)
sendIntervalCounter = 0;
sendIntervalCounter++;
}
#else
#if UNITY_2020_3_OR_NEWER
if (AccurateInterval.Elapsed(Time.timeAsDouble, NetworkServer.sendInterval, ref lastSendIntervalTime))
{
if (sendIntervalCounter == sendIntervalMultiplier)
sendIntervalCounter = 0;
sendIntervalCounter++;
}
#else
// timeAsDouble not available in older Unity versions.
if (AccurateInterval.Elapsed(NetworkTime.localTime, NetworkServer.sendInterval, ref lastSendIntervalTime))
{
if (sendIntervalCounter == sendIntervalMultiplier)
sendIntervalCounter = 0;
sendIntervalCounter++;
}
#endif
}
// check if position / rotation / scale changed since last sync
protected virtual bool Changed(TransformSnapshot current) =>
@ -235,7 +235,7 @@ public override void OnSerialize(NetworkWriter writer, bool initialState)
// regular serialisation the delta compression will get wrong values.
// Notes:
// 1. Interestingly only the older clients have it wrong, because at the end
// of the function, last = snapshot which is the initial state's snapshot
// of this function, last = snapshot which is the initial state's snapshot
// 2. Regular NTR gets by this bug because it sends every frame anyway so initialstate
// snapshot constructed would have been the same as the last anyway.
if (last.remoteTime > 0)

View File

@ -12,8 +12,8 @@ public class PlayerBuffers : NetworkBehaviour
public NTRCustomSendInterval NTRCustomSendInterval;
public NetworkTransform NetworkTransform;
public NetworkTransformReliable NetworkTransformReliable;
public TextMeshPro serverBufferText;
public TextMeshPro clientBufferText;
public TextMeshPro serverBufferText;
public TextMeshPro snapIntText;
[Header("Diagnostics - Do Not Modify")]
@ -27,8 +27,8 @@ private void OnValidate()
NetworkTransformReliable = GetComponent<NetworkTransformReliable>();
// Force overrideColorTags true so we can change the color without tags
serverBufferText.overrideColorTags = true;
clientBufferText.overrideColorTags = true;
serverBufferText.overrideColorTags = true;
this.enabled = false;
}
@ -46,6 +46,25 @@ public override void OnStopClient()
void Update()
{
/////// Client
if (NTRCustomSendInterval)
clientSnapCount = NTRCustomSendInterval.clientSnapshots.Count;
if (NetworkTransform)
clientSnapCount = NetworkTransform.clientSnapshots.Count;
if (NetworkTransformReliable)
clientSnapCount = NetworkTransformReliable.clientSnapshots.Count;
if (clientSnapCount < 2)
clientBufferText.color = Color.black;
else if (clientSnapCount < 3)
clientBufferText.color = Color.green;
else if (clientSnapCount < 4)
clientBufferText.color = Color.yellow;
else
clientBufferText.color = Color.red;
clientBufferText.text = $"C: {new string('-', clientSnapCount)}";
/////// Server
//serverSnapCount = networkTransformReliable.serverSnapshots.Count;
@ -60,25 +79,6 @@ void Update()
//serverBufferText.text = "S: " + new string('-', serverSnapCount);
/////// Client
if (NTRCustomSendInterval)
clientSnapCount = NTRCustomSendInterval.clientSnapshots.Count;
if (NetworkTransform)
clientSnapCount = NetworkTransform.clientSnapshots.Count;
if (NetworkTransformReliable)
clientSnapCount = NetworkTransformReliable.clientSnapshots.Count;
if (clientSnapCount < 2)
clientBufferText.color = Color.gray;
else if (clientSnapCount < 3)
clientBufferText.color = Color.green;
else if (clientSnapCount < 4)
clientBufferText.color = Color.yellow;
else
clientBufferText.color = Color.red;
clientBufferText.text = "C: " + new string('-', clientSnapCount);
/////// Snap Interpolation
//snapIntText.text = $"{networkTransformReliable.velocity.magnitude:N2}" +
// $"\n{transform.position}";
@ -86,8 +86,8 @@ void Update()
void LateUpdate()
{
serverBufferText.transform.forward = mainCamTransform.forward;
clientBufferText.transform.forward = mainCamTransform.forward;
serverBufferText.transform.forward = mainCamTransform.forward;
snapIntText.transform.forward = mainCamTransform.forward;
}
}

View File

@ -1,7 +1,6 @@
using Mirror;
using System;
using TMPro;
using UnityEngine;
using TMPro;
namespace TestNT
{

View File

@ -1,6 +1,6 @@
using System;
using UnityEngine;
using Mirror;
using System;
namespace TestNT
{
@ -249,6 +249,14 @@ void HandleTurning()
// Headless client forced to ground
void HandleJumping()
{
if (groundState != GroundState.Grounded)
{
// handles running off a cliff and/or player released Spacebar.
groundState = GroundState.Falling;
jumpSpeed = Mathf.Min(jumpSpeed, maxJumpSpeed);
jumpSpeed += Physics.gravity.y * Time.deltaTime;
}
else
jumpSpeed = Physics.gravity.y * Time.deltaTime;
}

View File

@ -1,7 +1,7 @@
using System.Collections.Generic;
using UnityEngine;
using Mirror;
using TMPro;
using System.Collections.Generic;
namespace TestNT
{

View File

@ -19,9 +19,9 @@ public class TestNTNetworkManager : NetworkManager
public GameObject playerNinjaPrefab;
public GameObject botPrefab;
public GameObject npcPrefab;
public GameObject botNinjaPrefab;
public GameObject npcPrefab;
public GameObject npcNinjaPrefab;
/// <summary>

View File

@ -36,5 +36,19 @@ public void StopHostCallsOnServerDisconnectForHostClient()
manager.StopHost();
Assert.That(manager.called, Is.EqualTo(1));
}
[Test]
public void StopClientCallsOnServerDisconnectForHostClient()
{
// OnServerDisconnect is always called when a client disconnects.
// it should also be called for the host client when we stop the host
Assert.That(manager.called, Is.EqualTo(0));
manager.StartHost();
manager.StopClient();
Assert.That(manager.called, Is.EqualTo(1));
Assert.That(NetworkServer.connections.Count, Is.EqualTo(0));
Assert.That(NetworkServer.localConnection, Is.Null);
Assert.That(NetworkClient.connection, Is.Null);
}
}
}

View File

@ -1,3 +1,8 @@
V1.34 [2023-03-15]
- Send/SendTo/Receive/ReceiveFrom NonBlocking extensions.
to encapsulate WouldBlock allocations, exceptions, etc.
allows for reuse when overwriting KcpServer/Client (i.e. for relays).
V1.33 [2023-03-14]
- perf: KcpServer/Client RawReceive now call socket.Poll to avoid non-blocking
socket's allocating a new SocketException in case they WouldBlock.

View File

@ -1,6 +1,162 @@
using System;
using System.Net;
using System.Net.Sockets;
namespace kcp2k
{
public static class Extensions
{
// non-blocking UDP send.
// allows for reuse when overwriting KcpServer/Client (i.e. for relays).
// => wrapped with Poll to avoid WouldBlock allocating new SocketException.
// => wrapped with try-catch to ignore WouldBlock exception.
// make sure to set socket.Blocking = false before using this!
public static bool SendToNonBlocking(this Socket socket, ArraySegment<byte> data, EndPoint remoteEP)
{
try
{
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return false;
// send to the the endpoint.
// do not send to 'newClientEP', as that's always reused.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3296
socket.SendTo(data.Array, data.Offset, data.Count, SocketFlags.None, remoteEP);
return true;
}
catch (SocketException e)
{
// for non-blocking sockets, SendTo may throw WouldBlock.
// in that case, simply drop the message. it's UDP, it's fine.
if (e.SocketErrorCode == SocketError.WouldBlock) return false;
// otherwise it's a real socket error. throw it.
throw;
}
}
// non-blocking UDP send.
// allows for reuse when overwriting KcpServer/Client (i.e. for relays).
// => wrapped with Poll to avoid WouldBlock allocating new SocketException.
// => wrapped with try-catch to ignore WouldBlock exception.
// make sure to set socket.Blocking = false before using this!
public static bool SendNonBlocking(this Socket socket, ArraySegment<byte> data)
{
try
{
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return false;
// SendTo allocates. we used bound Send.
socket.Send(data.Array, data.Offset, data.Count, SocketFlags.None);
return true;
}
catch (SocketException e)
{
// for non-blocking sockets, SendTo may throw WouldBlock.
// in that case, simply drop the message. it's UDP, it's fine.
if (e.SocketErrorCode == SocketError.WouldBlock) return false;
// otherwise it's a real socket error. throw it.
throw;
}
}
// non-blocking UDP receive.
// allows for reuse when overwriting KcpServer/Client (i.e. for relays).
// => wrapped with Poll to avoid WouldBlock allocating new SocketException.
// => wrapped with try-catch to ignore WouldBlock exception.
// make sure to set socket.Blocking = false before using this!
public static bool ReceiveFromNonBlocking(this Socket socket, byte[] recvBuffer, out ArraySegment<byte> data, ref EndPoint remoteEP)
{
data = default;
try
{
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// NOTE: ReceiveFrom allocates.
// we pass our IPEndPoint to ReceiveFrom.
// receive from calls newClientEP.Create(socketAddr).
// IPEndPoint.Create always returns a new IPEndPoint.
// https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
//
// throws SocketException if datagram was larger than buffer.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive?view=net-6.0
int size = socket.ReceiveFrom(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, ref remoteEP);
data = new ArraySegment<byte>(recvBuffer, 0, size);
return true;
}
catch (SocketException e)
{
// for non-blocking sockets, Receive throws WouldBlock if there is
// no message to read. that's okay. only log for other errors.
if (e.SocketErrorCode == SocketError.WouldBlock) return false;
// otherwise it's a real socket error. throw it.
throw;
}
}
// non-blocking UDP receive.
// allows for reuse when overwriting KcpServer/Client (i.e. for relays).
// => wrapped with Poll to avoid WouldBlock allocating new SocketException.
// => wrapped with try-catch to ignore WouldBlock exception.
// make sure to set socket.Blocking = false before using this!
public static bool ReceiveNonBlocking(this Socket socket, byte[] recvBuffer, out ArraySegment<byte> data)
{
data = default;
try
{
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// ReceiveFrom allocates. we used bound Receive.
// returns amount of bytes written into buffer.
// throws SocketException if datagram was larger than buffer.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive?view=net-6.0
//
// throws SocketException if datagram was larger than buffer.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive?view=net-6.0
int size = socket.Receive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None);
data = new ArraySegment<byte>(recvBuffer, 0, size);
return true;
}
catch (SocketException e)
{
// for non-blocking sockets, Receive throws WouldBlock if there is
// no message to read. that's okay. only log for other errors.
if (e.SocketErrorCode == SocketError.WouldBlock) return false;
// otherwise it's a real socket error. throw it.
throw;
}
}
}
}

View File

@ -17,7 +17,7 @@ public class KcpClient
public EndPoint remoteEndPoint;
// config
readonly KcpConfig config;
protected readonly KcpConfig config;
// raw receive buffer always needs to be of 'MTU' size, even if
// MaxMessageSize is larger. kcp always sends in MTU segments and having
@ -35,10 +35,10 @@ public class KcpClient
// events are readonly, set in constructor.
// this ensures they are always initialized when used.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3337 and more
readonly Action OnConnected;
readonly Action<ArraySegment<byte>, KcpChannel> OnData;
readonly Action OnDisconnected;
readonly Action<ErrorCode, string> OnError;
protected readonly Action OnConnected;
protected readonly Action<ArraySegment<byte>, KcpChannel> OnData;
protected readonly Action OnDisconnected;
protected readonly Action<ErrorCode, string> OnError;
// state
public bool connected;
@ -131,30 +131,11 @@ protected virtual bool RawReceive(out ArraySegment<byte> segment)
try
{
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// ReceiveFrom allocates. we used bound Receive.
// returns amount of bytes written into buffer.
// throws SocketException if datagram was larger than buffer.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive?view=net-6.0
int msgLength = socket.Receive(rawReceiveBuffer);
//Log.Debug($"KCP: client raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}");
segment = new ArraySegment<byte>(rawReceiveBuffer, 0, msgLength);
return true;
return socket.ReceiveNonBlocking(rawReceiveBuffer, out segment);
}
// for non-blocking sockets, Receive throws WouldBlock if there is
// no message to read. that's okay. only log for other errors.
catch (SocketException e)
{
if (e.SocketErrorCode != SocketError.WouldBlock)
{
// the other end closing the connection is not an 'error'.
// but connections should never just end silently.
@ -163,8 +144,6 @@ protected virtual bool RawReceive(out ArraySegment<byte> segment)
// see test: ConnectWithoutServer().
Log.Info($"KcpClient: looks like the other end has closed the connection. This is fine: {e}");
peer.Disconnect();
}
// WouldBlock indicates there's no data yet, so return false.
return false;
}
}
@ -175,27 +154,13 @@ protected virtual void RawSend(ArraySegment<byte> data)
{
try
{
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return;
socket.Send(data.Array, data.Offset, data.Count, SocketFlags.None);
socket.SendNonBlocking(data);
}
// for non-blocking sockets, SendTo may throw WouldBlock.
// in that case, simply drop the message. it's UDP, it's fine.
catch (SocketException e)
{
if (e.SocketErrorCode != SocketError.WouldBlock)
{
Log.Error($"KcpClient: Send failed: {e}");
}
}
}
public void Send(ArraySegment<byte> segment, KcpChannel channel)
{

View File

@ -554,7 +554,7 @@ void OnRawInputUnreliable(ArraySegment<byte> message)
// invalid unreliable messages may be random internet noise.
// show a warning, but don't disconnect.
// otherwise attackers could disconnect someone with random noise.
Log.Warning($"KcpPeer: received unreliable message while not authenticated. Disconnecting the connection.");
Log.Warning($"KcpPeer: received unreliable message while not authenticated.");
}
}

View File

@ -18,13 +18,13 @@ public class KcpServer
// events are readonly, set in constructor.
// this ensures they are always initialized when used.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3337 and more
readonly Action<int> OnConnected;
readonly Action<int, ArraySegment<byte>, KcpChannel> OnData;
readonly Action<int> OnDisconnected;
readonly Action<int, ErrorCode, string> OnError;
protected readonly Action<int> OnConnected;
protected readonly Action<int, ArraySegment<byte>, KcpChannel> OnData;
protected readonly Action<int> OnDisconnected;
protected readonly Action<int, ErrorCode, string> OnError;
// configuration
readonly KcpConfig config;
protected readonly KcpConfig config;
// state
protected Socket socket;
@ -154,26 +154,8 @@ protected virtual bool RawReceiveFrom(out ArraySegment<byte> segment, out int co
try
{
// when using non-blocking sockets, ReceiveFrom may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectRead)) return false;
// NOTE: ReceiveFrom allocates.
// we pass our IPEndPoint to ReceiveFrom.
// receive from calls newClientEP.Create(socketAddr).
// IPEndPoint.Create always returns a new IPEndPoint.
// https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
//
// throws SocketException if datagram was larger than buffer.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.socket.receive?view=net-6.0
int size = socket.ReceiveFrom(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, ref newClientEP);
segment = new ArraySegment<byte>(rawReceiveBuffer, 0, size);
if (socket.ReceiveFromNonBlocking(rawReceiveBuffer, out segment, ref newClientEP))
{
// set connectionId to hash from endpoint
// NOTE: IPEndPoint.GetHashCode() allocates.
// it calls m_Address.GetHashCode().
@ -186,11 +168,8 @@ protected virtual bool RawReceiveFrom(out ArraySegment<byte> segment, out int co
connectionId = newClientEP.GetHashCode();
return true;
}
// for non-blocking sockets, Receive throws WouldBlock if there is
// no message to read. that's okay. only log for other errors.
}
catch (SocketException e)
{
if (e.SocketErrorCode != SocketError.WouldBlock)
{
// NOTE: SocketException is not a subclass of IOException.
// the other end closing the connection is not an 'error'.
@ -198,10 +177,9 @@ protected virtual bool RawReceiveFrom(out ArraySegment<byte> segment, out int co
// at least log a message for easier debugging.
Log.Info($"KcpServer: ReceiveFrom failed: {e}");
}
// WouldBlock indicates there's no data yet, so return false.
return false;
}
}
// io - out.
// virtual so it may be modified for relays, nonalloc workaround, etc.
@ -217,30 +195,13 @@ protected virtual void RawSend(int connectionId, ArraySegment<byte> data)
try
{
// when using non-blocking sockets, SendTo may return WouldBlock.
// in C#, WouldBlock throws a SocketException, which is expected.
// unfortunately, creating the SocketException allocates in C#.
// let's poll first to avoid the WouldBlock allocation.
// note that this entirely to avoid allocations.
// non-blocking UDP doesn't need Poll in other languages.
// and the code still works without the Poll call.
if (!socket.Poll(0, SelectMode.SelectWrite)) return;
// send to the the endpoint.
// do not send to 'newClientEP', as that's always reused.
// fixes https://github.com/MirrorNetworking/Mirror/issues/3296
socket.SendTo(data.Array, data.Offset, data.Count, SocketFlags.None, connection.remoteEndPoint);
socket.SendToNonBlocking(data, connection.remoteEndPoint);
}
// for non-blocking sockets, SendTo may throw WouldBlock.
// in that case, simply drop the message. it's UDP, it's fine.
catch (SocketException e)
{
if (e.SocketErrorCode != SocketError.WouldBlock)
{
Log.Error($"KcpServer: SendTo failed: {e}");
}
}
}
protected virtual KcpServerConnection CreateConnection(int connectionId)
{

View File

@ -45,13 +45,21 @@ public class #SCRIPTNAME# : NetworkAuthenticator
public void OnAuthRequestMessage(NetworkConnectionToClient conn, AuthRequestMessage msg)
{
AuthResponseMessage authResponseMessage = new AuthResponseMessage();
conn.Send(authResponseMessage);
// Accept the successful authentication
ServerAccept(conn);
}
/// <summary>
/// Called when server stops, used to unregister message handlers if needed.
/// </summary>
public override void OnStopServer()
{
// Unregister the handler for the authentication request
NetworkServer.UnregisterHandler<AuthRequestMessage>();
}
#endregion
#region Client
@ -72,7 +80,6 @@ public class #SCRIPTNAME# : NetworkAuthenticator
public override void OnClientAuthenticate()
{
AuthRequestMessage authRequestMessage = new AuthRequestMessage();
NetworkClient.Send(authRequestMessage);
}
@ -86,5 +93,14 @@ public class #SCRIPTNAME# : NetworkAuthenticator
ClientAccept();
}
/// <summary>
/// Called when client stops, used to unregister message handlers if needed.
/// </summary>
public override void OnStopClient()
{
// Unregister the handler for the authentication response
NetworkClient.UnregisterHandler<AuthResponseMessage>();
}
#endregion
}

View File

@ -9,18 +9,34 @@ using Mirror.Discovery;
public struct DiscoveryRequest : NetworkMessage
{
// Add properties for whatever information you want sent by clients
// in their broadcast messages that servers will consume.
// Add public fields (not properties) for whatever information you want
// sent by clients in their broadcast messages that servers will use.
}
public struct DiscoveryResponse : NetworkMessage
{
// Add properties for whatever information you want the server to return to
// clients for them to display or consume for establishing a connection.
// Add public fields (not properties) for whatever information you want the server
// to return to clients for them to display or use for establishing a connection.
}
public class #SCRIPTNAME# : NetworkDiscoveryBase<DiscoveryRequest, DiscoveryResponse>
{
#region Unity Callbacks
#if UNITY_EDITOR
public override void OnValidate()
{
base.OnValidate();
}
#endif
public override void Start()
{
base.Start();
}
#endregion
#region Server
/// <summary>