Add StickGame Assets

This commit is contained in:
Dzejkobik007
2024-03-24 22:21:16 +01:00
parent 5a5812c0c7
commit 6c8b523d1f
6643 changed files with 596260 additions and 0 deletions

View File

@@ -0,0 +1,296 @@
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Transporting;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Utility.Performance;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.Connection
{
/// <summary>
/// A byte buffer that automatically resizes.
/// </summary>
internal class ByteBuffer
{
/// <summary>
/// How many more bytes may fit into the buffer.
/// </summary>
internal int Remaining => (Size - Length);
/// <summary>
/// Buffer data.
/// </summary>
internal byte[] Data { get; private set; }
/// <summary>
/// How many bytes currently into Data. This will include the reserve.
/// </summary>
internal int Length { get; private set; }
/// <summary>
/// Size of the buffer. Data.Length may exceed this value as it uses a pooled array.
/// </summary>
internal int Size { get; private set; }
/// <summary>
/// True if data has been written.
/// </summary>
internal bool HasData { get; private set; }
/// <summary>
/// Bytes to reserve when resetting.
/// </summary>
private int _reserve;
internal ByteBuffer(int size, int reserve = 0)
{
Data = ByteArrayPool.Retrieve(size);
Size = size;
_reserve = reserve;
Reset();
}
public void Dispose()
{
if (Data != null)
ByteArrayPool.Store(Data);
Data = null;
}
/// <summary>
/// Resets instance without clearing Data.
/// </summary>
internal void Reset()
{
Length = _reserve;
HasData = false;
}
/// <summary>
/// Copies segments without error checking, including tick for the first time data is added.
/// </summary>
/// <param name="segment"></param>
internal void CopySegment(uint tick, ArraySegment<byte> segment)
{
/* If data has not been written to buffer yet
* then write tick to the start. */
if (!HasData)
{
int pos = 0;
WriterExtensions.WriteUInt32(Data, tick, ref pos);
}
Buffer.BlockCopy(segment.Array, segment.Offset, Data, Length, segment.Count);
Length += segment.Count;
HasData = true;
}
/// <summary>
/// Copies segments without error checking.
/// </summary>
/// <param name="segment"></param>
internal void CopySegment(ArraySegment<byte> segment)
{
Buffer.BlockCopy(segment.Array, segment.Offset, Data, Length, segment.Count);
Length += segment.Count;
HasData = true;
}
}
internal class PacketBundle
{
/// <summary>
/// True if data has been written.
/// </summary>
internal bool HasData => (_buffers[0].HasData || (!_isSendLastBundle && _sendLastBundle.HasData));
/// <summary>
/// All buffers written. Collection is not cleared when reset but rather the index in which to write is.
/// </summary>
private List<ByteBuffer> _buffers = new List<ByteBuffer>();
/// <summary>
/// Buffer which is being written to.
/// </summary>
private int _bufferIndex;
/// <summary>
/// Maximum size packet the transport can handle.
/// </summary>
private int _maximumTransportUnit;
/// <summary>
/// Number of buffers written to. Will return 0 if nothing has been written.
/// </summary>
public int WrittenBuffers => (!HasData) ? 0 : (_bufferIndex + 1);
/// <summary>
/// Number of bytes to reserve at the beginning of each buffer.
/// </summary>
private int _reserve;
/// <summary>
/// NetworkManager this is for.
/// </summary>
private NetworkManager _networkManager;
/// <summary>
/// Packet bundle to use for last enqueued data.
/// </summary>
private PacketBundle _sendLastBundle;
/// <summary>
/// True if being used as an sendLast bundle.
/// </summary>
private bool _isSendLastBundle;
internal PacketBundle(NetworkManager manager, int mtu, int reserve = 0, DataOrderType orderType = DataOrderType.Default)
{
_isSendLastBundle = (orderType == DataOrderType.Last);
//If this is not the send last packetbundle then make a new one.
if (!_isSendLastBundle)
_sendLastBundle = new PacketBundle(manager, mtu, reserve, DataOrderType.Last);
_networkManager = manager;
_maximumTransportUnit = mtu;
/* Allow bytes for the tick.
* Modify reserve after making sendLast bundle
* so that the wrong reserve is not passed into
* the sendLast bundle. */
reserve += TransportManager.TICK_BYTES;
_reserve = reserve;
//Add buffer requires the right reserve so call after setting.
AddBuffer();
Reset(false);
}
public void Dispose()
{
for (int i = 0; i < _buffers.Count; i++)
_buffers[i].Dispose();
_sendLastBundle?.Dispose();
}
/// <summary>
/// Adds a buffer using current settings.
/// </summary>
private ByteBuffer AddBuffer()
{
ByteBuffer ba = new ByteBuffer(_maximumTransportUnit, _reserve);
_buffers.Add(ba);
return ba;
}
/// <summary>
/// Resets using current settings.
/// </summary>
internal void Reset(bool resetSendLast)
{
_bufferIndex = 0;
for (int i = 0; i < _buffers.Count; i++)
_buffers[i].Reset();
if (resetSendLast)
_sendLastBundle.Reset(false);
}
/// <summary>
/// Writes a segment to this packet bundle using the current WriteIndex.
/// </summary>
/// <param name="forceNewBuffer">True to force data into a new buffer.</param>
internal void Write(ArraySegment<byte> segment, bool forceNewBuffer = false, DataOrderType orderType = DataOrderType.Default)
{
/* If not the send last bundle and to send data last
* then send using the send last bundle. */
if (!_isSendLastBundle && orderType == DataOrderType.Last)
{
_sendLastBundle.Write(segment, forceNewBuffer, orderType);
return;
}
//Nothing to be written.
if (segment.Count == 0)
return;
/* If the segment count is larger than the mtu then
* something went wrong. Nothing should call this method
* directly except the TransportManager, which will automatically
* split packets that exceed MTU into reliable ordered. */
if (segment.Count > _maximumTransportUnit)
{
_networkManager.LogError($"Segment is length of {segment.Count} while MTU is {_maximumTransportUnit}. Packet was not split properly and will not be sent.");
return;
}
ByteBuffer ba = _buffers[_bufferIndex];
/* Make a new buffer if...
* forcing a new buffer and data has already been written to the current.
* or---
* segment.Count is more than what is remaining in the buffer. */
bool useNewBuffer = (forceNewBuffer && ba.Length > _reserve) ||
(segment.Count > ba.Remaining);
if (useNewBuffer)
{
_bufferIndex++;
//If need to make a new buffer then do so.
if (_buffers.Count <= _bufferIndex)
{
ba = AddBuffer();
}
else
{
ba = _buffers[_bufferIndex];
ba.Reset();
}
}
uint tick = _networkManager.TimeManager.LocalTick;
ba.CopySegment(tick, segment);
}
/// <summary>
/// Returns the packetBundle for send last.
/// </summary>
/// <returns></returns>
internal PacketBundle GetSendLastBundle() => _sendLastBundle;
/// <summary>
/// Gets a buffer for the specified index. Returns true and outputs the buffer if it was successfully found.
/// </summary>
/// <param name="index">Index of the buffer to retrieve.</param>
/// <param name="bb">Buffer retrieved from the list. Null if the specified buffer was not found.</param>
internal bool GetBuffer(int index, out ByteBuffer bb)
{
bb = null;
if (index >= _buffers.Count || index < 0)
{
_networkManager.LogError($"Index of {index} is out of bounds. There are {_buffers.Count} available.");
return false;
}
if (index > _bufferIndex)
{
_networkManager.LogError($"Index of {index} exceeds the number of written buffers. There are {WrittenBuffers} written buffers.");
return false;
}
bb = _buffers[index];
return bb.HasData;
}
/// <summary>
/// Returns a PacketBundle for a channel. ResetPackets must be called afterwards.
/// </summary>
/// <param name="channel"></param>
/// <returns>True if PacketBundle is valid on the index and contains data.</returns>
internal static bool GetPacketBundle(int channel, List<PacketBundle> bundles, out PacketBundle mtuBuffer)
{
//Out of bounds.
if (channel >= bundles.Count)
{
mtuBuffer = null;
return false;
}
mtuBuffer = bundles[channel];
return mtuBuffer.HasData;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb2f3ce9b5ac27f40b7daa9364fb4d60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,125 @@
using System.Runtime.CompilerServices;
namespace FishNet.Managing.Timing
{
public struct EstimatedTick
{
/// <summary>
/// How to handle old ticks, specifically related to EstimatedTick.
/// </summary>
public enum OldTickOption : byte
{
/// <summary>
/// Completely ignore old ticks.
/// </summary>
Discard = 0,
/// <summary>
/// Set LastRemoteTick but do not update RemoteTick.
/// </summary>
SetLastRemoteTick = 1,
/// <summary>
/// Set LastRemoteTick and RemoteTick.
/// </summary>
SetRemoteTick = 2,
}
/// <summary>
/// Local tick when this was last updated.
/// </summary>
public uint LocalTick;
/// <summary>
/// Last remote tick this was updated with that was not out of order or a duplicate.
/// </summary>
public uint RemoteTick;
/// <summary>
/// Last remote tick received regardless if it was out of order or a duplicate.
/// </summary>
public uint LastRemoteTick;
/// <summary>
/// True if the value was updated this tick.
/// </summary>
public bool IsCurrent(TimeManager tm) => (!IsUnset && LocalTick == tm.LocalTick);
/// <summary>
/// True if value is unset.
/// </summary>
//Only need to check one value for unset as they all would be if not set.
public bool IsUnset => (LocalTick == 0);
/// <summary>
/// Number of ticks LocalTick is being current LocalTick.
/// </summary>
public uint LocalTickDifference(TimeManager tm)
{
long value = (tm.LocalTick - LocalTick);
//Shouldn't be possible to be less than 0.
if (value < 0)
return 0;
else if (value > uint.MaxValue)
value = uint.MaxValue;
return (uint)value;
}
/// <summary>
/// Updates values.
/// </summary>
/// <param name="nm">NetworkManager to use.</param>
/// <param name="remoteTick">Remote tick being updated.</param>
/// <param name="ignoreOldTicks">True to not update if remote tick is older or equal to the last updated value.</param>
/// <returns>True if was able to update values.</returns>
public bool Update(TimeManager tm, uint remoteTick, OldTickOption oldTickOption = OldTickOption.Discard)
{
//Always set LastRemoteTick even if out of order.
LastRemoteTick = remoteTick;
//If cannot update with old values return.
if (oldTickOption != OldTickOption.SetRemoteTick && remoteTick <= RemoteTick)
return false;
//nm is assumed set here.
LocalTick = tm.LocalTick;
RemoteTick = remoteTick;
return true;
}
/// <summary>
/// Current estimated value.
/// </summary>
/// <param name="nm">NetworkManager to use. When null default value will be returned.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public uint Value(TimeManager tm)
{
return Value(tm, out _);
}
/// <summary>
/// Current estimated value. Outputs if value is current.
/// </summary>
/// <param name="nm">NetworkManager to use. When null default value will be returned.</param>
/// <param name="isCurrent">True if the value was updated this local tick.</param>
public uint Value(TimeManager tm, out bool isCurrent)
{
isCurrent = IsCurrent(tm);
if (tm == null)
return 0;
if (IsUnset)
return 0;
uint diff = (tm.LocalTick - LocalTick);
return (diff + RemoteTick);
}
/// <summary>
/// Resets values to unset.
/// </summary>
public void Reset()
{
LocalTick = 0;
RemoteTick = 0;
LastRemoteTick = 0;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36df408e03d0cab4ab728897541c2bf2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,111 @@
using FishNet.Broadcast;
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Transporting;
using FishNet.Object;
using FishNet.Transporting;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace FishNet.Connection
{
public partial class NetworkConnection
{
#region Private.
/// <summary>
/// PacketBundles to send to this connection. An entry will be made for each channel.
/// </summary>
private List<PacketBundle> _toClientBundles = new List<PacketBundle>();
/// <summary>
/// True if this object has been dirtied.
/// </summary>
private bool _serverDirtied;
#endregion
/// <summary>
/// Initializes this script.
/// </summary>
private void InitializeBuffer()
{
for (byte i = 0; i < TransportManager.CHANNEL_COUNT; i++)
{
int mtu = NetworkManager.TransportManager.GetLowestMTU(i);
_toClientBundles.Add(new PacketBundle(NetworkManager, mtu));
}
}
/// <summary>
/// Sends a broadcast to this connection.
/// </summary>
/// <typeparam name="T">Type of broadcast to send.</typeparam>
/// <param name="message">Broadcast data being sent; for example: an instance of your broadcast type.</param>
/// <param name="requireAuthenticated">True if the client must be authenticated for this broadcast to send.</param>
/// <param name="channel">Channel to send on.</param>
public void Broadcast<T>(T message, bool requireAuthenticated = true, Channel channel = Channel.Reliable) where T : struct, IBroadcast
{
if (!IsActive)
NetworkManager.LogError($"Connection is not valid, cannot send broadcast.");
else
NetworkManager.ServerManager.Broadcast<T>(this, message, requireAuthenticated, channel);
}
/// <summary>
/// Sends data from the server to a client.
/// </summary>
/// <param name="forceNewBuffer">True to force data into a new buffer.</param>
internal void SendToClient(byte channel, ArraySegment<byte> segment, bool forceNewBuffer = false, DataOrderType orderType = DataOrderType.Default)
{
//Cannot send data when disconnecting.
if (Disconnecting)
return;
if (!IsActive)
{
NetworkManager?.LogWarning($"Data cannot be sent to connection {ClientId} because it is not active.");
return;
}
//If channel is out of bounds then default to the first channel.
if (channel >= _toClientBundles.Count)
channel = 0;
_toClientBundles[channel].Write(segment, forceNewBuffer, orderType);
ServerDirty();
}
/// <summary>
/// Returns a PacketBundle for a channel. ResetPackets must be called afterwards.
/// </summary>
/// <param name="channel"></param>
/// <returns>True if PacketBundle is valid on the index and contains data.</returns>
internal bool GetPacketBundle(int channel, out PacketBundle packetBundle)
{
return PacketBundle.GetPacketBundle(channel, _toClientBundles, out packetBundle);
}
/// <summary>
/// Indicates the server has data to send to this connection.
/// </summary>
private void ServerDirty()
{
bool wasDirty = _serverDirtied;
_serverDirtied = true;
//If not yet dirty then tell transport manager this is dirty.
if (!wasDirty)
NetworkManager.TransportManager.ServerDirty(this);
}
/// <summary>
/// Resets that there is data to send.
/// </summary>
internal void ResetServerDirty()
{
_serverDirtied = false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5bc17ee5bac499347a6fbad9dd24b7b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using FishNet.Object;
using GameKit.Utilities;
using System;
using System.Collections.Generic;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection
{
public class LevelOfDetailData : IResettable
{
/// <summary>
/// Current level of detail for a NetworkObject.
/// </summary>
public byte CurrentLevelOfDetail;
/// <summary>
/// Previous level of detail for a NetworkObject.
/// </summary>
public byte PreviousLevelOfDetail;
internal void Update(byte lodLevel)
{
PreviousLevelOfDetail = CurrentLevelOfDetail;
CurrentLevelOfDetail = lodLevel;
}
public void ResetState()
{
CurrentLevelOfDetail = 0;
PreviousLevelOfDetail = 0;
}
public void InitializeState() { }
}
/// <summary>
/// Level of detail for each NetworkObject.
/// Since this is called frequently this field is intentionally not an accessor to increase performance.
/// </summary>
public Dictionary<NetworkObject, LevelOfDetailData> LevelOfDetails = new Dictionary<NetworkObject, LevelOfDetailData>(new NetworkObjectIdComparer());
/// <summary>
/// Number oftimes this connection may send a forced LOD update.
/// </summary>
internal int AllowedForcedLodUpdates;
/// <summary>
/// Last tick an LOD was sent.
/// On client and clientHost this is LocalTick.
/// On server only this is LastPacketTick for the connection.
/// </summary>
internal uint LastLevelOfDetailUpdate;
/// <summary>
/// Returns if the client has not sent an LOD update for expectedInterval.
/// </summary>
/// <returns></returns>
internal bool IsLateForLevelOfDetail(uint expectedInterval)
{
//Local client is immune since server and client share ticks.
if (IsLocalClient)
return false;
uint lastPacketTick = PacketTick.RemoteTick;
return ((lastPacketTick - LastLevelOfDetailUpdate) > expectedInterval);
}
/// <summary>
/// Number of level of detail update infractions for this connection.
/// </summary>
internal int LevelOfDetailInfractions;
private void ResetStates_Lod()
{
foreach (LevelOfDetailData data in LevelOfDetails.Values)
ResettableObjectCaches<LevelOfDetailData>.Store(data);
LevelOfDetails.Clear();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d9aba23f6fe8fd849a39712926efe055
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,98 @@
using FishNet.Component.Observing;
using FishNet.Managing;
using UnityEngine;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection
{
#region Internal.
/// <summary>
/// Current GridEntry this connection is in.
/// </summary>
internal GridEntry HashGridEntry = HashGrid.EmptyGridEntry;
#endregion
#region Private.
/// <summary>
/// HashGrid for the NetworkManager on this connection.
/// </summary>
private HashGrid _hashGrid;
/// <summary>
/// Last unscaled time the HashGrid position was updated with this connections Objects.
/// </summary>
private float _nextHashGridUpdateTime;
/// <summary>
/// Current GridPosition this connection is in.
/// </summary>
private Vector2Int _hashGridPosition = HashGrid.UnsetGridPosition;
#endregion
/// <summary>
/// Called when the FirstObject changes for this connection.
/// </summary>
private void Observers_FirstObjectChanged()
{
UpdateHashGridPositions(true);
}
/// <summary>
/// Initializes this for use.
/// </summary>
private void Observers_Initialize(NetworkManager nm)
{
nm.TryGetInstance<HashGrid>(out _hashGrid);
}
/// <summary>
/// Updates the HashGridPosition value for FirstObject.
/// </summary>
internal void UpdateHashGridPositions(bool force)
{
if (_hashGrid == null)
return;
float unscaledTime = Time.unscaledTime;
//Not enough time has passed to update.
if (!force && unscaledTime < _nextHashGridUpdateTime)
return;
const float updateInterval = 1f;
_nextHashGridUpdateTime = unscaledTime + updateInterval;
if (FirstObject == null)
{
HashGridEntry = HashGrid.EmptyGridEntry;
_hashGridPosition = HashGrid.UnsetGridPosition;
}
else
{
Vector2Int newPosition = _hashGrid.GetHashGridPosition(FirstObject);
if (newPosition != _hashGridPosition)
{
_hashGridPosition = newPosition;
HashGridEntry = _hashGrid.GetGridEntry(newPosition);
}
}
}
/// <summary>
/// Resets values.
/// </summary>
private void Observers_Reset()
{
_hashGrid = null;
_hashGridPosition = HashGrid.UnsetGridPosition;
_nextHashGridUpdateTime = 0f;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e1d1fe830d38ad043bd5e616cf6386ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,105 @@
using FishNet.Managing;
using FishNet.Managing.Timing;
using System;
using UnityEngine;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection
{
#pragma warning disable CS0414
#region Private.
/// <summary>
/// Last tick this connection sent a ping.
/// </summary>
private uint _lastPingTick;
///// <summary>
///// Number of times client has excessively sent a ping.
///// </summary>
//private float _excessivePingCount;
/// <summary>
/// Ticks expected between each ping.
/// </summary>
private uint _requiredPingTicks;
#endregion
#region Const.
/// <summary>
/// Number of times a ping may occur excessively before server will punish connection.
/// </summary>
private const byte EXCESSIVE_PING_LIMIT = 10;
#endregion
#pragma warning restore CS0414
/// <summary>
/// Initializes for ping.
/// </summary>
private void InitializePing()
{
//Give the client some room for error.
float requiredInterval = (NetworkManager.TimeManager.PingInterval * 0.85f);
//Round down so required ticks is lower.
_requiredPingTicks = NetworkManager.TimeManager.TimeToTicks(requiredInterval, TickRounding.RoundDown);
}
/// <summary>
/// Resets PingPong values.
/// </summary>
private void ResetPingPong()
{
//_excessivePingCount = 0;
_lastPingTick = 0;
}
/// <summary>
/// Called when a ping is received from this connection. Returns if can respond to ping.
/// </summary>
/// <returns>True to respond to ping, false to kick connection.</returns>
internal bool CanPingPong()
{
/* Only check ping conditions in build. Editors are prone to pausing which can
* improperly kick clients. */
TimeManager tm = (NetworkManager == null) ? InstanceFinder.TimeManager : NetworkManager.TimeManager;
/* Server FPS is running low, timing isn't reliable enough to kick clients.
* Respond with clients ping and remove infractions just in case the
* client received some from other server instabilities. */
if (tm.LowFrameRate)
{
//_excessivePingCount = 0f;
return false;
}
uint currentTick = tm.Tick;
uint difference = (currentTick - _lastPingTick);
_lastPingTick = currentTick;
//Ping sent too quickly.
if (difference < _requiredPingTicks)
{
//_excessivePingCount += 1f;
////Ping limit hit.
//if (_excessivePingCount >= EXCESSIVE_PING_LIMIT)
//{
// NetworkManager.LogWarning($"Kicked connectionId {ClientId} for excessive pings.");
// Disconnect(true);
//}
//Return to not send pong back.
return false;
}
//Ping isnt too fast.
else
{
//_excessivePingCount = UnityEngine.Mathf.Max(0f, _excessivePingCount - 0.5f);
return true;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20633bf6995f6534ba2b27e1eab3054d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,85 @@
using FishNet.Managing;
using FishNet.Managing.Predicting;
using FishNet.Managing.Timing;
using FishNet.Serializing;
using FishNet.Transporting;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection
{
/// <summary>
/// Average number of replicates in queue for the past x received replicates.
/// </summary>
private MovingAverage _replicateQueueAverage;
/// <summary>
/// Last tick replicateQueueAverage was updated.
/// </summary>
private uint _lastAverageQueueAddTick;
internal void Prediction_Initialize(NetworkManager manager, bool asServer)
{
if (asServer)
{
int movingAverageCount = (int)Mathf.Max((float)manager.TimeManager.TickRate * 0.25f, 3f);
_replicateQueueAverage = new MovingAverage(movingAverageCount);
}
}
/// <summary>
/// Adds to the average number of queued replicates.
/// </summary>
internal void AddAverageQueueCount(ushort value, uint tick)
{
/* If have not added anything to the averages for several ticks
* then reset average. */
if ((tick - _lastAverageQueueAddTick) > _replicateQueueAverage.SampleSize)
_replicateQueueAverage.Reset();
_lastAverageQueueAddTick = tick;
_replicateQueueAverage.ComputeAverage((float)value);
}
/// <summary>
/// Returns the highest queue count after resetting it.
/// </summary>
/// <returns></returns>
internal ushort GetAndResetAverageQueueCount()
{
if (_replicateQueueAverage == null)
return 0;
int avg = (int)(_replicateQueueAverage.Average);
if (avg < 0)
avg = 0;
return (ushort)avg;
}
/// <summary>
/// Local tick when the connection last replicated.
/// </summary>
public uint LocalReplicateTick { get; internal set; }
/// <summary>
/// Resets NetworkConnection.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Prediction_Reset()
{
GetAndResetAverageQueueCount();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0068eb93a417a44b9151b61a08d8547
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,69 @@
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
using FishNet.Serializing;
using System;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection
{
#region Public.
/// <summary>
/// Returns true if this connection is a clientHost.
/// </summary>
public bool IsHost => (NetworkManager == null) ? false : (NetworkManager.IsServer && (this == NetworkManager.ClientManager.Connection));
/// <summary>
/// Returns if this connection is for the local client.
/// </summary>
public bool IsLocalClient => (NetworkManager == null) ? false : (NetworkManager.ClientManager.Connection == this);
#endregion
/// <summary>
/// Returns the address of this connection.
/// </summary>
/// <returns></returns>
public string GetAddress()
{
if (!IsValid)
return string.Empty;
if (NetworkManager == null)
return string.Empty;
return NetworkManager.TransportManager.Transport.GetConnectionAddress(ClientId);
}
/// <summary>
/// Kicks a connection immediately while invoking OnClientKick.
/// </summary>
/// <param name="kickReason">Reason client is being kicked.</param>
/// <param name="loggingType">How to print logging as.</param>
/// <param name="log">Optional message to be debug logged.</param>
public void Kick(KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
{
NetworkManager.ServerManager.Kick(this, kickReason, loggingType, log);
}
/// <summary>
/// Kicks a connection immediately while invoking OnClientKick.
/// </summary>
/// <param name="reader">Reader to clear before kicking.</param>
/// <param name="kickReason">Reason client is being kicked.</param>
/// <param name="loggingType">How to print logging as.</param>
/// <param name="log">Optional message to be debug logged.</param>
public void Kick(Reader reader, KickReason kickReason, LoggingType loggingType = LoggingType.Common, string log = "")
{
NetworkManager.ServerManager.Kick(this, reader, kickReason, loggingType, log);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d45abc242399194b85e6c16bcb3676b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,433 @@
using FishNet.Component.Observing;
using FishNet.Documenting;
using FishNet.Managing;
using FishNet.Managing.Timing;
using FishNet.Object;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.SceneManagement;
using static FishNet.Managing.Timing.EstimatedTick;
namespace FishNet.Connection
{
/// <summary>
/// A container for a connected client used to perform actions on and gather information for the declared client.
/// </summary>
public partial class NetworkConnection : IEquatable<NetworkConnection>
{
#region Public.
/// <summary>
/// Called after this connection has loaded start scenes. Boolean will be true if asServer. Available to this connection and server.
/// </summary>
public event Action<NetworkConnection, bool> OnLoadedStartScenes;
/// <summary>
/// Called after connection gains ownership of an object, and after the object has been added to Objects. Available to this connection and server.
/// </summary>
public event Action<NetworkObject> OnObjectAdded;
/// <summary>
/// Called after connection loses ownership of an object, and after the object has been removed from Objects. Available to this connection and server.
/// </summary>
public event Action<NetworkObject> OnObjectRemoved;
/// <summary>
/// NetworkManager managing this class.
/// </summary>
public NetworkManager NetworkManager { get; private set; }
/// <summary>
/// True if connection has loaded start scenes. Available to this connection and server.
/// </summary>
public bool LoadedStartScenes() => (_loadedStartScenesAsServer || _loadedStartScenesAsClient);
/// <summary>
///
/// </summary>
public bool LoadedStartScenes(bool asServer)
{
if (asServer)
return _loadedStartScenesAsServer;
else
return _loadedStartScenesAsClient;
}
/// <summary>
/// True if loaded start scenes as server.
/// </summary>
private bool _loadedStartScenesAsServer;
/// <summary>
/// True if loaded start scenes as client.
/// </summary>
private bool _loadedStartScenesAsClient;
/// <summary>
/// ObjectIds to use for predicted spawning.
/// </summary>
internal Queue<int> PredictedObjectIds = new Queue<int>();
/// <summary>
/// TransportIndex this connection is on.
/// For security reasons this value will be unset on clients if this is not their connection.
/// </summary>
public int TransportIndex { get; internal set; } = -1;
/// <summary>
/// True if this connection is authenticated. Only available to server.
/// </summary>
public bool Authenticated { get; private set; }
/// <summary>
/// True if this connection IsValid and not Disconnecting.
/// </summary>
public bool IsActive => (ClientId >= 0 && !Disconnecting);
/// <summary>
/// True if this connection is valid. An invalid connection indicates no client is set for this reference.
/// </summary>
public bool IsValid => (ClientId >= 0);
/// <summary>
/// Unique Id for this connection.
/// </summary>
public int ClientId = -1;
/// <summary>
/// Objects owned by this connection. Available to this connection and server.
/// </summary>
public HashSet<NetworkObject> Objects = new HashSet<NetworkObject>();
/// <summary>
/// The first object within Objects.
/// </summary>
public NetworkObject FirstObject { get; private set; }
/// <summary>
/// Sets a custom FirstObject. This connection must be owner of the specified object.
/// </summary>
/// <param name="nob"></param>
public void SetFirstObject(NetworkObject nob)
{
//Invalid object.
if (!Objects.Contains(nob))
{
string errMessage = $"FirstObject for {ClientId} cannot be set to {nob.name} as it's not within Objects for this connection.";
if (NetworkManager == null)
NetworkManager.StaticLogError(errMessage);
else
NetworkManager.LogError(errMessage);
return;
}
FirstObject = nob;
}
/// <summary>
/// Scenes this connection is in. Available to this connection and server.
/// </summary>
public HashSet<Scene> Scenes { get; private set; } = new HashSet<Scene>();
/// <summary>
/// True if this connection is being disconnected. Only available to server.
/// </summary>
public bool Disconnecting { get; private set; }
/// <summary>
/// Tick when Disconnecting was set.
/// </summary>
internal uint DisconnectingTick { get; private set; }
/// <summary>
/// Custom data associated with this connection which may be modified by the user.
/// The value of this field are not synchronized over the network.
/// </summary>
public object CustomData = null;
/// <summary>
/// LocalTick of the server when this connection was established. This value is not set for clients.
/// </summary>
internal uint ServerConnectionTick;
/// <summary>
/// Tick of the last packet received from this connection which was not out of order.
/// This value is only available on the server.
/// </summary>
public EstimatedTick PacketTick;
[Obsolete("Use LocalTick instead.")] //Remove on 2023/06/01
public uint Tick => LocalTick.Value(NetworkManager.TimeManager);
/// <summary>
/// Approximate local tick as it is on this connection.
/// This also contains the last set value for local and remote.
/// </summary>
public EstimatedTick LocalTick;
#endregion
#region Const.
/// <summary>
/// Value used when ClientId has not been set.
/// </summary>
public const int UNSET_CLIENTID_VALUE = -1;
#endregion
#region Comparers.
public override bool Equals(object obj)
{
if (obj is NetworkConnection nc)
return (nc.ClientId == this.ClientId);
else
return false;
}
public bool Equals(NetworkConnection nc)
{
if (nc is null)
return false;
//If either is -1 Id.
if (this.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE || nc.ClientId == NetworkConnection.UNSET_CLIENTID_VALUE)
return false;
//Same object.
if (System.Object.ReferenceEquals(this, nc))
return true;
return (this.ClientId == nc.ClientId);
}
public override int GetHashCode()
{
return ClientId;
}
public static bool operator ==(NetworkConnection a, NetworkConnection b)
{
if (a is null && b is null)
return true;
if (a is null && !(b is null))
return false;
return (b == null) ? a.Equals(b) : b.Equals(a);
}
public static bool operator !=(NetworkConnection a, NetworkConnection b)
{
return !(a == b);
}
#endregion
[APIExclude]
public NetworkConnection() { }
[APIExclude]
public NetworkConnection(NetworkManager manager, int clientId, int transportIndex, bool asServer)
{
Initialize(manager, clientId, transportIndex, asServer);
}
internal void Dispose()
{
Deinitialize();
}
/// <summary>
/// Outputs data about this connection as a string.
/// </summary>
/// <returns></returns>
public override string ToString()
{
int clientId = ClientId;
string ip = (NetworkManager != null) ? NetworkManager.TransportManager.Transport.GetConnectionAddress(clientId) : "Unset";
return $"Id [{ClientId}] Address [{ip}]";
}
/// <summary>
/// Initializes this for use.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Initialize(NetworkManager nm, int clientId, int transportIndex, bool asServer)
{
NetworkManager = nm;
if (asServer)
ServerConnectionTick = nm.TimeManager.LocalTick;
TransportIndex = transportIndex;
ClientId = clientId;
/* Set PacketTick to current values so
* that timeouts and other things around
* first packet do not occur due to an unset value. */
PacketTick.Update(nm.TimeManager, 0, OldTickOption.SetLastRemoteTick);
Observers_Initialize(nm);
Prediction_Initialize(nm, asServer);
//Only the server uses the ping and buffer.
if (asServer)
{
InitializeBuffer();
InitializePing();
}
}
/// <summary>
/// Deinitializes this NetworkConnection. This is called prior to resetting.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void Deinitialize()
{
MatchCondition.RemoveFromMatchesWithoutRebuild(this, NetworkManager);
foreach (PacketBundle p in _toClientBundles)
p.Dispose();
_toClientBundles.Clear();
ServerConnectionTick = 0;
PacketTick.Reset();
TransportIndex = -1;
ClientId = -1;
ClearObjects();
Authenticated = false;
NetworkManager = null;
_loadedStartScenesAsClient = false;
_loadedStartScenesAsServer = false;
SetDisconnecting(false);
Scenes.Clear();
PredictedObjectIds.Clear();
ResetPingPong();
ResetStates_Lod();
AllowedForcedLodUpdates = 0;
LastLevelOfDetailUpdate = 0;
LevelOfDetailInfractions = 0;
Observers_Reset();
Prediction_Reset();
}
/// <summary>
/// Sets Disconnecting boolean for this connection.
/// </summary>
internal void SetDisconnecting(bool value)
{
Disconnecting = value;
if (Disconnecting)
DisconnectingTick = NetworkManager.TimeManager.LocalTick;
}
/// <summary>
/// Disconnects this connection.
/// </summary>
/// <param name="immediately">True to disconnect immediately. False to send any pending data first.</param>
public void Disconnect(bool immediately)
{
if (!IsValid)
{
//NetworkManager is likely null if invalid.
NetworkManager.StaticLogWarning($"Disconnect called on an invalid connection.");
return;
}
if (Disconnecting)
{
NetworkManager.LogWarning($"ClientId {ClientId} is already disconnecting.");
return;
}
SetDisconnecting(true);
//If immediately then force disconnect through transport.
if (immediately)
NetworkManager.TransportManager.Transport.StopConnection(ClientId, true);
//Otherwise mark dirty so server will push out any pending information, and then disconnect.
else
ServerDirty();
}
/// <summary>
/// Returns if just loaded start scenes and sets them as loaded if not.
/// </summary>
/// <returns></returns>
internal bool SetLoadedStartScenes(bool asServer)
{
bool loadedToCheck = (asServer) ? _loadedStartScenesAsServer : _loadedStartScenesAsClient;
//Result becomes true if not yet loaded start scenes.
bool result = !loadedToCheck;
if (asServer)
_loadedStartScenesAsServer = true;
else
_loadedStartScenesAsClient = true;
OnLoadedStartScenes?.Invoke(this, asServer);
return result;
}
/// <summary>
/// Sets connection as authenticated.
/// </summary>
internal void ConnectionAuthenticated()
{
Authenticated = true;
}
/// <summary>
/// Adds to Objects owned by this connection.
/// </summary>
/// <param name="nob"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void AddObject(NetworkObject nob)
{
if (!IsValid)
return;
Objects.Add(nob);
//If adding the first object then set new FirstObject.
if (Objects.Count == 1)
SetFirstObject();
OnObjectAdded?.Invoke(nob);
}
/// <summary>
/// Removes from Objects owned by this connection.
/// </summary>
/// <param name="nob"></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void RemoveObject(NetworkObject nob)
{
if (!IsValid)
{
ClearObjects();
return;
}
Objects.Remove(nob);
//If removing the first object then set a new one.
if (nob == FirstObject)
SetFirstObject();
OnObjectRemoved?.Invoke(nob);
}
/// <summary>
/// Clears all Objects.
/// </summary>
private void ClearObjects()
{
Objects.Clear();
FirstObject = null;
}
/// <summary>
/// Sets FirstObject using the first element in Objects.
/// </summary>
private void SetFirstObject()
{
if (Objects.Count == 0)
{
FirstObject = null;
}
else
{
foreach (NetworkObject nob in Objects)
{
FirstObject = nob;
Observers_FirstObjectChanged();
break;
}
}
}
/// <summary>
/// Adds a scene to this connections Scenes.
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
internal bool AddToScene(Scene scene)
{
return Scenes.Add(scene);
}
/// <summary>
/// Removes a scene to this connections Scenes.
/// </summary>
/// <param name="scene"></param>
/// <returns></returns>
internal bool RemoveFromScene(Scene scene)
{
return Scenes.Remove(scene);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01c7bfc71e29621408451fa2fa6b1a0b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,7 @@
using System.Runtime.CompilerServices;
namespace FishNet.Managing.Timing
{
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 139cb2731a4787e47919ef19010d7953
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: